Home
Research Publications
Teaching Students CV Software Funding Activities

Build Systems

Most software engineers soon tire of the often repetitive command line tasks required to build a software system.  Build systems such as Make and Ant automate this process and remove its tedium, while simultaneously optimizing the process to prevent unnecessary work and shorten build times.  This lab will introduce the basics of using the Apache Ant build automation tool.

Installing Ant

Some systems (such as Macs) come with Ant already installed.  Check to see if your system already has ant installed by attempting to run it from the command line:
ant -version
If ant is installed this command should result in Ant printing its version information.  If not, you'll need to install it.  Many Linux distributions have Ant available through their packaging system (e.g.: "sudo apt-get install ant" should install it on Debian-based systems).  Others will need to install it manually using the procedure outlined in the Ant manual available at this link.

This installation guide can sometimes be tricky, so I have provided another guide for a windows installation through cygwin. Linux users can simply use the sudo apt-get install ant command to install Ant.

Ant Buildfiles

Any project wishing to use Ant must supply an XML-based buildfile (usually named "build.xml") which specifies what Ant should do.  Buildfiles consist of a top level project element which contains at least one target subelement and usually some number of global property subelements.  We'll look at each of these subelements in turn before returing to how they're put together to form the top-level property element.

Global Properties

Global properties are similar to constants in that they allow you to associate a value with an identifier.  These identifiers can then be used elsewhere in the buildfile to refer to the value.  This is often used to define important values once that are then referenced multiple times throughout the buildfile (e.g.: to define the directory where the source files are located).  This concept is useful in improving buildfile readability by allowing all the important values in a buildfile to be defined in one continguous location.  It also makes it easier to modify a buildfile by enabling changes to be applied in a single location in the buildfile instead of multiple locations throughout.  

Properties are defined using a "property" element.  For example, the following defines the property "src.dir" to the value "src":
<property name="src.dir" value="src"/>
Once defined, a property's value can be recalled by placing its name between "${" and "}".  For example, if you've defined the "src.dir" property above you can use that property elsewhere with "${my_property}".  Properties can even be used in the definition of yet other properties, like so:
<property name="mypkg.dir" value="${src.dir}/com.example.mypkg"
Exercise:
Create the XML elements needed to define the following properties.  Use properties to define other properties wherever possible.

Name Value
src.dir src
build.dir build
dist.dir dist
dist.jar.dir dist/lib
jar.dir lib

Targets

A target is a grouping of tasks that need to be performed to reach a certain desired state.  A buildfile will typically include targets for such things as initializing the build directory structure, compiling the source files, and building a Java archive.  A target is defined by a sequence of task subelements, so we'll discuss these first.

Tasks

Ant's strength lies in its powerful collection of built-in tasks.  The tasks available cover pretty much anything you'll need to do during a build process, including tasks that work with archives, handle compilation, interact with version control systems, perform file/directory operations, etc.  We'll cover the absolute basics for a handful of the most common tasks here, but for a comprehensive look at what is available see the list of tasks in Ant's manual.  

Tasks are defined by an element with the tasks name.  The options available for each task is unique for that tasks.  Usually you need only include the bare minimum of information required to do the task and Ant takes care of the rest.  Tasks also are typically aware of whether or not they need to be run again.  For example, the compilation tasks won't actually compile unless the class files are out of date.  Here are some of the most commonly used tasks:
mkdir
The mkdir task is used to create a dictionary.  You need only supply the name of the directory you wish to create, for example:
<mkdir dir="${some.dir}">
This will create a new directory named whatever the value of the "some.dir" property is.
Exercise:
Create the task element necessary to create a directory named "build".
delete
The delete task is used for deleting files and directories.  You supply the name of the directory or file to delete.  Here are a couple of examples:
<delete dir="some_dir">
<delete file="some_file">
The first task will delete the directory "some_dir" and all of its contents; the second will delete the file "some_file".
Exercise:
Create the task element necessary to delete a directory named "build".
javac
The javac task is arguably the workhorse of the Ant build system; it compiles a Java source tree.  You must provide a directory containing the source and usually a destination for the compiled class files is also specified.  You may also need to specify a classpath for compilation if the source to be compiled relies upon classes defined elsewhere.  Lastly, you also will usually want to set the attribute "includeantruntime" to "false" which will ensure that only the classpath specified in the antfile is used and not any system or ant-specific classpath - this makes it more likely that your buildfile will work on all systems, not just yours.  An example javac task follows:
<javac srcdir="src" destdir="build" classpath="my_lib.jar" includeantruntime="false"/>
This task will compile all the Java files in the "src" directory and place the compiled class files in the "build" directory whilst mirroring the internal directory structure of the "src" directory (e.g.: if there is a java file at "src/com/example/MyClass.java" the generated class file will be placed in "build/com/example/MyClass.class".  The compilation will use a classpath of "my_lib.jar", so any classes found in that JAR can be used by the source without problems, and the classpath will not be polluted by any other classpath entries.  Not bad for a one-liner!
Exercise:
Create the task element necessary to compile all the java source files in the "my_src" directory, and place the resultant class files in the "my_classes" directory.  The element should also include the jarfile "my_jarfile.jar" in the compilation task's classpath.
jar
The jar task handles the process of packaging up a set of class files into a Java archive (JAR) file.  You need only specify the output jarfile's name and the base directory of the class heirarchy to be packaged up.  Here's an example:
<jar jarfile="my_lib.jar" basedir="build">
This packages up all the class files in the "build" directory and creates a valid Java archive named "my_lib.jar".
Exercise:
Create the task element necessary to create a jarfile named "my_jar.jar" from all the classfiles found in the "my_classes" directory.
java
The java task allows you to execute a Java program.  You must specify either a classname or an executable jarfile.  If you specify a jarfile, you must also set the "fork" attribute to true (which means that a separate Java instance than the one Ant itself is running in will be created.  Here's an example:
<java jar="executable_jar.jar" fork="true"/>
This executes the "executable_jar.jar" jarfile in its own Java virtual machine.
Exercise:
Create the task element necessary to execute the jarfile named "my_jar.jar".

We'll return now to the topic of targets.  All buildfiles must contain at least one target.  Targets are created using a target element which consists of some number of tasks that will be executed in sequential order whenever the target is to be built.  Here's an example target:
<target name="clean">
<delete dir="build_dir">
<delete file="temp_file">
</target>
Now whenever the "clean" target is run, the directory "build_dir" and the file "temp_file" will be deleted.  Notice that the target has a "name" attribute - every target must have a unique name.  You can also specify dependencies between targets.  This tells ant that before a target's tasks can be run that the target that it depends upon must have been executed.  Ant keeps track of these dependencies and ensures that they are met during the build process.  For example, say you have the following snippet in your buildfile:
<target name="jar", depends="compile">
...
</target>
Before performing the tasks contained in the "jar" target (not shown in snippet above), Ant will make sure to run the "compile" target.  This is very useful, because you can request that Ant build the "jar" target and it will automatically make sure that it is being built from up-to-date class files.

Projects

Now that we know the basic components of a buildfile, we can create the required top-level project element.  The project element can include the following attributes:
Here's an example project element (lower level elements are not included for simplicity):
<project name="MyProject" basedir="." default="jar">
<property name="src.dir" value="src"/>

<target name="clean">
....
</target>

<target name="compile">
....
</target>

<target name="jar" depends="compile">
....
</target>

<target name="run" depends="jar">
....
</target>
</project>
This defines a project with one global property ("src.dir") and four targets ("clean", "compile", "jar", and "run").  The default target in this project is "jar", and you can also see how targets can be set up to depend upon one another.

Running Ant

Ant is invoked by typing "ant" at the command line.  By default Ant will search for a default target (set via the top-level project element) if you don't specify a target argument.  Ant also supports requesting that a specific target be built by invoking ant with the requested target's name.  For example:
ant run
This would cause the run target to be built.  In the example project from above, this would cause the following build sequence (due to dependencies): "compile" -> "jar" -> "run".

Putting it all together

Using the elements introduced above, you'll now use Ant to build yet another CircleCalc program.
Exercise:
Checkout the Ant-based CircleCalc project from the following subversion URL:
http://subversion.assembla.com/svn/ee461l-circlecalcantstub/trunk/
This directory contains an incomplete Ant buildfile.  Your task is to fix it.  The final buildfile should meet the following requirements:
Important Notes:

Reference materials:
Developed with guidance from Miryung Kim

Copyright (c) 2011 Professor Miryung Kim and a course development TA Evan Grim

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A copy of the license can be found on the GNU web site here.