Why SCons rules, make drools, and autogoats eats your soul

Why SCons rules, make drools, and autogoats eats your soul

I posted on the icculus quake3 list about the possibility of using SCons; the build that id used was cons, though they also left behind a Makefile that was clearly out of date. The discussion didn’t last long: someone suggested using automake and the lead developers quickly doused that idea by stating that plain make would be the way to go.

It got me thinking, why did I think SCons was better than that, when there was strong demand to stay put, and suggestions to change to something I can’t stand? Why did I want to use SCons in the first place?

I think it all boils down to one particular use case, that I find so attractive that all other failings are massively outweighed: modifications to the build just work.

Let’s assume we have a project with three different build systems: plain make, GNU automake, and SCons. All three from a fresh checkout do the right thing and you end up with the binary you’re expecting.

Also, the build systems do the right thing when you modify the source code: make will detect the timestamp change and recompile; automake just generated a Makefile with the correct dependencies, so that make will detect the timestamp change and recompile. SCons will notice either a timestamp change or a MD5 sum change on the source file, and recompile.

There’s a caveat here: with automake and SCons you can guarantee that your build will be correct, with plain make you need to be sure that you’ve specified the dependencies correctly. This is a corner case though, it takes a special kind of project to mess this up, but it is possible and tracking it down and fixing it can lead to pain: at work, other developers can and have set up the build system to do something the wrong way, and then complain to me “I wanted to do this and it doesn’t work!”

My use case, however, is not modifying the source code, it’s modifying the build.

Say you’ve been hacking for a while, and your project is working, but you realise that you’ve foolishly left out -Wall -Werror from the CFLAGS. Let’s see what happens if you add it to each of the above projects and recompile:

  1. Plain make will do nothing. The targets already exist and the source code has not changed. Make knows nothing of recipe signatures, so the fact that the command line to do the build has changed is of no consequence. In order to get a correct build you will need to make clean, and that relies on the fact that you’ve written a target to do that correctly – more potential for getting it wrong.

    Let’s not limit ourselves to CFLAGS here; if there’s an option that only changes part of the build, then you are going to have to rebuild the whole project in order to take advantage of the change. That totally sucks on a project that takes between 30s and 5 minutes to build, because it’s too long to wait and not long enough to go get a coffee.

  2. Automake will either do nothing, or do something wrong. Depending on whether you ran configure with --enable-maintainer-mode and you have AM_MAINTAINER_MODE specified in configure.in, the generated Makefile may have no idea about its own dependencies. On the other hand, my experience with automakes 1.4, 1.5, 1.6, 1.7, and 1.8 have always caused some frustration with random regeneration errors – partially reconstructing the build system, usually caused by make’s own reliance on timestamps – and even when it gets it right you still have the problem of needing to do a full clean before a rebuild.

  3. SCons will rebuild your project correctly. SCons keeps a record of what the command line for each target was, and if that changes then it will consider the target to be out of date. If you change the CFLAGS for a project between builds, the project will be rebuilt correctly. If you change something that only affects part of the build, only those parts that are affected will be rebuilt.

There are three kinds of people who will be affected by your build system: users, distributors, and developers. The users, though automake has a lot of mindshare (with it’s configure; make; make install) mantra, don’t really care how it builds as long as the README explains how and that it works. They only need to build it once. The distributors don’t care what builds it as long as it builds and installs into the right places, i.e. FHS compliance out of the box – --prefix and DESTDIR variables or something equivalent so that the packages can be built. They may build it several times, but it’s all encapsulated in the packaging scripts and hidden behind a simple command tool: debian/rules or rpmbuild -ba or what have you.

Developers, who are building and hacking and building and tweaking and profiling and building, do care that their build is correct, and that the time spent interacting with the build system is as short as possible. As a developer, I don’t want to spend time cleaning, regenerating, and building just to make sure I get a correct build. I just want to type one command.

scons is that command.