Make Cheatsheet
Pattern and Implicit Rules
Use this Make reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Pattern rules
A pattern rule uses % as a wildcard stem and applies to every target that
matches. One rule replaces one hand-written rule per file. Recipe lines still
start with a real tab.
build/%.o: src/%.c $(CC) $(CFLAGS) -c -o $@ $<
- % matches one or more characters, including slashes, and matches the same
text in the target and in every prerequisite.
- The matched text is the stem, available as $*.
- A pattern rule with no recipe cancels the pattern.
- Multiple patterns can match. Make picks the rule whose prerequisites exist or
can themselves be built, preferring the shortest stem.
%.o: %.c # C
%.o: %.cc # C++, tried when no .c exists
%.pb.go: %.proto # multi-suffix patterns are fine
%.o: %.c generated/config.h # extra prerequisites apply to every match
%.o: %.s # no recipe at all cancels a built-inMultiple targets in one pattern rule mean the recipe produces all of them in a single run, which is the opposite of the explicit-rule behavior.
%.tab.c %.tab.h: %.y bison -d -o $*.tab.c $<
Static pattern rules
A static pattern rule applies a pattern to an explicit list of targets. Use it when a bare pattern rule would be too greedy or when the target list is computed.
OBJ := build/main.o build/util.o $(OBJ): build/%.o: src/%.c $(CC) $(CFLAGS) -c -o $@ $<
Read it as targets: target-pattern: prereq-patterns. Differences from a plain
pattern rule:
| Pattern rule | Static pattern rule | |
|---|---|---|
| Applies to | any matching target | only the listed targets |
| Errors if a listed target does not match | — | yes, at parse time |
| Can be chained through intermediates | yes | no |
| Used to build a target nobody asked for | yes | never |
The parse-time error is the selling point. A typo in OBJ fails immediately
instead of falling through to a built-in rule.
Suffix rules, the obsolete form
Suffix rules predate patterns and are the only implicit rule POSIX make
guarantees. GNU make still honors them for compatibility.
.SUFFIXES: .SUFFIXES: .c .o .c.o: $(CC) $(CFLAGS) -c -o $@ $<
- A double-suffix rule .c.o means "make .o from .c". A
single-suffix rule .c means "make an extension-less file from .c".
- Both suffixes must be listed in .SUFFIXES or the rule is read as an ordinary
target with a strange name.
- Suffix rules cannot express directory changes, so build/%.o: src/%.c has no
suffix-rule equivalent.
Write pattern rules. Only reach for suffix rules when the makefile must run under a non-GNU make.
Built-in rules, and turning them off
GNU make ships roughly 90 pattern rules and a matching set of default variables,
which is why make foo.o works in an empty directory that contains foo.c.
gmake -p -f /dev/null | less # print the entire built-in database gmake -p -f /dev/null | grep -A2 '^%.o:'
The defaults they use:
| Variable | Default | Used by |
|---|---|---|
CC | cc | %.o: %.c |
CXX | g++ | %.o: %.cc |
CFLAGS CXXFLAGS | empty | compile rules |
CPPFLAGS | empty | every preprocessor step |
LDFLAGS LDLIBS | empty | link rules |
RM | rm -f | clean recipes |
AR ARFLAGS | ar rv | archive rules |
.LIBPATTERNS | lib%.so lib%.a, lib%.dylib lib%.a on macOS | -lfoo prerequisites |
Built-ins cause surprises: a stray foo.c next to a foo target, or an
implicit RCS or SCCS lookup on every miss. Disable them for a measurable
speedup and predictable behavior.
MAKEFLAGS += --no-builtin-rules --no-builtin-variables .SUFFIXES:
gmake -r -R # same thing from the command lineDoing this means $(CC) and $(CFLAGS) are now undefined, so define every
variable your rules use.
VPATH and vpath
Make looks for prerequisites in the current directory only. VPATH adds a
search path, and lowercase vpath scopes it to a pattern.
VPATH = src:src/net:include # colon or space separated, all file types vpath %.c src # only .c files vpath %.h include generated # only .h files vpath # clear every vpath entry
- When make finds a prerequisite through the path, $< and $^ hold the
found path, so $(CC) -c $< works unchanged.
- Targets are still written to the current directory unless the recipe says
otherwise. Make does not rewrite $@.
- A target found through VPATH is only reused if it is up to date, otherwise
make rebuilds it locally, which can leave two copies.
vpath is the narrow, predictable form. Large projects tend to skip both and
compute explicit paths with $(wildcard ...) instead, because a search path
that silently picks up a stale file in another directory is hard to debug.
Chained rules and intermediate files
Make will chain implicit rules to reach a target, inventing the middle steps.
%.mid: %.in
cp $< $@
%.out: %.mid
cp $< $@Running make a.out with only a.in on disk produces:
cp a.in a.mid
cp a.mid a.out
rm a.midThe middle file is intermediate: created, used, then deleted, and its timestamp does not trigger rebuilds. Control that with special targets.
| Target | Effect |
|---|---|
.SECONDARY: a.mid | Keep the intermediate. With no prerequisites, keep all |
.PRECIOUS: %.mid | Do not delete on interrupt or error |
.INTERMEDIATE: x | Force a normally-permanent file to be intermediate |
.NOTINTERMEDIATE: | GNU make 4.4, opt out of the whole mechanism |
Chains are only searched for implicit rules, never for explicit ones, and never for static pattern rules. If a two-step build is not happening, that is usually why.
Gotchas
- A pattern rule whose prerequisite does not exist is silently skipped, and
make falls through to the next candidate or reports "No rule to make target".
Check with gmake -d 2>&1 | grep -i 'trying pattern'.
- % in a variable is not special. $(SRC:%.c=%.o) is substitution
reference syntax, which is a different feature from a pattern rule.
- Directory patterns must match exactly. %.o: %.c will not find
src/main.c for build/main.o, because the stem would have to be both
build/main and src/main.
- The stem includes directories. For build/net/tcp.o, $* is net/tcp,
so $(notdir $*) is what you usually want in messages.
- Empty recipes matter. %.o: %.c ; cancels the built-in rule, while
omitting the recipe entirely cancels the pattern.
- A pattern rule cannot be .PHONY. Phony targets are matched literally, so
mark the concrete names.