Site Tools


wiki:makefile-dependencies

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).

wiki/makefile-dependencies.md · Last modified: by 127.0.0.1