Makefile header dependencies

Table of Contents

1 Necessity of Makefile header dependencies

Makefile does not check which header is included in source code. When header file is updated, source code which include the header file will not be built. For avoiding this, it needs to clean all object file and compile it again. Compile all object file will take a lot of time, especially in case of C++.

For example, please see the following case.

$ tree.├── Makefile├── include│ └── sample.h├── sample└── sample.c1 directory, 4 files

Source code sample.c include header file sample.h.

$ cat sample.c#include <stdio.h>#include <sample.h>int main(void){ return 0;}

The include path is set to CFLAGS. String of “sample” is set to PROG variable.

$ cat MakefileCFLAGS := -I. -I./includeSRC := $(wildcard *.c)PROG := $(patsubst %.c,%,$(SRC))all: $(PROG)clean: @$(RM) $(PROG)

When $(PROG) is set to dependencies of all target, Makefile will use implicit rules for building object file of $(PROG). Running make command will call cc command with CFLAGS. Running make command again will not call cc command because object file of sample is already exists.

$ makecc -I. -I./include sample.c -o sample$ make: Nothing to be done for `all’.

The following target’s entry is equal with this implicit rules.

%:%.c $(CC) $(CFLAGS) $< -o $@

When updating timestamp of sample.c with touch command, make will compare timestamp of sample and sample.c, and create new sample.

$ touch sample.c$ makecc -I. -I./include sample.c -o sample

When updateing timestamp of sample.h with touch command, make does not create new sample though sample.h is newer than sample. Because sample.c includes sample.h, updating sample.h means updating sample.c, so new sample should be created.

$ touch include/sample.h$ makemake: Nothing to be done for `all’.

2 $(CC) -MM option

-M option and -MM option outputs included header files recursively.

-M option outputs include header which are in default compiler search path.

$ cc -M -I./include sample.c | headsample.o: sample.c /usr/include/stdc-predef.h /usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h <snip>

-MM option does not output include header which are in default -compiler search path. -MM option is useful for Makefile header dependencies.

$ cc -MM -I./include sample.csample.o: sample.c include/sample.h

3 %.d file

Automatic creation of %.d file is recommended.

  • Output “sample.o: sample.c include/sample.h” with $(CC) -MM option.
  • Add sample.d to target with sed command. When updating sample.c or sample.h, this will update sample.d.

Makefile is as below. The all target depends %.d and call $(PROG) target. For creating $(PROG) file, the % target will be called and implicit rules will be used.

CFLAGS := -I. -I./includeSRC := $(wildcard *.c)OBJ := $(patsubst %.c,%.o,$(SRC))DEP := $(patsubst %.c,%.d,$(SRC))PROG := $(patsubst %.c,%,$(SRC))all: $(DEP) @$(MAKE) $(PROG)clean: @$(RM) $(DEP) $(OBJ) $(PROG)ifneq ($(filter clean,$(MAKECMDGOALS)),clean)-include $(DEP)endif%.d: %.c $(info GEN $@) @$(CC) -MM $(CFLAGS) $< | sed ‘s/($*).o[ :]*/1.o $@ : /g’ > $@%: %.d

When updating sample.h, new sample will be created.

$ makeGEN sample.dcc -I. -I./include -c -o sample.o sample.ccc sample.o -o sample$ makemake[1]: `sample’ is up to date.$ touch sample.c$ makeGEN sample.dcc -I. -I./include -c -o sample.o sample.ccc sample.o -o sample$ touch include/sample.h$ makeGEN sample.dcc -I. -I./include -c -o sample.o sample.ccc sample.o -o sample

Android | Linux | SDL - Narrow Escape