Getting to know Ant: Part II
Assuming you read the last post, you should have a basic understanding of projects, targets and tasks. In this post, some different tasks, and try to make a slightly more practical ant script. Suppose our build requires a little bit more complexity than displaying “Hello World.” In this case, it has to meet the following requirements:
- The file HelloWorld.java must be compiled
- HelloWorld.class should include debug symbols.
- HelloWorld.class must be zipped
Let's start by creating a basic project skeleton. For right now, we'll only include one target: “build”. Remember to save your project in “build.xml”.
<project name="HelloBuilder" default="build">
<target name="build">
</target>
</project>
At this point, you've doubtlessly realized that unless we learn new tasks, we will be doomed to writing useless ant scripts forever. So that we might escape this horrible fate, we might as well learn some more of the tasks that Ant has provided for us. For this, two ant tasks will be needed: <javac> and <zip>. According to the official ant documentation, javac “Compiles a Java source tree.” Out of the dozens of parameters that it accepts, the only one that it actually requires is the src directory. For zip, we'll specify a few more. We need to tell it where to put the zip, what to name it and what to include. Before trying this example, you'll need a simple Java hello world. Make sure to save it to HelloWorld.java.
By adding these two tasks, we'll be able to complete the two items off of our list. Our new ant script is below.
<project name="HelloBuilder" default="build">
<target name="build">
<javac srcdir="." />
<zip destfile="artifacts.zip" basedir="." includes="HelloWorld.class" />
</target>
</project>
We're two-thirds of the way there. A quick look back at the javac documentation shows that we can build debug by adding [code]debug="true”[/code] to our javac task. Our new line would appear as follows:
<javac srcdir="." debug="true" />
So far things look rather nice. We could stop here, but there is a slight issue that we might want to address first: our “build” target now has two responsibilities: compilation and archiving. Left unmanaged, this can easily lead to a situation where everything in our build script is so interwoven that it becomes hard to maintain. Remember: targets are roughly the ant equivalent of functions. When programming we shouldn't have a function performing several tasks. Likewise, we shouldn't be any more sloppy here. But if we break our tasks out of “build” into “compile” and “archive”, does that mean that we have to run “ant compile” and then “ant archive.” And what if we want to run the entire build process. Which one do we specify then?
If you read through part one, you should remember just a little bit about target dependencies. We're going to use them to fix both of these issues. Before we can archive a class, we have to compile it. Adding a dependency to archive will fix the first problem. What about the second one? We can use dependencies here too. In the first example on the page, the target “build” is completely empty. We can use that to our advantage: we can create a target that doesn't contain any tasks, but has a dependency on both compile and archive. When we attempt to run that target, all of the other targets that need to be run will get called. This should give us our completed ant script.
<project name="HelloBuilder" default="build">
<target name="build" depends="compile, archive" />
<target name="compile">
<javac srcdir="." debug="true" />
</target>
<target name="archive" depends="compile">
<zip destfile="artifacts.zip" basedir="." includes="HelloWorld.class" />
</target>
</project>
Well, at the end of part two, we now have a build script that compiles a java file and zips it for us. On top of that, we've refactored our script to be a little bit more modular and readable. Next time we'll take a look at variables and how they can help lower the cost of refactoring and maintaining our build.