Cover | Table of Contents | Colophon
ant and calmly watching your boring build tool
handle complicated dependencies, deployment onto multiple servers via
FTP and SSH, and log errors. It is here that build tools like Ant
truly shine.javac. But
it's a different story when you start working with
multiple source files, with multiple dependencies, need to check code
out of a central repository automatically, test the code, and deploy
it to some remote destination. You can end up with dozens of tasks to
complete each time you want to build your application, which is the
last thing you want to spend time on when you're
already brain-dead from an all-night debugging session. When new
members join your team, you'll have to walk through
this whole process again, showing them the ropes and hoping they
don't break anything in the process.
It's for all of these reasons that
developers—and especially Java programmers—turn to Ant.make, Jam, Cons,
gnumake, and nmake, nothing is
as integrated into the Java programming language. Ant is pure Java;
you'll find line after line of Java code and
.java files if you obtain a source release of
the tool. Further, some of the most popular projects in the Java
universe are built using Ant; everything from Tomcat to JBoss to
Turbine can go from source to binary by typing
ant.Makefiles used. Ant caught on rapidly, and newer
versions followedAnt 1.1 (July 2000), 1.2 (October 2000), 1.3 (March
2001), 1.4 (September 2001), and 1.5 (July 2003). The version this
book uses—version 1.6.1—appeared in February of 2004.
Although James Davidson has moved on to work with other build tools,
Ant continues to evolve on an almost daily basis.http://ant.apache.org. Because
it's an open source project, it's
always developing. There are multiple authors, called
committers, which can write to
Ant's source code repositories. However, officially
sanctioned Ant versions don't appear too rapidly,
and when they do, they're usually backward
compatible enough to make sure your build files
aren't broken.http://ant.apache.org/ and click a link under
the Download title (either Binary Distributions or Source
Distributions).http://cvs.apache.org/builds/ant/nightly/.ant |___ bin Ant launch scripts | |___ lib Ant jars | |___ docs Ant documentation | |___ etc XSL for formatting Ant XML output
ANT_HOME
environment variable to the directory where you installed Ant.ANT_HOMEspecifically in Unix and Windows
NT/2000but it's better not to rely on them doing so
accurately.JAVA_HOME
environment variable to the directory where your JDK is installed.public class Project
{
public static void main(String args[])
{
System.out.println("No worries.");
}
}
<?xml version="1.0" ?>
<project default="main">
<target name="main" depends="compile, compress">
<echo>
Building the .jar file.
</echo>
</target>
<target name="compile">
<javac srcdir="."/>
</target>
<target name="compress">
<jar jarfile="Project.jar" basedir="." includes="*.class" />
</target>
</project>
ant
at the command-line prompt. Ant has been tested on many platforms,
including Linux; Unix versions from Solaris to HP-UX; Windows 9x, NT,
2000, and XP; OS/2 Warp, Novell Netware 6, and MacOS X.bash shell):-bash-2.05b$ ant
Buildfile: build.xml
compile:
[javac] Compiling 1 source file
compress:
[jar] Building jar: /home/httpd/vhosts/builder/Project.jar
main:
[echo]
[echo] Building the .jar file.
[echo]
BUILD SUCCESSFUL
Total time: 2 seconds
<?xml version="1.0" ?>
.
.
.
http://www.w3.org/TR/REC-xml/. XML 1.1 is out
now as well, but Ant build files are based on XML 1.0, and the
difference between these versions is small anyway, centering mostly
on the manner in which certain Unicode characters are supported.project element, which is the document
element—i.e., the element that contains all other
elements:<?xml version="1.0" ?> <project> . . . </project>
project
element.project. Those are dealt with
throughout the rest of this chapter and in Chapter 2.project
element are shown in Table 1-1.|
Attribute
|
Description
|
Required
|
|---|---|---|
name
|
%ant [options] [target [target2 [target3] ...]]
project element's default
target.|
Name
|
Description
|
|---|---|
-buildfile
file, or
-f
file, or
-file
file
|
Runs the build file specified by file.
|
-Dproperty=value |
Sets a property called property with a
value of value, and passes it to Ant.
|
-debug, -d |
Prints debugging information.
|
-diagnostics |
Prints diagnostics about Ant.
|
-emacs, -e |
if and
try/catch allow you to handle
several logic paths in Java, Ant's control tasks
allow you the same flexibility within the context of a build process.if statement. This typically involves two steps:if and
try/catch allow you to handle
several logic paths in Java, Ant's control tasks
allow you the same flexibility within the context of a build process.if statement. This typically involves two steps:condition is the name of the task Ant provides.
condition allows you to specify one or more
true/false tests (sometimes called criteria). If
all the criteria evaluate to true, a property, supplied to the
condition task, is set to true;
if one or more of the criteria evaluate to false, the property is
assigned a false value. You can check that
property's value later in the build file.available task
(covered later in the chapter) and sets the property
all.set (to true) if the files
are found: <condition property="all.set">
<and>
<available file="file1.java"/>
<available file="file2.java"/>
</and>
</condition>
-propertyfile option.message. This example points to the property
file with the property task's
file attribute, which can hold the fully qualified
name of the file. You can use the property
task's url attribute to point to
a property file.<?xml version="1.0" ?>
<project default="main">
<property file="build.properties" />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}" includes="*.class" />
</target>
</project>
=
|
Type
|
Description
|
|---|---|
|
Assertions
|
Enables, or disables, Java 1.4 assertions
|
|
Description
|
Holds a description of the project that can be viewed if you use the
Ant
-projecthelp command |
|
DirSet
|
Contains a group of directories
|
|
FileList
|
Contains a named list of files
|
|
FileSet
|
Contains a groups of files
|
|
File mappers |
javac task through compressing and packaging the
results with tasks such as jar and
tar. Along the way, I'll discuss
several central build issues, such as keeping track of the build
number, storing that number in a JAR file's manifest
file, getting input from the user and acting on that input, calling
one Ant target from another, creating Javadoc with the
javadoc task, and more.javac task compiles Java source code.
You've seen javac at work many
times in this book but haven't exhausted what this
task has to offer by any means. You can get an idea how extensive a
task it is by its huge number of attributes, shown in Table 3-1.|
Attribute
|
Description
|
Required
|
Default
|
|---|---|---|---|
bootclasspath
|
Specifies where to find any bootstrap class files.
|
No
| |
bootclasspathref
|
Specifies where to find any bootstrap class files, given as a
reference.
|
No
| |
classpath
|
Specifies the classpath you want to use. |
javac task compiles Java source code.
You've seen javac at work many
times in this book but haven't exhausted what this
task has to offer by any means. You can get an idea how extensive a
task it is by its huge number of attributes, shown in Table 3-1.|
Attribute
|
Description
|
Required
|
Default
|
|---|---|---|---|
bootclasspath
|
Specifies where to find any bootstrap class files.
|
No
| |
bootclasspathref
|
Specifies where to find any bootstrap class files, given as a
reference.
|
No
| |
classpath
|
Specifies the classpath you want to use.
|
No
| |
classpathref
|
Specifies the classpath you want to use, given as a reference to a
path.
|
No
| |
compiler
|
delete task.delete task is covered in detail in Chapter 4.input task, which
creates a new property based on user input.input to work.
This build file asks whether it's OK to delete the
bin directory, and if it's not,
the build fails. It queries the user using the
input task, and creates a new property,
do.delete, based on the user's
input.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .jar file." />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<input
message="Deleting bin directory OK?"
validargs="y,n"
addproperty="do.delete"
/>
<condition property="do.abort">
<equals arg1="n" arg2="${do.delete}"/>
</condition>
<fail if="do.abort">Build aborted.</fail>
<delete dir="${output}" />
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}" includes="*.class" />
</target>
</project>
%ant
Buildfile: build.xml
init:
[input] Deleting bin directory OK?(y,n)
y
[delete] Deleting directory /home/steven/input/bin
[mkdir] Created dir: /home/steven/input/bin
compile:
[javac] Compiling 1 source file to /home/steven/input/bin
compress:
[jar] Building jar: /home/steven/input/bin/Project.jar
main:
[echo]
[echo] Building the .jar file.
[echo]
BUILD SUCCESSFUL
Total time: 5 secondsantcall
task, which you can use to call one Ant task from another, and the
ant task, which calls Ant tasks in other build
files.antcall is that you're starting a
new instance of Ant and executing targets in it. When you call an Ant
target with antcall, its dependent targets are
executed in order, something that can be confusing if you think
you're calling a single target. Generally,
it's best to do things the standard way and let Ant
sort out the dependencies as it's supposed to.
However, Ant can make life easier, as when you have a build file that
creates a distribution for many different servers, and when varying
sets of tasks need to be executed for each. (Even in cases like that,
however, you can still set things up easily enough with
if and unless and true/false
properties.)antcall, you can think of that call
as creating a new project; all the properties of the current project
are available in that new project by default. The attributes of the
antcall task appear in Table 3-4.|
Attribute
|
Description
|
Required
|
Default
|
|---|---|---|---|
inheritAll
|
If true, means the task should pass all current properties to the new
Ant project. Properties passed to the new project will override the
properties that are set in the new project. |
ant, you can
execute build files outside the current build file, and you can
include other build files in the current file.
The old way of doing this was to rely on XML and the Ant XML parser
to do the work for you. For example, if you wanted to include the
entire contents of a document named shared.xml
at a specific point in a build file, you could start by declaring an
XML entity named, say, shared in your build file:<?xml version="1.0"?> <!DOCTYPE project [ <!ENTITY shared SYSTEM "file:shared.xml"> ]> . . .
&shared;, like this:<?xml version="1.0"?>
<!DOCTYPE project [
<!ENTITY shared SYSTEM "file:shared.xml">
]>
<project default="main" basedir=".">
&shared;
<target name="init">
.
.
.
</target>
.
.
.
</project>
import task that can be used to include build
files. The referenced files have to be complete Ant build files,
which are inserted whole (minus the XML declaration and
<project> and
</project> tags). Here's
how the above example would work using the import
task:<?xml version="1.0"?>
<project default="main" basedir=".">
<import file="shared.xml"/>
<target name="init">
.
.
.
</target>
.
.
.
</project>
import task appear in Table 3-8.|
Attribute
|
Description
|
Required
|
Default
|
|---|---|---|---|
javadoc task. This task creates
documentation using the Java javadoc tool, a
process that involves compilation, which merits including this task
in this chapter. This is one of the largest tasks in Ant because of
the enormous number of javadoc options.
/** This application prints out "No worries." */
public class Project
{
public static void main(String args[])
{
System.out.println("No worries.");
}
}
javadoc task in a new target I'll
name doc, which will put the generated Javadoc in
a doc directory, as you see in Example 3-3.
<![CDATA[...]> sections, which
the build file uses to pass data to the javadoc
tool.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .jar file." />
<property name="src" location="source" />
<property name="docs" location="docs" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, doc, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<mkdir dir="${output}" />
<mkdir dir="${docs}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="doc">
<javadoc
sourcefiles="${src}/Project.java"
destdir="${docs}"
author="true"
version="true"
use="true"
windowtitle="Project API">
<doctitle><![CDATA[<h1>Project API</h1>]]></doctitle>
<bottom><![CDATA[<i>Copyright © 2005</i>]]></bottom>
</javadoc>
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}" includes="*.class" />
</target>
</project>jar task JARs files for you. Example 3-4 is a fairly complex example, which creates a
new JAR file with an included manifest,
MANIFEST.MF, that contains several attributes.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .jar file." />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}"
includes="*.class" >
<manifest>
<attribute name="Author" value="${user.name}"/>
<section name="Shared">
<attribute name="Title" value="Example"/>
<attribute name="Vendor" value="MegaAntCo"/>
</section>
<section name="Copyright">
<attribute name="Copy" value="(C) MegaAntCo 2005"/>
</section>
</manifest>
</jar>
</target>
</project>
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.6.1 Created-By: 1.4.2_03-b02 (Sun Microsystems Inc.) Author: Steven Holzner Name: Shared Title: Example Vendor: MegaAntCo Name: Copyright Copy: (C) MegaAntCo 2005
jarsign task like this:<signjar jar="jarfile.jar" alias="development"
keystore="local.keystore"
storepass="secret"/>
buildnumber task is a basic task that can be used
to track these kinds of build numbers.build.number to the value
that was read in (or to 0, if no value was read). It will increment
the number by one and write the new value back to the file.file, which holds the name in which you want to
store the build number. This attribute is not required.
I'll take a look at an example using
buildnumber after discussing time stamps, the next
topic.tstamp task sets properties holding the current
time so you can time stamp your builds. This task creates the
DSTAMP (day stamp), TSTAMP
(time stamp) and TODAY properties in the current
project. By default, the DSTAMP property is in the
format "yyyyMMdd",
TSTAMP is in the format
"hhmm", and
TODAY is in the format "MMMM dd
yyyy". If you use this task, it's
almost invariably run in an initialization target.buildnumber
and tstamp.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .jar file." />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<buildnumber/>
<tstamp/>
<delete dir="${output}"/>
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}"
includes="*.class" >
<manifest>
<attribute name="Author" value="${user.name}"/>
<section name="Shared">
<attribute name="Title" value="Example"/>
<attribute name="Vendor" value="MegaAntCo"/>
<attribute name="Build" value="${build.number}"/>
<attribute name="Date" value="${TODAY}"/>
</section>
<section name="Copyright">
<attribute name="Copy" value="(C) MegaAntCo 2005"/>
</section>
</manifest>
</jar>
</target>
</project>
tar, gzip, and
zip; tasks to prepare directories for deployment
like delete and mkdir; and
tasks to deploy applications like copy and
move for local and network deployment, as well as
ftp, telent,
sshexec, and mail for remote
deployment. You'll see other deployment-related
tasks, such as touch to set the deployed
files' modification dates to a specific value (as is
done for commercial software deployments), fixcrlf
to fix text file line endings (as in readme,
.sh, .bat, .html, or .ini files)
for different platforms, and more. Finally, you'll
learn how to handle build automation, setting up builds to run on
their own, at a schedule of your choosing.get (used
to send administrative commands to web servers remotely),
serverdeploy, war, and other
Ant tasks designed to load web applications to various servers. Chapter 8 and part of Chapter 9
also specifically discuss how to deploy to Enterprise
JavaBean© (EJB) application servers.tar, gzip, and
zip. These are not the only way to package
applications; the jar task was covered in Chapter 3 on Java Development, and the
war task, a special form of the
jar task for Web applications that makes
allowances for files like the deployment descriptor
web.xml, will be covered in Chapter 8.tar task creates a TAR archive, handy for
archiving files for distribution. This task is directory-based and,
like other such tasks, forms an implicit FileSet that defines which
files—relative to the tar, gzip, and
zip. These are not the only way to package
applications; the jar task was covered in Chapter 3 on Java Development, and the
war task, a special form of the
jar task for Web applications that makes
allowances for files like the deployment descriptor
web.xml, will be covered in Chapter 8.tar task creates a TAR archive, handy for
archiving files for distribution. This task is directory-based and,
like other such tasks, forms an implicit FileSet that defines which
files—relative to the basedir attribute
setting—will be included in the archive.compression attribute to gzip
or bzip2, you can compress the output
.tar file to the specified format. For instance,
Example 4-1 compiles code and places the resulting
Project.class file in a
.tar.gz file,
Project.tar.gz, by setting the
compression attribute to gzip.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .tar.gz file." />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<tar
destfile="${output}/Project.tar.gz"
basedir="${output}"
includes="*.class"
compression="gzip"/>
</target>
</project>
delete and
mkdir. Both these tasks can be used locally or on
a network to set up the directory structure you need to deploy
applications.ftp task, coming up later in this chapter.delete
is great to clean up a previous installation or to clean deployment
directories before installing. This task deletes a single file, a
directory and all its files and subdirectories, or a set of files
specified by one or more FileSets.<delete file="/lib/Project.jar"/>
<delete dir="${dist}"/>
<delete includeEmptyDirs="true">
<fileset dir="${dist}"/>
</delete>
delete at work in
various places throughout the book, as in the build file in the
input folder for Chapter 3s code (repeated in Example 4-2), where the user is asked for confirmation
before deleting anything.<?xml version="1.0" ?>
<project default="main">
<property name="message" value="Building the .jar file." />
<property name="src" location="source" />
<property name="output" location="bin" />
<target name="main" depends="init, compile, compress">
<echo>
${message}
</echo>
</target>
<target name="init">
<input
message="Deleting bin directory OK?"
validargs="y,n"
addproperty="do.delete"
/>
<condition property="do.abort">
<equals arg1="n" arg2="${do.delete}"/>
</condition>
<fail if="do.abort">Build aborted.</fail>
<delete dir="${output}" />
<mkdir dir="${output}" />
</target>
<target name="compile">
<javac srcdir="${src}" destdir="${output}" />
</target>
<target name="compress">
<jar destfile="${output}/Project.jar" basedir="${output}" includes="*.class" />
</target>
</project>