We have a project (entity classes for use with Hibernate) that requires post-compilation byte-enhancement step.
If interested (skip if not): See https://docs.jboss.org/hibernate/orm/5.0/topical/html/bytecode/BytecodeEnhancement.html. This modifies *.class files produced by the Java compiler. In pure Gradle build we do this by applying the ‘org.hibernate.orm’ plugin.
We had this working with Gradle 5.6.4 and older Eclipse by doing something that amounts to unpleasant hacking (will describe that below). That hack no longer works with Gradle 6.1.1 and Eclipse 2019-12 - not sure of the exact cause but the symptom is clear. I’d like to take the opportunity to do this right … but I don’t know how to.
As it is, Buildship imports this project as normal and leaves Eclipse’s Java builder in (after Gradle builder). Eclipse ends up not configured to know anything about bytecode enhancement. The result is that the code compiles with no errors but the product does not work as the code isn’t enhanced (required).
Note that we keep Eclipse output folders (bin/…) separate from Gradle’s (build/…). Sharing them does not work us (does it for anyone) due to different compiler/compilation and bytecode styles.
Some people who use IntelliJ are not reporting issues. My guess is that IntelliJ gives up on compiling the project itself and relies entirely on Gradle to do so (but IntelliJ has other issues for us). How do we set our project up properly so that it “just works”?
Please help!
Our past hack, for those interested, others please DO NOT READ
You’ve been warned. This is downright nasty. We had to:
-
Disable Eclipse’s own Java builder (for some/unknown reason Eclipse-produced bytecode is not enhanceable) on this project by creating a
.externalToolBuilders/org.eclipse.jdt.core.javabuilder.launch
file with the following content:<?xml version="1.0" encoding="UTF-8" standalone="no"?> <launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType"> <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="org.eclipse.jdt.core.javabuilder"/> <mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/> <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/> </launchConfiguration>
-
Create an ANT (!) build file that can launch Gradle for us so we can use Eclipse’s ant builders.
<?xml version = "1.0"?> <project name="gradle-runner-ant" default="run-gradle" basedir="."> <description> Runs a Gradle task specified via the GRADLE_TASK system property. This allows Eclipse Ant builders to be conveniently used to run Gradle tasks. </description> <target name = "run-gradle"> <echo message="Running Gradle: ${GRADLE_TASK}"/> <java fork="true" failonerror="yes" classname="org.gradle.wrapper.GradleWrapperMain" classpath="gradle/wrapper/gradle-wrapper.jar"> <arg line="${GRADLE_TASK}"/> </java> </target> </project>
-
Create our Ant external tool builder:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType"> <booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/> <booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/> <booleanAttribute key="org.eclipse.ant.uiSET_INPUTHANDLER" value="false"/> <stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${project}"/> <stringAttribute key="org.eclipse.debug.ui.ATTR_CAPTURE_IN_FILE" value="${project_loc:/project-name/bin/enhanceBytecode.log}"/> <booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/> <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/> <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="product"/> <mapAttribute key="org.eclipse.ui.externaltools.ATTR_ANT_PROPERTIES"> <mapEntry key="GRADLE_TASK" value=":project-name:compileForEclipse"/> </mapAttribute> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/project-name/.classpath&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/project-name/.externalToolBuilders&quot; type=&quot;2&quot;/&gt;&#10;&lt;item path=&quot;/project-name/.project&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/project-name/build.gradle&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/project-name/src&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/product/gradle/gradle-runner-ant.xml}"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/> <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/product}"/> </launchConfiguration>
-
Register that external tool builder in our *.gradle file:
eclipse { project { String buildCommandName = 'org.eclipse.ui.externaltools.ExternalToolBuilder'; String launchHandle = '<project>/.externalToolBuilders/Gradle Java compiler with Hibernate bytecode enhancement.launch'; buildCommand buildCommandName, LaunchConfigHandle: launchHandle file { whenMerged { p -> def commands = p.buildCommands; // For some reason Eclipse-produced Java bytecode cannot be enhanced. // We have to use our official compiler to yield correct results. commands.removeAll { it.name == 'org.eclipse.jdt.core.javabuilder' } int index = commands.findIndexOf { (it.name == buildCommandName) && (it.arguments.LaunchConfigHandle == launchHandle) } def wsCommand = commands.remove(index); commands.add(0, wsCommand); // Do we have more than one builder of ours? It has been observed! // Remove duplicates. int duplicateIndex; while ((duplicateIndex = commands.lastIndexOf { (it.name == buildCommandName) && (it.arguments.LaunchConfigHandle == launchHandle) }) > 0) { commands.remove(duplicateIndex); } } } } }
-
Create our own compileForEclipse task in Gradle that depends on Gradle’s standard “compileJava”, then copies the results into Eclipse’s output folders.