Build systems for C/C++ projects tend to be complex and inscrutable. However often the requirements for a build system are as simple as "compile every source file in this directory tree and link them into a module". Also it would be nice not to require some fancy build system - GNU make is installed pretty much everywhere, right? Surely it's powerful enough to perform this simple task?
Indeed it is. However, nearly all makefiles I've encountered (perhaps I've missed the good ones?) have one or more of the following weaknesses:
- Dependencies are not automatically tracked, or are done so in a broken way (e.g. if I edit a header file and add a #include, the new dependency is not updated).
- If dependencies are tracked correctly, updating them performs too much work (e.g. pretty much any makefile that uses cc -M, since if you touch one header file it has to parse every header included by every source file that included the touched header, rather than just that one header!)
- Object files are built in the same directory as the source files. This is bad because if I want to change a compile setting, I need to delete them all (it's better to have different configurations in different directories, so when I switch I can still just update incrementally).
- Adding a new source file means manually adding it to the makefile.
- Requires a huge suite of perl scripts, etc., to implement this functionality, which is pretty much broken anyway, and no-one wants to fix it because the whole thing is so uuurgh...
- Rely excessively on recursive invocations of make (for why this is bad see http://miller.emu.id.au/pmiller/books/rmch/).
- Object files get dumped into a single directory - this leads to problems if two source files in different directories have the same name.
- Dependency tracking is some large, opaque process, which means that make -j is unable to optimize it.
To address this, I've created a simple makefile which I've called buildtree.mak. It does just that - let you build all the source files in a source tree into a module. All generated files are output into a specified directory. This directory mirrors the structure of the source tree. Dependencies are tracked automatically, but updated using as little work as possible.
The minimal dependency tracking utilizes the feature of GNU make where included makefiles are first updated using standard make rules. This means that dependency updating is thoroughly understood by make itself, and therefore it works well with make -j.
It is intended as a template - you can just dump it in your project and rename it to Makefile and hack away at it until it suits your needs. I've released it into the Public Domain, so do what you like with it (acknowledgements are always welcome).
Feedback, bugs and especially patches are welcome.
Post a Comment