Apache Ant Demystified – Parts 1 and 2

Share this article

One of the principles of good software engineering is the daily build. If you build your software every day then it acts as a barometer, showing the state of the project. If something’s broken, it gets noticed faster, so it gets fixed faster. The daily build becomes the heartbeat of the project. These are the reasons why Microsoft builds the five gigabytes of Windows source code every day, even though it takes twelve hours — on very powerful and expensive servers — to do so.

As you might imagine, in order to be able to perform daily builds, the build process has to be automatic and repeatable. It’s no good to rely on some harangued colleague typing a complicated command in a Command Prompt window to invoke the Java compiler with a classpath as long as this article. What’s needed is Another Neat Tool in the developer’s armoury. What’s needed is Apache Ant.

Part 1 of this tutorial starts below.

Part 2 of the tutorial starts here.

About Ant

Ant is an open-source Java-based build tool from the Apache Software Foundation. It’s rapidly become the de-facto build tool for J2EE projects, so for developers, it’s certainly worth becoming familiar with the basics of Ant, at the very least. Many modern Java development environments also feature Ant support.

Ant uses build files, which are simply XML files that tell the Ant program what to do. And because Ant is a Java program, it’s a cross-platform tool; you can move your build files from one operating system to another unchanged, providing you haven’t used any platform-specific features.

Although Ant is a build tool, don’t think that it’s only good for compiling Java code. Its name is apt; think of all the heavy loads a line of ants can carry on their backs. Ant is a veritable Swiss army knife of functionality. For example, it’s entirely feasible to use Ant to get the latest code from your source code repository, compile that code, run the unit tests and generate a report with the results, generate the Javadoc documentation, package the code into a J2EE application, deploy that application to an application server …and then send yourself an email when that’s all done. Phew! Ant can do all of that in response to a single command from you if you want it to. Not bad, eh? Make no mistake: being able to write and maintain Ant build files is a very useful skill for any Java developer to possess.

Obtain and Install Ant

Ant can be downloaded from the Downloads page of the Apache Ant Website. Like all Apache downloads, it’s available in both binary (pre-compiled) and source code form. I recommend that you download the binary distribution, at least to start with.

Locate the file that you downloaded, which will be named something like apache-ant-1.6.2-bin.zip. At the time of writing, 1.6.2 is the latest version of Ant. There is no installer program with Ant, so all you need to do is unzip the download to the directory in which you want to install the software, for example, C:Program Files. If you use WinZip to extract the files from the Zip file, make sure that the Use Folder Names option is checked so that the Ant installation folder structure is preserved.

Before we can get started with Ant, we need to modify the system path so that Ant can be started from any Command Prompt, irrespective of what the current directory is on the file system. We also need to add some system-wide environment variables that Ant needs if it is to operate correctly. The following instructions are for Windows XP; users of other operating systems should consult their documentation to find out how to modify the system path, and add and edit environment variables.

Right-click on the My Computer icon and select Properties. Then click the Advanced tab and select the Environment Variables button. Depending upon whether you’re the administrator of the computer or not, you can either add the Ant installation folder to the global path — which affects all users — or to the path for just your user account. If the New, Edit and Delete buttons are greyed out in the System Variables group, then you don’t have permissions to modify the global path and can only set Ant up for your own user account. Don’t worry, it will still work perfectly well. Let’s assume that you’re going to do just that.

If you haven’t already got a user variable named Path, click the New button and set the Variable Name to Path and the Variable Value to the full path to the bin folder in the Ant installation, for example, C:Program FilesApache Groupapache-ant-1.6.2bin, then click OK. If you already have a Path user variable, highlight it in the list, then click the Edit button. Add a semi-colon to the end of the existing path, then add the full path to the bin folder as described above. You might end up with a path looking vaguely like this: C:Program FilesWidgetWareWidget; C:Program FilesApache Groupapache-ant-1.6.2bin

Click OK when you’ve finished making the edit.

The next step is to set up a new environment variable called ANT_HOME. While you’re still within the Environment Variables dialogue, click the New button in the User variables group and create a new variable with a name of ANT_HOME and a value that corresponds to the Ant installation folder. For example, C:Program Filesapache-ant-1.6.2 Note that there’s no bin on the end this time. Click OK.

Finally, we need to set up another new environment variable: one that will tell Ant where your Java Development Kit (JDK) is installed. Follow the procedure described above for the ANT_HOME environment variable, but this time, set the Variable name to JAVA_HOME and the Variable value to the location at which your JDK is installed. On my machine it’s C:Program FilesJavajdk1.5.0, but yours may be different. Now click OK several times to close all the open dialogues and property sheets.

You can confirm that these steps have been successful by opening a Command Prompt and entering set ant_home followed by Enter, and then set java_home followed by Enter. The respective paths that you entered in the steps above should be displayed. If this doesn’t work, try logging off and back on again.

We also need to verify that Ant is installed correctly and ready to go. In the same Command Prompt window, type ant –version followed by Enter. If all is well, then Ant should respond by telling you which version it is and when it was compiled. If this is the case then we’re ready to start learning more about Ant and to write our first build file!

Good Things Come In Threes: Projects, Targets And Tasks

The three basic concepts in Ant are the project, targets and tasks. Actually, there are also properties, but who ever heard of good things coming in fours? The concepts are really simple: each Ant build file contains one and only one project, and that project must contain at least one target, but will usually contain more. Also, each target contains tasks. Let’s take a look at the project part of all that by diving right in and starting to write a build file.

Use a text editor to create a new file named build.xml and save it somewhere. You can use Notepad for this task, or you can use a fancy syntax-highlighting editor — it really doesn’t matter. Type or paste in the following:

<?xml version="1.0" encoding="UTF-8"?> 
<project name="helloworld" default="compile" basedir=".">
 <description>
   Build file for the Hello World application.
 </description>
</project>

The first line is simply a standard declaration that says that this is an XML 1.0 document that uses the UTF-8 character encoding. You might have come across this line, or something similar, before. Ant doesn’t actually require this to be present, but it’s a good idea to include it because Ant build files are XML documents and valid XML documents must start with this declaration.

Next, there’s a project tag with a name attribute. This is simply a name that we want to give the project; in this case, it’s the spectacularly unimaginative "helloworld". The project’s name is used when we call Ant build files from other Ant build files, which we don’t need to worry about for now.

The default attribute tells Ant which target to execute when no specific target is supplied on the command line. More about that later.

Finally, the basedir attribute is the path to the directory that Ant should use when working out paths. More often than not, it will be set to . (a single period), which is Windows and UNIX shorthand for referring to the same directory that the file itself is in. In other words, if you saved your build.xml file in C:SitePoint TutorialsAnt, then the basedir attribute would have the value C:SitePoint TutorialsAnt; when Ant works out any relative paths later, it uses that as a starting point.

Nested within the project tag is a description tag. This is where you can provide a more detailed description of the project that will get output if the person using the build file requests help on the build file.

As things stand, this build file isn’t very useful. In fact, it’s not even valid because we haven’t defined any targets. A target is a set of tasks that you want to be executed. You can tell Ant which target(s) you want to be executed and, when no target is given, the project’s default target is used. Let’s make our build file do something useful and add a target:

<?xml version="1.0" encoding="UTF-8"?>  
<project name="helloworld" default="compile" basedir=".">  
 <description>  
   Build file for the Hello World application.  
 </description>  
 
 <target name="compile" description="Compile all sources.">  
   <mkdir dir="classes" />  
   <javac srcdir="src" destdir="classes" />  
 </target>  
</project>

Here, we’ve nested a target tag within the project tag and set its name and description attributes. Again, the description gets output if the user of the build file requests help on it. We’ll see how to do that later. Two tasks are nested within the target tag: mkdir and javac. Ant comes with more built-in tasks than you can shake a stick at; see the online manual to get an idea. If these aren’t enough, you can always extend Ant with optional tasks that others have written. If that’s still not enough for you, not only are you pretty demanding, but you can always write your own Ant tasks using Java (naturally).

The mkdir task, as its name suggests, makes a directory. In this case, it makes the classes output directory into which our compiled Java classes will go. Remember that this happens relative to the location specified using the basedir attribute of the project tag. So, using the example from earlier, our mkdir task would create a classes directory directly under C:SitePoint TutorialsAnt.

Most Ant build files will use the javac task at some point. As you might have guessed, it’s a wrapper around javac.exe — the Java compiler. In this case, we’re using the srcdir attribute to tell it to find Java source code under the src directory, and to output the compiled classes to the classes directory.

Important: Ant is a harsh mistress; if you don’t create the classes directory first, the javac task will fail when it attempts to output some files there. That’s why we had to use the mkdir task first.

That’s it! We’ve defined our first target and used two built-in Ant tasks. If you have some simple Java source code that doesn’t depend on any other libraries on the classpath — I’m talking about a “Hello World” level of sophistication here — and that source code is located in the src directory, then this build file will compile the classes into the classes directory. The only part of Ant that’s left to learn is how to run the build file! Well, not quite. But let’s see how to do it anyway.

Using Ant

As I said earlier, many modern Java IDEs have integrated support for Ant, but before exploring on your own whether yours does, let’s learn how to run Ant the way nature intended: from the command line.

Open a Command Prompt at the directory in which your build.xml file is saved. Now type ant –projecthelp, followed by Enter. Ant should display the following information:

Buildfile: build.xml  
 
 Build file for the Hello World application.  
 
Main targets:  
 
compile Compiles all sources.  
Default target: compile

Ant tells us the name of the build file, the project description, and provides a list of the targets within the build file together with their descriptions. Note that if you don’t supply a description for a target, then that target won’t get listed at all as part of the project help. So unless you’re able to look at the source of the build file, you won’t even know the target is there.

Ant also tells us what the default target is. In this case, it’s the compile target. Notice that we didn’t have to tell Ant what our build file was called. Ant is hard-wired to look for a build file named build.xml in whichever directory you happen to be in when you type the ant command. That doesn’t mean that you have to call all your build files build.xml; it’s just a convenient feature. If you use another name, you should use the –f command line switch to tell Ant what your build file is called. For example, ant –f build_me.xml.

Now, type ant and press Enter. You should see something similiar to the following:

Buildfile: build.xml  
 
compile:  
 [mkdir] Created dir: C:SitePoint TutorialsAntclasses  
 [javac] Compiling 1 source file to C:SitePoint TutorialsAntclasses  
 
BUILD SUCCESSFUL  
Total time: 2 seconds

The above code shows that Ant found build.xml and executed its default target of compile. If you inspect that classes directory, you should find some 100% pure Java in there. Congratulations! You’ve successfully used Ant to compile some Java code! Ant displays in square brackets the name of each task as it executes, and also helpfully tells us how long the build took. This is a neat feature when your builds take hours and you want to bore people at parties by talking about how insanely complicated your project is.

One last thing to try for now: type ant again and press Enter. You’ll see this output:

Buildfile: build.xml  
 
compile:  
 
BUILD SUCCESSFUL  
Total time: 1 second

In this case, Ant didn’t do anything! It’s intelligent enough to realise that the code was already compiled and didn’t need to be compiled again. Ant practises build avoidance. This means that it checks the timestamps of files to determine whether they’ve changed or not, and only compiles files that have changed, thus reducing build times, often drastically on large projects.

In Part 1 of this series, we learnt why using Ant to do builds is a good idea, and were introduced to the core Ant concepts of the project, targets and tasks. We also wrote and executed a simple build file.

Now, let’s take a look at some of the other ways that Ant can make our lives as Java developers easier.

Target Dependencies

Ant targets can depend on other targets. When you think about it, this is actually really useful. You could have a target for creating a JAR file that depends on a target for compiling the source code. Let’s add two more targets to our build.xml file (note that a complete version of build.xml is available for download):

<?xml version="1.0" encoding="UTF-8"?>   
<project name="helloworld" default="compile" basedir=".">  
 <description>  
   Build file for the Hello World application.  
 </description>  
 
 <target name="compile" description="Compile all sources.">  
   <mkdir dir="classes" />  
   <javac srcdir="src" destdir="classes" />  
 </target>  
 
 <target name="clean" description="Clean up output directories.">  
   <delete dir="classes" />  
 </target>  
   
 <target name="rebuild" depends="clean,compile"  
         description="Cleanly compiles all sources." />
 
</project>

I’ve introduced some new syntax here. Let’s take the clean target first. This contains a delete task that simply deletes the classes output directory and its contents. Ant’s delete task is a versatile little fellow: it can delete files and directories in various combinations and ways. We also have a rebuild target that doesn’t contain any tasks at all! What’s going on here?

You’ll notice that the rebuild target has a new depends attribute set to clean,compile. This means that the rebuild target should first execute the clean target, before it executes the compile target. So the classes output directory will be deleted, then re-created and the Java code re-compiled. In other words, a full rebuild occurs, which is useful when you want to compile absolutely everything, and make sure that you haven’t got any old class files lying about for classes whose source code you’ve subsequently deleted. Having obsolete classes in your classpath without realising it can cause you to waste hours needlessly debugging.

Important: Ant only executes targets once, even when more than one target depends on it.

Run the rebuild target using ant rebuild and observe the output. We can now perform a clean build, safe in the knowledge that we’re using only those classes we think we’re using!

DRY Using Properties

When programming, it’s important to follow the DRY principle: Don’t Repeat Yourself. If you’re eagle-eyed, you may have noticed that some duplication is present in our build file as it stands. The classes directory is mentioned by name three times: twice in the compile target, and a third time in the clean target. What we need is a way to introduce some indirection, so that if we suddenly decide we’d like our compiled Java code to end up in a directory named bytecode, instead of classes, we only have to make the edit once. Fortunately, Ant’s got it covered with properties.

Let’s examine the syntax. This is what our build file looks like once we add a property to store the name of the output directory:

<?xml version="1.0" encoding="UTF-8"?>   
<project name="webforum" default="compile" basedir=".">  
 <description>  
   Build file for the WebForum application.  
 </description>  
   
 <property name="classes.dir" location="classes" />  
 
 <target name="compile" description="Compile all sources.">  
   <mkdir dir="${classes.dir}" />  
   <javac srcdir="src" destdir="${classes.dir}" />  
 </target>  
 
 <target name="clean" description="Clean up output directories.">  
   <delete dir="${classes.dir}" />  
 </target>  
 
 <target name="rebuild" depends="clean,compile"  
         description="Cleanly compiles all sources." />  
</project>

Properties are simply name/value pairs. In this case, the name is classes.dir and the value is classes. We actually use the location attribute to set the latter because we’re dealing with a file path that we want evaluated relative to the value set for the basedir attribute. For other types of properties, use the value attribute. When you want to refer to the actual value that the property represents, simply use the ${property name} syntax, for example, ${classes.dir}. If you try to use the build file now, you should find that it works exactly as before; the important point is that it’s easier to maintain.

Let’s suppose that for some bizarre reason we really did want our compiled classes to go into a directory named bytecode instead of classes. How would we achieve this? We could edit the property declaration and benefit from having only to make the change in one place, but I want to show you another way that’s very powerful.

Ant lets you override properties on the command line. You may have come across Java’s -D switch, which lets you set a system property. Ant uses the same syntax. Therefore, run ant –Dclasses.dir=bytecode rebuild and observe the results.

Our compiled classes end up in bytecode instead of classes. Were you expecting the classes directory to be deleted, because we told Ant to run the rebuild target, which, in turn, calls the clean target? If you were, be sure to remember that the clean target also refers to the value of ${classes.dir}, which we passed in. For this reason, the old classes directory is left in place. Also, note that there is no space between the -D and the property that’s being overridden.

Generally, you’ll only override properties this way if it’s a change that you want to make for that build only. If we permanently wanted to use bytecode in lieu of classes, we would edit the property declaration in the build file. Remember: a good build process is repeatable.

Hiding Targets

Sometimes, when you’re writing build files, you’ll want to hide targets so that they can’t be executed directly. You may wonder what use a target is if it can’t be executed directly. Well, it’s useful to be able to create so-called internal targets that do useful work for other targets that are visible. To hide a target, just place a minus sign before its name. Add the highlighted code below to our build file:

<?xml version="1.0" encoding="UTF-8"?>   
<project name="helloworld" default="compile" basedir=".">  
 <description>  
   Build file for the Hello World application.  
 </description>  
 
 <property name="classes.dir" location="classes" />  
 <property name="dist.dir" location="dist" />  
 <property name="dist.jarfile" value="helloworld.jar" />
 
 
 <target name="compile" depends="-init" description="Compile all sources.">  
   <mkdir dir="${classes.dir}" />  
   <javac srcdir="src" destdir="${classes.dir}" />  
 </target>  
 
 <target name="clean" description="Clean up output directories.">  
   <delete dir="${classes.dir}" />  
 </target>  
 
 <target name="rebuild" depends="clean,compile"  
         description="Cleanly compiles all sources." />  
 
 <target name="-init">  
 
   <!-- Create the time stamp. -->  
   <tstamp>  
     <format property="TODAY_UK" pattern="dd MMM yyyy HH.mm" locale="en_GB" />  
   </tstamp>  
 </target>  
 
 <target name="dist" depends="rebuild"  
         description="Creates the binary distribution.">  
 
   <mkdir dir="${dist.dir}/${TODAY_UK}" />  
   <jar basedir="${classes.dir}"  
        destfile="${dist.dir}/${TODAY_UK}/${dist.jarfile}" />  
 </target>
 
</project>

There are quite a few changes here! The compile target now depends upon the hidden init target. The init target itself uses Ant’s tstamp task to assign a date and timestamp to the new property TODAY_UK using a UK date and time format. We’ll use this property later in the build file, to create a new directory with a name that reflects the date and time at which the build was run. We hide this target because, on its own, it doesn’t do anything very useful, so we don’t want to invoke it from the command line.

Moving back to the top of the build file, we’ve set up a couple of new properties to define a dist output directory, and the file name of an output JAR file: helloworld.jar.

Finally, we’ve added a new dist target that depends on the rebuild target. Our old friend the mkdir task is used again. It uses both the dist.dir and TODAY_UK properties to create an output directory in which the JAR file will go.

Ant’s jar task is then used to create the JAR file. The jar task can accept a lot of different parameters, but here we’re setting just two of them. The basedir attribute tells the jar task where to find the compiled Java classes that we want to put into the JAR file, while the destfile attribute specifies the name of the output JAR file itself.

If you now enter ant dist into the Command Prompt, you should see output similar to this:

Buildfile: build.xml   
 
clean:  
 [delete] Deleting directory C:SitePoint TutorialsAntclasses  
 
-init:  
 
compile:  
 [mkdir] Created dir: C:SitePoint TutorialsAntclasses  
 [javac] Compiling 1 source file to C:SitePoint TutorialsAntclasses  
 
rebuild:  
 
dist:  
 [mkdir] Created dir: C:SitePoint TutorialsAntdist26 Feb 2005 12.38  
   [jar] Building jar: C:SitePoint TutorialsAntdist26 Feb 2005 12.38helloworld.jar    
 
BUILD SUCCESSFUL  
Total time: 2 seconds

Notice that Ant traversed the tree of dependencies that we’ve created. We told it to execute the dist target, which depends on the rebuild target, which in turn depends on the clean target, followed by the compile target. The compile target itself depends on the –init target. Ant automatically unravels all this, and executes the targets in the following order: clean, -init, compile, rebuild and finally dist.

One final point to note is that, if you peek inside the helloworld.jar file that Ant built (you can use WinZip to do this), you’ll see that Ant has helpfully automatically added a MANIFEST.MF file, which contains brief details of the build, such as the version of Ant and the Java compiler that were used. On my machine, it looks like this:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.2
Created-By: 1.5.0-b64 (Sun Microsystems Inc.)

Of course, you can always override this behaviour and get Ant’s jar task to use your own manifest file if you need to. The details are included in the Ant manual’s documentation for the jar task.

Target Aliasing

A technique that I often use in my build files is target aliasing. This means that I define a target that does nothing but invoke another. This may seem like a waste of time, but consider the scenario of providing a target that displays some simple help text to the user via Ant’s echo task:

<target name="help" depends="usage" />   
 
<target name="usage" description="Display usage information.">  
 <echo message="  Execute 'ant -projecthelp' for build file help." />  
 <echo message="  Execute 'ant -help' for Ant help." />  
</target>

The help target is aliased to the usage target by creating a dependency on the usage target. Now the user of our build file can get online help by typing either ant usage or ant help. Note that the help target doesn’t appear in the target listing displayed by Ant’s –projecthelp option, because we didn’t provide a description for it. For an additional refinement, make either the help or usage targets the default target, so that the user can get help if they just enter ant, with no arguments, on the command line.

Tip: Seriously consider making your build file user-friendly by making its default target one that doesn’t do anything potentially time-consuming and/or dangerous! Displaying help is a good candidate for the default.

Awesome Ant

We’ve really only just scratched the surface of Ant’s capabilities, but I hope I’ve given you a flavour of just how flexible and powerful a tool it is. In fact, you’d be really hard-pressed to think of anything useful that you’d want to do in the realm of Java development that Ant can’t handle.

It’s worth taking the time to look through the list of core tasks that Ant offers, and experimenting with some of them. If you go to the folder in which you installed Ant, you’ll find a docs folder. Within this is an index.html file that will open your very own local copy of the Apache Ant Website. This contains a link to a local copy of the Ant manual, which is generally well-written because Ant is a mature program now. The manual also has lots of links to some great external resources on Ant. Happy building!

Frequently Asked Questions about Apache Ant

What is the main purpose of Apache Ant in Java development?

Apache Ant is a Java-based build tool. In theory, it is kind of like the make, but without make’s wrinkles and with the full portability of pure Java code. It is used primarily for building and deploying Java projects but can be used for any task that can be described in terms of targets and tasks. It provides a number of built-in tasks allowing you to compile, assemble, test, and run Java applications. Ant can also be used effectively to build non-Java applications, for instance, C or C++ applications.

How can I get a list of build targets in Ant?

To get a list of build targets in Ant, you can use the command ‘ant -p’ or ‘ant -projecthelp’ in the command line. This will display a list of all available targets in your build file. Each target represents a step in the build process, such as compiling source code or creating a JAR file.

How does the delete task work in Apache Ant?

The delete task in Apache Ant is used to delete files or directories. You can specify the files or directories to be deleted either directly or through a fileset. The delete task will not delete directories that are not empty unless you set the ‘includeEmptyDirs’ attribute to ‘true’. It’s important to use this task carefully, as deleted files cannot be recovered.

What are targets in Apache Ant and how are they used?

Targets in Apache Ant are collections of tasks that are executed as a unit. Each target has a name and contains a series of tasks to be executed. Targets can depend on other targets, and the dependencies are executed before the target itself. Targets are specified in the build file and can be run individually using the command ‘ant target-name’.

How can I use Apache Ant to build non-Java applications?

While Apache Ant is primarily used for building Java applications, it can also be used to build non-Java applications. For instance, you can use Ant to compile C or C++ code by using the ‘cc’ task. You can also use Ant to execute shell scripts or batch files using the ‘exec’ task. The flexibility of Ant makes it a powerful tool for any build process.

How can I specify dependencies between targets in Apache Ant?

In Apache Ant, you can specify dependencies between targets using the ‘depends’ attribute. For example, if you have a target ‘compile’ that depends on another target ‘init’, you would specify this as follows: . This means that the ‘init’ target will be executed before the ‘compile’ target.

How can I use Apache Ant to run unit tests?

Apache Ant can be used to run unit tests by using the ‘junit’ task. This task runs tests written using the JUnit testing framework. You can specify the test classes to be run, and Ant will produce a report of the test results. This makes it easy to integrate unit testing into your build process.

How can I use Apache Ant to create a JAR file?

You can use the ‘jar’ task in Apache Ant to create a JAR file. This task takes a set of files and packages them into a JAR file. You can specify the files to be included in the JAR file either directly or through a fileset. You can also specify the manifest file to be used for the JAR file.

How can I use Apache Ant to compile Java code?

The ‘javac’ task in Apache Ant is used to compile Java code. You can specify the source files to be compiled either directly or through a fileset. You can also specify the classpath to be used for compilation. The ‘javac’ task produces .class files as output.

How can I extend Apache Ant with custom tasks?

Apache Ant can be extended with custom tasks by writing a Java class that extends the ‘org.apache.tools.ant.Task’ class. This class should override the ‘execute’ method to perform the desired task. Once the class is written, it can be used in a build file by using the ‘taskdef’ task to define the new task.

John TopleyJohn Topley
View Author

John is a J2EE developer based in the UK, with a particular interest in user interface development and Web standards. John's Weblog and knowledge base is at http://www.johntopley.com/.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week