Nonrecursive make advocacy

by havoc

When I set up the Mugshot
client build
I noticed that the automake manual suggests a
non-recursive setup, so I thought I’d try it. I’ve used a
non-recursive setup for every project since. The
automake manual
points to the classic 1997 paper, Recursive
Make Considered Harmful
, if you want the detailed rationale.

My first attempt at the Mugshot client build had one big Makefile.am
with every target inline in it; that was a downside. Owen nicely
fixed it with a convention: for build subcomponent “libfoo” put
“include Makefile-libfoo.am” in the Makefile.am, then put the build
for stuff related to “libfoo” in “Makefile-libfoo.am”.

I recommend doing your project this way. I’ve spent a lot less
time messing with weird build issues; nonrecursive make “just works”
as long as you get the dependencies right, while recursive make
involves various hacks and workarounds for the fact that make can’t
see the whole dependency graph. In particular, nonrecursive make
supports “make -jN” without extra effort, a big win since
most new computers have multiple cores these days.

Nonrecursive make has the aesthetic benefit that it keeps all
your build stuff separate from your source code. On top of
that, since srcdir != builddir will work more easily, you can make a
habit of building in a separate directory. Result: your source tree
contains source and nothing else.

GNOME unfortunately makes nonrecursive automake painful. Two issues
I’ve encountered are that “gtkdocize” generates a Makefile that is
broken in a nonrecursive setup, and that jhbuild always wants to do
srcdir=builddir even though my project is nice and clean and doesn’t
require that.

I’m not sure why GNOME started with recursive make and everyone has
cut-and-pasted it ever since; it’s possible automake didn’t support
nonrecursive make in older versions, or maybe it was just dumb luck.

With “weird build bugs due to recursive make” knocked off the
list, my current top automake feature requests:

  • A way to echo only the filename “foo.c” instead of a 10-line
    list of compiler flags, so I can see all warnings and errors at once without
    scrolling through pages of junk.
  • In a nonrecursive make setup, a way to set a base directory for
    SOURCES, so instead of “foo_SOURCES=src/foo.c src/bar.c” I can have
    “foo_SOURCE_BASEDIR=src foo_SOURCES=foo.c bar.c” or something along
    those lines.
  • Ability to include in the build a relative directory outside the
    source tree, like “../common” or “../some-not-installed-dependency” –
    this almost works but breaks in “make dist” because it tries
    to copy “../common” to “distdir/../common” – we fix that in the
    Mugshot client build with a little hack, but I can imagine an
    automake-level convention for how to handle it.

These are obviously pretty minor quibbles.

(This post was originally found at http://log.ometer.com/2007-07.html#14)

My Twitter account is @havocp.
Interested in becoming a better software developer? Sign up for my email list and I'll let you know when I write something new.