My Best ANT Practices
Until recently, I was responsible for developing, maintaining and providing support on the ANT build for a quite large project (a couple of thousand classes, accessing ~10 subsystems, 10-15 developers). With a couple of years occasional-use ANT experience, and two years of intense use on the project just described, here are my personal best ANT practices.
ANT has a pretty fantastic online Manual, which must be one of the last pages on the web to use frames. Hence, the links below pointing to specific parts of the manual actually point to documents that are supposed to be shown in a subframe, with an index on the left.
Given the choice, use Maven :-)
Really. In the absence of "special" conditions (corporate standards, legacy configuration, etc.), Maven is a great way to get a very feature-rich build with very little effort. For special needs, when you need to be very specific about how something should be done, you might still want to use ANT.
Don't use ANT as a scripting language
ANT is best used for standard build activities: clean, compile, test, package, verify, deploy, and managing the dependencies between them. ANT is easy to extend, but 99% of things you would reasonably need to do is included "in the box". Think at least twice every time you use an advanced (or rather, nontrivial) feature - a couple of years down the line, some unfortunate person will curse every "clever" hack in your build.
If you decide you just cannot get by without extending ANT, document your reasons somewhere visible - the people who have to maintain this code in 5 years will want to know! Take a step back, and reconsider. Is there the slightest chance that you are just having fun?
Learn how to use ant-contrib. But don't
You need to know how ant-contrib works, so you can tell when somebody is messing with the ANT conventions you know and love. ANT provides a simple and ordered universe for your build to live in. Deliberately, with some very constraining conventions: properties cannot change value once set, there are no loops, there is no explicit if-task, etc.
This is part of the beauty of ANT. When you see ant-contrib on the classpath, all bets are off - this is where all the cool, dynamic, script-like features went. And the three "constraining conventions" mentioned above are all out the window...
Sometimes, ant-contrib can be a life saver, and if you know what you're doing, maybe you really do need it - but rest assured, down the line, somebody is going to hate that decision. If you find yourself absolutely needing nested control structures (if, for, etc.), ask yourself if this part of your ANT build should really be a separate script? Written in a "real" scripting language?
Use the target 'depends' attribute to make dependencies explicit
Explicit target dependencies are easier to follow than targets invoking each other using antcall. Also, they are potentially more efficient, since ANT can ensure that each target runs at most once. The order of execution is not guaranteed though, which means that specifying "depends='clean,initialise'" does not guarantee that 'initialize' runs before 'clean' (so 'initialise' should depend on 'clean').
The logical first reflex of a programmer writing (s)his first ANT script is to use antcall, since programmers are used to invoking functions. But ANT should not be used as a scripting language, and compared to understanding a <target depends="..."> graph, understanding the call graph of targets invoking each other using antcall is a nightmare.
Use the ant[call] task to hide dependencies
Sometimes, hiding dependencies is what you want. If you modularize your build file, so e.g. if compile-build.xml handles compilation, then you would want to hide the details of what that means behind an ant invocation to compile-build.xml.
Modularize the build before it gets too big
Split each phase of your build into a separate build file (e.g. compile-build.xml, package-build.xml, test-build.xml, ...), and invoke them using the ant task from the main build file:
<target name="compile" depends="initialize"> <ant antfile="compile-build.xml" /> </target>
This minimizes/eliminates subtle dependencies between different phases of the build, since each phase runs in it's own ANT-invocation, isolated from the others. Define shared ANT resources (properties, paths, etc.) in a separate file (using the import task).
That way, the main build file gives you an overview of the entire build, and each phase build file provides an isolated and detailed view of just one build phase. Build code is just code - don't make people read it unless they really need to.
Use the power of ANT properties to say things once and only once
Properties embedded in other properties, so that each piece of information that is likely to change is hardcoded only once, helps minimise the impact of changes down the line:
myproject.home=${basedir} myproject.prod=${myproject.home}/prod myproject.prod.src=${myproject.prod}/src myproject.prod.conf=${myproject.prod}/conf myproject.test=${myproject.home}/test myproject.test.src=${myproject.test}/src myproject.test.conf=${myproject.test}/conf myproject.qa.conf=${myproject.home}/qa-conf myproject.qa.pmd.conf=${myproject.qa.conf}/pmd-config.xml myproject.qa.checkstyle.conf=${myproject.qa.conf}/checkstyle-config.xml myproject.bin=${myproject.home}/bin
Define all properties in a .properties-file
Since properties are nice, static things that do not change once you have set them, it is preferable to define them where nothing complicated and dynamic can happen. When I see properties defined directly in the ANT build file it makes me suspicious - it often means something is more complicated than it seems.
Sometimes, it is just not practical to define all properties externally, but for someone trying to get an overview of the structure of your project, well-named properties that are all defined in the same place (e.g. the property file) is very helpful. If references into ${myproject.home}/qa-conf are spread out in the build file(s), it is harder to figure out exactly what should be in that directory. On smaller projects, this does not matter much, but as the project (and the build) gets bigger and more complicated, it can make a real difference.
ANT properties are very useful, but don't overdo it
Once set, an ANT property is fixed. It is not considered a problem if you try to set it again, ANT just does nothing. This is useful e.g. for setting a default value just before using a property.
But it can get out of hand - it is easy to fall into the property-spaghetti trap. "Where does this property get set? Why isn't this property set? Etc." - you can set properties conditionally on (pretty much anything, including) the state of other properties, you can write property files in one build and read them in (or delete them!) from another (even from another instance of the same build file). When something goes wrong, complexity is your worst enemy. The build derails incomprehensibly, and error messages have no helpful relation to the actual problem.
Use macros to factor out common operations
Macros provide a simple and elegant way to extend the ANT vocabulary - e.g. make your own copy command, which ignores .foobar files and never complains, or your own checkout command, which by default checks out from your central corporate repository. Done well, macros are self-documenting code, which can sometimes be a good reason to use them, even if they are only invoked once.
Macros can also be used to hide implementation details - using a generic "message" macro greatly simplifies changing where messages go:
<macrodef name="message"> <attribute name="text" /> <sequential> <echo>@{text}</echo> </sequential> </macrodef> <target name="initialize"> <message text="Initializing..." /> <some-tasks /> <message text="Done initializing!" /> </target>
If later messages should go to the dev-channel on the internal chat system, only the macro changes, and e.g. verbosity options can be added easily.
Provide a description for public targets, and only for public targets
Targets that have a description attribute in their definition are considered public - this is used in IDEs (and in ANT itself) to determine which targets are of interest to the end user. Used correctly, this attribute provides vital clues to people trying to get their head around the build for the first time.
This should be obvious, since it is how ANT defines the difference between public and private targets, but I've seen too many build files with absolutely no correlation between public/private target and presence/absence of a description. People who do this seem to also have a preference for one big build file, which has hundreds of targets... Does the world really need 20 ways to start the build?
Cut the number of public targets to an absolute minimum
It should be simple to figure out how to run the build, and how to just run a particular phase (just compile&package, just run the tests, just produce the QA reports, ...). A new team member should be able to figure out basic usage of the build in 5 minutes.
This is where Maven really has the upper hand on ANT - most Maven projects will do exactly what you want them to when you type mvn package, mvn test, mvn verify. With ANT, you have to go the extra mile of reading the descriptions of the public targets - but since there are no standard life cycles in the box, each ANT build tends to be different - you can't trust your instincts about what the test target should do.
Include an XML comment for private targets
Private targets should not have a description attribute, but they should still be documented. XML is not the simplest of syntaxes to read, and on top, ANT tasks tend to be fine-grained, making it harder still to decipher what is going on.
A comment describing what is accomplished ("what", not "how") with a target provides invaluable guidance. Even if the comment will eventually be outdated.
Use the power of ANT, but don't be too clever
For-loops, parallel execution, deep nesting of build files invoking each other, conditions on targets, etc. are all powerful and occassionally(!) useful tools, but overdo it, and your build becomes a beast that even you, "THE BUILD MASTER!" are secretly scared of.
If, after a couple of weeks without modifying the build, you can't remember how it works ("where does this property come from? When does this target get invoked?"), and you can't figure it out in 2 minutes, consider how hard it will be for people who didn't write it to understand.
ANT is a powertool. As someone's uncle once said, "with great power comes great responsability".