19.1. A Brief Introduction to AntAnt is a build tool on the Apache Web site (ant.apache.org/) that ships as part of Eclipse. It differs from make and others of its ilk because Ant is written entirely in Java, can be extended without any native platform-dependent code, and has an XML-based syntax. What follows is a very brief introduction to Ant and its syntax. For more information, see the Ant Web site. 19.1.1. Build projectsAn Ant build script is XML-based with the following structure: <?xml version="1.0" encoding="UTF-8"?> <project default="target2" basedir="."> <target name="target1"> <task 1a> <task 1b> ... more tasks here ... </target> <target name="target2" depends="target1"> <task 2a> <task 2b> ... more tasks here ... </target> ... more targets here ... </project> Every Ant build script has exactly one project element that has the following attributes:
To execute a build script, select the build script in an Eclipse view, such as the Resource Navigator, and select the Run As > Ant Build... command (see Section 2.4.2, Building with Apache Ant, on page 83). 19.1.2. Build targetsA project contains one or more internal and external targets. The only thing that differentiates an internal target from an external target is that external targets have a description associated with them while internal targets do not. Every target can have the following attributes:
One build target can explicitly call another build target in the same build script using the <antcall> task, or in a different build script using the <ant> task. Alternatively, one target can depend on another target to achieve a similar effect, but only within the same build script. If target A depends on target B, which in turn depends on target C (see Figure 19-1), then if you execute target A, the Ant framework will execute first target C, then target B, and finally target A. This same effect could be achieved by having target A call target B, which in turn calls target C. Figure 19-1. Example build target dependencies.
The difference between these two approaches lies in how many times a target is executed. For example, let's assume a more complicated script in which A depends on B1, B2, and B3, and each of these depends on C. In this case, if you execute A then the Ant framework will execute C first, then B1, B2, and B3 in some undetermined order, and finally A. Note that in this case, target C was only executed once and not three times as you would expect. 19.1.3. Build tasksBuild targets are composed of a sequence of tasks to be executed. There are many different types of Ant tasks, some of which are listed on the next three pages. In this list, tasks with names that start with "eclipse" are not built into Ant, but are available either as part of Eclipse or as plug-ins for Eclipse (see Section 19.1.8, Ant extensions, on page 669). Much more complete documentation for Apache Ant tasks can be found on the Apache Ant Web site (ant.apache.org/)
19.1.4. Build propertiesA property is a name/value pair, where the name is case-sensitive. Properties can be used in the value of various task attributes by placing the property name between "${" and "}" in the attribute value. <property name="builddir" value="c:\build"/> <mkdir dir="${builddir}/temp"/> In this build script, the builddir property is assigned the value "c:\build" in the first task, and then this property is resolved in the dir attribute of the second task so that the c:\build\temp directory is created. An alternative form of the property task uses the location attribute: <property name="builddir" location="dir/subdir"/> When specified this way, the value is resolved relative to the ${basedir} before being associated with the builddir property. For example, if the ${basedir} is c:\temp, then the statement above would have associated builddir with the value c:\temp\dir\subdir. If the property task is modified slightly (notice the slash added before the dir/subdir): <property name="builddir" location="/dir/subdir"/> and ${basedir} is c:\temp, then the statement above would have associated builddir with the value c:\dir\subdir. Tip Using the location attribute without a drive letter is more portable; if you specify a drive letter, then your build scripts will only run on a Windows platform. Unfortunately, a reference to an undefined property will not be reported during Ant execution, but silently ignored. If a property has not been defined, then no string substitution is made. For example, if you reference the foo property before it has been defined: <echo message="the foo property is ${foo}"/> then Ant will leave ${foo} unchanged and the message displayed will be: the foo property is ${foo} This makes it more difficult to spot problems, and you might end up with some unusual file or directory names, such as: /temp/${plug-in.id}_3.1.0/icons 19.1.4.1. Predefined propertiesAnt provides several predefined properties including all the Java system properties, such as ${os.name}, as well as the built-in properties shown in Table 19-1. Eclipse provides two additional predefined properties, as shown in Table 19-2.
19.1.4.2. Property scopingProperties are global within a build script from the moment they are declared. If one task assigns a value to a property, another task within the same script can then use that property. In the following script, the foo and bar properties are each declared in separate targets and referenced in others: <?xml version="1.0" encoding="UTF-8"?> <project name="Test" default="test" basedir="."> <target name="init"> <property name="foo" value="xyz"/> <echo message="foo=${foo}"/> </target> <target name="sub1" depends="init"> <echo message="foo=${foo}"/> <property name="bar" value="abc"/> <echo message="bar=${bar}"/> </target> <target name="sub2" depends="init"> <echo message="foo=${foo}"/> <echo message="bar=${bar}"/> </target> <target name="test" depends="sub1,sub2"> <echo message="foo=${foo}"/> <echo message="bar=${bar}"/> </target> </project> Looking at the output, you can see that the properties foo and bar can be referenced anytime after they are declared. Buildfile: scoping_test_1.xml init: [echo] foo=xyz sub1: [echo] foo=xyz [echo] bar=abc sub2: [echo] foo=xyz [echo] bar=abc test: [echo] foo=xyz [echo] bar=abc BUILD SUCCESSFUL Total time: 234 milliseconds Closer inspection of both the script and the output reveals something disturbing. The bar property is declared in target sub1 and then referenced in target sub2 even though sub2 does not depend on sub1. This is important because Ant does not guarantee the order in which nondependent targets will be executed. In this first case, target sub1 just happened to be executed before target sub2, and thus sub2 could reference the bar property as expected. If you modify the test target's depends attribute as follows: <target name="test" depends="sub2,sub1"> then the sub2 target will be executed before the sub1 target, causing the bar property to be declared after it is referenced. Buildfile: scoping_test_2.xml init: [echo] foo=xyz sub2: [echo] foo=xyz [echo] bar=${bar} sub1: [echo] foo=xyz [echo] bar=abc test: [echo] foo=xyz [echo] bar=abc BUILD SUCCESSFUL Total time: 265 milliseconds In the simple test build script, the problem and solution are obvious, but as your product, and thus your build scripts, become more complex, this problem could be harder to diagnose. Tip The bottom line is that when task A references a property declared in task B, care must be taken to ensure that task A is directly or indirectly dependent on task B so that the build order is deterministic and the property will be declared before it is referenced. 19.1.4.3. Property mutabilityProperties are immutable once declared. For example, in the following build script: <?xml version="1.0" encoding="UTF-8"?> <project name="Test" default="test" basedir="."> <target name="init"> <property name="foo" value="xyz"/> <echo message="foo=${foo}"/> <property name="foo" value="123"/> <echo message="foo=${foo}"/> </target> <target name="test" depends="init"> <echo message="foo=${foo}"/> <property name="foo" value="abc"/> <echo message="foo=${foo}"/> </target> </project> the foo property is assigned in the init target; and once assigned, it cannot be modified (the exception to this rule is the <antcall> tasksee Section 19.1.5, <antcall> task, on page 664). Unfortunately, multiple assignments are quietly ignored and thus are quite a source of confusion. Buildfile: mutability_test_1.xml init: [echo] foo=xyz [echo] foo=xyz test: [echo] foo=xyz [echo] foo=xyz BUILD SUCCESSFUL Total time: 203 milliseconds 19.1.4.4. Properties outside targetsProperties are special in that they can be declared outside the scope of a target. A property declared in such a manner is defined before any target is executed and is immutable. For example, in the following build script: <project name="Test" default="test" basedir="."> <property name="foo" value="xyz"/> <target name="test"> <echo message="foo=${foo}"/> <property name="foo" value="abc"/> <echo message="foo=${foo}"/> </target> </project> the foo property is assigned its value before the test target is executed, and its value is not changed by the second property task within the test target. Buildfile: mutability_test_2.xml test: [echo] foo=xyz [echo] foo=xyz BUILD SUCCESSFUL Total time: 188 milliseconds 19.1.4.5. Properties on the command lineProperties can also be declared outside the build script. A property declared on the command line is defined before the build is launched and is immutable. For example, if you execute the build script described in the previous section using the Run As > Ant Build... command, switch to the Main tab panel (see Figure 19-2) and then enter the following in the Arguments field. -Dfoo=mop Then, the foo property is assigned its value before the build script is executed, and its value is not changed by the property declaration or property task within the build script. Buildfile: mutability_test_2.xml test: [echo] foo=mop [echo] foo=mop BUILD SUCCESSFUL Total time: 297 milliseconds Figure 19-2. Declaring a property as part of the Ant command line.Alternatively, properties can be specified by switching to the Properties tab panel (see Figure 19-3) and unchecking the Use global properties as specified in the Ant runtime preferences checkbox. The top part of the page contains individual property declarations, while the bottom part displays a list of files containing property declarations. Figure 19-3. Declaring properties and property files applicable to an individual build script.To specify properties applicable to all the build scripts in the workspace, open the Eclipse Preferences dialog and navigate to the Ant > Runtime preference page (see Figure 19-4). Similar to the Properties tab panel shown earlier, the top part of the preference page contains individual property declarations, while the bottom part displays a list of files that contain the property declarations. Figure 19-4. Declaring properties and property files applicable to all build scripts in the workspace.19.1.5. <antcall> taskThe <antcall> task has some unusual aspects worthy of discussion. Parameters specified in an <antcall> task override any properties specified elsewhere. For example, if the following build script is executed: <?xml version="1.0" encoding="UTF-8"?> <project name="Test" default="test" basedir="."> <target name="init"> <property name="foo" value="xyz"/> <echo message="in init, foo=${foo}"/> <property name="foo" value="123"/> <echo message="in init, foo=${foo}"/> </target> <target name="test" depends="init"> <echo message="in test, foo=${foo}"/> <antcall target="sub"> <param name="foo" value="gob"/> </antcall> <echo message="in test, foo=${foo}"/> </target> <target name="sub"> <echo message="in sub, foo=${foo}"/> <property name="foo" value="abc"/> <echo message="in sub, foo=${foo}"/> </target> </project> The foo property is assigned in the init target and should be immutable (see Section 19.1.4.3, Property mutability, on page 660). However, because foo is specified as a parameter in the <antcall> task, the value is modified for the duration of the <antcall> task; its original value is restored when the <antcall> task completes. Buildfile: mutability_test_3.xml init: [echo] in init, foo=xyz [echo] in init, foo=xyz test: [echo] in test, foo=xyz sub: [echo] in sub, foo=gob [echo] in sub, foo=gob [echo] in test, foo=xyz BUILD SUCCESSFUL Total time: 282 milliseconds The <antcall> task resets the depends calculations so that targets can be executed twice. Consider the previous build script with a slight modification. <?xml version="1.0" encoding="UTF-8"?>
<project name="Test" default="test" basedir=".">
<target name="init">
<property name="foo" value="xyz"/>
<echo message="in init, foo=${foo}"/>
<property name="foo" value="123"/>
<echo message="in init, foo=${foo}"/>
</target>
<target name="test" depends="init">
<echo message="in test, foo=${foo}"/>
<antcall target="sub">
<param name="foo" value="gob"/>
</antcall>
<echo message="in test, foo=${foo}"/>
</target>
<target name="sub" depends="init">
<echo message="in sub, foo=${foo}"/>
<property name="foo" value="abc"/>
<echo message="in sub, foo=${foo}"/>
</target>
</project>
This modification makes the sub target dependent on the init target. Even though the init target is executed prior to the test target, the init target is executed a second time before the sub target because the sub target was executed using the <antcall> task. In addition, the value for the foo property is different the second time the init target is executed, but as discussed before, returns to its original value when the <antcall> task completes. Buildfile: mutability_test_4.xml init: [echo] in init, foo=xyz [echo] in init, foo=xyz test: [echo] in test, foo=xyz init: [echo] in init, foo=gob [echo] in init, foo=gob sub: [echo] in sub, foo=gob [echo] in sub, foo=gob [echo] in test, foo=xyz BUILD SUCCESSFUL Total time: 375 milliseconds 19.1.6. macrodefWhen building complex Ant build scripts, you will find groups of similar operations. One way to refactor and parameterize these operations is by placing them in their own target and then calling them via <antcall> (see Section 19.1.5, <antcall> task, on page 664). Another way to group and parameterize operations is to create a new task using a macrodef. For example, modify the script from the previous example to use a macrodef. <?xml version="1.0" encoding="UTF-8"?> <project name="Test" default="test" basedir="."> <target name="init"> <sub fooval="xyz"/> <echo message="in init, foo=${foo}"/> <sub fooval="123"/> <echo message="in init, foo=${foo}"/> </target> <target name="test" depends="init"> <echo message="in test, foo=${foo}"/> <sub fooval="gob"/> <echo message="in test, foo=${foo}"/> </target> <macrodef name="sub"> <attribute name="fooval"/> <sequential> <echo message="in sub, foo=${foo}"/> <property name="foo" value="@{fooval}"/> <echo message="in sub, foo=${foo}"/> </sequential> </macrodef> </project> The first thing to notice in the script is that calls to the sub macrodef look exactly like calls to a built-in Ant task such as echo. The permitted attributes in that call are specified by the attribute tag as in the following. <attribute name="fooval"/> If the attribute is optional and need not be specified by the caller, then supply a default value for the attribute as in the following. <attribute name="fooval" default="a default value"/> Attributes can only be referenced in the macrodef in which they are defined and are preceeded by "@" rather than "$" prefix.
Attributes are resolved in the order that they are defined and before any build properties, leading to some interesting techniques. First, an attribute defined earlier can be used in the default value of an attribute defined later. For example, "foo" could be used in the default value of "bar" as shown here. <attribute name="foo"/> <attribute name="bar" default="a @{foo} value"/> Because attributes are resolved before properties, attributes can be used in the name of a property. For example, the "foo" attribute could be used to specify which property should be passed to the javac task. <macrodef name="sub">
<attribute name="foo"/>
<sequential>
<javac classpath="${classpath_@{foo}}" ... />
Finally, property mutability behaves exactly as if sub is a task. Buildfile: mutability_test_5.xml init: [echo] in sub, foo=${foo} [echo] in sub, foo=xyz [echo] in init, foo=xyz [echo] in sub, foo=xyz [echo] in sub, foo=xyz [echo] in init, foo=xyz test: [echo] in test, foo=xyz [echo] in sub, foo=xyz [echo] in sub, foo=xyz [echo] in test, foo=xyz BUILD SUCCESSFUL Total time: 437 milliseconds 19.1.7. Headless AntRunning Ant headless (from the command linewithout a UI) is well-documented (see ant.apache.org/), but using Eclipse-specific Ant tasks will cause the build script to fail; Eclipse-specific Ant tasks need Eclipse to execute properly. The following is a Windows batch file (batch files for other platforms will be similar) used to build the Favorites product by launching Eclipse headless and executing the Favorites build.xml file (the ==> denotes a continuation of the previous line and the characters must not be included). echo off setlocal REM **************************************************** set JAVAEXE="C:\j2sdk1.4.2_02\jre\bin\java.exe" set STARTUPJAR="C:\eclipse_310\startup.jar" set WORKSPACE="C:\eclipse_310\workspace" set BUILDFILE=build.xml REM **************************************************** if not exist %JAVAEXE% echo ERROR: ==> incorrect java.exe=%JAVAEXE%, edit this file ==> and correct the JAVAEXE envar if not exist %JAVAEXE% goto done if not exist %STARTUPJAR% echo ERROR: ==> incorrect startup.jar=%STARTUPJAR%, edit this file ==> and correct the STARTUPJAR envar if not exist %STARTUPJAR% goto done if not exist %WORKSPACE% echo ERROR: ==> incorrect workspace=%WORKSPACE%, edit this file ==> and correct the WORKSPACE envar if not exist %WORKSPACE% goto done if not exist %BUILDFILE% echo ERROR: ==> incorrect buildfile=%BUILDFILE%, edit this file ==> and correct the BUILDFILE envar if not exist %BUILDFILE% goto done REM **************************************************** :run @echo on %JAVAEXE% -cp %STARTUPJAR% org.eclipse.core.launcher.Main ==> -application org.eclipse.ant.core.antRunner ==> -data %WORKSPACE% -buildfile %BUILDFILE% REM **************************************************** :done pause Copy this batch file into an external_build.bat file located in the same directory as the build.xml file you want to run, and modify the batch variables shown in Table 19-3 to suit your environment.
The first part of the batch file assigns the script variables while the second part validates the existence of files specified by those script variables. The real work is accomplished in the third part, where Eclipse is launched without a UI and the build script is executed. 19.1.8. Ant extensionsSeveral of the tasks just listed are not part of Ant; some are part of Eclipse and others must be downloaded from the QualityEclipse Web site (www.qualityeclipse.com/). The additional tasks listed in Table 19-4 will not work outside of Eclipse.
By default, Eclipse executes Ant using an alternate JRE. If you are using Eclipse-specific tasks such as those listed before, and you encounter an error similar to the following: Buildfile: com.qualityeclipse.favorites\build.xml init: BUILD FAILED: file: com.qualityeclipse.favorites/build.xml:56: Could not create task or type of type: eclipsetools_classpath_modifications. Ant could not find the task or a class this task relies on. ... etc ... Total time: 406 milliseconds then you may need to execute the build script in the same JRE as the workspace. To accomplish this, select the build script, right-click, and then select Run As > Ant Build.... In the launch dialog, select the JRE tab and select the Run in the same JRE as the workspace radio button (see Figure 19-5). This enables the Eclipse-specific Ant tasks to access the underlying Eclipse functionality. Figure 19-5. JRE tab page in the Ant launch configuration. |