---
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
