TIL: Makefiles that are self-documenting, and process all extant files.
Self-documenting Makefiles
A trick from years ago, but I copy it around between projects enough that it deserves calling out. Add a target:
help: ## Show this help. @# Optionally add 'sort' before 'awk' @grep -E '^[a-zA-Z_\.%-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-10s\033[0m %s\n", $$1, $$2}' .PHONY: help
Decorate other targets with a descriptive '##' comment, like "Show this help" above. Now calling the 'help' target will summarize all the things the Makefile can do. eg:
$ make help help Show this help. setup Install required system packages using 'apt install'. %.pdf Generate given PDF from corresponding .tex file. all Convert all .tex files to PDF.
You might choose to make 'help' the first target in the Makefile, so that it
gets triggered when the user runs make
without arguments.
Process all extant files
Make's canonical paradigm is that you tell it the name of the file to generate, and it uses the tree of dependencies specified in the Makefile to figure out how to build it. Typically you'll use automatic variables like "$<" to represent the wildcarded source filename:
%.pdf: %.tex ## Generate given PDF from corresponding .tex file. pdflatex $<
The pitfall is that when invoking this, you have to name all the PDF files you want to generate. If the names are a fixed part of your build, they can be embedded in the Makefile itself:
all: one.pdf two.pdf three.pdf
But if their names are dynamic, you have to specify them on the command line, which is a pain:
$ make one.pdf two.pdf three.pdf
This is easy enough when re-generating all the PDFs that already exist:
$ make *.pdf
but is no help when you just have a bunch of .tex files and you just want Make
to build all of them. This is going the opposite way to canonical make usage.
We want to specify the existing source files (*.tex
, in this case), and have
Make build the resulting products.
To do it, we need our Makefile to enumerate the existing source files:
TEX_FILES = $(wildcard *.tex)
Using the 'wildcard' function here behaves better than a bare wildcard expansion, e.g. it produces no output when there are no matches, rather than outputting the unmatched wildcard expression.
Then use a substitution to generate the list of .pdf filenames:
all = $(TEX_FILES:%.tex=%.pdf)
Now make all
will generate one .pdf file for each extant .tex file, regardless of
whether the corresponding .pdf files already exist or not.