128 lines
3.9 KiB
Markdown
128 lines
3.9 KiB
Markdown
|
---
|
||
|
title: "Makefile Pattern Rules"
|
||
|
date: 2024-12-04T14:26:57-08:00
|
||
|
publishDate: 2024-12-05
|
||
|
resources:
|
||
|
- name: makefile
|
||
|
src: Makefile
|
||
|
tags:
|
||
|
- TIL
|
||
|
- Make
|
||
|
- Software
|
||
|
---
|
||
|
|
||
|
I recently found myself hacking on a Makefile (the GNU kind, not the [BSD
|
||
|
kind][bsdmake]) 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][patterns] 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:
|
||
|
|
||
|
```make
|
||
|
%.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][auto]
|
||
|
`$*`.
|
||
|
|
||
|
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:
|
||
|
|
||
|
```make
|
||
|
%.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:
|
||
|
|
||
|
```make
|
||
|
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][multiples] 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.
|
||
|
|
||
|
```make
|
||
|
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.
|
||
|
|
||
|
```make
|
||
|
%.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][matching].
|
||
|
|
||
|
---
|
||
|
|
||
|
Download the full [Makefile]({{< page/resource-ref makefile >}}).
|
||
|
|
||
|
[bsdmake]: {{< ref "blog/2024/bsd-make" >}}
|
||
|
[auto]: https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
|
||
|
[patterns]: https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html
|
||
|
[multiples]: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html
|
||
|
[matching]: https://www.gnu.org/software/make/manual/html_node/Pattern-Match.html
|