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