A guide to Ant, an advanced build system

From Elvanör's Technical Wiki
Jump to: navigation, search

Ant is a very advanced and flexible build system. It seems really modular and able to accomplish a lot of tasks. However, it also seems complex to understand and learn.

General

Basic concepts

  • Ant build files are XML files.
  • These XML buildfiles (default name, like "Makefile" for GNU Make, is "build.xml") contain a list of targets. A target itself contains a list of tasks.
  • A task is anything Ant can do: it can launch a Java compilation, copy files to a directory, and LOTS of other stuff.

Ant and Gentoo

  • On Gentoo, Ant uses the ANT_TASKS environment variable. This will create a default system classpath. Be cautious about this classpath: it may include some versions of jars you don't want to use. To disable the default system classpath, use ANT_TASKS="none".

Properties

  • It seems the file build.properties is read by default by Ant, so it may not be a good file name choice. In particular in Grails if you have such a file, Grails script won't work correctly.
  • When setting a property on the command line via the -D flag, always give a value via "=". Else it seems the next parameter (which can be the next property set via -D) will be used as the value. It will of course cause troubles.

Java properties vs Ant properties

  • Be very careful about the properties you define. If you set a property using -D while calling Ant, you are setting an Ant property, not a Java system property. This may not be obvious at first since some Ant properties are initialized from Java properties if not set. This is for instance the case with ${user.home}.
  • To set a Java system property, use -Dmy.property='value' inside the ANT_OPTS variable (at least on Gentoo; strangely the name is not JAVA_OPTS). The value of ANT_OPTS will be passed to the java command loading Ant; it will not be an Ant parameter. If done this way, the Java system property will be correctly set for any Java code executed via Ant (and in the same JVM, of course).

Environment

  • You can access the environment variables if you use the following task (access the variables via the prefix, like ${env.PATH}):
<property environment="env" />
  • Note however that some variables, like HOSTNAME, are not in the environment but internally defined by Bash (Ant cannot access them then). In this case the exec task allows you to define a property based on the result of the command. This can be useful.

Data Structures

File Sets

  • When invoking a task involving file sets (delete for example), and you want to also include empty directories, don't forget to add the includeemptydirs="true" attribute.
  • Pay attention to nested elements in file sets. For example, if you want all *.xml files, but not *.cfg.xml files, use:
<fileset dir="src/" includes="**/*.xml" >
    <exclude name="**/*.gwt.xml"/>
</fileset>

The following would not work:

<fileset dir="src/" includes="**/*.xml" />
<fileset dir="src/" excludes="**/*.gwt.xml"/>
  • As a nested element, include cannot contain a comma separated list. This is only valid when used as an attribute.
  • If you want to consider recursively all files inside a given directory, you must use the "**" construct:
includes="path/to/dir/**/*"

Resource Collections

  • Resource collections were introduced in Ant 1.7.0. They are a modern version of filesets, for example, they allow filesets to be merged (via the union element), and probably lots of useful other stuff. Unfortunately, not many tasks can make use of them currently. In these cases, you have to resort to the old filesets.

Paths

  • Note that paths represent just this (paths). As a result you cannot copy files using a pathlike structure; it would recognize absolute paths as such and would copy the file to its original location (so would not do anything).

Targets

  • If you use the unless or if attribute, the targets the current task depends on (via the depends attribute) will be executed even if the condition is not met.

Tasks

  • The scp task does not seem to work with directories containing subdirectories (the scp operation will hang). Update: this was due to a bug introduced in jsch 0.1.30 (the library Ant uses for SSH communication). Update to jsch 0.1.32 and a recent ant-jsch.
  • The tar task does not preserve file permissions (the GNU tar program does). This is because Java does not offer an API to fetch the permissions on a file. The Ant tar task still offers a way of setting the permissions explicitly if you need to.
  • The copy task does not allow you to use nested file elements, even if you nest those in a resources elements. This is probably a bug. You can use filesets representing only one single file instead (this works with absolute paths).

Taskdef

  • Note that the classpath nested element for the taskdef element will not be used as the classpath when running the class; it will only be used as the classpath to find the specified class. If other classes need to be loaded later during the execution, the "initial" classpath (the one setup by Ant itself) will be used.

SvnAnt

  • This very interesting Subversion task (developed at Tigris) allows you to do operations based on file selectors. You can define a fileset with all modified files, for example.
  • On Gentoo, make sure you compile with the subversion package with the java USE flag in order to use this task properly.

Javac / Groovyc

  • When specifying the srcdir attribute for javac or groovyc task, be sure to specify the root folder where the package hierarchy is expected. That is write src/java/ not src/java/com/kameleoon for instance. If you don't do that, all Java or Groovy files will be recompiled at every run.

Hints & Tips

  • To import another file from a first one, you can use the import task:
<import file="configuration.ant.xml"/>

In this case, the imported file must be a valid Ant XML file. You can also (more easily) just include a list of properties from a file directly with the property task.

  • To be able to reference a variable and expand it (eg, for ${DeploymentDirectory} to be replaced by /var/www/), you must use a property (here, with the name "DeploymentDirectory"). It won't work if you try to reference an XML node, for example.
  • In the javac task, don't include part of the package structure in the srcdir attribute. Eg, don't write srcdir=src/com/example/. Else it will confuse Ant, and Ant will rebuild every Java source file at every run.
  • To echo a path or file set, create a property and use the refid attribute, pointing to the file set. Then echo the property.
<fileset id="modified" dir="template">
	<svnModified/>
</fileset>	

<property name="modified.files" refid="modified"/>
<echo message="${modified.files}" />
  • You can build path structures containing file sets very easily with nested fileset nodes.
<path id="hosted.class.path">
  <pathelement path="${java.class.path}/" />
  <fileset dir="/usr/share/tomcat-5.5/common/lib" casesensitive="yes">
      <include name="**/*.jar" />
  </fileset>
</path>
  • To use special characters like " (quotes) in Ant, use the HTML/XHTML special representations (&quot;).
  • To print Ant's class path from the command line:
ant --execdebug

Gant

Gant seems to be a really excellent build system. It is a combination of Groovy code and Ant tasks. Quick notes about Gant:

  • ${root.property} WILL not refer to an Ant property in a Gant script, but (logically) to a Groovy variable. Access an Ant property in the following way:
ant.antProject.properties."root.property"

Set it using:

ant.property(name: "local.deployment.dir", value: "/var/lib/tomcat-5.5/webapps/")
  • Note that within Grails you can set such a property like this:
grails -DnightlyBuild=true war

Exiting from a Gant / Grails script

Issue the following statement:

System.exit(1)