If you aren't familiar with make (wherby I mean specifically GNU Make), it is a UNIX-based tool that reads a Makefile, which is a set of targets (with dependants) that get "made" when a target is out of date. What happens when a target is "made" is entirely up to the Makefile author. Usually, this means compiling source code into object or executable code, but it could be anything, including checkout of resources from source control or creating distributable archives. What happens during a make is typically calls to various shell commands. Since make originally was a UNIX program, make was designed assuming the full range of UNIX commands.
Make is very flexible, in that you have a wide latitude to define variables, manipulate those variables and pass them to any command available on your system. A simple Makefile might look like this:
FILE_BASE = hello
$(FILE_BASE) : $(FILE_BASE).o
gcc -o $(FILE_BASE) $(FILE_BASE).o
$(FILE_BASE).o : $(FILE_BASE).c
gcc -c $(FILE_BASE).c
hello is a target, with
hello.o being a dependant.
hello.o is also a target itself, with
hello.c as it's dependant. In most cases, make assumes that targets and dependants are files. So, in this example, if
hello has an earlier modification date than
hello.o, it will run the associated command (namely
gcc -o hello hello.o), which creates
hello and brings it up to date. So, if you had a whole slew of source files, make allows you to only recompile those that changed, not the whole tree. For more about what you can do with make, see the links at the end of the article.
ant is part of the Apache Jakarta project. Ant has a similar concept to make in terms of targets and dependants, but ant doesn't assume that targets and dependants are files by default. Ant also doesn't assume that the purpose of dependants are to bring targets up to date, it's merely a simple dependancy. Ant also makes use of built-in commands to implement targets, rather than assuming shell commands are available (although you can execute arbitrary shell commands). This makes ant run on any Java-supported platform. Many of the commands are specific to compiling or working with Java code. Since ant is written in Java, it has the added feature of running the Java compiler from within itself, which ends up making compilation quite fast. Ant relies on this fact, even, because the typical compilation target results in compiling every single java source it can find each time you run it. For users of make, this may sound horrendous, but the compilation step (for me) took the same amount of time to compile one class as it did for 50.
Ant also uses an XML-based syntax for creating the buildfiles (although it is not strict XML in the sense that a DTD for all of ant cannot easily be made, and the syntax allows for a lot more flexibility than you might expect in an XML file).Comparison of the two Tools
I'll try to capture my thoughts in the following pros/cons table:
- Fast Compiles.
- Easy to get up and running for compilation.
- True platform independance.
- Good error reporting of buildfile syntax errors.
- Property value assignment very simple and inflexible.
- Cannot create dynamic targets at build-time, making complex build tasks difficult to write.
- Compiling every source file every time might be a problem for large systems.
- Integration with other systems limited to Java classes or executing command line through Java.
- Wrapping commands in "builtin tasks" exposes them to breakage when underlying tasks change.
- Sparse documentation, not widely used
- Use for non-Java projects not realistic without more builtin commands.
- Incredibly flexible
- Run-time dynamicism simplifies creating complex tasks
- Powerful variable creation mechanism simplifies creating complex tasks
- Integration with other build and deployment tools is very simple through shell commands
- Assumes targets and dependants are files, which makes it easy to write targets that only do the work that is needed
- Extensive documentation, developer knowledge and proven track record as industry standard build tool
- Can be used for any type of project, even projects that mix languages and technologies.
- Requires UNIX command set installed to be effective.
- Requires some non-beginner level make coding to work effectively with Java compiler.
- Complex Makefiles can be difficult to debug if not architected properly.
- Typical UNIX user-unfriendly error messages increase learning curve.
- Full system compiles are slower because of multiple javac invocations (although this could be remedied by calling ant for compiles only :)
I've also come up with the following, that I believe characterizes the situations in which ant or make are warranted:
- Use ant if:
- You are a single developer or one of a very small development group working a small Java-based application and you don't know make.
- You are working on a project that will be developed or deployed on a platform other than UNIX or Windows
- Your developers are all using their own build scripts and you need something fast and are not looking to build a large general-purpose build system
- Use make if:
- You know make
- You are working on a medium to large sized project or on a project with more than a few developers.
- You need to integrate with a variety of configuration management tools
- You are building a medium to long-term solution for build and deployment that must handle a wide variety of tools and technologies
While ant is an interesting tool, I don't see it as a heavy-duty build tool for Java development. Furthermore, ant's overall design seems to be the opposite of make. ant tries to be all things to all people and reimplements functionality that's available and proven on UNIX. ant tries to have a command for everything. Looking at the list of builtin commands one can't help but get the feeling that ant was made by someone who did not understand make and didn't want to learn it, and slowly realized that there's a lot of functionality a build tool will need if it cannot rely on the UNIX command set. Comments on the ant homepage support this (e.g. "Makefiles are inherently evil") to an extent. make, on the other hand, is a framework for running arbitrary commands. True, make requires UNIX to be useful, but UNIX commands are available on almost all platforms where Java is being developed. Yes, make can be hard to learn, and it does have it's quirks, but it works and it works well for almost any situation. The make code to implement java compilation (ant's biggest draw, IMO) is quite simple (even if non-trival to derive). This is one time only operation. I think ant is great if you are a single developer who can't afford an IDE and don't want to mess with make. With ant, you'll be up and running quickly. For a Release Engineer or Lead Programmer on a project, though, make is a tried and tested tool that works just as well with Java as it does with C, and you know that it will do whatever you want it to do.