I. Overview of the main build system families
1. The Make model
From historical reasons mostly, the make-like tools have gained mainstream diffusion. The idea of these tools was to provide a minimal language in which dependencies between files are expressed, and a minimum scheduler using these dependencies for launching compilation commands.
This design is wrong for at least two reasons:
- extending the tool means extending a programming language
- backward compatibility is broken too easily
- the model relies too heavily on the concept of files
- the language in which make was written (C) leaves plenty of room for programming mistakes
2. Extending Make means creating chains of applications
The main concepts for extending software, such as: function overloading, inheritance, composition do not exist in simple languages such as Make. Because fixing the Make language was too difficult (the space/tab problem still exists in gmake), the only solution is the development of makefile generators, or tools that generate code that make can read. Makefile generators in their turn were not created with portability or configuration in mind, and other tools were created to generate code for these code generators.
The most famous chain of applications is the group Autoconf,Automake,Libtool,Make. A macro languages (m4) is used to generate the Makefiles and makes it pretty difficult to debug and to maintain. More recently the same mistake was made in QMake with a new programming language. In CMake the developers have at least tried to include the configuration system, yet users still need to create wrappers for it (kde unsercmake, etc). In OMake the developers have even added a shell.
For launching commands in parallel or for caching the compilation results, tool chains abstracting make from the files system have had to be written too (distcc, ccache).
3. The Ant family
Ant-like tools contain XML-based configuration scripts, and extensions written in Java, therefore reusing the power of the Java libraries. They were written in the hope that machines could create new production rules, and that people could control the compiler tool chains right from their favorite IDEs (Eclipse..). The commands are executed in a procedural manner, and some room is left to declare some targets that may be executed in parallel.
There are various shortcomings:
- The XML file format is too verbose already, but expressing conditions in XML is even worse. An XML interface used to be provided in Waf and was removed because users hated it.
- Extensions are java programs which must compiled. They make it difficult to customize the system for new scenarios.
- Tasks are not executed in parallel by default and reduces performance. Parallel execution must be declared explicitely, and leads to complicated and verbose code.
4. On native build systems
Native build systems such as Scons and Waf use the Python programming language to allow library code reuse and extensible APIs. The dynamic typing nature of Python makes it easy to write new rules, however bugs might hide and not pop up until much later.
5. Domain specific programming languages - bad idea
Designing a new programming language is easy, but designing one that can be extended is extremely difficult. A build system is a tool, not a programming language. The same mistake can be spot in other contexts as well, for example in the field of constraint programming, tons of domain-specific languages have been written, yet the most successful tools use existing languages or languages very similar to existing ones Choco:Java, Facile:Ocaml, etc. Creating a custom programming language in the hope it will make the system more flexible is chasing two very different animals at the same time.
In the context of build systems, there are at least 3 concepts to remember:
- do not design domain-specific languages
- do not create compilers (writing makefiles)
- scripting languages are critical for flexibility
II. comparison between various build tools
| | Waf | Scons | Fbuild | Maven | Ant | CMake |
| Interpreter | Python 2.3 to 3.1, Jython 2.5 | Python 2.2 to 2.6 | Python 3 | Java | Java | Macro language |
| Licensing | New BSD | New BSD | New BSD | Apache | Apache | GPL/BSD |
| Parallel builds | Yes | Yes | No (except for c and c++) | Must be defined manually | Must be defined manually | Yes |
| Build phases | Yes | No | Only configuration/build | Yes | No | No |
| Direct execution | No | No | Yes | Yes | Yes | No |
| Dependencies | hash | hash | hash | timestamp | timestamp | timestamp |
| Execution speed | Good | ~ | ~ | ~ | ~ | Good |
| Unit testing | Yes | Yes | No | Yes | No | Yes |
| Access to task execution | Yes | No | Yes | No | No | No |
| Continuous integration | Yes | No | No | No | No | No |
| Colored output | Yes | Not by default | Yes | No | No | Yes |
| Build folders | Yes | Complicated setup | No | Yes | No | Yes |
| Build kind | Process-oriented (tasks) | File-oriented (nodes) | File-oriented (nodes) | File-oriented (files) | File-oriented (files) | File-oriented (files) |
Two entries I'd add to the table are "Provides Mechanism for Finding Libraries," and "Assist with Compiler/Linker Portability" though hopefully someone else can figure out a less verbose way to say it ;) One of the nice things about CMake is that it provides many stock scripts that search the most common install locations of well known open source libraries like Boost, SDL, etc. Also, it lets you specify generic preferences like "turn optimization on" rather than "pass -O2" to the compiler. I think Scons does that too, dunno about waf (haven't read into the manual too far yet).
This table lackes some other tools to compare to, like Premake and Bam.