Make Wildcard Patterns AKA build all the files you find

The Problem

You want Make to automatically build all sources in a given folder without having to specify them each time in the Makefile.

The Solution

You can combine a wildcard to match all the sources, then use a patsubst to name the targets from these, and finally use a static pattern rule to generate the commands to build these.

Note that you could also use a for loop in a shell script inside the Makefile, but you might as well not use Make at that point :)

Example: Generating Graphs with Graphviz

In this example you want to generate PNG images from all the Graphviz dot files in your current directory. These can take a while so you want to use Make to only rebuild the images you've modified.

General wildcard patterns

Build all the dot files in the current directory to PNG files.

SOURCES=$(wildcard *.dot)
TARGETS=$(patsubst %.dot,%.png,$(SOURCES))

all: $(TARGETS)

%.png: %.dot
	dot -Tpng -o $@ $<
$ make
dot -Tpng -o my-graph.png my-graph.dot
dot -Tpng -o my-other-graph.png my-other-graph.dot

$ touch my-graph.dot

$ make
dot -Tpng -o my-graph.png my-graph.dot

Wildcards with subdirectories

This is a little more sophisticated, you want a directory with sources to go into a build directory. I include this mostly because I can't remember the right amount of times to include the build/ bit in the static patern and the patsubst.

See also: [order only pre-requisites]({{< ref "../order-only-prerequisites/index.md" >}}).

BUILDDIR=build
SOURCES=$(wildcard src/*.dot)
TARGETS=$(patsubst src/%.dot,$(BUILDDIR)/%.png,$(SOURCES))

all: $(TARGETS)

$(TARGETS): | $(BUILDDIR)  # build the build directory first

$(BUILDDIR):
	mkdir $(BUILDDIR)

$(BUILDDIR)/%.png: src/%.dot
	dot -Tpng -o $@ $<

Note: I originally had $(TARGETS): $(SOURCES) | $(BUILDDIR) but this caused all the targets to be rebuilt when one source changed. Defeating one of the goals of this approach.

$ make
mkdir build
dot -Tpng -o build/my-graph.png src/my-graph.dot
dot -Tpng -o build/my-other-graph.png src/my-other-graph.dot

$ touch src/my-graph.dot

$ make
dot -Tpng -o build/my-graph.png src/my-graph.dot

Improved Wildcards

You can further refine this by merging a few of the lines together (the GNU Make Manual shows this in their examples):

BUILDDIR=build
SOURCES=$(wildcard src/*.dot)
TARGETS=$(patsubst src/%.dot,$(BUILDDIR)/%.png,$(SOURCES))

all: $(TARGETS)

$(BUILDDIR):
  mkdir $(BUILDDIR)

$(TARGETS): $(BUILDDIR)/%.png: src/%.dot | $(BUILDDIR)
  dot -Tpng -o $@ $<

Apart from being a little neater this has worked better for me in some situations with more complex patterns.

You can also take the simple case and slightly modify it:

SOURCES=$(wildcard *.dot)
TARGETS=$(patsubst %.dot,%.png,$(SOURCES))

all: $(TARGETS)

$(TARGETS): %.png: %.dot
  dot -Tpng -o $@ $<

Updates

2023-01-13

Added the Improved Wildcards example.