Pages

Saturday, January 30, 2010

Ant - Another Neat Tool

Today we will learn about the basics of ant . For those who are new to Ant tool ,this entry will help them to move forward.Here aim using my Eclipse 7.5 [ we will use the in build ant for this.

Ant is a java based build tool . According to the author of ant tool , the acronym is "another neat tool". Build tools are mostly used in software Development to transform source code and other input files into an executable form. Traditional projects in c and c++ use make tool in Unix which performs the build invoking shell commands. Ant is also similar to make but instead of invoking platform specific shell commands , it uses cross platform java classes. The build file is in xml format .lets see how we can write build files ,

Now lets write a basic build file ,
<project basedir="." default="" name="basicBuild">
<echo>this is a basic build</echo>
</project>

So for every build file in ant ,we will have a project element. In the above build file I specified the echo which displays a message at runtime .

Now lets move to more complex examples,

Project & Target :The project element contain target elements. A target element defines a set of tasks , that we want to execute . We can tell ant as to which target to execute when no default is defined. The top level element project needs to have some attributes like

default : says which target to execute by default .
basedir : the path to the directory that ant should use when working paths . [.]refers to the current working directory that build file is in.

Below is the example which explains the project and target elements ,

<project basedir="." default="compile" name="basicBuild">
<target name="compile">
<echo>This is the basic echo</echo>
<description>Basic Build</description>
</target>
</project>

We have defined a default target which is "compile" . we also provided a basic description for the target.

Properties : properties in ant are like programming variables which has a property and value assigned to it. However like in other programming languages , a value for a variable is set only once [ much like mutable ] .here is a sample example as to how to use a property and how to access the property value

<project basedir="." default="compile" name="basicBuild">
<property name="name" value="Jagadish" ></property>
<target name="compile">
<echo>This is the basic echo</echo>
<description>Basic Build</description>
<echo>${name}</echo>
</target>
</project>

You can see , we will be using the Unix style of accessing the variable value. there are number of predefined propertied in ant. The system properties set by the java environment are available as a ant properties . Meanwhile ant also sets some of the properties like ant version which can be accessed.

Properties are also used to define the files paths and directory paths , but this could cause problems across different platforms with different path separators . Ant provides a location attribute which holds the path in platform neutral way.

Defining Dependencies : in a more complex build , there may be many tasks to perform like compiling the java class ,making a jar file of them , compressing them e.t.c . Many of the task in ant will be having a specific order like we cannot build a jar when we did not compile the class. So the order is to compile the classes first and then making a jar with them.

Ant provides a flexible way of handling these dependencies . It provides a "depends" attribute to a target such that when we define the depends attribute , the target which is specified in depends attribute is executed first then the current one ,

<project basedir="." default="compile" name="basicBuild">
<property name="name" value="Jagadish" ></property>
<property name="sub-Name" value="${name}.manchala"></property>

<target name="compile" depends="build">
<echo>This is the basic echo</echo>
<description>Basic Build</description>
<echo>$ {name}</echo>
<echo>${sub-Name}</echo>
</target>

<target name="build">
<echo>This is the basic build</echo>
</target>

</project>

In the above code , the target with name build is executed first and then the target with name compile is executed.

An ant is primarily a build tool and it provides an excellent support for invoking the java related tasks.consider the javac element

<project basedir="." default="compile" name="basicBuild">
<property name="name" value="Jagadish" ></property>
<property name="sub-Name" value="${name}.manchala"></property>

<target name="compile" depends="build">
<echo>This is the basic echo</echo>
<description>Basic Build</description>
<echo>$ {name}</echo>
<echo>$ {sub-Name}</echo>
</target>
<target name="build">
<property name="createDir" value="com.myCompany.classes"></property>
<echo>This is the basic build</echo>
<mkdir dir="$ {createDir}"/>
<javac srcdir="." destdir="${createDir}"></javac>
</target>
</project>

In the above code we have used the basic javac Command to compile all the java files that are in src directory and place them in the new directory that we created using the mkdir element. there are some attributes for the javac command that may be very useful like

classpath : much like adding -classpath to javac command
debug : provides the debug information .

An important thing is that ant does not compile classes that are already compiled if their time stamp value doesn't changes .so it is a good practice to clean the compiled classes first and then build. As we know that ant is implemented itself in java , the jvm in which ant is currently running invokes the javac for compiling the classes in the same jvm which gives us more performance while building projects.

In some cases we need to invoke a new compiler separately in such cases use the fork attribute of the java element . And if we need to specify a javac executable and pass some memory setting we can do that as ,

<javac executable="c:/java/bin/javac" memorymaximumsize="128M"></javac>

Creating Jar : creating jar in ant is very simple. It provides a basic jar element which does the work .

<jar destfile="pack.jar" basedir="."></jar>
I just created a jar file named "pack.jar" with the current directory.as we know for every jar file we will have a manifest file . Ant provides support to include a basic manifest file in the jar.the contents of the manifest file can be specified by using the manifest element as ,

<jar destfile="pack.jar" basedir=".">
<manifest>
<attribute name="Build-By" value="jagadish"/>
</manifest>
</jar>

Time Stamping Builds : It is often necessary to build the environment with time and date. The tstamp element in ant provides with options to includes date and time .the timestamp does not produce any output generally but it puts value to 3 properties which can used.

The 3 properties include ,

DSTAMP : current date
TSTAMP : current time
TODAY : today date in string format

So we can use this as

<target name="build">
<property name="createDir" value="com.myCompany.classes"></property>
<echo>This is the basic build</echo>
<mkdir dir="$
{createDir}"/>
<javac srcdir="." destdir="${createDir}"></javac>
<tstamp/>
<jar destfile="pack-$ {DSTAMP}.jar" basedir=".">
<manifest>
<attribute name="Build-By" value="jagadish"/>
</manifest>
</jar>
</target>

The tstamp can be used to set different properties like current time zone e.t.c by using a format element.

File System Operations : in this section , we will work on file operations including creating , deleting of directories e.t.c
Creating Directory :
<property name="createDir" value="com.myCompany.classes"></property>
<mkdir dir="${createDir}"/>
Deleting a Directory :
<property name="createDir" value="com.myCompany.classes"></property>
<delete dir="${createDir}"></delete>
Copying a file:
<copy file="./BasicBuild.java" tofile="./BasicBuildTestCopy.java"/>
Moving a file :
<move file="./BasicBuild.java" tofile="./BasicBuildTestCopy.java"/>

We can also move and copy files from one directory to other directory.

Creating Zip and Unzip : ant provides options for zipping and unzipping . This is much similar as creating a jar file.
<zip destfile="basic.zip" basedir="." ></zip>
We have facilities for creating zip , gzip ,bzip2 and tar.

Replacing Tokens: the last file operation is replacing tokens , in this the token that is to be replaced is searched from the file and replaced with the new value , as
<replace file="nothing.txt" token="old" value="new"></replace>

Pattern Matching : in the previous file operations , we have seen that we perform tasks on individual files and directories , but there are situations where we need to perform operations on a group like compiling a group of java classes e.t.c. in such cases pattern matching will be useful. Pattern matching is done by using wild cards , * which matches zero or more characters and ? Which matches exactly one character. The pattern to match all files of java is *.java .

matching is also performed on directories , a pattern with src*/.java says to include java files in directories with prefix src .there is a another pattern construct ** which includes all directories , */.java matches all java files in all directories. This construct includes all the names with String "test" in them */test.These patterns are used in include and exclude elements.

directory Sets: directory set are used to select directories based on the pattern .the element "dirset" is used for this case ,starting from the base directory.
File Sets : file sets are used to define a set of files .this is done by selecting the directory first in which the files occur.

Execution Tasks : we have a facility in ant to run a program as an executable task , as
<exec executable="notepad.exe"></exec>

Using Selectors : a file set is used to specify a group of files , and contents of this group can be used with include and exclude elements. We can also use selectors with include and exclude elements to select files .

Here is a list of core selectors available with ant ,
Size : this attribute is used to select files based on their sizes. The default unit is bytes . We can use when attribute to specify nature of comparison like less ,more e.t.c.
Contains : use this to select files that contains a given text.
Filename : this helps to get files based on the file name.
Date : to select files based on the date parameter , like last Modified e.t.c.
Present : selects files with the same name in the directory specified .

These are some of the core selectors , we can dig for more of them and their usages.
Chaining Builds : in a normal large projects , there exists more that one build file . In such a cases to run all the build files would be a time consuming . We have a facility in ant which allows us to chain 2 or more build files.we can call ant file as ,

<ant antfile="secondBuild.xml" target="showMsg" inheritall="false"/>

Here is a sample snippet codes ,

secondBuild.xml:
<project basedir="." default="">
<target name="showMsg">
<echo>This is my Seo=cond Build File</echo>
</target>
</project>

Build.xml:
<project basedir="." default="compile" name="basicBuild">
<target name="compile">
<ant antfile="secondBuild.xml" target="showMsg" inheritall="false"/>
<echo>This ismy first build file</echo>
</target>
</project>

We have a point to consider here , the properties that are set in the parent ant file are inherited to the ant files that are included. In such case we need to set the attribute inheritall="false".

Targets Based On Condition : we can use a condition in ant in order to perform a condition based logic. Here is a simple condition operation ,

<project basedir="." default="do-windows" name="basicBuild">
<target name="do-windows" if="is.windows">
<echo>This is windows</echo>
</target>

<condition property="is.windows">
<os family="windows"/>
</condition>
</project>

Custom Ant Tasks : even though we write tasks that perform various operations , there may be some times we need to write our own ant tasks which performs some validations or complex post build verification checks .in that case ant provides a way to write our own ant tasks .

let's see how we can write our own ant tasks, we need

create a java class that subclass org.apache.tools.ant.Task class and overrides the execute method
packaging your class files into a jar file [ optional ]
register the custom task in the ant build file using the taskdef element.

lets write a simple helloWorld custom ant task , in this custom task we will write a log message to the standard output console .

lets start with the code ,

public class CustomAnt extends Task {
public void execute() throws BuildException {
System.out.println("We R In The Custom Defined Ant Task");
}
}

now we will write a antTasks.properties file which holds the keys as ,

customant=com.prokarma.CustomAnt

save that in a file and now create a jar file named sample.jar with files CustomAnt.java and antTasks.properties.

now our ant build files looks as ,

<project name="SimpleJavaPro" default="compile" basedir=".">
<description>This is jagadesh</description>
<echo>This is jagadesh in here</echo>
<taskdef resource="antTasks.properties" classpath="sample.jar"></taskdef>
<target name="compile">
<customant/>
</target>
</project>

we can just run the ant file and see the output .

let move to a more advanced level of creating custom ant tasks where we provide a user input value on which the response will be shown depending on the value sent .

here is the code for customAnt.java

public class CustomAnt extends Task {

private String myName="";

public CustomAnt() {
// TODO Auto-generated constructor stub
}

public String getMyName() {
return myName;
}

public void setMyName(String myName) {
this.myName = myName;
}

public void execute() throws BuildException {
if(this.myName.equalsIgnoreCase("jagadesh")){
System.out.println("the Name Passed Is Wrong");
}else if(this.myName.equalsIgnoreCase("jagadish")){
System.out.println("the Name Passed Is Right");
}
}

}

I added a private String variable which will be checked for a perticular string depending on the value sent through the ant build file .
we have no changes in the antTasks.properties file but in the ant build file we need add a attribute to the customAnt element as follows ,

<project name="SimpleJavaPro" default="compile" basedir=".">
<description>This is jagadesh</description>
<echo>This is jagadesh in here</echo>
<taskdef resource="antTasks.properties" classpath="sample.jar"></taskdef>
<target name="compile">
<customant myName="jagadish"/>
</target>
</project>

pass jagadish as a value to myName and you will that the name passed it right or it will say the name passed is wrong.


Hence this completes the Ant basics . hope this helped you,
Read More