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