antcontrib foreach executed in parallel does not raise errors - ant

I have the following ant script that I can't seem to find a way to make fail when parallel is set to true for antcontrib's foreach task. Any ideas?
<project name="asdf" >
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="../lib/ant/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>
<target name="build">
<foreach target="exex-subant" param="foreach.dir" parallel="true" maxthreads="4" inheritall="true" list="1,2,3">
<param name="target" value="build" />
</foreach>
</target>
<target name="exex-subant">
<fail>test</fail>
</target>
</project>

This occurs because when executed in parallel, <foreach> uses the <parallel> task, but does not set the "failonany" property, or give any way to say that the task should fail if any iteration failed.
Fortunately, there is a relatively easy workaround, which is to use <for> instead of <foreach>. In your example, that would look like this:
<target name="build">
<for param="foreach.dir" parallel="true" list="1,2,3">
<sequential>
<antcall target="exex-subant" inheritall="true">
<param name="target" value="build" />
<param name="foreach.dir" value="#{foreach.dir}" />
</antcall>
</sequential>
</for>
</target>
Note that you have to explicitly pass in the foreach.dir property, which will then be accessible in the exex-subant target as ${foreach.dir}.
This will execute all iterations in parallel, but the script will fail if any one of them fails (it will not execute anything beyond the for task).
Note that in order to use the for task, you'll need ant 1.6 or higher, and will need to change your taskdef to:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="../lib/ant/ant-contrib-1.0b3.jar" />
</classpath>
</taskdef>
If for some reason you need to support older versions of ant, then you would have to change the exex-subant target slightly, so that it changed something when it failed. For example, you could wrap the current logic in exex-subant within a try/catch, and in the catch block it could create a file. Then after the foreach terminates you can check to see if that file exists, and fail the build if it does. That way, if any execution of the foreach fails, the ant script will fail after the foreach finishes.
Note that you can't just set a property in exex-subant on failure, since the property won't propagate back to the foreach loop (which is why I suggested creating a file). But I'd strongly recommend just using the for task and requiring ant 1.6 or higher.

Related

Ant for script how to continue if any iteration fails

I writing an ant build file to check all translate files in a directory.
I want the ant for script to continue checking the rest files if an checking error in any file appear.
My ant task:
<taskdef name="validate" classname="ValidateTranslateFile">
<classpath>
<pathelement location="${ant-libs-dir}/TranslateFileUtilities.jar" />
<pathelement location="../web/WEB-INF/lib/commons-io-2.5.jar" />
<pathelement location="../web/WEB-INF/lib/commons-lang3-3.5.jar" />
</classpath>
</taskdef>
<for param="program">
<path>
<fileset dir="../web/WEB-INF" includes="*.txt" />
</path>
<sequential>
<validate targetFile="#{program}" checkLanguages="true" checkKeysOrder="true" />
</sequential>
</for>
</target>
the Result:
it checks the files till the first error appear, and then the BUILD FAILED.
Could any one help me with that?
The for task is not part of standard ANT. It is a 3rd party extension documented here:
http://ant-contrib.sourceforge.net/tasks/tasks/for.html
The documentation suggests using a "keepgoing" attribute to ignore errors, this might be the answer you're looking for.
If this is a custom ANT task you are writing, perhaps you should consider refactoring the code to operate on a fileset? This would enable you to call task as follows:
<validate checkLanguages="true" checkKeysOrder="true">
<fileset dir="../web/WEB-INF" includes="*.txt" />
</validate>
Simpler and more robust.

ant conditional execution (if-else statement in ant) and checking that a property isn't empty string

Currently I have this code in ant script:
<target name="-pre-compile" if="${log.disabled}">
<antcall target="remove-logs"/>
</target>
It's pretty self explanatory: if log.disable=true in properties file, then my remove-logs target will be called. Now I need to make minor changes to if/else. Pseudo-code:
<target name="-pre-compile">
if("${log.disabled}")
<antcall target="remove-logs"/>
else if("${log.disabled.level}" != "")
<antcall target="remove-logs-levels"/>
</target>
how can I write that in ant?
Also, note that all these if/them ant-contrib do not work for me:
<target name="test-xxx">
<echo level="info">testing</echo>
<if>
<equals arg1="${log.disabled}" arg2="true" />
<then>
<echo message="true" />
</then>
<else>
<echo message="false" />
</else>
</if>
</target>
get's me this output:
custom_rules.xml:73: if doesn't support the nested "equals" element.
PS. Once again, I ended up writing javascript instead of wasting time with ant itself. What a crappy make replacement...
Ant provide conditionals. Frankly using ant-contrib is easier. It provides an if task. See a more detailed answer in ant-contrib - if/then/else task

Loop through directory structure in ant

We want to loop through directory structure in ant without using foreach .
Is there any elegant way to do the same ?
The apply task can iterate over a set of directories or files
<target name="run-apply">
<apply executable="echo">
<dirset dir="src"/>
</apply>
</target>
I personally like the groovy ANT task
<target name="run-groovy">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
<dirset id="dirs" dir="src"/>
<groovy>
project.references.dirs.each {
ant.echo it
}
</groovy>
</target>
The installation of the task jar is easily automated:
<target name="install-groovy">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/groovy-all.jar" src="http://search.maven.org/remotecontent?filepath=org/codehaus/groovy/groovy-all/2.1.1/groovy-all-2.1.1.jar"/>
</target>
Finally if you're iterating thru other build files, the subant task is very useful:
<target name="run-subant">
<subant>
<fileset dir="src" includes="**/build.xml"/>
</subant>
</target>
Short answer: Not really. There are ways around this, but I prefer the ant-contrib <for/> task for clarity and simplicity. With the <local/> task, you can now localize values of variables. Before, you sometimes had to use ant-contrib's <var/> task to reset the values, so you could loop through them over and over.
<for param="directory">
<fileset dir="${some.dir}"/>
<sequential>
<local name="foo"/>
<local name="bar"/> <!-- Properties that may change with each iteration -->
<!-- Here be dragons -->
</sequential>
</for>
It's clean, simple, and easy to understand. The big issue many people have with Ant Contrib is that not everyone may have it installed in their $ANT_HOME/lib directory. Far enough. So, if you use ant-contrib, put it as part of your project.
I'll put the ant-contrib jar in ${basedir}/antlib/antcontrib and then put this in my program:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<fileset dir="${basedir}/antlib/antcontrib"/>
</classpath>
</taskdef>
Now, when someone checks out my project, they have ant-contrib already installed (since it's inside my project) and accessible (since I point my <taskdef> task at the location of ant-contrib.jar in my project).

How to pass multiple parameters to a target in Ant?

I have this dummy target:
<mkdir dir="${project.stage}/release
<war destfile="${project.stage}/release/sigma.war">
...
...
</war>
What I want to do is provide two parameters say "abc" & "xyz" which will replace the word release with the values of abc and xyz parameters respectively.
For the first parameter say abc="test", the code above will create a test directory and put the war inside it.Similarly for xyz="production" it will create a folder production and put the war file inside it.
I tried this by using
<antcall target="create.war">
<param name="test" value="${test.param.name}"/>
<param name="production" value="${prod.param.name}"/>
</antcall>
in the target which depends on the dummy target provided above.
Is this the right way to do this.I guess there must be some way to pass multiple parameters and then loop through the parameters one at a time.
unfortunately ant doesn't support iteration like for or foreach loops unless you are refering to files. There is however the ant contrib tasks which solve most if not all of your iteration problems.
You will have to install the .jar first by following the instructions here : http://ant-contrib.sourceforge.net/#install
This should take about 10 seconds. After you can simply use the foreach task to iterate through you custom list. As an example you can follow the below build.xml file :
<project name="test" default="build">
<!--Needed for antcontrib-->
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<target name="build">
<property name="test" value="value_1"/>
<property name="production" value="value_2"/>
<!--Iterate through every token and call target with parameter dir-->
<foreach list="${test},${production}" param="dir" target="create.war"/>
</target>
<target name="create.war">
<echo message="My path is : ${dir}"/>
</target>
</project>
Output :
build:
create.war:
[echo] My path is : value_1
create.war:
[echo] My path is : value_2
BUILD SUCCESSFUL
Total time: 0 seconds
I hope it helps :)
Second solution without using ant contrib. You could encapsulate all your logic into a macrodef and simply call it twice. In any case you would need to write the two parameters at some point in your build file. I don't think there is any way to iterate through properties without using external .jars or BSF languages.
<project name="test" default="build">
<!--Needed for antcontrib-->
<macrodef name="build.war">
<attribute name="dir"/>
<attribute name="target"/>
<sequential>
<antcall target="#{target}">
<param name="path" value="#{dir}"/>
</antcall>
</sequential>
</macrodef>
<target name="build">
<property name="test" value="value_1"/>
<property name="production" value="value_2"/>
<build.war dir="${test}" target="create.war"/>
<build.war dir="${production}" target="create.war"/>
</target>
<target name="create.war">
<echo message="My path is : ${path}"/>
</target>
</project>
I admit that I don't understand the question in detail. Is ${project.stage} the same as the xyz and abc parameters? And why are there two parameters xyz and abc mentioned, when only the word "release" should be replaced?
What I know is, that macrodef (docu) is something very versatile and that it might be of good use here:
<project name="Foo" default="create.wars">
<macrodef name="createwar">
<attribute name="stage" />
<sequential>
<echo message="mkdir dir=#{stage}/release " />
<echo message="war destfile=#{stage}/release/sigma.war" />
</sequential>
</macrodef>
<target name="create.wars">
<createwar stage="test" />
<createwar stage="production" />
</target>
</project>
The output will be:
create.wars:
[echo] mkdir dir=test/release
[echo] war destfile=test/release/sigma.war
[echo] mkdir dir=production/release
[echo] war destfile=production/release/sigma.war
Perhaps we can start from here and adapt this example as required.

subant on condition

I'd like to execute subant on some condition. something like:
<if>
<equals value="value1" property="${some.property">
<then>
<subant target="#{target}" failonerror="true" inheritall="false">
<buildpath refid="some-ref1" />
</subant>
</then>
<else>
<subant target="#{target}" failonerror="true" inheritall="false">
<buildpath refid="some-ref2" />
</subant>
</else>
</if>
But can't find a way to do it. Read the ant manual and googled, but no solution is found.
Thanks.
I believe the error may lie in your equals tag. Instead of using the 'value' and 'propery' attributes, try using 'arg1' and 'arg2', i.e.:
<equals arg1="value1" arg2="${some.property}">
Check out the examples in the ant-contrib doc: http://ant-contrib.sourceforge.net/tasks/tasks/if.html.
If the problem is that your 'if', 'then', and/or 'else' tags are not resolving properly, then you may be missing the ant-contrib libraries. Ant-contrib is not natively included with ant, but you can download it here: http://sourceforge.net/projects/ant-contrib/files/
Per the ant-contrib site (http://ant-contrib.sourceforge.net/), here's what you must do to install ant-contrib:
Option 1: Copy ant-contrib-0.3.jar to the lib directory of your Ant installation. If you want to use one of the tasks in your own project, add the lines
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
to your build file.
Option 2: Keep ant-contrib-0.3.jar in a separate location. You now have to tell Ant explicitly where to find it (say in /usr/share/java/lib):
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="/usr/share/java/lib/ant-contrib-0.3.jar"/>
</classpath>
</taskdef>
Please look up a <target if="${some.property}>. You may want another target with an unless.
If the property has to do with a file existing, see Ant task to check a file exists?. Even if this is not your main concern, I am sure you can get the idea from the accepted answer.
Do you mean calling another target
if so here is
<if>
<equals value="value1" property="${some.property">
<then>
<antcall target="#{target}" failonerror="true" inheritall="false">
</then>
<else>
<antcall target="#{target}" failonerror="true" inheritall="false">
</else>
</if>

Resources