A Short Tutorial on Ant

Ant Hello World

There is a long standing tradition in software of starting with "Hello World." We'll start our series of Ant tutorials with the same. I'm going to assume that you've successfully installed Ant and that you understand the basics of XML syntax. And just in case it makes a difference, I'm using version 1.9.3:

$ ant -version
Apache Ant version 1.9.3 compiled on April 8 2014

Copy the following lines into a file named "build.xml" (the default name assumed by ant).

<project default="hello">
  <target name="hello">
    <echo message="Hello, World"/>
  </target>
</project>

And execute ant.

$ ant
Buildfile: build.xml

hello: [echo] Hello, World

BUILD SUCCESSFUL Total time: 2 seconds

Now that we've got a working build file, let's take a closer look at it's contents:

Next, let's create another build file and examine some common command line options in ant. Copy the following lines into a file named "echo.xml" (the new lines are shown in bold).

<project default="hello">
  <target name="hello">
    <echo message="Hello, World"/>
  </target>
<target name="goodbye"> <echo message="Goodbye, Cruel World"/> </target> </project>

Execute ant using:

$ ant -f echo.xml goodbye
Buildfile: echo.xml

goodbye: [echo] Goodbye, Cruel World

BUILD SUCCESSFUL Total time: 2 seconds

Finally, let's take a look at the target depends attribute. Edit "echo.xml" and add the target "all" as shown below:

<project default="hello">
  <target name="hello">
    <echo message="Hello, World"/>
  </target>

<target name="goodbye"> <echo message="Goodbye, Cruel World"/> </target>
<target name="all" depends="hello,goodbye" /> </project>

The target depends attribute specifies targets that should be achieved prior to executing the current target. Ant will attempt to execute depends targets left to right, but this may be altered by their respective depends targets. In this case "hello" and "goodbye" have no dependencies, so they will execute in that order.

$ ant -f echo.xml all
Buildfile: echo.xml

hello: [echo] Hello, World

goodbye: [echo] Goodbye, Cruel World

all:

BUILD SUCCESSFUL Total time: 2 seconds

Ant Hello World Revisited

We'll continue our Ant tutorials with "Hello World," only now we'll execute a Java class. Start by creating a simple hello class in the file hello.java as shown below.

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

From the command line, compiling, jarring and executing this class is as simple as:

$ javac hello.java
$ jar -cvf hello.jar hello.class
added manifest
adding: hello.class(in = 415) (out= 285)deflated 31%)
$ java -classpath hello.jar hello
Hello World

Now let's write an Ant build file to compile the hello.java file. Create hello.xml file with the following content:

<project default="compile">
  <target name="compile">
    <javac srcdir="." />
  </target>
</project>
Note that this build file defines a "javac" task for the target "compile". When executed, this task will compile all files in the directory rooted at "srcdir".

Executing ant from the command line:

$ rm *.class
$ ant -f hello.xml compile
Buildfile: hello.xml

compile: [javac] /home/cs144/tmp/hello.xml:3: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds [javac] Compiling 1 source file

BUILD SUCCESSFUL Total time: 4 seconds
You can ignore the 'includeantruntime' warning. (This is due to a bug in our ant version. You can turn off this warning by adding includeantruntime="false" attribute to the javac task.)

Now, let's add a target to create the hello.jar file (added lines are shown in bold). The destfile attribute is required and both the basedir and the includes attributes are necessary in this case. The jar file will only contain the manifest file without the basedir attribute and the includes attribute is required to exclude all the non-class files (in particular, the jar file cannot contain itself). The regular expression used in the includes attribute will match all class files in the directory tree rooted at basedir. More specifically, ** is used to match zero or more directories and * is used to match zero or more characters. Therefore **/*.class will match any file in the base and any of its subdirectories that end with .class

<project default="compile">
  <target name="compile">
    <javac srcdir="." />
  </target>
<target name="jar" depends="compile"> <jar destfile="hello.jar" basedir="." includes="**/*.class" /> </target> </project>

Executing ant from the command line:

$ rm *.jar
$ ant -f hello.xml jar
Buildfile: hello.xml

compile:
jar: [jar] Building jar: /Tutorial/Ant/Jar/hello.jar

BUILD SUCCESSFUL Total time: 2 seconds $ jar -tvf hello.jar jar -tvf hello.jar 0 Wed Jan 22 17:06:32 EST 2003 META-INF/ 55 Wed Jan 22 17:06:32 EST 2003 META-INF/MANIFEST.MF 335 Wed Jan 22 16:36:16 EST 2003 hello.class

Finally, let's add a target to execute our class (added lines in bold). We ensure that the jar is always built first by having the execution target run depend upon the jar target. In this example, both the classname and the classpath attributes of the java task are used to completely specify the class to execute — eliminating a dependence upon the CLASSPATH environment variable. And we request a new JVM by setting fork="true"; providing the class full access to the java runtime and preventing a call to System.exit() from terminating the ant process.

<project default="compile">
  <target name="compile">
    <javac srcdir="." />
  </target>
<target name="jar" depends="compile"> <jar destfile="hello.jar" basedir="." includes="**/*.class" /> </target>
<target name="run" depends="jar"> <java classname="hello" fork="true"> <classpath path="hello.jar" /> </java> </target> </project>

And execute:

$ant -f hello.xml run
Buildfile: hello.xml

compile:
jar:

run: [java] Hello World

BUILD SUCCESSFUL Total time: 2 seconds

Using Ant Properties

In this section, we will use ant Properties to specify the work directory structure that will contain our .class and .jar files. The work directory structure tends to be a personal preference, and properties will allow us to centralize the structure definition so that any change will be relatively pain-free. We specify the directories with a location attribute, thereby binding the property to a file system location. This allows us to pass the property to another ant process without any local directory side effects.

<project default="all">
  <property name="obj-dir" location="obj" />
  <property name="lib-dir" location="lib" />

  <target name="init">
    <mkdir dir="${obj-dir}" />
    <mkdir dir="${lib-dir}" />
  </target>

  <target name="clean-init">
    <delete dir="${obj-dir}" />
    <delete dir="${lib-dir}" />
  </target>

<target name="all" depends="init"/> <target name="clean" depends="clean-init"/> </project>

This build file features:

We can now execute the init target to create the work directories, and the clean-init target to remove the work directories.

$ ls
build.xml
$ ant 
Buildfile: build.xml

init: [mkdir] Created dir: /Tutorial/Ant/Properties/obj [mkdir] Created dir: /Tutorial/Ant/Properties/lib

all:

BUILD SUCCESSFUL Total time: 3 seconds $ ls build.xml obj/ lib/ $ ant clean Buildfile: build.xml

clean-init: [delete] Deleting directory /Tutorial/Ant/Properties/obj [delete] Deleting directory /Tutorial/Ant/Properties/lib

clean:

BUILD SUCCESSFUL Total time: 2 seconds $ ls build.xml

Now, let's add targets to compile, jar and execute a java class. We'll just copy over the hello.java class from our previous installment and place it in the src directory. The compile and jar targets from before remain largely the same; but we add the destdir attribute to the javac task to specify where the .class files are to be stored and we can remove the includes attribute from the jar task. Our new build.xml looks like this:

<project default="all">
  <property name="obj-dir" location="obj" />
  <property name="lib-dir" location="lib" />
  <property name="src-dir" location="src" />

<target name="init"> <mkdir dir="${obj-dir}" /> <mkdir dir="${lib-dir}" /> </target> <target name="clean-init"> <delete dir="${obj-dir}" /> <delete dir="${lib-dir}" /> </target> <target name="compile" depends="init"> <javac srcdir="${src-dir}" destdir="${obj-dir}" /> </target> <target name="clean-compile"> <delete> <fileset dir="${obj-dir}" includes="**/*.class" /> </delete> </target>

<target name="jar" depends="compile"> <jar destfile="${lib-dir}/hello.jar" basedir="${obj-dir}" /> </target> <target name="clean-jar"> <delete file="${lib-dir}/hello.jar" /> </target> <target name="run" depends="jar"> <java classname="hello" fork="true"> <classpath path="${lib-dir}/hello.jar" /> </java> </target>


<target name="all" depends="run"/> <target name="clean" depends="clean-init"/> </project>

Before we finish, it is also possible to give a unique "id" to an element and reference the element later using the id. For example, instead of specifying the classpath directly, we can first define a path element and reference it in the classpath specification as follows:

  ...
  <path id="path1">
     <pathelement location="${lib-dir}/hello.jar" />
  </path>
  ...
     <java classname="hello" fork="true">
        <classpath refid="path1" />
     </java>
  ...

This way, you can specify the actual location of the path at one place and refer to it multiple times later without repeating the same information. After all these changes, now we can execute our class and clean up our work directories.

$ ant run
Buildfile: build.xml

init: [mkdir] Created dir: /Tutorial/Ant/Properties/obj [mkdir] Created dir: /Tutorial/Ant/Properties/lib

compile: [javac] /home/cs144/tmp/hello.xml:3: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds [javac] Compiling 1 source file to /Tutorial/Ant/Properties/obj

jar: [jar] Building jar: /Tutorial/Ant/Properties/lib/hello.jar

run: [java] Hello World

BUILD SUCCESSFUL Total time: 4 seconds $ ant clean Buildfile: build.xml

clean-init: [delete] Deleting directory /Tutorial/Ant/Properties/obj [delete] Deleting directory /Tutorial/Ant/Properties/lib

clean:

BUILD SUCCESSFUL Total time: 2 seconds