An Ant Primer: Part I
by Merwyn Welcome
(June, 2003)We want to hear from you! Please send us your FEEDBACK.
The following technical article may contain actual software programs in source code form. This source code is made available for developers to use as needed, pursuant to the terms and conditions of this license.
Table of Contents
- Introduction
- What is Ant?
- Why Ant?
- What is the Build Process?
- Installing Ant
- Building a Simple Application
- The Ant Directory Structure
- More Ant Build File Syntax
- Ant Datatypes and Properties
- References
Introduction
This article is presented in two parts. The first part is based on the standard Ant version 1.5. The second part is based on ASAnt, the Sun Microsystems extension to the basic Ant tool for building and deploying Sun ONE Application Server (Java 2 Platform, Enterprise Edition [J2EE]) applications.What is Ant?
Ant is a build tool for Java platform programmers that helps to automate the building and deployment of the Java platform and J2EE software applications. It is written completely in the Java programming language. As such, Ant is a cross platform application; it runs on any platform that supports the Java 2 Platform, Standard Edition (J2SE), versions 1.3.x. Ant has a simple XML syntax. It is easy to learn and to use, to create powerful Ant build files to compile and bundle applications in .jar, .ear, or .war files, and to deploy J2EE software applications. Ant is easy to extend. In fact, this is exactly what Sun Microsystems has done to create ASAnt. Because of its execution speed and the fact that Ant build files can be executed from within other Ant build files, this makes Ant quite scalable. Ant is often referred to as an arguably better Make. Make is a C/C++ build tool that is used for building C/C++ applications. There are several different Make tools. Neither the tools nor the Make files are cross platform.Why Ant?
Ant is used by a wide number of Java platform development tools, notably IDEs like JBuilder, and Sun ONE Studio developer tools. Once an Ant build file has been created and tested, it is easy even for someone unfamiliar with the build process of the application to correctly build and deploy the application using Ant and the build script. The same build script can be used on other platforms with little or no modification. Ant build files use a relatively simple syntax that makes creating them easy to learn. Ant build files can be created in an iterative process, therefore, creating powerful Ant build files can be done relatively quickly.What is the Build Process?
The Ant build process may include some or all of the following processes: compiling Java programming language source code; generating documentation; testing applications; packaging and deploying J2EE platform applications, and dynamically generating more source code. All of these processes can be automated using Ant tool.Installing Ant
A quick way to tell if Ant is installed properly is to enter the following command in a command-line terminal window:%ant -versionIf Ant is properly installed, you will get a message stating the version of Ant that is running. Otherwise, you will get an error message.Installing Ant is easy. Simply download the latest Ant distribution from http://jakarta.apache.org/ant/ and set the environment variables:
ANT_HOME and JAVA_HOMEAssuming that Ant is in /Tools/Ant/apache-ant-1.5.3-1, and that the Java platform is in /J2SDK:setenv ANT_HOME /Tools/Ant/apache-ant-1.5.3-1setenv JAVA_HOME /J2SDKsetenv PATH ${PATH}:${ANT_HOME}/bin:${JAVA_HOME}/binwill set the ANT_HOME and JAVA_HOME environment variables and add them to your PATH.Building a Simple Application
Although HelloWorld is quite a simple Java programming language application, it allows us to demonstrate some of the power of Ant without being distracted by the complexities of Java platform programming.public class HelloWorld { public static void main(String args[]) { System.out.println("Hello World!"); } }Listing 1
Following is a simple build file.
<?xml version="1.0"?> <project name="firstbuild" default="compile"> <target name="compile"> <javac srcdir="." /> <echo>Compilation Complete!! </echo> </target> </project>Listing 2
Here is the same build file with a few embedded comments that are intended to make the build file more readable.
<?xml version="1.0"?> <!-- build.xml - A simple Ant build file --> <!-- Note - No DTD --> <!-- Note - Named build file --> <project name="HelloWorld" default="compile"> <!-- Note - Single target --> <target name="compile"> <javac srcdir="." /> <echo>Compilation Complete!!</echo> </target> </project>Listing 3
The most unusual aspect of an Ant build file is that you cannot include a DTD (Document Type Definition) of your own.
The nodes or elements used in our first Ant build file were:
projecttargetjavacechoEvery Ant build file must have a <project> element. The <project> element defines the name of the Ant project. It sets the default target to be executed if none is supplied when this build file is invoked. This <project> element has only one subnode, a <target> element. The <project> element has two attributes, the name attribute and the default attribute. These attributes are used to set the project name and the default target respectively.The <target> elements are where all the Ant work takes place. This <target> element has two subnodes, a <javac> element and an <echo> element. The <target> element has one attribute - the name attribute. Each project may have several target elements. The name attribute names each target. One of the target names should match the names given to the default attribute.
The <javac> element has one attribute, the scrdir attribute. The scrdir attribute sets the directory where the source code to be compiled by the javac element is located.
The <echo> element simply echoes the text between the opening and closing tags. The <echo> element is very useful for informational and/or debugging purposes.
By default, Ant looks for a file called build.xml in the current working directory. You may give your Ant build file a more descriptive name if you so desire. If you have named your Ant build file build.xml, and have included a default target as we have in this example, then use the following command to run your first Ant build script:
%antIf you have used a more descriptive name for your Ant build file, such as helloWorld.xml, and have included a default target as we have in this example, then use the following command to run your first Ant build script:%ant -f helloWorld.xml%ant -buildfile helloWorld.xmlAnt does a fairly decent job of error reporting. For an example of Ant's reporting of errors in the Ant build file and in the Java programming language source code, run the labs in examples three and four. Also, for more detailed Ant messages, use the verbose option when running Ant.%ant -verbose%ant -verbose -f helloWorld.xml%ant -verbose -buildfile helloWorld.xmlThe Ant Directory Structure
It is not strictly necessary to follow the suggested directory structure but it strongly recommended that you do, at least until you are comfortable enough with it to make your own modifications. Imposing a directory structure helps to: automate cleanup after the build process is complete, reflect Java programming language package structure for easier Builds, and deploy your .jar files to the correct location.In the following diagram, the build file build.xml, is contained in the project folder/directory at the same level as the folder containing the source code. The diagram shows the three directory paths of an Ant project: the source files directory, the intermediate class files directory, and the distributable archive files directory. The build and dist directories are created by the build file.
![]()
Figure 1.
More Ant Build File Syntax
<?xml version="1.0"?> <!-- build.xml - A simple Ant build file --> <!-- Note - No DTD --> <!-- Note - Named build file --> <!-- Note - Multiple targets --> <project name="HelloWorld" default="execute"> <description> Compiles and runs a simple java application </description> <!-- Note - Target to initialize the project --> <target name="initialize" description="Initializes the project by creating temporary directories" > <mkdir dir="build/classes" /> <mkdir dir="dist" /> </target> <!-- Note - Target to compile the project source --> <target name="compile" depends="initialize" description="Compile the project source code" > <javac srcdir="src" destdir="build/classes" /> </target> <!-- Note - Target to execute the archived jar --> <target name="execute" depends="archive" description="Executes the archived jar" > <echo level="warning" message="running"/> <java jar="dist/helloWorld.jar" classpath="dist" failonerror="true" fork="true" /> </target> <!-- Note - Target to archive the compiled classes --> <!-- Note - Also provides a manifest file --> <target name="archive" depends="compile" description="Archives the compiled classes" > <jar destfile="dist/helloWorld.jar" manifest="MANIFEST.MF" basedir="build/classes" /> </target> <!-- Note - Target to re-initialize the project --> <target name="cleanUp" depends="initialize" description="Re-initializes the project by removing temporary directories" > <delete dir="build" /> <delete dir="dist" /> </target> </project>Listing 4
The project for the above Ant build file is named HelloWorld; the default target is execute. If this build file is run without any arguments, it will execute the application it has built. Each node or element of the build file contains a description attribute that may be used to give additional information about the build file and the various targets.
If Ant is run with the projecthelp option by default, it displays all of the available targets. When the description attribute is included in the target element, the description is displayed after each target name. The description helps the user understand the function of the various targets. The projecthelp option maybe used as follows:
%ant -projecthelp%ant -projecthelp -f helloWorld.xml%ant -projecthelp -buildfile helloWorld.xmlThis build file has five targets: initialize, compile, execute, archive, and cleanUp. It has two separate dependency paths. The execute target has been set as the default. Following is the dependency path for the execute target:
execute --> archive --> compile --> initializeThe cleanUp target has a much shorter dependency path:
cleanUp --> initializeThe initialize target initializes the project by creating the temporary directories build/classes and dist as recommended above. The mkdir subnode is used to create the temporary directories. The initialize target is the only target that does not depend on another target.
The compile target compiles all the project source code. The compile target depends on the initialize target. The javac subnode is used to compile all the project source code. The srcdir attribute sets the directory where the source code to be compiled by the javac element is located. The destdir attribute sets the directory where the compiled classes are to be saved after they are compiled by the javac element. If the initialize target fails for any reason, such as a disk full error, the compile target will not be executed.
The archive target archives all the compiled classes into a project .jar file. The archive target depends on the compile target. The jar subnode is used to archive all the project's compiled classes. The destfile attribute sets the directory where the archive is to be saved by the jar subnode. The manifest attribute sets the name of the manifest file to be used by jar subnode. The basedir attribute sets the directory where the classes to be archived by the jar subnode are located. If the compile target fails for any reason, such as a compilation error, the archive target will not be executed.
The execute target executes the archived project .jar. The execute target depends on the archive target. The java subnode is used to execute the project .jar file. The jar attribute sets the name and location of archived project .jar file to be executed by the java element. The classpath attribute points to the directory where the project .jar file is located. If the archive target fails for any reason, such as a corrupt manifest file, the execute target will not be executed.
When taking the cleanUp target path, the cleanUp target re-initializes the project by removing temporary directories. The cleanUp target depends on the initialize target. The delete subnode is used to delete the temporary directories. The dir attribute sets the location of the temporary directories to be deleted by the delete element. If the initialize target fails for any reason, such as a disk full error, the cleanUp target will not be executed.
Ant Datatypes and Properties
Ant datatypes and properties are two of the most powerful features of the Ant build tool. Ant datatypes and properties make build files adaptable, maintainable, controllable, and reusable.A typical use of Ant datatypes is to handle files and paths. In the following build file snippet, a build file fileset is created from all Java programming language source files in the src directory and all its subdirectories. The fileset is named source.files.
<fileset dir"=src" includes="**/*.java" id="source.files">Once this list of files is associated with the fileset named source.files, source.files may be used anywhere a fileset is required - such as in the following snippet:<copy todir="backup"> <fileset refid="source.files"> </copy>Ant datatypes can even be used to select files with certain text strings, as in the following snippet.<copy todir="newdir" includeemptydirs="false"> <fileset dir="web"> <contains text="Test"/> </fileset> </copy>Or they can be used to ensure that the resulting fileset does not contain duplicates, as in this snippet:<copy todir="newdir" includeemptydirs="false"> <fileset dir="web"> <not> <present targetdirs="source.files"/> </not> </fileset> </copy>Here, in the following two snippets, the classpath is being set.<classpath> <pathelement location="dist/some.jar"> </classpath><classpath> <pathelement path="build/classes;dist/some.jar"> </classpath>In the following snippet, the classpath is being set to all the .jar files in the distribution directory.<classpath> < fileset dir="dist"> <include name="*.jar"/> </fileset> </classpath>Often it is necessary to alter the contents of a file (or several files) "on the fly" during the build process. The following build file creates a fileset of all the JavaServer Pages (JSP) files in the preprocess directory, and in all subdirectories. Each file in the fileset is copied into a new directory created by the build file. Before a file is copied to the new directory, its contents are searched for the existence of the tokens DATE and TIME. Whenever they are found, they are replaced with the current date and time respectively.<?xml version="1.0"?> <!-- build.xml - A simple Ant build file --> <!-- Note - No DTD --> <!-- Note - Named build file --> <project name="Process" default="process"> <!-- Note - Target to initialize the project --> <target name="initialize"> <mkdir dir="processed" /> </target> <!-- Note - Process and copy JSPs --> <target name="process" depends="initialize" > <!-- Note - <tstamp/> initializes the DSTAMP and TSTAMP properties --> <tstamp/> <copy todir="processed" overwrite="true"> <fileset dir="preprocess" includes="**/*.jsp"/> <filterset> <filter token="DATE" value="${DSTAMP}"/> <filter token="TIME" value="${TSTAMP}"/> </filterset> </copy> </target> </project>Listing 5
A sample JSP to be be modified by the build file in Listing 5:
<html> <head> <title>Example JSP </head> <body> System build time: @DATE@ at @TIME@ </body> </html>Listing 6
Ant properties are very similar to Java programming language variables. At their simplest, they are a mapping between names and values. They are similar to java.util.properties. Ant provides some built-in properties, including: ant.file, ant.home, ant.java.version, ant.version, and basedir. Following is a simple script that displays some of the properties of an Ant build environment.
<?xml version="1.0"?> <!-- build.xml - A simple Ant build file --> <!-- Note - No DTD --> <!-- Note - Echo Ant built-in properties --> <project name="Properties" default="Display" basedir="."> <!-- Note - Single target --> <target name="Display"> <echo message="ant.file = ${ant.file}" /> <echo message="ant.home = ${ant.home}" /> <echo message="ant.project.name = ${ant.project.name}" /> <echo message="ant.java.version = ${ant.java.version}" /> <echo message="ant.version = ${ant.version}" /> <echo message="basedir = ${basedir}" /> <echo message="user.name = ${user.name}" /> <echo message="user.home = ${user.home}" /> <echo message="java.home = ${java.home}" /> </target> </project>Listing 7
Ant properties are often saved in a properties file. Given an Ant property file named build.properties, use the following statement to load the properties contained in build.properties:
<property file="build.properties">Here is an example of some properties being set:<property name="build.classes" value="build/classes" /> <property name="build" value="build" /> <property name="dist" value="dist" /> <property name="src" value="src" /> <property name="manifest" value="MANIFEST.MF" /> <property name="build.nodebug" value="no" /> <property name="build.debug" value="yes" /> <property name="build.deprecation" value="on" />Be careful where and when you set the values of properties, because the value of a property does not change once it has been set.This is the revised build file from Listing 4, using properties instead of literal values.
<?xml version="1.0"?> <!-- build.xml - A simple Ant build file --> <!-- Note - No DTD --> <!-- Note - Named build file --> <!-- Note - Multiple targets --> <project name="HelloWorld" default="execute"> <!-- Note - Set up build file properties --> <property name="build.classes" value="build/classes" /> <property name="build" value="build" /> <property name="dist" value="dist" /> <property name="src" value="src" /> <property name="manifest" value="MANIFEST.MF" /> <property name="build.nodebug" value="no" /> <property name="build.debug" value="yes" /> <property name="build.deprecation" value="on" /> <description> Compiles and runs a simple java application </description> <!-- Note - Target to initialize the project --> <target name="initialize" description="Initializes the project by creating temporary directories" > <mkdir dir="${build.classes}" /> <mkdir dir="${dist}" /> </target> <!-- Note - Target to compile the project source --> <target name="compile" depends="initialize" description="Compile the project source code" > <javac srcdir="${src}" destdir="${build.classes}" debug="${build.nodebug}" deprecation="${build.deprecation}" <include name="**/*.java"> /javac> </target> <!-- Note - Target to execute the archived jar --> <target name="execute" depends="archive" description="Executes the archived jar" > <echo level="warning" message="running"/> <java jar="${dist}/helloWorld.jar" classpath="${dist}" failonerror="true" fork="true" /> </target> <!-- Note - Target to archive the compiled classes --> <!-- Note - Also provides a manifest file --> <target name="archive" depends="compile" description="Archives the compiled classes" > <jar destfile="${dist}/helloWorld.jar" manifest="${manifest}" basedir="${build.classes}" /> </target> <!-- Note - Target to re-initialize the project --> <target name="cleanUp" depends="initialize" description="Re-initializes the project by removing temporary directories" > <delete dir="${build}" /> <delete dir="${dist}" /> </target> </project>Listing 8
I hope that this brief primer has given you some understanding of the power of Ant. I encourage you to download, install Ant and start playing with it. It will significanly improve your build process. In Part II we will look at the extentions found in ASAnt, and deploying J2EE applications with Ant.
References
- Ant: The Definitive Guide, by Jesse Tilly and Eric M. Burke
- NetBeans: The Definitive Guide, by Tim Boudreau, Jesse Glick, Simeon Greene, Vaughn Spurlin and Jack Woehr
- Java Tools for eXtreme Programming, by Richard Hightower and Nicholas Lesiecki
- Apache Ant http://jakarta.apache.org/ant
- Frequently Asked Questions About Ants http://www.antcolony.org/FAQ2.htm
DOC ID# 1915