Running ANT with Different JDKs via EXEC (APT/WSGEN workaround) - ant

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!

Related

Bug in ant 1.65 effects build which is running in ant 1.7+

Updated question
I have (since realizing that i have two versions of Ant in my classpath, as reported by my builder) come to the finding that my core issue is simply that an older version of Ant exists in my classpath.
To clarify, the following is reported at the beggining of my build process :
WARNING: multiple versions of ant detected in path for junit
WARNING: multiple versions of ant detected in path for junit
[junit] jar:file:/usr/share/ant/lib/ant.jar!/org/apache/tools/ant/Project.class
[junit] and jar:file:/home/vagrant/Development/..../lib/ant-1.6.5.jar!/org/apache/tools/ant/Project.class
My new (simplified) question, then, is :
What is the best way to force both ant and junit to utilize the correct ant version when running my junit tests, given that some other versions of ant might be transient dependencies in my classpath due to ivy ?
Original question
I notice that this version of ANT fixes a junitvm but :
http://svn.apache.org/repos/asf/ant/core/trunk/WHATSNEW
This bug involves the fact that the junitvmwatcher files created by ANT are not closed.
However, In my recent ant build, the jvmwatcher bug persists.
Any ideas why this bug might exist in an up to date ant installation ?
The correct version of ANT should be the one running the build. So, what I often do is create a global exclusion for ANT in my ivy.xml file as follows:
..
..
<!-- Global exclusions -->
<exclude org="org.apache.ant"/>
</dependencies>

#Grab annotation fails under Ant

I'm using a #Grab annotation to grab the definition of an Html parser I can give to the XMLSlurper (I think it's the tagsoup parser) and all is good when I run my script from the cmd line. If I invoke the same script from Ant I get an Ivy NoClassDefFound error. I think it may berelated to having Ivy in Antlib. Is there another way to parse Html without customizing the slurper via #Grab?
This:
#Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='1.2' )
doc = new XmlSlurper(new org.ccil.cowan.tagsoup.Parser()).parse(confluenceWebPageInputStream)
Works just fine from the command line but when I run it from an Ant build target:
<target name="update-wiki-chart">
<echo message="Will update chart for version ${version}"/>
<java dir="${basedir}" classname="groovy.lang.GroovyShell">
<arg value="ParseWikiPage.groovy"/>
<classpath refid="groovylib"/>
</java>
</target>
where groovyLib is a path ref pointing to the Groovy-1.8.6 jar downloaded from our internal Nexus repo, I get the NoClassDefFound error. I'm thinking this is probably due to having Ivy installed in Antlib causing the class loader to find it in two places. I just thought of something while writing this post. I can probably run java in forked mode or do something to cause it to not see/share Ant's classpath.It's been a few years since I've wrestled w/ Ant and class loader issues. My project is a little delinquent due to the bug and I'm looking for a quick/easy fix.
I just tried running my groovy on the cmd line via the "java" cmd and loading groovy-all jar in the class path and I realized that I get the NoClassDefFound error there as well. It has nothing to do with collisions with Ivy under Antlib. Rather, I am missing Ivy altogether. I had assumed it was included in Groovy-all.jar. I just need a clever way of passing Ivy from AntLib into my java task to get this all up and running.
Sounds like you're missing one or more jars from the classpath. I'd suggest digging around the classpathref you've labelled "groovylib".
A less error prone way to launch groovy from within ANT using the groovy ANT task.
Here's a similar example to your use-case:
Parse HTML using with an Ant Script
My example uses ivy directly to manage all build dependencies. The Grab annotations are still supported but obviously these would only manage the dependencies of the groovy script.

compile jdk via ant

I want to compile jdk files in order to include debug infromation.
I'd like to use ant, because it's included in my NetBeans environement, so i've done the following:
unzipped /src.zip in a tmp directory
created a very simple build.xml file (one default target, one taks) in my tmp directory:
<?xml version="1.0" encoding="UTF-8"?>
<project name="CompileJDK" default="default" basedir=".">
<target name="default">
<javac srcdir="."
destdir="jdkwd"
debug="on"
/>
</target>
</project>
created a jdkwd directory
launched ant without parameters (just >log.txt)
This leads to 100 compilation errors such as:
[javac] C:\jdkdebug\java\awt\Window.java:196: cannot find symbol
[javac] symbol : class IdentityArrayList
[javac] location: class java.awt.Window
[javac] private static final IdentityArrayList<Window> allWindows = new IdentityArrayList<Window>();
I have just one JDK installed on my machine, so i don't know why it does not resolve all this references.
UPDATE:
The majority of these unresolved references belongs to the package:
sun.awt.util
The question now is corrected to: where are the missing jdk files?
Building the JDK itself is a complex process and is not achievable by a simple javac call wrapped inside an ant project.
You should look at the OpenJDK Build README to get instructions on how to build for your platform.
http://www.oracle.com/technetwork/java/faq-141681.html
A14. Where can I get the Java programming language source code?
Java Software has two separate bundles of source code that you can obtain at no charge:
The Java 2 SDK, Standard Edition itself contains a file called src.zip that contains the source code for the public classes in the java package. Because this does not contain sun.* classes, you cannot do a complete build of the Java technology from these source files. These source files are for your information, to supplement the documentation, so you can see how Java technology works.
The full source code release is available from us by going to the Community Source Code Licensing web site..
The community source code link is incorrect: it's now http://download.java.net/openjdk/jdk7/
Try adding a classpath to your javac call.
<classpath path="/PATH/to/missing_class/" />
Also, try running ant with the -d and -v options. It's a lot of output but will show you where its searching for classes.
According to this post (from 2007), you should include rt.jar and tools.jar on your classpath to compile the JRE sources.
However, I tried that, and it doesn't work for me (100 errors).
There are more elaborate and older (2004) instructions in christhielen's post in the Java bug requesting debug symbols.
If you use Gentoo compiling OpenJDK would be as simple as running emerge dev-java/icedtea.
There is a debug use flag that would switch off all optimizations, I haven't tried it myself but chances are that this is what you want. If it's not - then it should not be a big deal to change build scripts, but would require you to learn a little bit of portage.

Use javac fork attribute with IBM JDK

I have a large ant build that I'm working on, that is currently running out of memory. One ways I've read that can help mitigate this problem is to use javac fork="true"
to run javac in a separate jvm.
My problem is that I need to compile the project with the IBM JDK (this is not the JDK referenced by JAVA_HOME, and I would prefer it not to be). I tried setting the executable attribute of Ant's javac, to the path to IBM's javac but no joy (the project still won't compile). Ant's docs for the executable attribute state:
Complete path to the javac executable to use in case of fork="yes". Defaults to the compiler of the Java version that is currently running Ant. Ignored if fork="no".
Since Ant 1.6 this attribute can also be used to specify the path to the executable when using jikes, jvc, gcj or sj.
Does anyone have any ideas?
Thanks -
I have used a single ANT build to compile a set of classes in 1.7 and another set of classes in 1.6 and it works fine.
Both 1.7 and 1.6 are IBM JDK's. JAVA_HOME points to the 1.7 JDK & ANT version is 1.9
Below are the ANT tags used
1.7 compilation using JAVA_HOME
`<javac srcdir="${LOCAL_SOURCE_PATH}/temp" debug="true" deprecation="false" fork="true" memoryInitialSize="1024M" memoryMaximumSize="1500M" verbose="${verbose}">`
1.6 compilation using javac tags executable attribute
`<javac srcdir="${LOCAL_SOURCE_PATH}/branchtmp" fork="true" executable="${JAVA6_HOME}/bin/javac" compiler="javac1.6" debug="true" deprecation="false"memoryInitialSize="1024M" verbose="${verbose}">`
Hope this helps.

How do I compile my Delphi project on the command line?

Has anyone ever managed to compile their Delphi 6 & 7 (NOT any Delphi > 7 ) projects using the command line?
All the instructions I see are not very clear on what exactly needs to go where!
Am looking for step-by-step kind of instructions.
Answers should be limited to Delphi 6 & 7: I understand Delphi 2006 and > uses MSBuild which is far much easier.
Links are also high appreciated.
Gath
This is not difficult to do. I have a standard Delphi 5 install on my machine here, and when I open a command prompt, navigate to the $(DELPHI)\Demos\Threads directory and enter dcc32.exe thrddemo.dpr the application is built on the command line.
For your own project you may need to add some switches to include file directories, output directories, defines or similar things. Running dcc32.exe without parameters gives a list of switches and parameters. It is all described in the documentation, as well.
For repeatability you should create a batch file or a regular Makefile.
Note that both the project cfg file and the common dcc32.cfg in the Delphi directory contain important settings. For some information about how they affect the build see for example this link on Delphi Wikia.
For build automation, I use Apache Ant, which is a software tool for automating software build processes. I use it for all my projects, from Delphi 6 to Delphi 2009, and Free Pascal.
Things it can do "out of the box" include MD5 checksum generation, ZIP file creation, text search/replace (useful for copyright header generation), execution of SQL statements, XSLT processing.
For example, to compile all projects with Delphi 6, this is (a part of) the script:
<target name="compile_d6">
<!-- Compile with Delphi 6 -->
<apply executable="${d6}\Bin\dcc32" failonerror="true" output="build-d6.log" >
<!-- rebuild quiet -->
<arg value="-B"/>
<arg value="-Q"/>
<!-- file paths -->
<arg value="-I${source};${indy10}/Lib/System"/>
<arg value="-O${source};${indy10}/D6;${jcl}/d6"/>
<arg value="-U${source};${indy10}/D6;${jcl}/d6"/>
<!-- all *.dpr files in current directory -->
<fileset dir=".">
<patternset><include name="*.dpr"/></patternset>
</fileset>
</apply>
</target>
Free open source CI (Continous Integration) servers like Hudson/Jenkins support Apache Ant build scripts out of the box, which means that you can have them build the project automatically whenever you checked in a change in the source repository.
You can build everything using this command line:
"C:\Program Files\Borland\Delphi7\Bin\DCC32.exe" -Q -B your-project.dpr
Put this line in a .bat file so you don't need to type it always. Take a look at the command line options running this:
"C:\Program Files\Borland\Delphi7\Bin\DCC32.exe" -h
BTW: -Q is quiet compile and -B will rebuild everything. If you want a quickier compilation don't use -B.
It will use all the options in your-project.cfg file. I've found this dof2cfg executable very useful. With it I can edit the .dof text file and propagate the change to the command line and IDE. No need to edit it for each project.
Important warning for long paths: Delphi 7 command line compiler has a very weird bug. If your path is very long, it will fail with an inscrutable error: a access violation without any meaningful information. If it fails without any reasonable motive, try to reduce the maximum path size and the filename size. Put the project in the root folder usually solve it.
FinalBuilder makes it very easy. Give it a try.
I would suggest combination of NAnt and dcc32, but there's also Juancarlo AƱez's "WAnt - A Pascal-Friendly Build Tool". I've been using modified version of the 1.x instead of the 2.x alpha. Since it's open source, I could extend the code to output log in XML with the same format as NAnt, so I can integrate it with CruiseControl.NET.
For later versions of Delphi, this should be changed to:
"C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\bin\DCC32.exe" -h

Resources