Makefile Pattern Rules
I recently found myself hacking on a Makefile (the GNU kind, not the BSD kind) that made heavy use of pattern rules and grouped rules. These are concepts I haven’t spent a lot of time with so I wrote myself a small test Makefile to explore them in a little more detail.
#Pattern Rules
Pattern rules drive most of the usage of make
as a tool for
building software. make
includes a bunch of pattern rules for many of the
kinds of source files you’re likely to encounter. For example, it has implicit
rules for building .o
files out of .c
files.
Here’s a simple pattern rule that generates text files:
%.txt:
@echo "=> Making $*.txt"
python -c 'print("$*!".title())' > $@
The %
is tells make
that this is a patten rule. It’s a placeholder for a
string of non-whitespace characters, which make
calls a stem. You can
reference the stem in the body of the rule with the automatic variable
$*
.
This particular rule creates a text file with a particular stem by echoing a string to a file with this little Python snippet.
Here’s a slightly more complex pattern rule:
%.x : %.a
@echo "=> Making X file from $< with pattern rule"
echo "[X]" > $*.x
cat "$<" >> $*.x
The major difference with this rule is that it has a prerequisite (the part
after the colon) that also has a %
. So, this rule defines how to build a .x
file from a .a
file with the same stem.
Later on, if I write a rule like this:
zip.a : florp.txt
@echo "=> Making $@ with $<"
cat $< > $@
make
will understand that it first needs to generate florp.txt
with the
pattern rule for %.txt
. Then it can execute this rule to build zip.a
.
#Grouped Rules
Grouped rules are another feature of make
that lets you specify more than one
output for a given rule. If you’re writing C family languages, this
is useful for generating a header and source file pair, and making sure that
they get updated together.
zip.x zip.y &: zip.a
@echo "=> Making XY files from $< with explicit rule"
echo "[X]" > zip.x
cat "$<" >> zip.x
echo "[Y]" > zip.y
cat "$<" >> zip.y
Generally when you write a rule with more than one output, make
understands
that each of the ouput files is built separately with the same rule. However, a
rule with a &:
separator indicates that the outputs are built from a single
invocation of the rule. make
will rebuild the rule if any of the outputs is
out-of-date.
#Altogether Now
You can combine pattern rules and group rules into a single rule too. This rule
creates a pattern for building a pair of .x
and .y
files from a .a
file
with a particular stem.
%.x %.y : %.a
@echo "=> Making XY files from $< with pattern rule"
echo "[X]" > $*.x
cat "$<" >> $*.x
echo "[Y]" > $*.y
cat "$<" >> $*.y
These rules are always treated as a group. It doesn’t matter if you use :
or
&:
.
#Rule Precedence
The way make
decides which rule to use to produce a file are a little subtle,
especially when you combine pattern rules and explicit rules. In general an
explicit rule should take precedence over a pattern. In my Makefile, the rule
that explicitly builds zip.x
and zip.y
will win over the pattern for %.x
and %.y
. When multiple pattern rules match a target, the rules are more
complex.
Download the full Makefile.