Apache Ant: Learn Apache Ant from IBM Experts
Section 1. Getting started
What is this tutorial about?
In this tutorial, you’ll learn about Ant, a build tool for Java TM projects. Ant has quickly become popular among Java developers because of its flexibility and ease of use, so you it owe it to yourself to find out more about it. You don’t need any prior experience with or knowledge of Ant before you proceed. We’ll first see the basic structure of Ant build files, and learn how to invoke the tool. We’ll walk through the steps involved in writing a build file for a simple Java project, and then look at some of Ant’s other useful functions, including filesystem operations and pattern matching. We’ll finish by writing our own Java class that extends Ant’s functionality. In the course of the tutorial, we’ll show you how to run Ant both from the command line and from within the open source Eclipse IDE. You don’t need both environments.
Section 2. Ant basics
Ant basics introduction
In this section, we’ll give you an overview of Ant’s capabilities and strengths, and discuss its history and rise in popularity. We then delve straight into the fundamentals of Ant by examining the basic structure of a minimal build file. We’ll also introduce the concepts of properties and dependencies. What is Ant? Apache Ant is a Java-based build tool. According to its original author, James
Duncan Davidson, the name is an acronym for another neat tool. Build tools are used in software development to transform source code and other input files into an executable form (and maybe into installable product images as well). As an application’s build process becomes more complex, it becomes increasingly important to ensure that precisely the same build steps are carried out
during each build, with as much automation as possible, in order to produce consistent builds in a timely manner. Traditional projects in C or C++ often use the make tool for this purpose, where the build tasks are performed by invoking shell commands, and dependencies are defined between each of the build tasks so that they are always performed in the necessary order.
Ant is similar to make in that it defines dependencies between build tasks; however, instead of implementing build tasks using platform-specific shell commands, it uses cross-platform Java classes. With Ant, you can write a single build file that operates consistently on any Java platform (as Ant itself is implemented in the Java language); this is Ant’s greatest strength.
Ant’s other key strengths are its powerful simplicity and its ability to be seamlessly extended with custom capabilities. Hopefully, you will appreciate these strengths after completing the rest of this tutorial.
A little history
Ant started out as an internal component of Tomcat, the servlet container that is used in the reference implementation for the Java Servlet and JavaServer Pages (JSP) technologies. The Tomcat codebase was donated to the Apache Software Foundation; there, it became part of the Apache Jakarta project, which has a mission to produce open source, server-side solutions for the Java platform. The
usefulness of Ant was quickly recognized and its usage spread throughout the other Jakarta subprojects. Thus, it was made into a Jakarta subproject of its own, with the first independent release being made in July of 2000. Since then, Ant’s popularity has grown and grown. It has won numerous industry awards and become the de facto standard for building open source Java projects. In
November of 2002, this success was acknowledged by Ant’s promotion to a top-level Apache project. At the time of writing, the current stable release of Ant is 1.5.4, which supports all
JDK versions from 1.1 onwards. Beta versions of the next release, 1.6, are also available; these require JDK 1.2 or later. Planning is also underway for a future 2.0 release, which will involve a major rearchitecture. Ant 2.0 will feature improved consistency and enhanced functionality, whilst still retaining Ant’s core goals of simplicity, ease of understanding, and extensibility.
Anatomy of an Ant build file
Ant does not define its own custom syntax; instead, its build files are written in XML (see Resources ). There are a defined set of XML elements that Ant understands, and, as you’ll see in a later section, new elements can be defined to extend Ant’s capabilities. Every build file consists of a single top-level project element, which in turn contains one or more target elements. A target is a defined step in a build that performs any number of operations, such as compiling a set of source files. The operations themselves are performed by other specialized task tags, as we’ll see later. These tasks are then grouped together into target elements, as desired. All of the operations required for a build could be placed into a single target element, but that would reduce flexibility. It is usually preferable to split the operations into logical build steps, with each step contained in its own target element. This allows you to perform one individual part of the overall build without necessarily performing other parts. For instance, by only invoking certain targets, you could compile your project’s source code without creating an installable project image. The top-level project element needs to contain a default attribute that specifies the target to be executed if none is specified when Ant is invoked. Then the target itself needs to be defined, using the target element. Here is a minimal build file:
Note that this is well-formed XML, with an XML declaration specifying the version of XML being used (this is not currently required by Ant, but it is good practice), and with every element being properly closed. It is also possible to open and close an element in one go. So, instead of the separate start and end tags for the target element above, we could have written it as follows:
This more concise form can be clearer when an element doesn’t have any enclosed content.
Adding descriptions
The build file we saw in the previous section is nice and concise, but it doesn’t say much about the actual project being built. There are a number of ways to make it more descriptive without changing the functionality. Here’s an example:
A simple project introducing the use of descriptive tags in Ant build files.
As you can see, XML comments can be used throughout build files to improve clarity. Also, Ant defines its own description element and description attribute, which can be used to provide more structured annotations.
Properties
Properties in Ant are like programming language variables in that they have a name and a value. However, unlike normal variables, properties in Ant cannot be changed once they have been set; they are immutable, like String objects in the Java language. This might seem restrictive at first, but it is in keeping with Ant’s philosophy of simplicity: after all, it is a build tool, not a programming language. If
you do attempt to give an existing property a new value, this isn’t treated as an error, but the property does retain its existing value. (This behavior can be useful, as we’ll see.)
Based on the descriptive names of the elements and attributes seen so far, it should come as no surprise that the mechanism for setting properties in Ant looks like this:
To refer to this property in other parts of the build file, you would use this syntax: ${metal} For example, to use the value of one property as part of the value of another, you would write a tag that looks like this:
There are number of predefined properties in Ant. Firstly, all of the system properties set by the Java environment used to run Ant are available as Ant properties, such as ${user.home}. In addition to these, Ant defines a small set of its own properties, including ${ant.version}, which contains the version of Ant, and ${basedir}, which is the absolute path of the project’s directory (as defined by the directory containing the build file, or by the optional basedir attribute of the project element). Properties are often used to refer to files and directories on the filesystem, but this could cause problems across different platforms with different path separator characters ( / versus \, for example). Ant’s location attribute is specifically designed to hold filesystem paths in a platform-neutral way. You would use location in place of value like so:
The path separator characters used for the location attribute are converted to the correct ones for the current platform; and, as the filename is relative, it is taken to be relative to the project’s base directory. We could just as easily have written this instead:
Both versions of this tag will behave in the same fashion across different platforms. The only thing to avoid if portability is required is DOS-style drive letters in
filenames. It is also more flexible to use relative path names instead of absolute ones where possible.
Defining dependencies
Building a project generally requires a number of steps — first compiling source code and then packaging it into Java Archive Files (JARs) files, for example. (See Resources for more information on using JARs.) Many of these steps have a clearly defined order — for instance, you can’t package the classfiles until they have been generated by the compiler from the source code. Instead of specifying the targets in sequence order, Ant takes the more flexible approach of defining dependencies, just as make and similar build tools do. Each target is defined in terms of all the other
targets that need to be completed before it can be performed. This is done using the depends attribute of the target element. For example:
This approach allows you to request a build of any stage of the project; Ant will execute the defined prerequisite stages first. In the example above, if you ask Ant to developerWorks® ibm.com/developerWorks complete the compile step, it determines that the targets init and preprocess need to be executed first. The init target doesn’t depend on anything, so that will
be executed first. Then Ant will look at the preprocess target and find that it depends on the init target; because it has already run that, it doesn’t do so again, and executes the preprocess target. Finally, the compile task itself can be executed. Note that the order in which the targets appear in the build file is not important: it is only the depends attributes that determine execution order.