Rules shown in previous chapter named the input and output files (the prerequisites and the target) explicitly. This chapter shows how to use patterns to represent identical parts of similar filenames used in a rule.
Our Makefile is better than it was, but still contains a lot of redundancy.
The rules for figure-1.svg
and figure-2.svg
are identical except for the '1' and '2' in their names,
as are the rules for summary-1.dat
and summary-2.dat
.
We'd like to "fold" the rules for the figures together for two reasons. First, if we add a third figure, we don't want to have to duplicate rules a third time. Second, if we ever want to change the way we generate figures, we'd like to make that change once, in one place: if we have to make it in several places, the odds are good we'll forget one, and then waste time trying to figure out why some of our commands aren't running.
The way to do this in Make is to use a pattern rule to capture the common idea. Here's our Makefile rewritten to use such a rule:
# pattern-rule.mk
figure-%.svg : summary-%.dat
python create_figure.py $@ $^
summary-1.dat : data-1-*.dat
python stats.py $@ $^
summary-2.dat : data-2-*.dat
python stats.py $@ $^
summary-1.dat : stats.py
summary-2.dat : stats.py
In this rule, %
is a wildcard.
When it is expanded, it has the same value on both sides of the rule:
if it matches '1' on the left, it must match '1' on the right as well.
%
only means something to Make, though.
It doesn't have a value in the rule's action, which is handed off to the shell for execution.
So in the action, we have to use the automatic variables $@
and $^
as before.
Let's try running our modified Makefile:
$ make -f pattern-rule.mk
python stats.py summary-1.dat data-1-1.dat data-1-2.dat data-1-3.dat
summary-1.dat
is updated, but not summary-2.dat
or either of the figure files.
The reason the other commands didn't run is that pattern rules don't create dependencies:
they just tell Make what to do if there's a dependency.
In other words, if Make decides it wants to create figure-1.svg
, it can use our pattern rule,
but we still have to tell Make to care about figure-1.svg
.
Let's do this by putting the rule for paper.pdf
back in our Makefile:
# use-pattern.mk
paper.pdf : paper.tex figure-1.svg figure-2.svg
cat $^ > $@
figure-%.svg : summary-%.dat
python create_figure.py $@ $^
summary-1.dat : data-1-*.dat
python stats.py $@ $^
summary-2.dat : data-2-*.dat
python stats.py $@ $^
summary-1.dat : stats.py
summary-2.dat : stats.py
Here, paper.pdf
depends on figure-1.svg
and figure-2.svg
.
Make now knows that it needs these figures.
Since there aren't specific rules for them, it uses the pattern rule instead.
make
rulesIt's tempting to go one step further, and make paper.pdf
depend on figure-*.svg
:
paper.pdf : paper.wdp figure-*.svg
cat $^ > $@
This doesn't work, though.
The reason is that the figure files may not exist when Make starts to run—after all, Make creates them.
In that case, figure-*.svg
will expand to nothing,
so Make would mistakenly believe that paper.pdf
depended only on paper.tex
.
This kind of bug can be very hard to figure out,
and while Make does have a debugger called GMD,
it's not an easy tool for beginners to use.
Our raw data files do always exist, though, so we can get rid of some more redundancy by folding these two rules into one
using the *
wildcard:
# all-patterns.mk
paper.pdf : paper.tex figure-1.svg figure-2.svg
cat $^ > $@
figure-%.svg : summary-%.dat
python create_figure.py $@ $^
summary-%.dat : data-%-*.dat
python stats.py $@ $^
summary-1.dat : stats.py
summary-2.dat : stats.py
It's safe to do this because Make isn't responsible for creating data-1-whatever.dat
and data-2-whatever.dat
:
there's no possibility of the *
missing things because it's evaluated when Make starts running.
Just as a reminder, the %
is a Make wildcard:
it matches the same thing on the left and right side of a pattern rule.
*
is a shell wildcard:
it matches zero or more characters in a filename when it's evaluated.
We cannot get rid of the last bit of redundancy by making summary-%.dat
depend on stats.py
.
Even with this pattern rule, the summary files only depend on the corresponding raw data files, not on stats.py
.
The reason is that when Make sees two or more pattern rules that could match a filename,
it uses the first and ignores the other.
It's another wart, and another source of hard-to-find headaches in Makefiles.
If we really want to avoid making summary-1.dat
and summary-2.dat
depend on stats.py
separately,
the only way is to go back to using false dependencies.
This Makefile tells Make to update the timestamps on the raw data files using touch
whenever stats.py
changes.
Doing this indirectly triggers the re-creation of the summary files—it does what we want, just in a roundabout way.
# false-dependencies.mk
paper.pdf : paper.tex figure-1.svg figure-2.svg
cat $^ > $@
figure-%.svg : summary-%.dat
python create_figure.py $@ $^
summary-%.dat : data-%-*.dat
python stats.py $@ $^
~~~ data--.dat : stats.py touch $@