Pattern Rules

Overview

Teaching: 10 min
Exercises: 0 min
Questions
  • How can I define rules to operate on similar files?

Objectives
  • Write Make pattern rules.

Our Makefile (available via git checkout 05-patterns) still has repeated content. The rules for each .dat file are identical apart from the text and data file names. We can replace these rules with a single pattern rule which can be used to build any .dat file from a .txt file in books/:

%.dat : books/%.txt countwords.py
	python countwords.py $< $*.dat

% is a Make wildcard. $* is a special variable which gets replaced by the stem with which the rule matched.

This rule can be interpreted as: “In order to build a file named [something].dat (the target) find a file named books/[that same something].txt (the dependency) and run countwords.py [the dependency] [the target].”

If we re-run Make,

$ make clean
$ make dats

then we get:

python countwords.py books/isles.txt isles.dat
python countwords.py books/abyss.txt abyss.dat
python countwords.py books/last.txt last.dat

Note that we can still use Make to build individual .dat targets as before, and that our new rule will work no matter what stem is being matched.

$ make sierra.dat

which gives the output below:

python countwords.py books/sierra.txt sierra.dat

Using Make Wildcards

The Make % wildcard can only be used in a target and in its dependencies. It cannot be used in actions. In actions, you may however use $*, which will be replaced by the stem with which the rule matched.

Our Makefile is now much shorter and cleaner:

# Generate summary table.
results.txt : testzipf.py isles.dat abyss.dat last.dat
	python $< *.dat > $@

# Count words.
.PHONY : dats
dats : isles.dat abyss.dat last.dat

%.dat : books/%.txt countwords.py
	python countwords.py $< $*.dat

.PHONY : clean
clean :
	rm -f *.dat
	rm -f results.txt

This episode has introduced pattern rules, and used the $* variable in the dat rule in order to explain how to use it. Arguably, a neater solution would have been to use $@ to refer to the target of the current rule (see below), but then we wouldn’t have learned about $*.

%.dat : books/%.txt countwords.py
	python countwords.py $< $@

Key Points

  • Use the wildcard % as a placeholder in targets and dependencies.

  • Use the special variable $* to refer to matching sets of files in actions.