Recursion

One of the simplest tasks to automate in Gnu Make is directory recursion.

Traditional Makefile

In this example, Make needs to recurse into five directories. In a traditional Makefile, this results in a lot of repetition, as follows:

Makefile

default:
        $(MAKE) -C database
        $(MAKE) -C network
        $(MAKE) -C client
        $(MAKE) -C server
        $(MAKE) -C gui

test:
        $(MAKE) -C database test
        $(MAKE) -C network test
        $(MAKE) -C client test
        $(MAKE) -C server test
        $(MAKE) -C gui test

clean:
        $(MAKE) -C database clean
        $(MAKE) -C network clean
        $(MAKE) -C client clean
        $(MAKE) -C server clean
        $(MAKE) -C gui clean

all: default test

Rewritten Makefile

We’ll optimize this Makefile by first combining the “default”, “clean”, and “test” targets into a single line, and using ”$@” to get the target name instead of hard coding it.

Next, we’ll replace all the separate calls to $(MAKE) with a shell look that reads the SUBDIRS variable.

Finally, targets will be followed by the double colon (::) instead of the single colon. This is required by Make to allow multiple instances of the same target (i.e., there are two instances of “default”, and there could be more instances of “clean” and “test” in a real Makefile).

Makefile

SUBDIRS=database network client server gui

default::

all:: default test

default clean test::
        for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@ || exit 1 ; done

Breaking it out

Now that we know how to simplify the Makefile, it’s time to move the reusable code into its own file.

While we’re at it, we’ll add a switch that let’s us turn off recursion.

subdirs.mk

ifndef NORECURSE

default clean test::
        for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@ || exit 1 ; done

endif

To use “subdirs.mk”, your Makefile might look like:

Makefile

SUBDIRS=database network client server gui
include subdirs.mk