Rebuilding When CPPFLAGS Changes

[article]
Summary:

GNU Make has no way of detecting that some targets ought to be rebuilt, because it doesn't take into account changing the commands. If, for example DEBUG=1 causes the flags passed to the compiler to change then the target ought to be rebuilt. This article shows how, in a few lines of GNU Make code, to make that happen.

For example, what happens if you do a non-debug build (by typing make) and then run a debug build by typing make DEBUG=1. Unless the build has been structured so that the names of targets are dependent on whether the build is debug or non-debug, nothing happens at all.

GNU Make has no way of detecting that some targets ought to be rebuilt, because it doesn't take into account changing the commands. If, for example DEBUG=1 causes the flags passed to the compiler to change then the target ought to be rebuilt.

This article shows how, in a few lines of GNU Make code, to make that happen.

An Example

Here's an example Makefile that's used throughout this article to demonstrate the rebuilding when commands change system. To make the operation of the system very clear I've avoided using built-in GNU Make rules so this Makefile isn't as simple as it could be.

The Makefile creates two .o files: foo.o and bar.o by compiling corresponding .c files. The compilation is done using the built-in variable COMPILE.C (which will be something like normally be the name of a suitable compiler for your system, references to variables like CPPFLAGS and use of $@ and $< to compile the right thing).

There's a specific reference to $(DEBUG), it's turned into a preprocessor variable called DEBUG using the compiler's -D option. I haven't bothered showing the contents of foo.c and bar.c as they are irrelevant.

all: foo.o bar.o

foo.o: foo.c
    $(COMPILE.C) -DDEBUG=$(DEBUG) -o $@ $<

bar.o: bar.c
    $(COMPILE.C) -o $@ $<

Here's what happens if we first run make (which means that DEBUG is undefined) followed by make DEBUG=1.

$ make
g++    -c -DDEBUG= -o foo.o foo.c
g++    -c -o bar.o bar.c

Now foo.o and bar.o have been created (with DEBUG empty) and so typing make again does nothing:

$ make
make: Nothing to be done for `all'.

Typing make DEBUG=1 also does nothing, even though the object file foo.o would likely be different if it were rebuilt with DEBUG defined.

$ make DEBUG=1
make: Nothing to be done for `all'.

The signature system described below will correct that, with very little work for the Makefile maintainer.

The Example Revisited

To fix the problem described above this article introduces a helper Makefile called signature. The contents and working of signature are described below, but first let's look at how the example Makefile is modified to used the signature helper.

include signature

all: foo.o bar.o

foo.o: foo.c
    $(call do,$$(COMPILE.C) -DDEBUG=$$(DEBUG) -o $$@ $$<)

bar.o: bar.c
    $(call do,$$(COMPILE.C) -o $$@ $$<)

-include foo.sig bar.sig

Three changes have been made to the file: firstly include signature has been added at the start so that the code that handles the signature updating is included. The commands in the two rules have been wrapped with $(call do,...) and the $ signs for each command have been quoted with a second $.

Lastly for each .o file being managed by signature there's an include of a corresponding .sig file. The final line of the Makefile includes foo.sig (for foo.o) and bar.sig (for bar.o). Notice that -include is used in case the .sig file is missing.

Before seeing how this works here are some example of it in operation. First run a clean build (i.e. with no .o files present) and then rerun make to see that there's nothing to do:

$ make
g++    -c -DDEBUG= -o foo.o foo.c
g++    -c -o bar.o bar.c
$ make
make: Nothing to be done for `all'.

But now setting DEBUG to 1 on the make command-line cause foo.o to rebuild because its 'signature' (i.e. the actual commands to

About the author

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!