“make
is a widely used and valuable development tool. It’s a “build” tool: it builds programs and documentation according to a “recipe”. It could really be used for anything where someone edits some files, and then runs a series of processing steps to generate some other form from the edited files. For the most part, however, it’s just used to build and install software. make
has its origins in Unix somewhere, and these days each BSD project and the GNU project have their own version.
I often get the impression that many otherwise knowledgeable and skilled developers don’t have more than rudimentary knowledge of make
, and could benefit from a more solid understanding. I don’t particularly blame them: make
is certainly ancient and has odd syntax and quirks. So many developers do the minimum necessary to add their new sources to the build, and then go back to working on the actual code. Having a good build system and understanding how it works can make development and deployment of software much more pleasant, so I humbly suggest taking the time to really learn one.
This blog post is about using a subset of the features of GNU Make to write “good” build systems. There are a variety of build systems out there, some of which use make
as a component, like CMake and GNU Autotools, and some of which don’t, like scons or jam or ninja. I won’t fault anyone for using another build tool if it’s convenient, but I will judge it based on whether it gets reasonably close to this simple goal:
A developer should be able to run a single command (like make [args]
) and get the correct result ASAP.
In more detail:
- The build system should produce the exact same final outputs as if it was doing a completely clean from-scratch build, even when it’s not.
- The build system should be flexible.
- The build system should be reasonably fast.
Flexibility:
- The build system should work with different filesystem hierarchies, where the locations of dependencies and outputs can be flexibly specified.
- The build system should be able to install to an arbitrary “root” directory, so the results can be easily packaged, or simply installed into a filesystem tree for a separate system.
- The build system should be able to be configured to do a cross-compile for a different architecture, if applicable. It’s usually enough to enable the compiler and linker paths and options to be conveniently specified.
Speed:
- A fast build system will avoid unnecessary work, re-using existing results if they would be exactly the same if regenerated.
- A fast build system will do tasks in parallel when possible.
Developers of the software don’t want to wait too long to see and test the result of a change. Neither do integrators working on an embedded OS or a large application, and for them, reducing a number of short build times can add up to a big difference. Those developers also need the flexibility to be able to integrate the software into their system without too much patching.
If a developer is working on a piece of software, makes a change, runs a build, and later figures out that the change didn’t get into the build, he’ll become paranoid and always clean the source/build tree before a build. This really hurts a quick feedback cycle…”
http://www.ploxiln.net/make.html
