7 November 2008

Forking parallel subants from Ant

Ever wanted to do something in parallel in your Ant build file? Well there is the Parallel-Task, which should do the trick, shouldn't it? Unfortunately the Ant Manual says that

The primary use case for <parallel> is to run external programs such as an application server, and the JUnit or TestNG test suites at the same time.

And that's what all the examples are about - to start Tomcat in a separate thread. In my case I needed to fork several <subant>-calls, i.e. to fork several Java virtual machines with the same properties as the original one and wait till all of them were finished.

To start an independent Ant run, which means to fork a sibling Java process with the same Ant properties, you have to use the <java> task, which allows forking with the fork attribute:
<parallel>
<java classname="org.apache.tools.ant.Main"
fork="true" clonevm="true" dir="${basedir}"
resultproperty="sub_res_1" output="sub_1.log">
<arg value="-buildfile" />
<arg value="${basedir}\sub_1.xml" />
<arg value="target" />
<!-- add jvmarg sysproperty as needed -->
</java>
<java classname="org.apache.tools.ant.Main"
fork="true" clonevm="true" dir="${basedir}"
resultproperty="sub_res_2" output="sub_2.log">
<arg value="-buildfile" />
<arg value="${basedir}\sub_2.xml" />
</java>
<java classname="org.apache.tools.ant.Main"
fork="true" clonevm="true" dir="${basedir}"
resultproperty="sub_res_3" output="sub_3.log">
<arg value="-buildfile" />
<arg value="${basedir}\sub_3.xml" />
</java>
</parallel>
The output of these parallel Ant calls is collected and printed to the screen:
<concat>
<fileset dir="." id="spawn.logs">
<include name="sub_1.log" />
<include name="sub_2.log" />
<include name="sub_3.log" />
</fileset>
</concat>
<delete>
<fileset refid="spawn.logs" />
</delete>
Finally we have to determine if one of the <subant>s failed:
<condition property="spawn.error">
<isfailure code="${sub_res_1}" />
</condition>
<condition property="spawn.error">
<isfailure code="${sub_res_2}" />
</condition>
<condition property="spawn.error">
<isfailure code="${sub_res_3}" />
</condition>
<fail if="spawn.error" message="sub failed" />
That's it. Note that Ant versions below 1.7 do not support the <isfailure/> condition and you have to use
<not>
<equals arg1="0" arg2="${sub_res_1}" />
</not>
(Download Ant scripts.)

2 comments:

Anonymous said...

Hi Peter

Thanks for the pointers - I'm making use of this technique in an ant script I'm currently working on.

One thing, I couldn't get the clonevm="true" to work. I was getting "Error: Could not find or load main class Files" errors.

I managed to work around this by using:

classname="org.apache.tools.ant.launch.Launcher"
classpath="${env.ANT_HOME}\lib\ant-launcher.jar"

and not using clonevm="true". Not sure if it's the best workaround, but it seems to do the trick.

I'm just mentioning it, in case someone else has the same issue.

Thanks
Guy

Peter Kofler said...

Guy,
thank you for letting me know.

I guess this is due to classpath changes in Ant. I created these code long time ago, using an old version of Ant. Your solution is valid.

Cheers