Antcall is evil

Antcall is evil. Trust me on this one. This Ant task will hurt you. Use Macrodef instead.

A brief introduction to make

Make is the daddy of Ant. It originally came from the Unix world but it has been ported to just about every computer system, as it's the de facto way to build c code. Make reads makefiles, which tell make how to do your bidding. Inside the makefile, you declare targets, declare their dependencies, and let make do all the work. Here's a simple example of a makefile:

jsimpson$ cat Makefile
a: b c
echo "A: I depend on b and c "

b:
echo "B: I depend on nothing"

c: d
echo "C: I depend on d"

d:
echo "D: I depend on nothing"

The format of the makefile is simple. A target begins with a dependency line containing a name, a colon and an optional list of dependencies. The dependency line is followed by one or more command lines that are indented with a tab. The command lines do the work. In the example above you can see that target A depends on targets B and C, and that target C depends on target D. When we execute the makefile, make determines the order in which it should execute the targets to do the work. Have a look:

jsimpson$ make
echo "B: I depend on nothing"
B: I depend on nothing
echo "D: I depend on nothing"
D: I depend on nothing
echo "C: I depend on d"
C: I depend on d
echo "A: I depend on b and c "
A: I depend on b and c

The make that is installed on my Mac decided that the appropriate order in which to execute the targets was B, D, C and then A. It could have chosen D, C, B and then A. It doesn't matter because you tell make the dependency rules and then make satisfies them. This approach has been working for authors of Unix software since 1977 , thanks to Stuart Feldman at Bell Labs, who wrote the original make. Make is still actively used on many projects today, but for the bulk of Java and .NET projects, you'll be using Ant and NAnt. This is okay. Make isn't exactly cross-platform, and the two tools are evolved with support for building their platforms.

How does this help me write good Nant or Ant buildfiles?


What you are doing when you write a makefile, or build.xml or project.build is declaring dependencies. What the build tool (Ant) does with those dependencies is create a dependency graph. We can visualize the dependency graph of our simple build:

It becomes very important to specify the dependencies of each target correctly. If you write a build target that doesn't declare it's dependencies fully or correctly (say, it actually depends on the output of another target, which is run sometimes) then you end up with a broken or unreliable build. It's an easy mistake to make, especially as complexity on your project grows. But how does all this relate antcall?

Antcall seems okay at first glance. Each use of this Ant task will execute a named target in the same buildfile as the calling element. Which is really convenient if you explicitly want to invoke a target at a specific point in your buildfile. But what if that target has dependencies? The Antcall documentation hints at the pain:

When a target is invoked by antcall, all of its dependent targets will also be called within the context of any new parameters.

If your antcall (or Nant call) target has no dependencies, then you have done nothing wrong but violate the dependency based style of the tool. If it has dependencies, then they will be relentlessly run every time you call the target.

One pattern I have seen a lot of is using the [ant]call to specify dependencies in order, rather than declaring them as such:

<target name="chico"/>
<target name="zeppo"/>
<target name="harpo"/>
<target name="groucho">
<antcall target="chico"/>
<antcall target="zeppo"/>
<antcall target="harpo"/>
</target>

In a twisted way I can see why the idea is attractive: You want to be sure of the order of target execution. To be really sure, you need to specify dependencies on the targets. So in this case you'd declare that harpo depended on zeppo, for example.

<target name="groucho" depends="chico,zeppo,harpo">
</target>

ant will attempt to preserve the order in which you declared them. The crucial difference is that should one of those targets depend on something else, it can adjust the order of target execution to accommodate it.

Another aspect that I haven't covered yet is reuse. If you want to try and invoke a target several times with different parameters, the dependency model doesn't really cut it. Before Ant 1.6 came out, there really was no choice but to use antcall.

Anyway, my advice for anybody using antcall is to substitute it for macrodef, where you really need reuse.

Thanks to T Ashitani for the online dot implementation.

DevOps New Zealand