Make Cheatsheet

Automatic Variables

Use this Make reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

The complete table

Automatic variables are set fresh for every rule, and only exist inside a recipe (or in a target-specific variable value). They are not available in the makefile body.

VariableValue
$@The target name
$<The first prerequisite
$^All prerequisites, duplicates removed, order-only excluded
$+All prerequisites, duplicates kept, in the order written
$?Prerequisites newer than the target
$*The stem matched by a pattern rule
$%The member name, for an archive target like lib.a(obj.o)
$|The order-only prerequisites

Every one has directory and filename halves. D gives the directory part without a trailing slash, F gives the filename part.

FormFor sub/out.oFor a two-word $^
$(@D)sub—
$(@F)out.o—
$(<D) $(<F)dir and file of $<—
$(^D) $(^F)—dirs and files of every word
$(*D) $(*F)dir and file of the stem—

$(@D) is . when the target has no directory part.

Worked example

Given sub/a.c and sub/b.c, with only sub/b.c newer than the target:

sub/out.o: sub/a.c sub/b.c sub/a.c | sub
	@echo "$$@  = $@"
	@echo "$$<  = $<"
	@echo "$$^  = $^"
	@echo "$$+  = $+"
	@echo "$$?  = $?"
	@echo "$$(@D) = $(@D)   $$(@F) = $(@F)"

Output:

$@  = sub/out.o
$<  = sub/a.c
$^  = sub/a.c sub/b.c
$+  = sub/a.c sub/b.c sub/a.c
$?  = sub/b.c
$(@D) = sub   $(@F) = out.o

Note that the order-only prerequisite sub appears in none of $^, $+, or $?. That is what makes cc -o $@ $^ safe next to an order-only mkdir rule.

Which one to use where

SituationUse
Compile one source to one object$<
Link or archive all inputs$^
Pass the same input twice on purpose$+
Rebuild only what changed, incremental tools$?
Derive a sibling name in a pattern rule$*
Create the target's directory inside the recipe$(@D)
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<          # $< , exactly one source

app: $(OBJ)
	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)  # $^ , every object

libfoo.a: $(OBJ)
	$(AR) rcs $@ $?                      # $? , only the changed members

%.pb.go: %.proto
	protoc --go_out=. $<
	@echo "stem was $*"                  # $* , the path minus the suffix

The stem, $*

$* is only meaningful in a pattern rule or a static pattern rule, and it holds whatever % matched, including any directory part.

build/%.o: src/%.c
	@echo "$* = $*"     # for build/net/tcp.o , $* = net/tcp

In an explicit rule $* is empty in GNU make when the target has a recognized suffix, and undefined behavior otherwise. Do not rely on it outside pattern rules. Use $(basename $@) instead.

Making the directory from $(@D)

Two idioms, both common. The order-only version runs mkdir once, the $(@D) version runs it on every compile but needs no extra rule.

# Order-only prerequisite, preferred.
build/%.o: src/%.c | build
	$(CC) -c -o $@ $<
build:
	mkdir -p $@

# Inline, works for arbitrarily deep trees.
build/%.o: src/%.c
	@mkdir -p $(@D)
	$(CC) -c -o $@ $<

For deep source trees the inline form is simpler, because the order-only form would need one directory target per subdirectory.

Escaping and the mistakes people hit

- $$ for shell variables. $@ is make's target. A shell positional or variable must be $$@, $$1, $$HOME. - $< in a rule with no prerequisites is empty, and the command silently runs against nothing. Guard with $(if $<,...) or restructure. - $^ deduplicates. If a link line genuinely needs a library twice, use $+. - $? is empty on the first build because the target does not exist yet, so every prerequisite counts as newer only after the target appears. Rules built on $? need a fallback for the create-from-scratch case. - Automatic variables are not visible in $(info ...) at the top level. They only exist while a rule is being processed. Print them from inside the recipe. - Filenames with spaces break everything. Make has no quoting for prerequisite lists. Rename the files.