One of the common objections to using non-recursive Make is that with recursive Make its possible to go to anywhere in the source code tree and type 'make'. Doing so typically builds the objects that are defined by the Makefile at that level in the tree (and possibly below
if the Makefile recurses).
Oftentimes, non-recursive Make systems (based on include statements instead of make
invocations) do not offer this flexibility and Make must be run from the top-level directory. Even though non-recursive Make is typically more efficient (meaning that running from the top-level directory should be quick), it's important to be able to give developers the same
level of functionality as a recursive Make system.
In this article I outline a pattern for non-recursive Make systems that supports the familiar make-anywhere style common to recursive Make systems. Typing 'make' in a directory will build everything in that directory and below.
A simple recursive Make
Imagine a simple project with the following sub-directories:
/src/
/src/library/
/src/executable/
/src/ is the top level directory and is where you'd type 'make' to get a full build. Inside /src/ there's the library/ directory which build a library called lib.a from source files lib1.c and lib2.c:
/src/library/lib1.c
/src/library/lib2.c
The /src/executable/ directory builds an executable file called exec from two source files (foo.c and bar.c) and the library lib.a:
/src/executable/foo.c
/src/executable/bar.c
The classic recursive Make solution to this means putting a Makefile in each subdirectory that contains the rules to build the objects and a top-level Makefile that recurses into each sub-directory. Here's the contents of /src/Makefile
SUBDIRS = library executable
.PHONY: all
all:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done
It enters each directory in turn and runs Make to build first the library and then the executable. The dependency between the executable and the library (i.e. the fact that the library needs to be built before the exectuable) is implicit in the order in which the directories are specified in SUBDIRS.
An improvement on that is to unwind the loop inside the rule for all, create separate rules for each sub-directory and then explicitely specify the dependency between executable and library.
Here's how to do that using phony targets for each directory:
SUBDIRS = library executable
.PHONY: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
.PHONY: all
all: $(SUBDIRS)
executable: library
That's much clearer, but it's still recursive with separate Make invocations for each sub-directory.
A flexible non-recursive Make system
When moving to non-recursive Make the ideal top-level Makefile would look like this:
SUBDIRS = library executable
include $(addsuffix /Makefile,$(SUBDIRS))
That simply says to include the Makefile in each sub-directory. The trick is to make that work! Before showing you how I make that actually work here are the skeletons of the contents of the Makefiles in the library and executable sub-directories.
# /src/library/Makefile
include root.mak
include top.mak
SRCS := lib1.c lib2.c
BINARY := lib
BINARY_EXT := $(_LIBEXT)
include bottom.mak
and
# /src/executable/Makefile
include root.mak
include top.mak
SRCS := foo.c foo.c
BINARY := exec
BINARY_EXT := $(_EXEEXT)
include bottom.mak
Each of those Makefiles specifies the source files to be built (in the SRCS macro), the name of the final linked binary (in the BINARY macro) and the type of the binary (using the BINARY_EXT macro which is set from special macros _LIBEXT and _EXEEXT).
To actually make this work they both include common Makefiles root.mak, top.mak and bottom.mak which are located in the /src/ directory.
Since the .mak included Makefiles are not in the sub-directories GNU Make needs to go
looking for them. The simplest way to do that is to use the [t]-I[/tt] command-line option to GNU Make that adds a directory to the include search path. To find the .mak files in the /src you can do:
make -I /src
It's unfortunate to ask a user to add anything to the Make command-line so I use a simple method of automatically walking up the source tree to find the .mak files. Here's the actual Makefile for /src/library:
sp :=
sp +=
_walk = $(if $1,$(wildcard /$(subst $(sp),/,$1)/$2) $(call _walk,$(wordlist 2,$(words $1),x $1),$2))
_find = $(firstword $(call _walk,$(strip $(subst /, ,$1)),$2))
_ROOT := $(patsubst %/root.mak,%,$(call _find,$(CURDIR),root.mak))
include $(_ROOT)/root.mak
include $(_ROOT)/top.mak
SRCS := lib1.c lib2.c
BINARY := lib
BINARY_EXT := $(_LIBEXT)
include $(_ROOT)/bottom.mak
The _find function walks up a directory tree starting from the directory in $1 looking for






