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.