Table of Contents
Makefile Dependencies
Dependencies tell Make what files a target depends on. When a prerequisite is newer than the target, Make rebuilds. For C programs, headers are implicit dependencies—changing a header should rebuild all .o files that included it.
Tracking header dependencies: A simple approach is to list headers explicitly:
main.o: main.c util.h config.h gcc -c -o main.o main.c util.o: util.c util.h gcc -c -o util.o util.c
When util.h changes, both main.o and util.o are rebuilt. When config.h changes, only main.o is rebuilt. This works but is tedious to maintain.
Automatic dependency generation: Most projects generate dependencies automatically:
%.d: %.c @set -e; gcc -MM $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ -include $(SRCS:.c=.d)
This generates .d files (dependency lists) from .c files. The -include reads them (- prefix ignores missing files). On the next build, the dependencies are known.
Simpler approach (gcc -M):
depend: gcc -MM *.c > dependencies.txt include dependencies.txt
Run make depend to generate dependencies, then include them. Each .o file's dependencies are tracked.
Modern approach (compiler-generated):
CFLAGS = -O3 -Wall -MMD -MP %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< -include $(wildcard *.d)
-MMD tells the compiler to generate .d files. -MP adds phony prerequisites to avoid errors if headers are deleted. The compiler maintains dependencies as a side effect of compilation.
Rebuilding due to timestamp changes: Make uses file modification timestamps. If a file's timestamp is newer than its target, the target is rebuilt.
touch util.h # update timestamp make # main.o and util.o are rebuilt
Circular dependencies: Make detects circular dependencies and reports an error:
a: b b: a
This is invalid. Don't create circular dependencies.
Implicit dependencies: Some files depend on things Make doesn't know about. Make configuration to a default value:
app: config.h config.h: @echo "#define VERSION 1" > config.h
This ensures config.h exists and is up to date before building app.
Phony dependencies: A phony target can be a prerequisite:
.PHONY: check check: @echo "Running checks..." app: main.o check gcc -o app main.o
app depends on check. Every build of app runs check first (since check is phony and always out of date).
