Section 17.4. Core Tasks


17.4. Core Tasks

We've already used a few of the core tasks included with Ant in our earlier examples. Our compile target uses the javac task, which invokes the Java compiler, and our archive target uses the jar task, which creates a jar archive from a set of files. In addition to these Java-specific tasks, we've also seen some general-purpose tasks, like the mkdir task that creates a directory and the delete task for deleting files and directories.

Many, many other tasks bundled with the Ant distribution can be combined in a virtually infinite set of combinations to do what you need to do in your development process. We don't intend to document all of them hereagain, for complete coverage of the core Ant tasks, go to the Ant project site (http://ant.apache.org) and pick up Ant: The Definitive Guide (O'Reilly). In the following sections, we briefly discuss some general functional areas that are encountered in nearly every development project and some of the common ways that Ant supports these needs with its core tasks. These tasks are the workhorse tasks in Ant that you'll find yourself using over and over again.

17.4.1. File and Directory Tasks

At the heart of any software development effort is the need to do things to files and directories. Files need to be moved or copied from one place to another, directories need to be created or deleted, sets of files need to be pulled together into archives in various formats. Naturally, Ant provides numerous built-in tasks that support these needs.

17.4.1.1. Basic entities

Ant provides some fundamental entities that are used in various tasks to represent files, sets of files, and paths. Chief among them is the <fileset>. A <fileset> defines a set of files starting from a base directory. A <fileset> is typically defined as a child element of a task, to indicate a set of files to be moved, filtered, or otherwise manipulated by the task. The files to be included in the <fileset> can be specified explicitly by naming them or by using filters. Filters are defined using attributes on the <fileset> element, child elements of the <fileset>, or both. This entry defines a <fileset> containing all of the files in the /tmp directory, for example:

 . . . <fileset dir="/tmp" includes="*"/> . . . 

The * matches any set of zero or more characters.

A <fileset> that includes everything in C:/Windows, excluding files ending in .ini, could be defined like this:

 . . . <fileset dir="C:/Windows">     <exclude name="*.ini"/> </fileset> . . . 

As a final example, if you wanted to include all of the files, recursively, that end in .java or .xml, but wanted to exclude Ant buildfiles, you would write something like this:

 . . . <fileset dir="${basedir}">     <include name="**/*.java"/>     <include name="**/*.xml"/>     <exclude name="**/build.xml"/> </fileset> . . . 

The expression ** is a shortcut for any sequence of directories or subdirectories. So **/*.java matches any file at any level within the chosen base directory that ends in .java.

To make file selections more modular, Ant also includes the concept of a <patternset>. A <patternset> is a set of file inclusion and exclusion filters that can be given a name and then referenced by that name whenever they are needed. We might define a <patternset> at the top of our buildfile, for example, to select all of the Java files in a given directory:

 . . . <patternset >     <include name="**/*.java"/> </patternset> . . . 

We can then use this pattern elsewhere in our buildfile when we need to operate only on Java files under a base directory. A <patternset> can be used in any <fileset> to act as the filter for selecting the files in the set:

 . . . <fileset dir="${java.src.dir}/java">     <patternset ref/> </fileset> . . . 

We can also use a <patternset> in various tasks that support them as child elements for filtering an activity. The zip task is one of these, since it allows the use of a <patternset> to specify what files to include in the zip file it generates:

 . . . <zip destfile="${ant.project.name}-src.zip" basedir="${java.src.dir}/java">     <patternset ref/> </zip> . . . 

Note that in both of the previous two examples, we could have also defined the <patternset> in place rather than referencing the previously defined <patternset> by name.

17.4.1.2. Creating, moving, copying, and deleting

New directories are created using the mkdir task:

 . . . <mkdir dir="/tmp/stuff"/> . . . 

Copying files and directories is done with the copy task. You can copy a file from one place to another:

 . . . <copy file="${ant.project.name}.ear"       todir="${jboss.home}/server/default/deploy"/> . . . 

This will create a copy with the same filename as the original file. You can also create a copy with a different name:

 . . . <copy file="config/app.properties"       tofile="config/app.properties-orig"/> 

In addition to the single file attribute, the copy task also accepts <fileset> child elements that specify the set of files to be copied. So you can, for example, copy the contents of an entire directory to another location:

 . . . <copy todir="/tmp">     <fileset dir="${java.src.dir}" includes="**"/> </copy> . . . 

The copy task has a number of other attributes to control how it behaves, such as whether existing files should be overwritten in the destination (overwrite) and whether source directories should be flattened out when the files are copied to a destination file (flatten).

Use the move task to move and rename files. To rename a single file, for example:

 . . . <move file="${ant.project.name}.war" tofile="${ant.project.name}-old.war"/> . . . 

To move a file from one location to another, preserving its original name:

 . . . <move file="${ant.project.name}.ear"       todir="${jboss.home}/server/default/deploy"/> . . . 

Note that this is similar to our earlier example of using the copy task, but in this case, the file doesn't remain in its original location.

You can also move whole sets of files from one location to a new directory, using nested <fileset> elements. To move an entire directory from one place to another:

 . . . <move todir="${jboss.home}/server/default/deploy">     <fileset dir="war-tmp"/> </move> . . . 

To move a subset of files from one directory to another, you would use a <fileset> with include/exclude filters and <patternset> elements. The following example moves all of the XML files from the project directory to a directory in /tmp:

 . . . <mkdir dir="/tmp/xml-files"/> <move todir="/tmp/xml-files">     <fileset dir="${basedir}">         <include name="**/*.xml"/>     </fileset> </move> . . . 

This entry preserves the subdirectory structures of the original directory. If there were a file config/web.xml in the original directory, for example, it will be copied into a config/web.xml file in the new directory.

Optional attributes on the move task mirror those on the copy task. They allow you to control things such as whether the moved files are flattened into a single directory (flatten) and whether files of the same name in the destination directory should be overwritten (overwrite).

Deleting files and directories is done with the delete task. Deleting a file is simple:

 . . . <delete file="${ant.project.name}.ear"/> . . . 

Deleting a directory is very similar:

 . . . <delete dir="/tmp/xml-files"/> . . . 

This deletes the directory and all of its contents.

To delete a subset of files and directories in an existing directory, you use nested <fileset> elements with include/exclude filters. To delete all of the files ending in .tmp in a directory, for example:

 . . . <delete>     <fileset dir="${basedir}">         <include name="**/*.tmp"/>     </fileset> </delete> . . . 

17.4.1.3. Making archives

Ant includes several tasks for dealing with file archives of different formats, including Java archives (jars), tar files, zip files, and bzip2 files.

Java archives can be created and unpacked using the jar and unjar tasks. Creating a jar archive involves specifying the files to include in the archive and where the archive file should be created. The simplest use of the jar task is to simply jar up an entire directory. This example creates a jar from the contents of our project's classes directory, and names the jar file after the name of the project:

 . . . <jar basedir="classes" destfile="${ant.project.name}-lib.jar"/> . . . 

You can, of course, be more selective about what goes into the jar, using all the filtering tools we've seen earlier. The jar task has includes and excludes attributes that can be used to do basic filters on what's pulled from the basedir. The following creates a jar from the contents of classes but leaves out any Java properties files:

 . . . <jar basedir="classes" destfile="${ant.project.name}-lib.jar"      excludes="**/*.properties"/> . . . 

Nested <fileset> elements can also be used to do more elaborate filtering of the jar contents.

The jar task also includes ways to manage the MANIFEST file for the archive. You can either include a full MANIFEST file using the manifest attribute, or you can use the task to construct a manifest on the fly using a manifest child element, specifying the contents of the manifest using attributes and child elements on the nested manifest element.

The unjar task is used to extract files from a jar archive. To extract all of the contents into a destination directory:

 . . . <unjar src="/books/2/177/1/html/2/${ant.project.name}-lib.jar" dest="${tmpdir}/jar-contents"/> . . . 

You can operate on multiple archives concurrently by specifying one or more <fileset> elements as child elements of the unjar task. The following example extracts the contents of all of the Xerces libraries in our library directory into the project's classes directory:

 . . . <unjar dest="classes">     <fileset dir="${lib.dir}/xerces-2.6.2">         <include name="*.jar"/>     </fileset> </unjar> . . . 

You can use nested <patternset> elements to select specific subsets of the archive contents to extract. The following example pulls only the classes that are not in packages starting with javax from the Xerces libraries:

 . . . <unjar dest="classes">     <fileset dir="${lib.dir}/xerces-2_6_2">       <include name="*.jar"/>     </fileset>     <patternset>         <exclude name="javax/**"/>     </patternset> </unjar> . . . 

Ant provides support for a number of other popular archive formats as well. The tar/untar tasks support tar archives, the zip/unzip tasks support archives in the zip format, the gzip/gunzip tasks support gzipped archives, and the bzip2/bunzip2 tasks support archives in the bzip2 format. We don't provide examples for all of them since they largely work in a similar fashion to the jar/unjar tasks. There are some minor format-specific distinctions among the tasks to watch out for. For example, only the jar task deals with archive manifests since the other archives don't have manifests, and the tar task has a longfile option that lets you pick one of several ways tar can deal with long filenames.

17.4.2. Basic Java Tasks

Naturally, Ant provides strong support for basic Java activities, such as compiling code and running Java standalone applications. When using the Java-related tasks in Ant, it's important to understand how paths and, specifically, classpaths , are specified in Ant.

17.4.2.1. Paths and classpaths

Path lists are a common construct in most operating systems. Lists of directory paths are used to specify where to locate executable programs and support libraries. In Java, the classpath specifies where to locate classes at runtime. Ant supports both general paths and Java classpaths with the <path> and <classpath> entities.

A general-purpose path is defined using the <path> element. The directories and files included in the path are specified using nested <pathelement>, <fileset>, <dirset>, or <filelist> elements. The <pathelement> element is used to explicitly include a single entry on the path, either a file or a directory. This example defines a <path> containing the /usr/bin and /usr/local/bin directories:

 . . . <path >     <pathelement location="/usr/bin"/>     <pathelement location="/usr/local/bin"/> </path> . . . 

You can also define colon- or semicolon-separated lists of entries as a property and include these in your path using a <pathelement> with a path attribute. This entry defines the same <path> as the previous example:

 . . . <property name="tmp.path" value="/usr/bin:/usr/local/bin"/> <path >     <pathelement path="${exec.path2}"/> </path> . . . 

When defining path properties like this, you don't need to worry about using the path separator that's appropriate for the current operating system. Ant will automatically convert the path to the proper format for the current environment (e.g., semicolons for Windows, colons for Unix).

A <fileset>, <dirset>, or <filelist> is used to add one or more entries to the path in a batch. This example defines a <path> with the project classes directory and any jars in the Xerces directory:

 . . . <path >     <pathelement location="classes"/>     <fileset dir="${lib.dir}/xerces-2_6_2">         <include name="*.jar"/>     </fileset> </path> . . . 

It's important to note when using a <fileset>, <dirset>, or <filelist> in a path (either a general path or a classpath) that the order in which the matching entries are added to the path is undefined. So if the order is important in your case (e.g., you want one library to take precedence over another because of potentially overlapping class definitions), you should use explicit <pathelement> enTRies instead.

A <path> can be composed of other <path>s as well, using references to their IDs. We can construct another <path> that includes everything in our previous example <path>, plus the Xalan jars:

 . . . <path >     <pathelement ref/>     <fileset dir="${lib.dir}/xalan-2_6_0/bin">         <include name="*.jar"/>     </fileset> </path> . . . 

Classpaths are defined the same as paths. For example:

 . . . <classpath >     <pathelement ref/>     <fileset dir="${lib.dir}/xalan-2_6_0/bin">         <include name="*.jar"/>     </fileset> </classpath> . . . 

The difference between a <path> and a <classpath> is that a <path> can be defined outside of any target definitions and referenced as needed in your buildfile targets. A <classpath> is always a child element of a task, used to specify where the task will locate its classes.

17.4.2.2. Compiling Java code

Java code is compiled in Ant using the javac task. At heart, the javac task compiles a set of Java source files and generates a set of classes in a destination directory. At its simplest, using the javac task requires specifying only a source directory and a destination directory:

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

In this case, the javac task recursively searches the source directory for Java files (those ending in .java) that are newer than their equivalent class files in the destination directory. "Equivalence" is defined simply as the .class file with a package and base name that matches the directory and base filename of the .java file. The javac task does not (by default) attempt to parse the source files to do more refined equivalence checking.

If you want to compile source code from multiple source directories, you can use nested <src> elements instead of the srcdir attribute. For example:

 . . . <javac destdir="classes">     <src path="${common.src.dir}"/>     <src path="${java.src.dir}"/> </javac> . . . 

This entry compiles all of the Java files found in the two source directories into the classes directory.

You can be more selective in terms of the source files to be compiled as well. includes and excludes attributes on the task can be used to specify filters to be applied to the contents of the source directories, or you can use nested <include>/<exclude> elements.

It's usually necessary to specify the classpath to be used when compiling Java code. By default, the javac task uses the classpath used to invoke Ant, which often doesn't include key libraries needed for your project. You can specify the compile classpath using the classpath attribute and a path string, for example:

 . . . <javac destdir="classes"        srcdir="${java.src.dir}"        classpath="${lib.dir}/lib1.jar;${lib.dir}/lib2.jar}"/> . . . 

or you can refer to a path defined elsewhere, using the classpathref attribute:

 . . . <path >     <pathelement location="${lib.dir}/lib1.jar"/>     <pathelement location="${lib.dir}/lib2.jar"/> </path> . . . <javac destdir="classes" srcdir="${java.src.dir}"        classpathref="proj.compile.classpath"/> . . . 

or you can use a nested <classpath> element, defining the path structure directly within the javac task invocation:

 . . . <javac destdir="classes" srcdir="${java.src.dir}">     <classpath>         <pathelement location="${lib.dir}/lib1.jar"/>         <pathelement location="${lib.dir}/lib2.jar"/>     </classpath> </javac> . . . 

You can use numerous other options to control the compilation process. The compiler attribute specifies which Java compiler should be used, for example. By default, the compiler associated with the JVM that is running Ant will be used. The following example specifies that a Java 1.4 compiler should be used, if available on the system:

 . . . <javac destdir="classes" srcdir="${java.src.dir}" compiler="javac1.4"/> . . . 

If you want to specify a particular compiler for all invocations of the javac task, you can use the built-in property build.compiler. If a compatible compiler cannot be found in the user's environment when the task runs, an error will be generated.

Another important option on the javac task is whether to generate debug information in the class files, using the debug attribute. This is important when dealing with development environments in which an IDE will need this debug environment in order to provide step-through debugging features. To include debug information in the class files, set the debug attribute to on:

 . . . <javac destdir="classes" srcdir="${java.src.dir}" debug="on"/> . . . 

Many other options are available, allowing you to specify compiler arguments, how much memory for the compilation JVM to use, and so on.

17.4.2.3. Running Java programs

You can also run compiled Java code, using the java task. This comes in handy when a Java utility provided by a tool or vendor hasn't been wrapped by an Ant task or when you want to invoke some project code from the Ant buildfile, perhaps to test that a build completed successfully.

If you simply want to invoke the main method of a Java class:

 . . . <java classname="org.jaf.MyApp"/> . . . 

If you have a jar archive with a manifest that specifies a main class, you can invoke its main method like so:

 . . . <java jar="${lib.dir}/app1.jar"/> . . . 

These simplistic versions of the java task are usually not sufficient. For one thing, you often need to provide a classpath that includes all of the required libraries for the Java application. This is done roughly the same way as specifying a classpath for the javac task. You can specify an explicit classpath as a string using the classpath attribute:

 . . . <java classpath="${lib.dir}/lib1.jar" classname="org.jaf.MyApp"/> . . . 

or you can refer to a predefined path using the classpathref attribute:

 . . . <path >     <pathelement location="${lib.dir}/lib1.jar"/> </path> . . . <java classpathref="proj.run.classpath" classname="org.jaf.MyApp"/> . . . 

or you can use an embedded <classpath> element, specifying the classpath explicitly or by referencing paths defined elsewhere in the buildfile:

 . . . <javac classname="org.jaf.MyApp">     <classpath>         <pathelement location="${lib.dir}/lib1.jar"/>     </classpath> </javac> . . . 

It's also likely that you will need to provide command-line arguments when running the Java application. You may need to provide arguments that are passed to the Java class in its invocation arguments, or you may need to pass arguments to the JVM to control its runtime configuration. The former is done using nested <arg> elements on the java task:

 . . . <java classname="org.jaf.MyApp" classpathref="proj.run.classpath">     <arg value="-noAudio"/>     <arg line="-user Jim"/> </java> . . . 

JVM arguments are specified the same way, except you use a <jvmarg> nested element instead:

 . . . <java classname="org.jaf.MyApp" classpathref="proj.run.classpath">     <arg value="-user Jim"/>     <jvmarg value="-Dorg.jaf.MyApp.noAudio=true"/> </java> . . . 

17.4.3. Miscellaneous Utilities

Besides the categories discussed in the previous sections, you'll find yourself using some other miscellaneous tasks in Ant over and over.

17.4.3.1. Ant's echo task: System.out.println( )

Possibly the most commonly used task in Ant is the echo task, which prints a message to the standard output. This is useful both for diagnostic messages and for debugging Ant buildfiles that seem to be misbehaving:

 . . . <echo message="Compiling a whole mess o' Java code. . ."/> <javac srcdir="${java.src.dir}" destdir="classes"/> . . . 

17.4.3.2. Calling external programs

We saw earlier how Java programs can be invoked from Ant. You can also invoke other external programs, using the exec task. This task should be used with caution since it often leads to buildfiles that are not easily portable to different platforms (or even the same platform with a different configuration). Still, the exec task is handy in a pinch.

The command to execute is specified with the executable attribute:

 . . . <property name="browser.exec" value="/usr/local/bin/mozilla"/> <exec executable="${browser.exec}"/> . . . 

Command-line arguments are specified using nested <arg> elements, similarly to the java task:

 . . . <property name="browser.exec" value="/usr/local/bin/mozilla"/> <property name="project.doc.dir" value="docs"/> . . . <exec executable="${browser.exec}">     <arg value="${project.doc.dir}/index.html"/> </exec> . . . 

Environment variables can be set for the executable using nested <env> elements. If, for example, you need to specify the path to be used to find the executable:

 . . . <exec executable="mozilla">     <env key="PATH" path="/usr/local/bin:/usr/bin"/> </exec> . . . 

To help make the use of exec more portable, you can use the os attribute to control when the command will be executed. If the current platform type (as specified by the JVM's os.name system property) is listed in the comma-delimited list specified in the os attribute, Ant will attempt to execute the command; otherwise, it will skip the exec task entirely. This can be used to create a sort of conditional, platform-specific invocation of a particular type of command, for example:

 . . . <property name="win.browser.exec" value="mozilla.exe"/> <property name="unix.browser.exec" value="mozilla"/> <property name="win.browser.path" value="C:\Progra~1\Mozilla"/> <property name="unix.browser.path" value="/usr/bin:/usr/local/bin"/> . . . <exec os="Windows 2000,Windows XP" executable="${win.browser.exec}">     <env key="PATH" path="${win.browser.path}"/> </exec> <exec os="Linux,Solaris,FreeBSD,Mac OS X"       executable="${unix.browser.exec}">     <env key="PATH" path="${unix.browser.path}"/> </exec> . . . 

If this example is run on a Windows 2000 or Windows XP machine, the first exec task will be executed, since the os.name Java system property will match one of the entries in the os attribute. This exec task uses the Windows-specific versions of the relevant properties, set to reasonable defaults for a typical Windows installation of Mozilla. The second exec task will not run because the os.name value will not match any of the entries in its os attribute.

17.4.3.3. Importing custom Ant tasks

Many application servers and development tools provide their own custom Ant tasks that can be used in your buildfiles. A J2EE server may provide a task that automatically deploys a component module to its server, for example. You may decide to write your own custom tasks as well and therefore need to import them into your buildfiles.

External tasks are imported into a buildfile using the taskdef task. If the classes defining the task are on the runtime classpath, all you need to do to import a task is to specify the name of the task and the class that implements it. This example imports a task provided by the BEA WebLogic server distribution:

 . . . <taskdef name="wldeploy"          classname="weblogic.ant.taskdefs.management.WLDeploy"/> . . . 

If the classes that implement the imported tasks aren't on the runtime classpath, you can specify the classpath that taskdef should use, with a nested <classpath> element:

 . . . <taskdef name="wldeploy"          classname="weblogic.ant.taskdefs.management.WLDeploy">     <classpath>         <pathelement path="${weblogic.home}/server/lib/weblogic.jar"/>     </classpath> </taskdef> . . . 

17.4.3.4. Invoking targets

Sometimes it's very useful to call Ant targets programmatically from other targets in your buildfile. Making one task depend on another using the depends attribute is a limited way to do this. But the target named in the depends attribute will always be run before the dependent target, and sometimes you need to check some conditions or set some property values before the supporting target is called. In these situations, you can use the antcall task to explicitly invoke a target from anywhere within another target.

Invoking a target is a simple matter of naming the target as an attribute of the antcall task:

 . . . <target name="main-target">     <!-- Perform some other tasks -->     <antcall target="sub-target"/> </target> . . . 

It's important to remember that, when you use the antcall task in a target, Ant executes the secondary target as if the entire buildfile were reinvoked. By default, all of the property values that have been set at the point of the antcall execution are inherited by the new Ant runtime, which effectively makes the new target run as if it were running within the original runtime. If for some reason you don't want the new target execution to inherit the current property settings, you can use the inheritAll attribute:

 . . . <antcall target="sub-target" inheritAll="false"/> . . . 

You can also explicitly pass specific property values into the new target using nested <param> elements:

 . . . <antcall target="sub-target">     <param name="param1" value="foo"/>     <param name="param2" value="bar"/> </antcall> . . . 

This use of antcall makes the target call similar to a method call, a very effective tool indeed.



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