Multilevel makefiles

The command section of a makefile rule may recursively call "make". We can use this to organize a multipart project, or set of projects, into a set of subdirectories which each have their own makefile.

Suppose, for example, that we have a project which has separate UDP and TCP sections. We would like to keep the code for each of these sections in different subdirectories like so:

	         ---udp
	        /
	project<
	        \
	         ---tcp
We will put a makefile in each of the directories: project, udp, and tcp. The job of the top-level makefile (the one in "project") is to direct the operation of the lower-level makefiles. Their job is to build the udp and tcp versions of the project. The top makefile might look like this:

all:
	(cd udp; make all)
	(cd tcp; make all)

install:
	(cd udp; make install)
	(cd tcp; make install)

clean:
	(cd udp; make clean)
	(cd tcp; make clean)

The first command for the target "all", starts a subshell which goes into the "udp" subdirectory then runs "make all". That "make" will build the UDP version then return to the top-level makefile. The second command for "all" then starts a subshell which goes into the "tcp" subdirectory and runs "make all". This "make" will build the TCP version of the project before returning to the top-level makefile. How the UDP and TCP versions are actually built are hidden from the top-level makefile, and can be changed without changing the top-level makefile. (Except that they must provide the targets used by this makefile.)

The makefile for the UDP version might look like this:


INSTDIR=/usr/local/bin

all: server client

server: server.c
	gcc -Wall -o server server.c

client: client.c
	gcc -Wall -o client client.c

install: server client
	mv server client ${INSTDIR}
 
clean:
	-rm -f server.o client.o core

When the top-level makefile runs (cd udp; make all), this makefile will build server and client in the udp subdirectory. Running (cd udp; make install), from the top level makefile will cause this makefile to move the executables to /usr/local/bin. Also, running (cd udp; make clean) from the top-level makefile will cause the objects and a possible core file to be removed from the udp subdirectory.

Another reason for using multi-level makefiles is to isolate user-customizable variable settings to the top-level makefile. This lets the user work exclusively with the top-level makefile, rather than having to go into each subdirectoy to change variable values. The top-level makefile then passes variable values to the lower-level makefiles which need them.

The UDP makefile above appears to violate this idea because it sets INSTDIR explicitly, but a variable setting from the command line takes precedence over a setting in the makefile. By changing the "install" rule in the top-level makefile to pass a value for INSTDIR to one or both of the lower-level makefiles, we can isolate the customization to the top level:


all:
	(cd udp; make all)
	(cd tcp; make all)

install:
	(cd udp; make install INSTDIR=/usr/local/bin)
	(cd tcp; make install INSTDIR=/usr/local/bin)

clean:
	(cd udp; make clean)
	(cd tcp; make clean)

Now the user just hase to change the values passed as INSTDIR. We can tighten this a little by using a variable at the top level:

INSTDIR=/usr/bin

all:
	(cd udp; make all)
	(cd tcp; make all)

install:
	(cd udp; make install INSTDIR=${INSTDIR})
	(cd tcp; make install INSTDIR=${INSTDIR})

clean:
	(cd udp; make clean)
	(cd tcp; make clean)

Note that the commands in the "install" rule are interpreted as:

	(cd udp; make install INSTDIR=/usr/bin)
	(cd tcp; make install INSTDIR=/usr/bin)

These commands will run "make" in each of the subdirectories and use "/usr/bin" as INSTDIR in each, even if INSTDIR is set in the lower-level makefile. If we run:

	(cd udp; make install)

"make" will use INSTDIR as defined in "udp/makefile" because it is not overridden by the top-level makefile. Also, if we go into the udp subdirectory and run make from there (without using the top-level makefile), INSTDIR will be defined as in "udp/makefile". This is one reason that running lower-level makefiles by hand should be discouraged.
This page was last modified .