Introduction to the Ant Script Library

This article is about learning to build your software using Ant. Writing a new build.xml file from scratch can be difficult, especially if you are not very familiar with Ant. To make this easier, I have written some reuseable libraries that I will show in my examples below. The libraries are free and you can find documentation and a downloadable zip from the website at http://www.exubero.com/asl/.

Example Project: Deep Thought

For the purposes of this example, suppose you are developing code for an AI project called Deep Thought. You've been developing the code in an IDE, but are now looking build the code using Ant.

The project structure currently looks like this:

%tree
.
`-- src
    `-- example
        `-- DeepThought.java

Install the Ant Script Library

The first thing to do is grab a copy of the Ant Script Library, which can be retrieved from http://www.exubero.com/asl/download.html. Download and unzip the file into a subdirectory of your project.

Build the Code

Create a new file called build.xml in the top level directory of the project, with the following contents:

<project name="DeepThought" default="dist">
    <property name="java-build.src-dir" location="src"/>
    <import file="ant-script-library/asl-java-build.xml"/>
</project>

Because the directory containing the source code is not exactly as expected by the Ant Script Library, we have to override the default location by setting the property java-build.src-dir before importing the Java build module from the Ant Script Library. The line <import file="ant-script-library/asl-java-build.xml"/> does the work of making all the build targets available to your script.

The project structure now looks like this:

%tree
.
|-- build.xml
|-- ant-script-library
|   |-- asl-java-build.xml
|   `-- asl-java-test.xml
`-- src
    `-- example
        `-- DeepThought.java

With only four lines of Ant build, what can our script do? Looking at the project help is a good first place to look:

%ant -p
Buildfile: build.xml

Main targets:

 clean           Deletes files generated by the build
 compile         Compiles the java source
 copy-resources  Copies resources in preparation to be packaged in jar
 dist            Create a distributable for this java project
 generate        Generates source code
 jar             Create a jar for this java project
Default target: dist

Not bad! We can compile the code, and package the compiled code into a jar file. The default dist target does all this:

%ant dist
Buildfile: build.xml

generate:

-init-classpath:

compile:
    [mkdir] Created dir: /workspace/deepthought/target/classes
    [javac] Compiling 1 source file to /workspace/deepthought/target/classes

copy-resources:

-init-time:

-init-jar-classpath:

jar:
    [mkdir] Created dir: /workspace/deepthought/target/dist
      [jar] Building jar: /workspace/deepthought/target/dist/DeepThought.jar

dist:

BUILD SUCCESSFUL
Total time: 1 second

From the BUILD SUCCESSFUL message, we can see that the build worked. All files created by the build are put into a subdirectory called target. This includes generated class files, and any other artifact from the build. The file we can distribute to users of our application has been created in target/dist/DeepThought.jar.

Test the Code

Now that the distributable jar file has been created, we can run some unit tests against it. In this case we'll be using JUnit. It's good practice to put unit tests in a parallel directory structure to the source code. With this in mind, we can create a test case in the file test/example/DeepThoughtTest.java. We will also need a copy of the junit.jar library. If we put this in a directory called "lib", it will be automatically picked up by the build. This gives us a directory layout like this:

%tree
.
|-- build.xml
|-- ant-script-library
|   |-- asl-java-build.xml
|   `-- asl-java-test.xml
|-- lib
|   `-- junit.jar
|-- src
|   `-- example
|       `-- DeepThought.java
`-- test
    `-- example
        `-- DeepThoughtTest.java

There's one final change: we need to tell our build.xml that it needs to import the testing targets from the library file asl-java-test.xml. Edit build.xml so that it looks like this:

<project name="DeepThought" default="dist">
    <property name="java-build.src-dir" location="src"/>
    <property name="java-test.src-dir" location="test"/>
    <import file="ant-script-library/asl-java-build.xml"/>
    <import file="ant-script-library/asl-java-test.xml"/>
</project>

With this change, there are now a number of new test related targets available from our build:

%ant -p
Buildfile: build.xml

Main targets:

 clean                 Deletes files generated by the build
 compile               Compiles the java source
 copy-resources        Copies resources in preparation to be packaged in jar
 dist                  Create a distributable for this java project
 generate              Generates source code
 jar                   Create a jar for this java project
 test-all              Runs all tests
 test-integration      Runs integration tests
 test-run-integration  Runs the integration tests
 test-run-unit         Runs the unit tests
 test-unit             Runs unit tests
Default target: dist

The test-all target looks like a good candidate. Here's how to run that target from the command line:

%ant test-all
(output truncated)

-test-init-classpath:

-test-compile:
    [mkdir] Created dir: /workspace/deepthought/deepthought/target/test-classes
    [javac] Compiling 1 source file to /workspace/deepthought/deepthought/target/test-classes

-test-copy-resources:

-test-init-cobertura:

-test-instrument:
[cobertura-instrument] Cobertura 1.9 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
[cobertura-instrument] Instrumenting 1 file to /workspace/deepthought/deepthought/target/instrumented-classes
[cobertura-instrument] Cobertura: Saved information on 1 classes.
[cobertura-instrument] Instrument time: 149ms

-test-prepare:

-test-init-fileset-unit:

test-run-unit:
    [mkdir] Created dir: /workspace/deepthought/deepthought/target/test-data
    [junit] Testsuite: example.DeepThoughtTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.157 sec
    [junit] 
    [junit] Cobertura: Loaded information on 1 classes.
    [junit] Cobertura: Saved information on 1 classes.

-test-init-fileset-integration:

test-run-integration:

test-all:

BUILD SUCCESSFUL
Total time: 5 seconds

There's a lot more output this time. However, the final message from Ant is BUILD SUCCESSFUL, and we can see earlier in the output that our tests were run successfully:

[junit] Testsuite: example.DeepThoughtTest
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.157 sec

That's it! We have a complete compile, package and test build up and running, with only a relatively small build.xml and the Ant Script Library. The build.xml could have been smaller again if we had followed the expected directory layout, and so we would not have to explicitly define the src and test directories.

The Ant Script Library has many more features than shown here, including reporting on code metrics, dependency management, SCM integration and software release targets. Refer to the documentation at http://www.exubero.com/asl/ for more details.


Is there a problem or mistake on this page? Do you want to contribute some changes? Send me an email at joe@exubero.com.