Section 17.3. Ant Fundamentals


17.3. Ant Fundamentals

Before we look at using Ant for tasks specific to enterprise application development, let's run through the basic mechanics of Ant, for readers who haven't used Ant before. If you've used Ant before, you can skip this section and jump right to "Enterprise Tasks" later in this chapter.

17.3.1. Buildfiles

When you execute Ant, you specify a command file that it should use to load the definitions of targets. The buildfile uses an XML format to define the targets and the tasks involved in running each target. The root XML element of the buildfile is always a <project> element, which defines the name of the project and the default target that the buildfile will execute (if you don't specify one when invoking Ant). The <project> element in Ant buildfiles supports the attributes listed in Table 17-1.

Table 17-1. Project element attributes

Attribute name

Purpose

Required

name

Descriptive name of the project, sometimes used by IDEs and other tools to describe a particular project buildfile. The project name can be accessed in the buildfile using the ant.project.name built-in property.

No

default

Name of the default target to be invoked when Ant is invoked on this buildfile with no specific target specified.

Yes

basedir

The root directory where Ant will execute all targets and tasks in this buildfile. This base directory will be the starting point for any relative paths used in the buildfile. The base directory can also be specified using the basedir built-in property. This same property can be used to access the project's base directory in the buildfile. If the base directory isn't specified, it defaults to the directory containing the buildfile.

No


Within the root <project> element, you define the targets for your project, with each target consisting of a series of tasks to execute in order to satisfy the overall target. Example 17-1 shows a very basic Ant buildfile that contains a single target, hello, that simply prints a message to the console.

Example 17-1. An extremely simple Ant buildfile
 <!-- Simple Ant buildfile; default (and only) target just       prints "Hello, world" to the console --> <project name="JEnt-Ant-simple" default="hello">     <target name="hello">         <echo message="Hello, world"/>     </target> </project> 

To execute targets from this buildfile, we would usually place it in a directory with a default filename of build.xml and invoke Ant on the command line from the directory with the build.xml file:

 > ant hello Buildfile: build.xml   hello:      [echo] Hello, world   BUILD SUCCESSFUL Total time: 1 second 

In this degenerately simple Ant buildfile, the project is named Jent-Ant-simple, according to the name attribute on the <project> element. The default attribute on the <project> element is used to specify the name of the default target that is invoked if this buildfile is run with no target specified. Within the <project> element, we've defined a single target using a <target> element. The target is named using the name attribute. Finally, within the target, we've defined the tasks that should be executed when this target is invoked. In this case, we're using a built-in Ant task named echo, which echoes a text message to the console.

When we invoked the ant command, Ant first looked in the current directory for the default buildfile, build.xml. Once the file was found, Ant parsed the buildfile and searched for a target with a name of hello, the target specified on the command line. Once it found that target in our buildfile, it began executing the tasks defined within that target. In our case, Ant had to execute only one task, the echo task that prints our message to the console.

Since we specified that the hello target should be the default, we could also invoke the target by just running Ant with no arguments:

 > ant 

This should result in the same console output. If we named our buildfile something other than the default of build.xml, we could specify the name of the buildfile with the -buildfile option:

 > ant -buildfile build.xml hello 

The command-line arguments -file and -f are synonyms for the -buildfile argument.

17.3.2. Defining Targets, Their Tasks, and Their Relationships

As we saw in our very simple example, targets are defined in the Ant buildfile as child <target> elements of the root <project> element. The <target> element supports the attributes shown in Table 17-2.

Table 17-2. Target element attributes

Attribute name

Purpose

Required

name

The name of the target, used to reference this target elsewhere in the buildfile and to invoke it from the command line.

Yes

depends

A list of targets (comma-delimited) on which this target depends. When this target is invoked, its list of predecessor targets will be invoked before the tasks in this target are invoked. The predecessor targets are executed in the order they are listed in the depends attribute, and the dependencies are handled recursively.

No

if

This target will be executed only if the property named by this attribute has been set.

No

unless

This target will not execute if the property named by this attribute has been set.

No

description

A readable description of the target, sometimes used by IDEs and other tools to display the contents of a buildfile.

No


A buildfile can simply contain a flat collection of independent targets, but often you will want to make use of target dependencies in order to make things more efficient.

A slightly more involved (and more useful) buildfile is shown in Example 17-2. This sample buildfile demonstrates basic use of target dependencies.

Example 17-2. Simple target dependencies
 <!-- A simple buildfile that compiles a set of Java sources      from the src directory, placing the results in the classes      directory, and can also create a jar from the classes --> <project name="JEnt-Ant-basic" default="archive">     <target name="compile"            description="Compile all code in src dir to the classes dir">         <!-- Create the classes dir, if needed -->         <mkdir dir="classes"/>         <!-- Compile the java code into classes -->         <javac srcdir="src" destdir="classes"/>     </target>     <target name="archive" depends="compile"             description="Create an archive from the contents of the classes dir">         <jar destfile="classes.jar" includes="classes/**"/>     </target>     <target name="clean"             description="Remove all artifacts created from other targets">         <!-- Remove the classes dir created by the compile target -->         <delete dir="classes"/>         <!-- Remove the jar generated by the archive target -->         <delete file="classes.jar"/>     </target> </project> 

We've defined three targets here: compile, archive, and clean. The compile target runs javac on a set of code in the src directory and puts the generated classes into the classes directory. It does this by first using the built-in mkdir task to create the classes directory (if it doesn't exist) and then running the built-in javac task, which runs the Java compiler on the source code directory specified in the srcdir attribute and puts the results in the directory named by the destdir attribute. Additional details on these built-in tasks can be found in "Core Tasks" later in this chapter.

The archive target creates a jar file from all of the classes in the classes directory, using the built-in jar task to create the archive. We specified that the archive target depends on the compile target using the depends attribute on the target definition. This ensures that whenever we run the archive target, the source code is compiled first and then the archive is created. With this dependency in place, if we invoke the archive target:

 > ant archive 

Ant detects the dependency, runs the compile target, and then runs the tasks in the archive target:

 Buildfile: build.xml compile:     [mkdir] Created dir: classes     [javac] Compiling 1 source file to /jent/examples/ant/ex-basic/classes archive:     [jar] Building MANIFEST-only jar: /jent/examples/ant/ex-basic/classes.jar     [jar] Building jar: /jent/examples/ant/ex2-jar/classes.jar BUILD SUCCESSFUL Total time: 7 seconds 

Finally, we've included a clean target that removes all artifacts generated by the other targets in the buildfile. It uses the built-in delete task to remove the classes directory created by the compile target and the jar archive created by the archive target. It's good practice to define a target like this in your project buildfiles to make it easy to clear out the cruft left from using the buildfile. This can be helpful, for example, in situations in which you're using a source control system. A developer may check out the project files (including the Ant buildfile), use Ant to build classes and deploy components as he works on the code, and at some point he'll want to check in his changes. He will want to avoid checking in development artifacts, like class files and archives created for deployment purposes. The clean target allows you and other developers working on the project to clear out any transient files before checking in their updates.

17.3.3. Properties

So far, all of the targets and tasks we've defined in our examples have been explicitly specifiedall of the filenames and directory locations have been hardcoded into the buildfile itself. Similar to writing software, the use of literals like this is not a good long-term approach to managing buildfiles. It makes your buildfiles inflexible, and when a change is necessary, it typically needs to be made in several places, which makes errors more likely. In our previous example, the jar file created by the archive target is named classes.jar. If we wanted to change that to something else (if, for example, another project or library named its archive by the same name), we'd have to make the change to both the jar task in the archive target (the destfile attribute would have to be changed), and the second delete task in the clean target would need to be changed as well.

Ant supports the use of properties to set configurable parameters used in buildfiles. Properties have a name and a string value, and once they are set, they can be used anyplace a string literal is needed, such as an attribute value on a target or task.

17.3.3.1. Setting properties

Properties can be set in a number of different ways. The most straightforward way is to use the Ant <property> element to set a property directly in the buildfile:

 <project name="sample" default="compile">   <property name="java.src.dir" value="src"/>   . . . 

Note that the property task normally sits outside any target definitions so that property values can be used in any targets that need them. You can include the property task in your target definitions, but these property settings won't be processed until the target is invoked.

Another way to manage properties is to place them in a separate Java-style properties file, and import the entire file into the buildfile using a different variation of the property task. Here is how you would import a properties file using the local path to the file:

 <project name="sample" default="compile">   <property file="build.properties"/>   . . . 

In this example, the file must be named build.properties and located in the same directory as the buildfile. The property task also allows you to refer to a properties file using a Java-style resource name (located using the Java classpath) or (as of Ant 1.6) using a URL that refers to a properties file.

You can also load all of the current user's system environment variables using the property task:

 . . .   <property environment="env"/> . . . 

This task defines an Ant property for each environment variable found in the user's system settings. You refer to these properties using the prefix that you specified in the environment attribute. In our example, we specified env as the prefix, so an environment variable named CLASSPATH would be referenced as env.CLASSPATH in our buildfile.

You can load multiple sets of properties, if needed, using multiple instances of the property task. This can be used for managing distinct collections of properties or overlapping properties settings that allow individual developers to override default project properties. It's important to remember the precedence rules for Ant properties in these cases, though. Properties are set in the order that they are encountered in parsing the buildfile, but the first value encountered for a property wins over any subsequent ones. In other words, properties in Ant are immutableonce they're set, their value can't be changed. This can lead to potentially confusing situations, such as:

 . . .   <property name="foobar" value="firstvalue"/>   <property name="foobar" value="secondvalue"/> . . . 

Because of the precedence rules in Ant, the property named foobar ends up with the value firstvalue in this case because it encounters the first property task and sets the foobar property there and the second property setting is ignored. As long as you remember the immutable nature of properties, it isn't hard to keep these precedence rules straight.

17.3.3.2. Referencing properties

Regardless of how a property is set, it can be referenced within the buildfile using the syntax ${property name}. With our earlier java.src.dir property set, for example, we can reference it elsewhere in the buildfile:

 . . .   <target name="compile">     <javac srcdir="${java.src.dir}" destdir="classes"/> . . . 

We can use properties to improve our example buildfile, as shown in Example 17-3. Here, we're loading a set of properties from a file named build.properties, shown in Example 17-4 and then using these properties in place of the string literals we were using in the previous version. The source directory, for example, is referenced in the compile and clean targets using the value of the java.src.dir property. With these changes to our example buildfile, we can now make changes to the project directory structure and reflect them in one place, the build.properties file, rather than searching through the buildfile for all places affected by a change.

Example 17-3. Simple buildfile using properties
 <!-- A simple buildfile that compiles a set of Java sources      from the src directory, placing the results in the classes      directory, and can also create a jar from the classes --> <project name="JEnt-Ant-props" default="archive">     <property file="build.properties"/>     <target name="compile"             description="Compile all source code into the classes dir">         <!-- Create the classes dir, if needed -->         <mkdir dir="${java.classes.dir}"/>         <!-- Compile the java code into classes -->         <javac srcdir="${java.src.dir}" destdir="${java.classes.dir}"/>     </target>     <target name="archive" depends="compile"             description="Create an archive from the contents of the classes dir">         <jar destfile="${ant.project.name}.jar"              includes="${java.classes.dir}/**"/>     </target>     <target name="clean"             description="Remove all artifacts created in other targets">         <!-- Remove the classes dir created by the compile target -->         <delete dir="${java.classes.dir}"/>         <!-- Remove the jar generated by the archive target -->         <delete file="${ant.project.name}.jar"/>     </target> </project> 

Example 17-4. build.properties file loaded by buildfile
 # Location of the Java source, relative to the base directory # of the project java.src.dir = src   # Location of the generated Java classes, relative to the # base directory of the project java.classes.dir = classes 



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net