I hope this finds an ANT build master who has some experience running ANT with different JDK versions concurrently!
I have inherited a fairly old set of libraries & ANT build files that currently work under Java 6/7. In attempting to modernize this system to utilize Java 8 or any future JDK upgrades, I've been working on a means of refactoring these build files to work with Java 8.
The current build system has the following characteristic:
- Ant 1.7.0 & Java 1.7.0_55
- Usage of the deprecated APT tool via ANT APT tasks
- Usage of wsgen (jaxws related libraries) via ANT WSGEN tasks (e.g. classname="com.sun.tools.ws.ant.WsGen")
- Effectively the build files mix code generation and compilation to create our desired output.
The APT tool was deprecated in Java 1.7, one needs to move to using JAVAC in 1.8 and beyond!
In attempting to replace the usages of APT with JAVAC I found that not only is the tool removed but so are the numerous packages/class/interfaces associated with the APT tool. I've seen this topic discussed here in multiple Q&A's, however most of the proposed solutions were non-starters.
The one suggestion I did find which had promise was to separate the code generation (usages of APT) from the compilation (JAVAC) to reside in different ANT build files. After separating the build file into a "main" build file and a "codegen" build file, this seems to work just fine.
Example:
<property name="apt.output.file" value="codegen.apt.${java.version}.log" /> <!-- A different log file will exist when I run the "main" ant build file using Java 7 vs 8 for comparison purposes. -->
<exec executable="${exec.ant.cmd}" osfamily="unix" failonerror="true" output="${apt.output.file}" >
<arg line="${ant.apt.cmd.line.options}" />
<env key="JAVA_HOME" value="${exec.java.home}"/> <!-- Java 1.7 -->
<env key="ANT_HOME" value="${ant.home.path}"/> <!-- Ant 1.7.0 -->
<env key="PATH" value="${exec.java.home}/bin" /> <!-- Reset PATH to be certain -->
<!-- The various properties are defined in the build file, and would be fairly unsurprising. -->
</exec>
Now that the "codegen" pieces were self-contained, this allows me to:
- Run the "main" ANT build file with Java 8.
- For the "codegen" pieces, use an EXEC as per above to specify the ANT/JAVA version to do the *.java code generation.
The good news, for those of you in similar situations, is that this seems to work fine for APT. I changed my JAVA_HOME & PATH to point at Java 8, and it seems to work fine (assuming APT is limited to just *.java source generation, no compilation).
Now here's the rub and what I hope someone might have some expertise on. I found that the APT tool was also used to create an ANT build file (wsgen-build.xml). Said file defines the WSGEN task and then uses it a number of times:
<project name="some-wsgen-fragment" default="wsgenall">
<target name="wsgenall">
<taskdef name="wsgen" classname="com.sun.tools.ws.ant.WsGen" >
<classpath>
<pathelement path="${jaxws.home}/lib/jaxws-tools.jar" />
</classpath>
</taskdef>
<echo message="wsgen for SomeWebServiceImpl" />
<wsgen destdir="${classes.server.gen.dir}" sourcedestdir="${something.gen.dir}" sei="com.something.or.other.SomeWebServiceImpl">
<classpath>
<pathelement path="${classes.server.gen.dir}" />
<path refid="classpath.all" />
</classpath>
</wsgen>
... Repeated usages of wsgen exactly as above, with different classes
Similar to the APT tasks, the "main" build file invokes an EXEC to run an "old" ANT with appropriate parameters to invoke this generated build file under the same environment it works under.
Under Java 1.7 & ANT 1.7.0, this works fine.
When I change Java to 1.8, I change the JDK for JAVA_HOME and the "java.target" to 1.8, I get the following mysterious error:
[echo] wsgen for SomeWebServiceImpl
Finding class com.something.or.other.SomeWebServiceImpl
[antcall] Exiting codegen-buildfile.xml.
BUILD FAILED
codegen-buildfile.xml:58: The following error occurred while executing this line:
wsgen-build.xml:9: Requires JDK 5.0 or later. Please download it from http://java.sun.com/j2se/1.5/
at org.apache.tools.ant.ProjectHelper.addLocationToBuildException(ProjectHelper.java:541)
at org.apache.tools.ant.taskdefs.Ant.execute(Ant.java:418)
at org.apache.tools.ant.taskdefs.CallTarget.execute(CallTarget.java:105)
The issue to me "feels" like the following:
Why does an ANT running Java 8 interfere with an ANT running Java 7 and how might this be remedied?
Some Q&A that has lead me to this question:
Have you tried using ANT -v -d for figuring this out? Yes, I'm generating a log file based on the ${java.version} used for the "main" build file. I generate a file for Java 8 and one for Java 7, and diff the two. There are no environment/property/settings difference between the two (Java 7 vs Java 8) until I hit the error referenced above. There are minor variations in the order classes get loaded, but that is it.
When you change between Java 7 and Java 8, are you making sure that change isn't affecting your EXEC tasks? The diff of the log files as referenced above verifies this, but when I invoke EXEC the properties in the first example above are effectively hard-coded with the ENV tags being used to ensure the environment variables are also the same regardless of the Java version used in the "main" build file.
Surely there is some environment variable difference causing this issue? I'm logging a number of variables attempting to diagnose the issue. So far the "codegen" ANT build file has everything the same, regardless if the "main" ANT is run in either Java 7 or 8: ANT_HOME, JAVA_HOME, PATH, ant.version, java.version, java.vm.version, java.class.path, java.ext.dirs. The verbose ANT log bears this out as well, no property/environment/classpath issues between them.
I can't tell from your EXEC example, but are you using full paths? Perhaps the PATH variable is an issue? While not shown, I'm specifying the full paths to ANT and JAVA since I need to specify the ANT/JDK exactly to ensure I'm running the specific versions desired.
How are you invoking the generated build file with the wsgen tasks (wsgen-build.xml) that is having the problem? The "main" build file invokes an EXEC on the "codegen" build file, as per the example above. The only difference is a different build target, one target is used for APT, another is used for the WSGEN piece. In said WSGEN target within the "codegen" build file, the wsgen-build.xml is currently imported and the target invoked via ANTCALL. I've also tried using just the ANT task, both have the same result.
Can you run your EXEC commands from the command shell? Yes, if I run the step on the command line, it either works or not depending on if Java 7 or 8 is set as JAVA_HOME and which appears first on the PATH. For example, if I update JAVA_HOME to be Java 8, I get the following error: java.lang.NoClassDefFoundError: com/sun/mirror/apt/AnnotationProcessorFactory. This is correct since that class no longer exists in Java 8 (but does exist in 7).
Have you tried using a script or batch file instead of using EXEC on the ANT executable? Yes, same error resulted. I tried this with a simple .bat file that set the requisite environment variables and invoked the command from the shell.
What OS are you on, have you tried a different machine/OS to see if this is environment specific? I have a Win7 & a RHEL 7 environment, this problem happens in both and I'm using the osfamily attribute to conditional-ize the EXEC command per platform.
Ant 1.7.0 is pretty old, have you tried an updated Ant? Same issue occurs using Ant 1.9.4 as well. The 1.9.4 Ant only runs for the "main" build file, the "codegen" build file will use 1.7.0 since I'm explicitly setting that via the EXEC tasks. I did try 1.9.4 even for the "codegen" ant build file, but it made no difference in the error.
If you managed to read thru this in its entirety, my hat off to you sir or madam! Thank you for any insight/advice you may have!