In the last Ant tutorial, you were introduced to the basic build operations that can be done using Ant script. Do read and practice that before going through this one. This tutorial will introduce file operations that can be done through Ant script, extending Ant with custom tasks and CVS operations.

Creating directory

Ant has a task called mkdir, which is used to create directories. It is similar to Windows and UNIX/Linux create directory commands:

Java Code:
<mkdir dir="archive/pattern/dump1"/>
Slash (/) is used as a directory separator. Which slash to use, depends on your choice. Ant can handle both slashes and converts them to the required one depending on the platform. It can also handle mixture of both slashes. In the above example, we created a directory called archive, with sub directory called pattern which also has a subdirectory called dump1. This is a useful feature of the mkdir task which creates parent directories if they don't exist already. Antís mkdir task is smarter Ė and if the target directory already exists, the mkdir task will not complain, and will do nothing.

Deleting directory

The delete ant task is used to remove directories from the file system.

Java Code:
<delete dir="archive/pattern/dump1"/>
The delete task deletes a specified directory, along with any files and sub-directories it contains. In the given example, dump1 folder will be removed along with all the files and subdirectories. In order to delete a single file, you must use a file attribute instead of dir.

Copy/Move

Your building process may involve copying or moving files or directories. Gone are the days when you had to do all this manually, which introduces chances of mistakes and thus complications may arise. Ant has tasks to make copy and move operations simple and easy.

Consider that you want to make a copy of a file. Then use copy command with file and tofile attributes.

Java Code:
<copy file="src/Pattern.java" tofile="src/PatternCopy.java"/>
Similarly you can move a file and also can rename the file:

Java Code:
<move file="src/Pattern.java" tofile="src/PatternCopy.java"/>
In the above copy and move examples, we did copy/move in the directory. You may want to copy or move files in other directories without renaming the file. This is also possible as shown in the example below:

Java Code:
<copy file="src/PatternA.java" todir="archive"/>
<move file="src/PatternB.java" todir="archive"/>
Ant outputs (on the console) a summary of the move and copy operations it performs including information like number of files it has moved or copied. This is very useful but you may wish to get more information like names of the files involved. In that case, you can set the verbose attribute to true.

Creating / unpacking achieves

Creating the jar files is easy (already shown in the last tutorial).

Java Code:
<jar destfile="packagea.jar" basedir="classes"/>
You may wish to create a zip file instead. Syntax is very simple:

Java Code:
<zip destfile="output.zip" basedir="classes"/>
If you are interested in creating tar files or other achieve files, use the following syntax:

Java Code:
<gzip src="output.tar" zipfile="output.tar.gz"/>
Files can also be compressed using the GZip and BZip2 tasks. For example:

Java Code:
<gzip src="output.tar" zipfile="output.tar.gz"/>
Another common task is to uncompress the archive files. Itís also very simple and easy.

Java Code:
<unzip src="output.tar.gz" dest="extractDir"/>
What if the extractDir already contains the files? Should Ant overwrite the files or not? By default, Ant will overwrite the files in the destination directory. The overwrite attribute can also be included to control the overwrite behavior. To uncompress other archive files, the related task names are untar, unjar, gunzip, and bunzip2.

Replace task

Ant also provides a replace task, which is used to find and replace tokens in the files. The token attribute is used to specify the string to be searched for, and the value attribute specifies the new string that should replace all occurrences of the token string. For example:

Java Code:
<replace file="properties.txt" token="V1.2" value="v1.3"/>
To have a detailed output, you may set the summary attribute to true. This causes the task to output the number of occurrences of the token string that were found and replaced.

Pattern matching

Pattern matching and file selectors are powerful mechanisms that greatly enhance the capabilities of Ant tasks. In the copy, move and delete tasks, we specified the specific named files and directories. It is often useful to perform those operations on a group of files at once. For example: remove all the files in a given directory that end with .java. As done in popular operating systems, this is done using wildcard characters:

ē * matches zero or more characters
ē ? matches exactly one character

The pattern to match all files ending with .java would therefore be simply *.java. If you want to use wild cards with directories, itís also possible for example, the pattern archive*/*.zip would match all zip files in any directories with a prefix of archive.

To match any number of directories, use **. For example, the pattern **/*.properties would match all property files under the current directory structure. **/ specifies all the directories under the current directory and *.properties specifies any file which has extension .properties.

File system tasks can be made more specific using patterns, such as with a nested fileset element. Letís understand this through an example:

Previously, we copied a single file with this task:

Java Code:
<copy file="src/Test.java" todir="archive"/>
If we wanted to use a pattern instead, we could replace the file attribute with a
fileset element, like this:

Java Code:
<copy todir="archive">
<fileset dir="src">
<include name="*.java"/>
</fileset>
</copy>
This will copy all the java files in the src directory to archive directory.

We use include element to specify the pattern we are looking for. Remember that the fileset would by default consist of all the files under the specified src directory. Similarly, we can add an exclude element with another pattern, which will remove matches from the include specification.

There can be multiple include and exclude elements which means files with be searched containing the required patterns and then all those files will be removed which holds the exclude patterns.

Using selectors

You now know that we use fileset to specify a group of files, and the contents of this group can be defined using include and exclude patterns. We can also use special elements called selectors along with include and exclude to select files.

I will list the core selectors that are available:


size
Use this to select files based on their size. The default unit is bytes and you may change this using units attribute. Use when attribute to specify the nature of the comparison like less, more, or equal. There is a value attribute which defines the target size against which the size of each file is compared.

contains
Use contains selector, when you wish to get the files that contain the given text. The text attribute is used to give the text to be searched. By default, the search is case sensitive which can be changed by providing casesensitive="no".

filename
This selector helps to find files with the given name. It is same as include element, and the same as the exclude element when negate="yes" is specified.

present
Selects those files from the current directory structure that
have the same name and relative directory structure as those in a specified targetdir directory.

depend
Itís more or less like present selector with a slight difference. The matching files are restricted to those that have been modified more recently than the corresponding files in the targetdir location.

date
To select the files based on the date on last modified date, use date selector. It is really very useful. When attribute is used to specify whether the comparison is
before, after, or equal the given date. That given date is specified using the datetime which actually givens the date and time to compare against. The date time format is: MM/DD/YYYY HH:MM AM_or_PM.

depth
You may specify no of levels in the directory to search for the required file, using depth selector. Use min and/or max attribute to select files with a desired number of directory levels.

You may also combine or nest multiple selectors to achieve the desired results. The Ďandí selector container selects only those files that are selected by all of the selectors it encloses. Other selector containers include or, not, none, and majority are also very common.

The following example combines two selectors using and selector container. The fileset that selects only those files that are larger than 100 bytes and also contain the string "pattern":

Java Code:
<fileset dir="dir">
<and>
<contains text="pattern"/>
<size value="100" when="more"/>
</and>
</fileset>
Chaining builds

The build process is more complex when you are dealing with large projects. One approach to build is to have a single monolithic build file that does everything. This surely will have high coupling. The second and better approach is to split the build file into a number of smaller files where one file calls others to perform specific tasks. Itís really simple to call one Ant build from another using the ant task. You need to specify just the build file to use, using the antfile attribute, and Ant will build the default target in that build file. For example:

Java Code:
<ant antfile="sub-build.xml"/>
There is an issue which should be understood. You have properties defined in your Ant scripts. In this case, if you include an Ant script in your parent Ant file, properties will be inherited. This behavior can be avoided by specifying inheritAll="false".

Let's understand all this through an example. We have following Ant script that we wish to call:

Java Code:
<?xml version="1.0"?>
<project default="showMsg">
<target name="showMsg">
<echo message="Message=${message}"/>
</target>
</project>
Here is the parent Ant script that will eventually call the above script.

Java Code:
<?xml version="1.0"?>
<project default="callShowMsg">
<target name="callShowMsg">
<ant antfile="sub.xml" target="showMsg" inheritAll="false">
<property name="message" value="Welcome to Java!"/>
</ant>
</target>
</project>
The important thing to note is

Java Code:
 <ant antfile="sub.xml" target="showMsg" inheritAll="false">
We specified the file name and target name. Optional attribute inheritAll is used to indicate that we do not want to inherit properties from sub.xml.

Output:
Java Code:
Buildfile: build.xml
callShowMsg:
showMsg:
[echo] Message=Welcome to Java!
BUILD SUCCESSFUL
Total time: 0 seconds
CVS repositories

Concurrent versions system is a source code control system that is designed to keep track of changes made by a number of different developers. It is very important to use CVS in software development houses to maintain source code.

Ant provides close integration with CVS which means that you can automate build environments. A single build file can extract one or more modules from the source code repository, build the project, and even generate patch files based on the changes that have been made since the previous build.

If you want to use CVS from Ant build script, make sure that cvs command is installed on your machine and is available from the command line.

The following example build file extracts a module from a CVS repository:

Java Code:
<?xml version="1.0"?>
<project name="CVS Extract" default="extract" basedir=".">
<property name="cvsRoot" value=":pserver:anonymous@dev.eclipse.org:/home/eclipse"/>
<target name="extract">
<cvs cvsRoot="${cvsRoot}"
package="org.eclipse.swt.examples"
dest="${basedir}"/>
</target>
</project>
To use cvs task, you must know the use of cvsRoot attribute. It is used to give complete reference to the CVS repository, including connection method and user details. The format of this parameter is:

Java Code:
[:method:][[user][:password]@]hostname[:[port]]/path/to/repository
We are connecting to the central repository for the Eclipse project as an anonymous user. The other attributes then specify the module or package we wish to extract and the destination for the extracted files.

Custom tasks

Ant provides various core and optional tasks which you use to automate the build process. Very seldom, you need to define your own customized tasks.

To implement a simple custom task, you need to extend the org.apache.tools.ant.Task class and override the execute() method. Follow the sample below:

Java Code:
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
public class FileSorter extends Task {
// The method executing the task
public void execute() throws BuildException {}
}
The execute() method is throws BuildException which will thrown to signal the failure back.

Conclusion

Ant includes a large no of tasks which makes build process simple and straightforward. Since itís written in Java, it is platform independent. Itís a must used tool if you are developing complex Java projects and want to make build process simple and efficient.