Ant: Building a Better Build File
Date: May 6, 2005
From the developer who uses VIM or Emacs to the developer who uses a more complex IDE such as NetBeans, Eclipse, or IntelliJ's Idea, Jakarta's Ant has made their life much easier and more consistent. However, there does seem to be a growing trend to make build files that are overly complex and difficult to maintain. I doubt that anyone starts out writing a build file with the goal of making it un-maintainable, but as a project grows, the build tends to become more and more complex—often with multiple targets that do the same thing in a slightly different way. Listed in this article are a few ways to keep your build file lean, mean, and totally maintainable.
K.I.S.S.
Although this may seem like old advice for a seasoned programmer, it bears repeating: Avoid having Ant targets that do more than one task unless it is logical for them to be combined. For instance, imagine a target that does the following:
- Removes old class files
- Compiles all source code
- Creates a Java archive of all class files
- Creates a Web archive of all files
- Copies the Web archive over to the development server
Although it is definitely a useful target, its scope is quite limited. Unless you want to always completely rebuild and redeploy the application you are working on, this target will not see very much use. Consider using several smaller targets with the following names instead:
- clean
- compile
- jar
- war
- deploy
- all
With these targets, you can then assign dependencies as follows:
- jar depends on compile
- war depends on jar
- deploy depends on war
- all depends on clean and deploy
In this situation, there is a single target that does everything that the previous large target did by calling ant all. In fact, you can set this up as the default target; you can merely call ant to rebuild the entire project. But, more importantly, if you just want to recompile the class files you just modified to ensure that they compile, you can do that without having to deploy everything.
By keeping the targets small and simple, the build file becomes more flexible, useful, and easier to maintain.
Name Your Build File build.xml and Put It in the Root
If you have used Unix's make utility, you know how annoying it is when the make files are not named consistently. The same holds true for Ant's build.xml. I have seen numerous projects in which the build file is renamed or moved from the root of the project, or both.
What this does is turn a three-letter command into an exercise in frustration. Consider which of these two commands would be easier to use frequently:
ant ant -f etc/ant/build/project.xml
Ant expects a file named build.xml to be located in the current directory, which is generally the root of the project. Keeping the build file with its default name and location will make using Ant more consistent and useable.
Keep the default target in the build file to be the most common usage of the build file. Generally this means that the default target should be either compile or jar. If the default target is some form of deploy or a complete project rebuild, it will not be used often—and the most common usage of the build file will be more complex than necessary.
Use Consistent Naming
Most developers know that consistency is a good thing. Even more so with Ant—it is important for the names of common Ant targets. Keeping the target names the same across projects avoids having to constantly review the build file to determine what the target names are (and more importantly, what each target does).
If your build file always has the following targets, then the most common functions of the build file can be used without review:
- clean
- compile
- jar
- war
- ear
- test
- help
Naturally, not all these targets apply to all projects, and there certainly will be additional targets beyond them. That is where the help target comes into play, by creating a help target that prints out all the targets and their purposes, making your build file that much easier to use.
Keep Environmental Variables Out
A frequent problem that I see in Ant build files is hard-coded file locations or path information. This invariably leads to problems because not all developers on a team have all the files in the same location (or even use the same operating system). Thus, each developer keeps his own "version" of the build file, making it harder to maintain. A single change to the master build file can have disastrous effects.
To avoid this problem, it is best to keep all environment-specific variables out of the build file itself. This of course leads to the following question: How do I tell the build file where everything is and where to put things?
The question has two answers. One solution is to use environmental variables. Just like specifying JAVA_HOME and ANT_HOME, other environmental variables can be set and used inside the build file. Specifying CATALINA_HOME instead of hard-coding tomcat's location can make the build file a lot cleaner and more robust. Setting JBOSS_HOME or even a LIBRARY_HOME in the environmental variables and then using those variables inside of the build file to point to libraries can remove a lot of inconsistencies between developer machines. If this route is chosen, place the following tag in the ant build file. It can access all your environmental variables by appending "env" to the name of the variable:
<property environment="env"/>
For instance, if you have JBOSS_HOME defined as an environmental variable, it is easy to reference it as ${env.JBOSS_HOME}.
The second option is to use a user-specific properties file along with the Ant build file by using a properties file with simple name value pairs and then loading it dynamically into the build file at run time to eliminate all environment-specific variables. Ant even has a function built into it to load a properties file. Using this function along with the variable ${user.name} (also built into Ant) allows the build file to look for a user-specific properties file to find all its file locations.
In keeping with the other suggestions noted previously, when you add variables to the build file, make sure that their variable names make sense and are easy to understand. Naming the variable JBOSS_HOME instead of JBOSS lets the other developers know that the variable points to the root of the JBoss installation—not to one of its numerous library directories.
Keep It Clean
Anyone who has been using Ant for any reasonable length of time will understand what targets, filesets, and tasks are used for. Therefore, it is unnecessary to have a large number of comments in the build file that details every single action. Avoid this by having clear and concise comments in the build file.
If the target's purpose is not clear (such as clean or compile), explain what the target does briefly. Don't go overboard with comments that will end up just cluttering the file and making it hard to read. Also, an overabundance of comments tends to become outdated as the project continues (because comments are usually not updated when the targets are changed).
Conclusion
Ant is an incredibly flexible and useful tool. With that flexibility, however, comes the capability to easily make it complex and unwieldy. Following the suggestions presented in this article will help ensure that your build files will be useful and manageable.