Antix - <if/> and <forEach/> tasks for Ant

A long time ago I was involved with the Ant project and part of the philosophy was that Ant was not executable xml. So this meant that <if/> and <for/> tasks were out. Implementing the equivalent functionality involved complex sets of tasks and properties to be defined.

Fast-forward many years and Ant still does not provide this functionality out of the box. I rarely use ant these days opting instead to use Buildr or Rake depending on the project. But when I do use Ant I find myself re-implementing the same set of tasks - usually <if/> and <forEach/>. A while ago I consolidated all the different implementations under one source tree, Antix on Github.

Someone asked me how to use them so here is a basic description...


The simplest way to install Antix is to download the jar and add a taskdef to your build file.


<taskdef resource="org/realityforge/antix/antlib.xml">
  <classpath path="path/to/antix-1.0.0.jar"/>
  This task library can also be put in the
  ${ANT_HOME\}/lib directory, in such case this
  classpath node is not needed


The <if/> task

The <if/> is simple in that it has two child elements; conditions and sequential. The sequential has a a sequential list of tasks to execute if all of the conditions evaluate to true.


    <equals arg1="${}" arg2="true"/>
    <echo message="The property is set to true!"/>

The <forEach/> task

The <forEach/> takes a list of white space separated values and invokes a nested sequential element for each value, setting a specific parameter to the value during the invocation.


<forEach property="day" list="Mon Tue Wed Thu Fri">
    <echo message="Day = @{day}"/>

will print ..

[echo] Day = Mon
[echo] Day = Tue
[echo] Day = Wed
[echo] Day = Thu
[echo] Day = Fri

The <property-copy/> task

Ant properties are not allowed to be nested so you need to do some hackery to get get nested properties to work properly. The Antix library implements the approach recommended by the FAQ by implementing a property-copy task that will evaluate the property two layers deep and copy the value to another property.

This is typically used when you are selecting from a variety of different build configuration settings. i.e. Should you generate the EJB or web service generator.


<property name="ejb.service.generator"
<property name="ws.service.generator"

<property name="generator-type" value="ws"/>
<property-copy name="service.generator"

will print ..


The <dbgmsg/> task

The dbgmsg task will only print the specified message if the property named "debug" is set to a value. This is mostly used when debugging builds.

<dbgmsg message="My debug message"/>

The <start-phase/> and <end-phase/> tasks

The start-phase and end-phase tasks are used to print the time it takes for various build phases. Each phase has a name and a timer starts when start-phase is executed and is stopped when end-phase executes. Both tasks echo a message at warning level (if the property named timing.check is set) or at the verbose level.


<start-phase phase="integration-tests"/>
<end-phase phase="integration-tests"/>

will print ..

[echo] Starting phase 'integration-tests' at 18:15:39
[echo] Completing phase 'integration-tests' at 18:15:39 (Duration = 48ms)

The <toAscii/> task

Copy a file while replacing non-ascii characters with the character '?'.


<toAscii src="SomeNonAsciiFile.txt" dest="SomeAsciiFile.txt"/>

The <selectRegex/> task

The selectRegex task attempts to extract a value from a string based on a regular expression and assign that value to a property. Often I use this to extract out results from tests to do further processing.

<selectRegex property="that"
             pattern="string (.*) will"
             value="My string that will attempt to be matched."/>

will print ..

[echo] that=that

The <timer/> task

The timer task can either be a "start" or "stop" timer. A "start" timer sets a property to now indicating a start time. A "stop" timer sets a property to now that indicates an end time and it calculates the duration from the corresponding start time. Mostly this task is not not directly used but instead used by the start-phase and stop-phase tasks described above.

<timer property="mytimer" stop="false"/>
<echo>Start: ${mytimer.start}</echo>
<timer property="mytimer" stop="true"/>
<echo>Stop: ${mytimer.end}</echo>
<echo>Duration: ${mytimer.duration}</echo>

will print ..

[echo] Start: 1312706159383
[echo] Stop: 1312706159397
[echo] Duration: 14