Standard Targets


Steve Loughran wrote an Ant guide called Ant In Anger . This guide explains many pitfalls and recommends ways to use Ant. Two very useful suggestions are a list of names for targets and how to divide buildfiles .

The following are some of Steve's recommended names for Ant top-level targets:

  • test: Run the junit tests.

  • clean: Clean out the output directories.

  • deploy: Ship the JARs, WARs, and so on to the execution system.

  • publish: Output the source and binaries to any distribution site.

  • fetch: Get the latest source from the CVS tree.

  • docs/javadocs: Outputs the documentation.

  • all: Perform clean, fetch, build, test, docs, and deploy.

  • main: The default build process (usually build or build and test).

The following are some recommended names for Ant internal targets:

  • init: Initialize properties and perform other intialization tasks ; read in per- user property files.

  • init-debug: Initialize debug properties.

  • init-release: Initialize release properties.

  • compile: Perform the actual compilation.

  • link/jar: Make the JARs or equivalent.

  • staging: Carry out any pre-deployment process in which the output is dropped off and then tested before being moved to the production site.

We'll discuss some of the thoughts from Ant in Anger in this chapter and the next ; however, we strongly suggest that you read this guide, because it contains excellent guidelines for using Ant. The guide is included with the Ant binary distribution under the docs directory.

Before we go any further, let's look at a simple example to cement some of the concepts of Ant. The next section presents a straightforward buildfile.

Simple Example

Let's start with a very small, straight-forward example of using Ant. We will begin with the now- infamous hello world example. It will create an output directory and then compile a Java source file called HelloWorld.java to the output directory.

The Java source file HelloWorld.java is stored in ./src/xptoolkit as follows :

 package xptoolkit; public class HelloWorld{     public static void main(String []args){         System.out.println("Hello World!");     } } 

The following ant buildfile, build.xml, compiles the source file:

 <project name="hello" default="compile">   <target name="prepare">     <mkdir dir="/tmp/classes" />   </target>   <target name="compile" depends="prepare">     <javac srcdir="./src" destdir="/tmp/classes" />   </target> </project> 

When you run Ant from the command line, it looks for a buildfile called build.xml in the current working directory. To specify a buildfile with a different name, you must specify the buildfile name using the buildfile command-line argument (discussed in detail later).

Notice that the hello project has targets called compile and prepare. The hello project specifies the compile target as the default target. The compile target has a task called javac, which compiles the location specified by srcdir to the directory specified by the destdir attribute. The built-in task javac compiles Java source. Because the default directory location for a project is the current working directory, the javac task will look for a directory called src (srcdir="./src") under the current working directory and compile the contents of the src directory to the /tmp/classes directory.

Notice that the compile target's "depends" attribute points to the prepare target (depends="prepare"). As a result, all the tasks associated with the prepare target will be executed before the tasks associated with the compile target. This is a good thing-- otherwise , the javac task might try to compile the source code to a directory that did not exist.

As you can see, you can use the targets and their dependencies to logically build, deploy, and test a complex system. The next section shows you how to set up your Ant buildfiles.

Setting Up Your Environment

If you are running Unix, install Ant in ~/tools/ant; if you are running Windows, install Ant in c:\tools\ant. You can set up the environment variables in Windows by using the Control Panel. However, for your convenience, we created a Unix shell script (setenv.sh) and a Windows batch file ( setenv .bat) to set up the needed environment variables.

Your UNIX setenv.sh should look something like this:

 # # Setup build environment variables using Bourne shell # export USR_ROOT=~ export JAVA_HOME=${USR_ROOT}/jdk1.5 export ANT_HOME=${USR_ROOT}/tools/ant export PATH=${PATH}:${ANT_HOME}/bin 

Your Windows setenv.bat should look something like this:

 : : Setup build environment variables using DOS Batch : set USR_ROOT=c: set JAVA_HOME=%USR_ROOT%\jdk1.5set CLASSPATH=%USR_ROOT%\jdk1.5\lib\tools.jar;%CLASSPATH%  set ANT_HOME=%USR_ROOT%\tools\Ant PATH=%PATH%;%ANT_HOME%\bin 

Both of these setup files begin by setting JAVA_HOME to specify the location where you installed the JDK. This setting should reflect your local development environmentmake adjustments accordingly . Then, the files set up environment variable ANT_HOME, the location where you installed Ant. The examples in this book assume that you have installed Ant in c:\tools\ant on Windows and in ~/tools/ant on Unix; make adjustments if necessary. There are sample setup scripts as well the sample code at this book's Web site (www.wrox.com).

Running Ant for the First Time

To run the sample Ant buildfile, go to the directory that contains the project files. On our computer, they are stored under /CVS/XPToolKit/examples/chap4. The directory structure and files looks like this:

 /CVS/XPToolKit/examples/chap4    setenv.bat    setenv.sh    build.xml        ./src/xptoolkit            HelloWorld.java 

To run Ant, navigate to the examples/chap4 directory and type ant. As stated earlier, Ant will find build.xml, which is the default name for the buildfile. For example, here is the command-line output you should expect:

 $ ant Buildfile: build.xml prepare:     [mkdir] Created dir: /tmp/classes compile:     [javac] Compiling 1 source file to /tmp/classes BUILD SUCCESSFUL Total time: 3 seconds 

Notice that the targets and their associated tasks are displayed. That's it! We wrote our first Ant buildfile. In the next section, we describe how to use Ant properties.

Working with Properties

You'll often find it helpful to define properties. The properties in Ant are similar to the properties in java.lang.System.getProperites(). The properties can be set by the property task; so, the properties can also be set outside Ant. You can use properties for task attributes by placing the property name between "${" and "}", similar to the way environment variables are set in the Bourne shell. For example, if an "outputdir" property is set with the value "/tmp", then the "outputdir" property could accessed in an attribute of a task: ${outputdir}/classes would be a resolved to /tmp/classes.

Thus we could change the Ant buildfile to use properties as follows:

 <project name="hello" default="compile">   <property name="outputdir" value="/tmp"/>   <target name="prepare">     <mkdir dir="${outputdir}/classes" />   </target>   <target name="compile" depends="prepare">     <javac srcdir="./src" destdir="${outputdir}/classes" />   </target> </project> 

This Ant buildfile defines the "outputdir" property. Then, the buildfile uses the property in the "dir" attribute of the mkdir task of the prepare target and the "destdir" attribute of the javac task of the compile target. The property is used in many attributes; then, if it has to change, you only change it once. For example, if you change the location of the output directory using properties, you only have to make the change once, in onenot twoattribute assignments. Using properties this way can make your buildfiles flexible.

Paths, Filesets , Patternsets, and Selectors

Of course, your Java source files are unlikely to be as simple as the "hello world" example. You may need to use external libraries. For example, you may need to use one or more external libraries (JAR or ZIP files) with Java binaries to compile the source code of your project.

Ant can make it simple to set up the classpath for your project. You can use the path element tag, which can contain pathelement tags and filesets. There are two types of pathelements: path and location.

A location pathelement sets a single JAR or directory, and a path pathelement sets a colon - or semicolon-separated list of locations (directories and JARs) similar to the CLASSPATH environment variable. The fileset can define a group of files from one directory. This is convenient , for example, when all your library files (JAR files) are in one directory and you don't want to specify them by name. These concepts are much harder to explain than to show; so, look at the next example.

The following is a simple example that uses the Apache Log4J library file (log4j.jar) as if the HelloWorld.java source code needed it. The example shows several ways to set up the path:

 <project name="hello" default="compile">   <property name="lib" value="../lib"/>   <property name="outputdir" value="/tmp"/>   <path id="1">     <pathelement location="." />     <pathelement location="${lib}/log4j.jar"/>   </path>   <path id="2">     <pathelement path=".;${lib}/log4j.jar"/>   </path>   <path id="3">     <pathelement location="." />     <fileset dir="${lib}">         <include name="**/*.jar"/>     </fileset>   </path>   <target name="compile">     <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="1"/>     </javac>        <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="2"/>     </javac>        <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="3"/>     </javac>        <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath id="1">             <pathelement location="." />             <pathelement location="${lib}/log4j.jar"/>         </classpath>     </javac>      </target> </project> 

Notice that the three path tags define almost the same classpath, with the exception that the classpath with the id of 3 includes all JAR files that exist in ${lib}. Here the tags are repeated without the rest of the buildfile for clarity:

 <path id="1">     <pathelement location="." />     <pathelement location="${lib}/log4j.jar"/>   </path>   <path id="2">     <pathelement path=".;${lib}/log4j.jar"/>   </path>   <path id="3">     <pathelement location="." />     <fileset dir="${lib}">         <include name="**/*.jar"/>     </fileset>   </path> 

Also notice that to use these three path tags with the javac task, you need only set the reference of the classpath element to the reference id of the paths defined previously. Here they are, referenced respective to the last example:

 <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="1"/>     </javac>        <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="2"/>     </javac>        <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath refid="3"/>     </javac> 

It's important to note that the javac task with <classpath refid="1"/> would set the classpath to the path set defined by <path id="1">. This is called referring to a classpath by reference . In addition, you can refer to a path in-line using the classpath subtag in the javac task, demonstrated as follows:

 <javac srcdir="./src" destdir="${outputdir}/classes" >         <classpath id="1">             <pathelement location="." />             <pathelement location="${lib}/log4j.jar"/>         </classpath>     </javac> 

There will be times when you need to build a fileset that includes some files and excludes others. For example:

 <fileset dir="./src" casesensitive="yes">   <include name="**/*.java"/>   <exclude name="**/*BAD*"/> </fileset> 

This fileset is designed to pull all of the files within the ./src directory based on a case sensitive search and include all files with a .java extension in them. At the same time, the system needs to exclude any files with the text BAD embedded anywhere in the filename. You will also notice that through these examples, we've hardcoded the srcdir to be "./src". In most cases you will use a property name for the source files as well. For example, we would use:

 <fileset dir="${src.dir}">   <include name="**/*.java"/> </fileset> 

In this code example, we've set the source directory for the fileset to be ${src.dir}. We will of course need to set this property in the build file at some point.

Selectors

Now we've built our filesets using the criteria of the filenamethe file has an extension of .java, appears in a directory trees, etc.but what if we wanted to use other criteria? Ant allows additional criteria for file selection like these core selectors:

  • < contains > : Select files based on whether or not they contain a specific text string. Its attributes are:

    • test="value" where value is the text to test against.

    • casesensitive="value" where value is true/false.

    • ignorewhitespace="value" where value is true/false.

       <fileset dir="${src.dir}"          includes="**/*.java"  >   <contains test"fullCode"/> </fileset> 
  • < date > : Select files based on their modification date.

    • datetime="value" where value is the time to test against MM/DD/YY HH:MM AM or PM.

    • millis="value" where value is the number of milliseconds since 1970 to test against.

    • when="value" where value is before, after, equal.

       <fileset dir="${src.dir}">   <date     datetime="2/2/2004 12:00 AM"    when="after"  /> </fileset> 
  • < depend > : Select files based on whether their modification date is recent when compared to other files.

    • targetdir="value" where value is the directory to compare against.

    • granularity="value" where value is the number of milliseconds to compare with.

       <fileset dir="${src.dir}">   <depend targetdir="dir"           granularity="1000"   /> </fileset> 
  • < depth > : Select files at a particular directory depth.

    • min="value" where value is the minimum number of level below the base.

    • max="value" where value is the maximum number of level below the base to be selected.

       <fileset dir="${src.dir}"          includes="**/*.java"  >   <depth min="1" max="4" /> </fileset> 
  • < different > : Select files that are different from other files.

    • targetdir="value" where value equals the directory to compare files with.

    • ignoreFileTimes="value" where value is true/false to determine if file times are ignored; default is true.

    • granularity="value" where value is number of milliseconds used to determine if a file is different.

       <fileset dir="${src.dir}">   <different targetdir="dir"              granularity="100"   /> </fileset> 
  • < filename > : Select files based on filename, such as <include> and <exclude>.

    • name="value" where value is the name of the file to include.

    • casesensitive="value" where value is true/false.

    • negate="value" where value is either true/false. A true value turns this selector into an exclude.

       <fileset dir="${src.dir}">   <filename name="FileF.java"/> </fileset> 
  • < present > : Select files based on whether they exist or not in a specific location.

    • targetdir="value" where value is the directory to compare against.

    • present="value" where value is srconly and both. srconly means they only appear in the src directory and not the targetdir; both means the files are selected if they appear in both directories.

       <fileset dir="${src.dir}">   <present targetdir="deploy"/> </fileset> 
  • < containsregexp > : Select files based on a regular expression.

    • expression="value" where value is the regular expression.

       <fileset dir="${src.dir}">   <containsregexp expression="a[1..9]b"/> </fileset> 
  • < size > : Select files based on their size.

    • value="value" where value = filesize.

    • units="value" where value = k, M, G or IEC standard. Not required.

    • where="value" where value = less, more, equal.

       <fileset dir="${src.dir}">   <size value="5" units="Ki" when="more"/> </fileset> 
  • < type > : Select files based on whether they are files or directories.

    • type="value" where value = dir or file.

       <fileset dir="${src.dir}">   <type type="dir"/> </fileset> 

For an example of how to use the selectors, consider the following code that will only pull files with a size greater than 5 kb:

 <fileset dir="${src.dir}">   <size value="5" units="Ki" when="more"/> </fileset> 

In this fileset we are pulling all files that are more than 5 kb within the ${src.dir}. The units can be changed to be any of k, M, or G; if the units attribute is not included in the element, then the default is bytes.

If just one selector doesn't fit the needs of your <fileset>, you can use a Selector Container. The Selector Containers allow the various selectors to be combined with each other in a Boolean fashion. The current containers are:

  • < and > : Select a file if it matches all contained selectors.

  • < majority > : Select a file if it. matches a majority of the contained selectors.

  • < none > : Select a file only it matches none of the contained selectors.

  • < not > : Reverses the result of a single selector.

  • < or > : Selects a file if it matches one contained selectors.

To see how the selector containers work, let's consider the <and>. For example:

 <fileset dir="${src.dist}" includes="**/*.java">     <and>         <contains test="CoreCode" />         <date datetime="12/31/2003 12:00 AM" when="after"/>     </and> </fileset> 

In this fileset, we want to match all of the files with a .java extension that contain the text CoreCode and have a current modified date after 12/31/2003. If a file matches both of the selectors, contains and date, then it will match the <and> selector container. Clearly the use of the selectors and the containers provides for a quite complex and comprehensive way to choose files.

Patternsets

If you want to match a set of files but you aren't interested in the granularity of the selector but you still want to be able to reuse the matches, the patternset might be the answer. For example, we might have a set of files that match on the following filename criteria:

 <include name="core/**/*.java"/>   <include name="extended/**/*.java" if="extended"/>   <exclude name="**/*BAD*"/> 

These criteria will match all java files in the core directory structure as well as the files in the extended directory structure, but only if the extended property is set. In addition, the BAD files will be excluded. If this is a common fileset in your code, you can build a patternset. For example:

 <patternset id="coreandextended">   <include name="core/**/*.java"/>   <include name="extended/**/*.java" if="extended"/>   <exclude name="**/*BAD*"/> </patternset> 

This specific pattern of include and exclude elements can be referenced in fileset elements or a task that includes an implicit fileset just by using the id. For example:

 <fileset>   <patternset refid="coreandextended"/> </fileset> 

In addition to the <include> and <exclude> elements, the patternset supports the includes, includesfile, excludes, excludesfile attributes. The patternset id can be used within any Ant construct that allows the <patternset> element. For example, we could have a <patternset> like the following:

 <patternset id="jarpath">   <include name="prodjars/**/*.jar" unless="extended"/>   <include name="devjars/**/*.jar" if="extended"/>   <exclude name="**/*BAD*"/> </patternset> 

This patternset will include all jars in one of two paths, prodjars or devjars, based on the value of the extended property. This patternset could be used if you have specific jars used during production that don't include specific optimizations or logging found in the devevelopment jars. When we build a <target> for compiling our project, we will include a <path> element that uses the patternset. For example:

 <path id="base.path">   <pathelement path="${classpath}"/>     <fileset>         <patternset refid="jarpath"/>     </fileset>   <pathelement location="classes"/> </path> 

Finally, we would build a <target> using the path id which uses our patternset id:

 <target>   <javac>     <classpath refid="base.path">   </javac> </target> 

Conditional Targets

You don't always have to execute a target. You can write targets that are executed only when a certain property is set or when a certain property is not set. For example, let's say we need to run a buildfile in the Windows XP (no pun intended) development environment and the Solaris production environment. Our development environment does not have the same directory structure as our production environment; thus, we may write a script that looks like this:

 <project name="hello" default="run">   <target name="setupProduction" if="production">       <property name="lib" value="/usr/home/production/lib"/>       <property name="outputdir" value="/usr/home/production/classes"/>   </target>   <target name="setupDevelopment" unless="production">       <property name="lib" value="c:/hello/lib"/>       <property name="outputdir" value="c:/hello/classes"/>   </target>   <target name="setup" depends="setupProduction,setupDevelopment"/>   <target name="run" depends="setup">      <echo message="${lib} ${outputdir}" />    </target>  </project> 

Notice that the setupDevelopment target uses unless="production". This means the target should be executed unless the production property is set. Also notice that the setupProduction target uses if="production". This means to execute this target only if the production property is set. Now we need to set the property (or not set it) to control the behavior of the tasks.

To set a property when you execute Ant, you need to pass the property to Ant. This technique is similar to the way you would pass a system property to the Java interpreter. When you execute Ant, you pass the argument Dproduction=true (it does not have to equal true, it just has to be set). Following is an example of running the buildfile (build2.xml) in production mode:

 C:\...\chap2>ant -buildfile build2.xml -Dproduction=true Buildfile: build2.xml setupProduction: setupDevelopment: setup: run:      [echo] /usr/home/production/lib /usr/home/production/classes BUILD SUCCESSFUL Total time: 0 seconds 

From this output, we can see that run was the default target for the project. The run target depended on setup, which depended on our two conditional targets (depends="setupProduction,setupDevelopment"). Thus, because we set the production property on the command line (-Dproduction=true) the setupProduction target was executed rather than setupDevelopment. Running setupProduction sets the "outputdir" and "lib" properties to their Unix environment values. We can see this by looking at the output of the run target's echo task (<echo message="${lib} ${outputdir}" />), which displays the following:

 run:      [echo] /usr/home/production/lib /usr/home/production/classes 

What happens when you have deployment descriptor files that differ between the two environments? You use filters, as discussed in the next section.

Using Filters

Filters can be used to replace tokens in a file with their proper values for that particular environment. One scenario that comes to mind follows the example in the previous section. Let's say we have a production database and a development database. When deploying to production or development, we want the values in our deployment descriptor or properties file that refer to the JDBC URL of the needed database to refer to the correct database:

 <project name="hello" default="run">   <target name="setupProduction" if="production"> <filter token="jdbc_url" value="jdbc::production"/>   </target>   <target name="setupDevelopment" unless="production"> <filter token="jdbc_url" value="jdbc::development"/>   </target>   <target name="setup" depends="setupProduction,setupDevelopment"/>   <target name="run" depends="setup">     <copy todir="/usr/home/production/properties" filtering="true">       <fileset dir="/cvs/src/properties"/>     </copy>   </target>   </project> 

Again, setupProudction and setupDevelopment are executed conditionally based on the production property. But this time, instead of setting properties, they set filters as follows:

 <target name="setupProduction" if="production"> <filter token="jdbc_url" value="jdbc::production"/>   </target>   <target name="setupDevelopment" unless="production"> <filter token="jdbc_url" value="jdbc::development"/>   </target> 

The filter in the setupProduction target sets jdbc_url to jdbc::production. The filter in the setup-Development target sets jdbc_url to jdbc::development. The copy task in the run target turns on filtering, which applies the filter to all in the fileset. Thus, the copy task will copy recursively all the files from the /cvs/src/properties directory into the /usr/home/production/properties directory, replacing all the occurrences of the string "@jdbc_url@" with "jdbc::production" if the "production" property is set or jdbc::development if the "production" property is not set.

Of particular warning here is the fact that any file in the given fileset will be searched for the @filter@ string and this includes binary files. There is a good chance a binary file will become corrupt during the 'filter' process therefore, only text files should be in the copied from directory.

Filtersets

As you might expect, we can have more than one filter in our target that needs to be replaced . Building on our previous example, in addition to the jdbc_url, there is also a specific table within the database that we need to access. The code has been written such that the table name is used through the many SQL statements. Thus, we will have another filter like

 <target name="setupProduction" if="production"> <filter token="jdbc_url" value="jdbc::production"/> <filter token="table" value="accounts"/>   </target>   <target name="setupDevelopment" unless="production"> <filter token="jdbc_url" value="jdbc::development"/> <filter token="table " value="tempacc"/>   </target> 

When the D command-line option is provided, both of the <filter> elements will be executed by the <copy> command. Of course, this isn't going to work for us because the <copy> element is pulling the properties file and not the source code. So let's look at another option for filtering that uses the concept of a filterset. Filtersets are designed to:

  • Group like filters.

  • Assign a reference ID to the filterset.

  • Choose the begintoken and endtoken.

For example, we might build a filterset containing several filters that will be used to change our source code as well as the jdbc_url:

 <project name="hello" default="run">   <target name="setupProduction" if="production"> <filter token="jdbc_url" value="jdbc::production"/>       <filterset id="myFilterSet" begintoken="(*)" endtoken="(*)">         <filter token="TABLE" value="accounts"/>         <filter token="USERNAME" value="fullprod"/>       </filterset>   </target>   <target name="setupDevelopment" unless="production"> <filter token="jdbc_url" value="jdbc::development"/>       <filterset id="myFilterSet" begintoken="(*)" endtoken="(*)">         <filter token="TABLE" value="tempacc"/>         <filter token="USERNAME" value="readtemp"/>       </filterset>   </target>   <target name="setup" depends="setupProduction,setupDevelopment">     <copy toDir="${build.dir}">       <filterset refid="myFilterSet"/>       <fileset dir="${cvssrc.dir}"/>     </copy>   </target>   <target name="run" depends="setup">     <copy todir="/usr/home/production/properties" filtering="true">       <fileset dir="/cvs/src/properties"/>     </copy>   </target>   </project> 

In this example, we've added two filtersets to the production and development targets. There are a few new attributes to discuss within the filtersets. The first is the id used to assign a value to the filterset which can be used with a refid elsewhere throughout the build file. The second is the use of the begintoken and endtoken attributes. Typically, the filtering will check to find the replacement text using the matching text of @value@ but using a filterset allows us to change those values. In our example, we've changed them to (*)value(*).

We've also added another <copy> element to the build script within the setup target. Here we will be pulling the source code from a directory where the source code was pulled from CVS and placing the new files in a build directory. During the copy, the system will filter the code and replace our TABLE and USERNAME values depending on the production property. Notice we've used the refid attribute to specify the filterset to use in our example.

FilterChains and FilterReaders

If you have been a Unix developer, you have probably come to take the redirection abilities of the shell for granted. It is not uncommon to do things like this:

 ps ef   grep myprocess  more 

This command sequence says to first obtain a listing of the currently executing processes, store the results in a temporary buffer, pass the buffer to the application called grep which will return any lines in the buffer with the text myprocess in it and finally control the output of the text to the screen using the more command. The symbol which is called a pipe allows the chaining together of the various Unix shell commands.

As you might expect, this capability was one of the common requests of the Ant team and it has been delivered in the form of FilterChains and FilterReaders. A FilterChain is basically a grouping of FilterReaders which are either user defined or built-in generic filters. Let's look at an example before getting into specifics. Here is a filterchain that will pull all of the lines in the source code that contains one of two strings "Today's Date Is" or "date is". Those strings which contain the strings will be further passed to the <replacetokens> filter which will update the @DATE @ strings with today's date. The FilterChain allows for the piping of various FilterReaders.

 <copy toDir="${src.dir}" tofile="${dest.file}">   <filterchain>     <linecontains>           <contains value="Today's Date Is">           <contains value="date is">     </linecontains>     <replacetokens>           <token key="DATE" value="${TODAY}"/>     </replacetokens>   </filterchain>   <fileset dir="${cvssrc.dir}"/> </copy> 

FilterChains can be used in several tasks including concat, copy, loadfile, loadproperties, and move. The built-in FilterReaders are defined here:

  • ClassConstants: This is a built-in filter that will pull all of the constants defined in a specific Java class and output those constants as Ant property values. For example:

     <loadproperties srcfile="FileFReader.class">   <filterchain>     <classconstants/>   </filterchain> </loadproperties> 

    In this example, the constants found in the class FileFReader will be extracted in the format of name=value.

  • ConcatFilter: This is a built-in filter that will either prepend or append a specific file to all of the files pulled as part of the filter or copy. For example:

     <copy toDir="${src.dir}" tofile="${dest.file}">   <filterchain>     <concatfilter prepend="source2.txt">   </filterchain>   <fileset dir="${cvssrc.dir}"/> </copy> 
  • DeleteCharacters: This is built-in filter that will remove specific characters from the supplied data. You can use backslashes in the specific removal string. For example:

     <loadfile srcfile="myfile.txt" property="${filehead}">   <filterchain>     <deletecharacters chars="bad"/>   </filterchain> </loadfile> 
  • EscapeUnicode: This is a built-in filter that will convert all non-ASCII characters to their escaped equivalent. For example:

     <filterchain>   <escapeunicode/> </filterchain> 
  • ExpandProperties: This is a built-in filter that will search through the supplied data and substitute a value for all Ant properties of the form ${ name }. For example, if we have a file called header.txt with an Ant style variable of ${url}, then the following filter will replace it with www.company.com.

     <property name="url" value="www.company.com" /> <loadfile property="myfile" srcFile="header.txt">   <filterchain>     <expandproperties/>   </filterchain> </loadfile> 
  • FilterReader: The FilterReader filter is a base filter used to allow a developer the ability to create their own filters.

  • HeadFilter: This is a built-in filter that reads x lines from a file and optionally skips y number of lines and places the lines in a specific property For example:

     <loadfile srcfile="myfile.txt" property="${filehead}">   <filterchain>     <headfilter lines="25"/>   </filterchain> </loadfile> 

    If you need to skips some lines first before capturing then use the skip attribute. For example:

     <loadfile srcfile="myfile.txt" property="${filehead}">   <filterchain>     <headfilter lines="25" skip="5"/>   </filterchain> </loadfile> 
  • LineContains: This is a built-in filter that pulls lines which contain a specific text string. For example, this filter will read the myfile.txt file, look for a couple string and place the results in the myfile property.

     <loadfile srcfile="myfile.txt" property="myfile">   <filterchain>     <linecontains>       <contains value="DATE">       <contains value="New Name">     </linecontains>   </filterchain> </loadfile> 
  • LineContainsRegExp: This is a built-in filter that works in the same manner can LineContains but instead we have the ability to be a regular expression. For example:

     <loadfile srcfile="myfile.txt" property="myfile">   <filterchain>     <linecontainsregexp>       <regexp pattern="date*">     </linecontainsregexp>   </filterchain> </loadfile> 
  • PrefixLines: This is a built-in filter that will append a specific text string to all lines. For example:

     <loadfile srcfile="myfile.txt" property="myfile">   <filterchain>     <prefixlines prefix="DONE" />   </filterchain> </loadfile> 
  • ReplaceTokens: This is a built-in filter that works in the same manner as the <filter> element by replacing specific tokens within the data. There is also the option of changing the begin and end tokens. For example:

     <loadfile srcfile="myfile.txt" property="myfile">   <filterchain>     <replacetokens>       <token key="DATE" value="${today}">       <tokenchar begintoken="(*)"                  endtoken="**" />     </replacetokens>   </filterchain> </loadfile> 
  • StripJavaComments: This is a built-in filter that strips all comments from the data. For example:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain>     <stringjavacomments />   </filterchain> </loadfile> 
  • StripLineBreaks: This is a built-in filter that strips all linebreaks from the data. The default linebreak is '\r\n\ but can be changed with the linebreaks param. For example:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain>     <striplinebreaks />   </filterchain> </loadfile> 

    To change the linebreak characters, use the following example:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain>     <striplinebreaks>     <linebreaks="[]"/>     </striplinebreaks>   </filterchain> </loadfile> 
  • StripLineComments: This is a built-in filter that will remove all lines that begin with specific comment characters. For example:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain> <striplinecomments>   <comment value="//"/> </striplinecomments>   </filterchain> </loadfile> 
  • TabsToSpaces: This is a built-in filter that will convert all tabs into a specific number of spaces. The default number of spaces is 8. For example:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain>     <striplinebreaks />   </filterchain> </loadfile> 

    To change the number of spaces:

     <loadfile srcfile="myfile.java" property="myfile">   <filterchain>     <striplinebreaks>        <lines="5"/>     </striplinebreaks>   </filterchain> </loadfile> 
  • TailFilter: This is a built-in filter that reads the tail of the supplied data in the opposite fashion as the HeadFilter. For example:

     <loadfile srcfile="myfile.txt" property="myFile">   <filterchain>     <tailfilter lines="25"/>   </filterchain> </loadfile> 

Nested Builds

Each project (library module, Web application, set of Enterprise JavaBeans, applet, and so on) should have its own directory with its own buildfile. A large project can depend on lots of other projects. Ant allows one Ant buildfile to call another. Thus, you may have an Ant file that calls a hierarchy of other Ant buildfiles. You can nest buildfiles using this method to build many projects and their dependent projects.

The ant task runs a specified buildfile. The ant task can be used to build subprojects and related projects. You have the option of specifying the buildfile name or just the directory (the file build.xml in the directory specified by the "dir" attribute is used). You also have the option of specifying a particular target to execute. If you do not specify a target to execute, the default target is used. Any properties that you set in the called project are available to the nested buildfile.

The following are some examples of calling another Ant buildfile from your Ant buildfile. We can call a buildfile from the current buildfile and pass a property as follows:

 <ant antfile="./hello/build.xml">     <property name="production" value="true"/> </ant> We can call a buildfile from the current buildfile. When you call the ant task, if you don't specify an antfile attribute, then it will use ./hello/build.xml) as follows:<ant dir="./hello"/> 

Notice above that we only specified the directory. The default buildfile is build.xml; if you only specify the directory, then build.xml is assumed.

We can call a buildfile from the current buildfile and specify that the run target should execute (if the run target was not the default target, it would execute anyway) as follows:

 <ant antfile="./hello/build.xml"  target="run"/> 

Listeners and Loggers

Keeping track of what Ant is doing during a build is important for the Configuration Management team as well as the developers. Ant provides two different mechanisms to handle the tracking including listeners and loggers. A listener is triggered based on seven events:

  • Build started

  • Build finished

  • Target started

  • Target finished

  • Task started

  • Task finished

  • Message logged

The Ant supplied loggers are used within the Ant command-line process. For example:

 ant logger <loggerclassname> 

The loggers are:

  • DefaultLogger: This is the standard output produced by Ant when no logger command-line class is provided. As an example, using a version simple build.xml script, the command line

     ant logger org.apache.tools.ant.DefaultLogger 

    produces the following output:

     Buildfile: build.xml all: clean: build:     [mkdir] Created dir: /home/node2/hello/classes     [javac] Compiling 1 source file to /home/node2/hello/classes build: jar:     [mkdir] Created dir: /home/node2/hello/dist/lib       [jar] Building jar: /home/node2/hello/dist/lib/HelloWorld.jar BUILD SUCCESSFUL Total time: 1 minute 15 seconds 
  • NoBannerLogger: This logger will eliminate the empty targets from the output. Using the same build script as above, the command line

     ant logger org.apache.tools.ant.nobannerLogger 

    produces the following output:

     Buildfile: build.xml build:     [mkdir] Created dir: /home/node2/hello/classes     [javac] Compiling 1 source file to /home/node2/hello/classes jar:     [mkdir] Created dir: /home/node2/hello/dist/lib       [jar] Building jar: /home/node2/hello/dist/lib/HelloWorld.jar BUILD SUCCESSFUL Total time: 27 seconds 

    Notice how the empty targets are not displayed.

  • MailLogger: If you would like to send emails to users based on the output from the Ant build, you can use the MailLogger. There are numerous property to be set with MailLogger. The specific ones are:

    • MailLogger.mailhost: The host for the mailer.

    • MailLogger.user: The user to use if authorization is required on your mailer.

    • MailLogger.password: The password to use if authorization is required on your mailer.

    • MailLogger.from: The from address for the mailer.

    • MailLogger.replyto: The replyto address for the mailer.

    • MailLogger.failure.to: A comma-separated list of email addresses.

    • MailLogger.success.to: A comma-separated list of email addresses.

    To use the MailLogger, set the above property in the build script and execute Ant with the following command-line:

     ant -logger org.apache.tools.ant.listener.MailLogger 

    After the build has executed, the specified users in the failure.to or success.to properties will be sent the test from the DefaultLogger.

    The Ant loggers works with the listeners to provide output to standard print streams as well as specific a logging level.

  • XmlLogger: This logger will produce an XML file called log.xml with the Ant output. The command line is:

     ant -listener org.apache.tools.ant.XmlLogger 

    The resulting output is quite extensive and provides many different outputs from the build.

Common Tasks

Ant includes quite a few common tasks already implemented, several of which we've discussed already (the javac and copy elements, for instance). In this section we will explore some of the other common tasks that will be useful in your quest of automating java builds. The following tasks aren't the only ones available, so you should consult the Ant documentation for a complete list.

BuildNumber

If you would like to have Ant keep track of a build number for you, then the BuildNumber task can be used. This task will attempt to read a number from a specific file, increment the number and then write the number back to the file. If a file is not specified, the task will try to open a file called build.number. The format of the task is:

 <buildnumber/> <buildnumber file="version"/> 

Chmod

If you are working under a Unix-based file system, you can use the chmod task to change the permissions on a file or set of files. For example:

 <chmod perm="w+w">   <fileset dir="dist">     <include name="**/deploy/**"/>   </fileset> </chmod> 

This chmod common task will set all files within the deploy path to world writable.

CVS

If you use the CVS version-control software package, you will want to take advantage of Ant's common task called cvs that enables you to pull code from the repository. Here's an example task:

 <cvs cvsRoot=":pserver:node2@localhost:/home/node2/repository"        package="HelloWorld"        dest="${cvs.dir}"  /> 

This task will log into a CVS server on the local machine, extract the project called HelloWorld and place the resulting files in the cvs.dir directory. There are many options to the cvs common task, so consult the Ant manual. See also chapter 3 of this book for more information about CVS.

Parallel

There may be times when you need to execute numerous Ant tasks at the same time or without waiting for one task to complete before starting another one. A good example is creating a task for starting up a

project once it has been built. If you need to start more than one VM, you will need to use the parallel task. For example:

 <project>     <target name="start">         <parallel>             <java... VM 1 here>             <java... VM 2 here>         </parallel>     </target> </project> 

In this example, specifying a target of start will cause two Java Virtual Machines to be started without one VM having to execute before the second one starts.

Jar

In most cases the result of an Ant build will or should be a JAR file. The JAR task can be used for this purpose. Here's an example of the task:

 <jar destfile="${deploy}/lib/app.jar"        basedir="${build}/classes"  /> 

This task will take all of the class files found in the ${build} directory and place them in a JAR file called app, which it then places in the ${deploy}/lib directory.

Mappers

When we create a target that pulls specific files such as the compile sequence, we know that the .java files will be converted to .class files. For the most part, we only specify the source files but not the target files. It just so happens that Ant handles all of the details for us using a mapper class; the specific mapper is called FileNameMapper. If you need to change the way the mapping is performed from source to target files, a <mapper> element should be used. The basic format of the <mapper> element is:

 <mapper     type="mapper type"     classname="the classname of the mapper if not built-in"     classpath="the classpath to find the class specified in classname"     classpathref="a reference to use for the classpath"     from="depends on the mapper"     to="depends on the mapper" /> 

Ant includes a number of built-in mappers:

  • Identity: This mapper will use the source name as the target name. For example:

     <depend targetdir="build/classes">   <mapper type="identity"/> </depend> 

    If the source filename is FileFReader.java then the target will be FileFReader.java.

  • Flatten: This mapper will strip all path information from the source file name and set the target to just the filename itself. For example:

     <depend targetdir="build/classes">   <mapper type="flatten"/> </depend> 

    If the source filename is /usr/local/FileReader, then the target will be FileReader.

  • Merge: This mapper will ignore the source filename and output a specific target value. For example:

     <depend targetdir="build/classes">   <mapper type="merge" to="outputName.txt"/> </depend> 

    If the source filename is File.txt, then the output will be outputName.txt.

  • Glob: This mapper will match files based on the 'to' expression which needs to contain a single * wildcard, and it produce a target file using the matched text and the pattern in the 'from' expression. For example:

     <depend targetdir="build/classes">   <mapper type="glob" from="*.java" to="*.java.txt"/> </depend> 
  • Regexp: This mapper works in a similar manner as glob, but instead both the 'to' and 'from' expressions are allowed to be regular expressions. The values of \0-\9 are used as references to the specific matches from the 'from' expression. For example:

     <depend targetdir="build/classes">   <mapper type="regexp" from="*a[1..9]" to=" 
     <depend targetdir="build/classes">   <mapper type="regexp" from="*a[1..9]" to="\0java.txt"/> </depend> 
    java.txt"/> </depend>

    For this example, we would match on anya3 and the results would be anyjava.txt.

  • Package: This mapper will convert from a / delimited filepath to one with periods. For example:

     <depend targetdir="build/classes">   <mapper type="package" from="*.java" to="New-*"/> </depend> 

    For this example, the filename /usr/local/file would become New-usr.local.file.

  • Unpackage: This mapper will convert from a period based filepath to one with / characters.




Professional Java Tools for Extreme Programming
Professional Java Tools for Extreme Programming: Ant, XDoclet, JUnit, Cactus, and Maven (Programmer to Programmer)
ISBN: 0764556177
EAN: 2147483647
Year: 2003
Pages: 228

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