Apache Ant in action

What is Apache Ant

Apache Ant is a powerful tool for preparing packages of Java code, from compiling classes, preparing mainfest files, creating and running jar files, to manipulating directories and files, zipping and taring files and much more.
Ant buildfile is basically a xml file. It contains methods or tasks (called targets) that the buildfile can execute. Each target has its own distinctive name that can be executed individually.

To follow this tutorial you need to have Ant installed and set the path to the ant executable.
Eclipse IDE already comes with support for writing and executing ant buildfile. The individual targets can be invoked by right-click. This tutorial will show the invocation of ant targets from console.

Prepare project

Let's start with the most simple ant buildfile.
Create a custom directory (eg. HelloWorld) which is your project directory. Inside project directory create new directory called src and put all java classes in it (including packages). In my case the path to main Java class looks like this: ~/HelloWorld/src/my/test/ant/HelloWorld.java

Create ant buildfile called build.xml

Create a file build.xml in project directory and fill it with the following contents:

<?xml version="1.0" encoding="UTF-8" ?>
<project>

<target name="clean" >
<delete dir="build" />
<delete dir="dist" />
</target>

<target name="compile" >
<mkdir dir="build" />
<javac srcdir="src" destdir="build" />
</target>

<target name="jar" >
<mkdir dir="dist" />
<jar destfile="dist/HelloWorld.jar" basedir="build" >
<manifest>
<attribute name="Main-Class" value="my.test.ant.HelloWorld" />
</manifest>
</jar>
</target>

<target name="run" >
<java jar="dist/HelloWorld.jar" fork="true" />
</target>

</project>

The root element of ant buildfile is <project> element.
The <target> elements represent actions or methods that buildfile can execute. Each target has its own name. The first target (called clean) will delete temporary directories that are used during building process. The second target compiles java files and puts compiled classes to build directory. The next target creates jar file from compiled classes and adds manifest file which is mandatory if jar contains main method. Created jar file is put into dist directory. The last target runs the created jar file.

Executing ant buildfile

The ant buildfile is executed from the base directory where build.xml file is located (usually this is the project directory).

Execute clean target to delete build and dist directories (if they exist):

$ ant clean

Execute compile target to compile classes:

$ ant compile

Execute jar target to create jar:

$ ant jar

Execute run target to run the main method of created jar:

$ ant run

Or run all commands at once:

$ ant compile jar run

If your buildfile filename differs from build-xml, you need to provide buildfile with -f option:

$ ant -f build_my_project.xml

Enhanced build.xml

Now we got enough courage to enhance basic buildfile and add some more options to it. Copy the lines below to build.xml file:

<?xml version="1.0" encoding="UTF-8" ?>
<project name="HelloWorld" basedir="." default="main" >

<property name="src.dir" value="./src" />
<property name="build.dir" value="./build" />
<property name="dist.dir" value="./dist" />
<property name="version" value="1.0" />

<property name="main-class" value="my.test.ant.HelloWorld" />

<target name="clean" >
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>

<target name="compile" >
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" />
</target>

<target name="jar" depends="compile" >
<mkdir dir="${dist.dir}" />
<jar destfile="${dist.dir}/${ant.project.name}.jar" basedir="${build.dir}" >
<manifest>
<attribute name="Main-Class" value="${main-class}" />
</manifest>
</jar>
</target>

<target name="run" depends="jar" >
<java jar="${dist.dir}/${ant.project.name}.jar" fork="true" />
</target>

<target name="clean-build" depends="clean,jar" />

<target name="main" depends="clean,run" />

</project>

The project element now has some arguments: name - the name of the project, basedir - sets the base directory to current directory (ie. project directory) and the attribute default, which declares default target that will be invoked when started from the command line.
The next few <property> elements are used to store some predefined values and constants that are repeatedly used all over the build.xml file. Such predefined values can hold the paths to commonly used directories (like src, build, dist, lib...), main-class declaration, project version or any other value.

The most notable enhancement here is depends attribute inside target element. It tells ant that execution of a target depends on prior execution of another target. For example: run target depends on jar target. This means that before run target is executed jar target must also be executed and since jar target depends on compile target, also compile target must be executed before the application is actually run.
The execution of last two targets (clean-build and main) depends on the execution of dependent targets.

Executing ant buildfile

When ant starts it automatically searches for build.xml file, which is the default name of ant buildfile. But if the name of the buildfile differs from default (eg: testBuild.xml), then the ant buildfile is invoked with:

$ ant -f testBuild.xml <target_name>

Including external libraries

Many Java applications use libraries, APIs or frameworks... These libraries are referenced in your application, and therefore must be included in the classpath of the project to be successfully compiled.

In our project directory (HelloWorld) create a directory called lib and put all necessary libraries (jars) into it.

The following buildfile will show you how to add external jars to the classpath and compile the classes.

<?xml version="1.0" encoding="UTF-8" ?>
<project name="HelloWorld" basedir="." default="run" >

<property name="src.dir" value="src" />
<property name="lib.dir" value="lib" />
<property name="build.dir" value="build" />
<property name="dist.dir" value="dist" />

<property name="main-class" value="my.test.ant.HelloWorld" />
<property name="version" value="1.0" />

<path id="classpath" >
<fileset dir="${lib.dir}" includes="**/*.jar" />
</path>

<target name="clean" >
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
</target>

<target name="compile" depends="clean" >
<mkdir dir="${build.dir}" />
<javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="classpath" />
</target>

<target name="jar" depends="compile" >
<mkdir dir="${dist.dir}" />
<tstamp>
<format property="TODAY" pattern="dd.MM.yyyy hh:mm" />
</tstamp>
<jar destfile="${dist.dir}/${ant.project.name}.jar" basedir="${build.dir}" >
<manifest>
<attribute name="Manifest-Version" value="1.0" />
<attribute name="Creation-Date" value="${TODAY}" />
<attribute name="Created-By" value="Matjaz Cerkvenik" />
<attribute name="Main-Class" value="${main-class}" />
<attribute name="Class-Path" value="library1.jar library2.jar" />
<attribute name="Content" value="HelloWorld example project" />
<attribute name="Project-Version" value="${version}" />
<attribute name="Compiled-By" value="${ant.java.version}" />
</manifest>
</jar>
<copy todir="${dist.dir}" >
<fileset dir="${lib.dir}" >
<include name="**/*.jar" />
</fileset>
</copy>
</target>

<target name="run" depends="jar" >
<java fork="true" classname="${main-class}" >
<classpath>
<path refid="classpath" />
<path location="${dist.dir}/${ant.project.name}.jar" />
</classpath>
</java>
</target>

</project>

At the beginning of this ant buildfile we added path element, which points to the lib directory and imports all jar files from that directory.
Furthermore we extended the manifest element, which now includes much more attributes besides Main-Class. Most remarkable atribute is Class-Path, which must include a list of all libraries (jars, separated by blank space) that HelloWorld project depends on.

Right after HelloWorld jar is created in the dist directory, we also copy all dependent libraries from lib directory to dist directory. Why? Because Class-Path attribute in manifest claims that libraries should be in the same directory as HelloWorld.jar. If you run the jar from command line:

$ java -jar HelloWorld.jar

Otherwise use ant buildfile to compile, build and run jar:

$ ant run