Ant Patterns

Ant is a common build tool with very extensive documentation. It is still widely used and I recommend this tool for automating file system heavy steps. You can even call Ant tasks directly in Groovy using the AntBuilder which brings you an extremely powerful and well documented API without any hustle.

Here I have gathered a few Ant Patterns that you should be aware of when you build an Ant script.

Presence Validator for Properties

The properties can be loaded from a properties file, xml file or set directly using the property task. A property can only be defined once and cannot be overridden as it has the semantics of a final constant.

To ensure that a property is set, you can use the fail task to abort unless the property has been defined beforehand.

<fail unless="property.key"/>
<echo message="property.key = ${property.key}"/>

However, failing without a message makes the error very hard to come by. Therefore, the message attribute should be used to indicate the failure reason.

<fail message=""the property property.key is not defined" unless="property.key"/>
<echo message="property.key = ${property.key}"/>

If there are many property checks in a build file, these two lines have to be duplicated for every property to check. Much better is the use of a macrodef task which is basically a custom task with private properties named attributes.

<!-- definition of validate-presence-of-property task -->
<macrodef name="validate-presence-of-property">
       <attribute name="propertyKey"/>
       <sequential>
               <fail message="the property @{propertyKey} is not defined" unless="@{propertyKey}"/>
               <echo message="@{propertyKey} = ${@{propertyKey}}"/>
       </sequential>
</macrodef>

<!-- usage of the macrodef -->
<validate-presence-of-property propertyKey="my.property" />

The @{propertyKey} resolves to the given attribute value, in the example above to my.property. If you need the value of my.property, you have to evaluate it using the standard evaluation mechanism ${@{propertyKey}}.

The presented solution has been adapted from this presentation. This pattern can be reused for validate the presence of files, folders and the like using arbitrary conditions.

Default Values for Properties

A very common use case is to define a property with a default value and let the user override that value with a property file. As a property can only be initialized once, we can use this to implement this very simple by importing the properties from a file and try to set the properties to their default values afterwards. Setting the default value will fail (without an error) in case the property already exists.

<!-- include properties from file -->
<property file="my.properties"/>

<!-- set defaults which are only set if the properties do not already exist -->
<property name="some.property" value="default-value"/>

Updating/Removing Itself

Ant can delete/modify its own build file. This can be useful if you want to update the build file with an update target. A use case for this is to generate the build.xml from another xml file via xslt as I have implemented it in this project. In that case, I generate the build.xml from the manifest.xml of a Joomla extension.

<!-- updating using a stylesheet -->
<xslt in="manifest.xml" out="build.xml" style="some-stylesheet.xsl" force="true"/>
<!-- removing -->
<delete file="build.xml"/>

For this pattern, I was inspired by this blog post which shows that ant can delete itself.

Executing Platform Independent

Executing commands on the command line with ant can be problematic with the exec task. Under linux or Mac OSX you can execute the command directly while you need to prepend cmd /c under Windows as described here. Because the scripts should be platform independent, you can make use of the osfamily attribute of the exec task which executes the task only if the operating system matches the given osfamily.

<exec executable="cmd" osfamily="windows">
     <arg value="/c"/>
     <arg value="ruby"/>
     <arg value="-v"/>
</exec>
<exec executable="ruby" osfamily="unix">
     <arg value="-v"/>
</exec>

However, I you need to call several commands on the command line, you get code duplication. Macrodef to the rescue.

<!-- macro for executing a command plattform independent -->
<macrodef name="execute">
       <attribute name="executable"/>
       <element name="args" implicit="yes"/>
       <sequential>
                <exec osfamily="windows" executable="cmd">
                       <arg value="/c"/>
                       <arg value="@{executable}"/>
                       <args/>
                </exec>
                <exec osfamily="unix" executable="@{executable}">
                       <args/>
               </exec>
      </sequential>
</macrodef>

<!-- usage -->
<execute executable="ruby">
    <arg value="-v"/>
</execute>

Simple Usage Information

Most tools provide usage information when called without parameter. As we want to write self-documenting code, you can use the ant -p or ant -projecthelp commands which print all targets which have the description attribute set on the console. We can let Ant call itself via command line as shown in the following listing:

<project name="test" default="usage">
     <target name="usage" description="usage information">
        <exec executable="cmd">
            <arg value="/c"/>
            <arg value="ant"/>
            <arg value="-p"/>
        </exec>
    </target>
</project>

It is important to set the default attribute of the project to usage to call the usage target when no targets are specified.

You can combine the pattern Executing Platform Independet with this to get the following ant script:

<project name="test" default="usage">
     <target name="usage" description="usage information">
        <execute executable="ant">
            <arg value="-p"/>
        </execute>
    </target>
</project>

More about printing usage information in Ant can be found here.

Collection of Links for Other Useful Ant Tipps

Advertisements

One thought on “Ant Patterns

  1. Pingback: Ant Patterns – Continued « Simon Harrer's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s