14 comments

[ 0.24 ms ] story [ 16.7 ms ] thread
Don't do it!

https://accu.org/journals/overload/14/71/miller_2004/

Recursive Make Considered Harmful

By Peter Miller

[delayed]
Having a clean DAG still breaks down with recursive Make: if binary X depends on library A, then X's Makefile either has a rule to recurse to A (and then parallel builds of A might happen, even when building from the top level, unless you use -j1) or it doesn't (and then you can only build from the top level Makefile and you might give up parallelism due to granularity of the top-level DAG).

At the time that essay was written, Make was the best runner available, and the only portable one. There are better options now.

Yeah, no: We use recursive make extensively with a mix of "just normal makefiles" and autoconf schtuff, and it all works fine and reliably.

It's like any programming: Understand your tools, understand your requirements, understand your design and implementation (code carefully), test, review the design and implementation, test, repeat as necessary.

Still a big fan of make all these decades later. One day I may reach BJJ blue belt level.

The proliferation of recursive make has been harmful for make itself!

Most of the make replacement tools are promoted by propaganda which attacks a strawman version of make, whereby it is assumed to be at the center of a shitty recursive situation.

Like anything else, recursive make can be used in a right way and in the wrong way.

It can be used in a wrong way, like described in the article linked by you, when it results in slow project building.

How I use recursive make, it does not introduce any slowdown whatsoever.

I organize my projects so that there is a one-to-one correspondence between leaf Makefiles and the final files that are generated as a result of the building process, regardless of the file types, e.g. executable files, static libraries, dynamic libraries, executable files including GUI resources, documentation files etc.

A generated file may depend on source files located in many directories. Those files are searched and used by the single "make" command that generates one result file (by using the GNU make directory search functions, e.g. "wildcard", "notdir", "addsuffix", "addprefix"), not by recursive make invocations.

I use recursive "make" invocations only for generating multiple result files with a single make command in the top of a directory tree that contains subdirectories where each result file will be built (which also store the corresponding intermediate files, e.g. object files).

The directory tree used for project building is located elsewhere than the source files and its tree structure is not related to the tree structure of the source files, but it is determined only by the number of final files that are produced when building the project, which will be included in a release.

The `-j` is meant for the top-level invocation only, and the $(MAKE) pattern is standard.

The `+` or $(MAKE) "tricks" to ensure the jobserver is inherited in submake and any other subprocess are no longer needed in gmake 4.4 (from 2022) because it defaults to a FIFO instead of pipes. The author should just upgrade gmake :).

Has Apple backported anything recent to their forked gmake? Have they ever backported any features for that matter?
Apple does not ship any version of gmake. The only included make is BSD make.
On my machine (Golden Gate):

   [2026-10-01 9:35:10] % which make
   /usr/bin/make
   [2026-10-01 9:35:39] % /usr/bin/make --version
   GNU Make 3.81
   Copyright (C) 2006  Free Software Foundation, Inc.
   This is free software; see the source for copying conditions.
   There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

   This program built for i386-apple-darwin11.3.0
On Sequoia it was the same.
How it works is that the top-level invocation runs the job server, and the children feed requests to it. The top-level controls the parallelism.
Setting environment variable `MAKEFLAGS=-j8` transfers to subprocesses.