paulgorman.org/technical

GNU make

(June 2018)

https://www.gnu.org/software/make/manual/html_node/Quick-Reference.html#Quick-Reference

The make utility recompiles only those parts of a program that need to be recompiled. It can do more than just compile C code — it automatically changes some files of any kind when other files change. It needs a “Makefile” to tell it what to do. The Makefile contains one or more rules.

Here’s a simple example. We write the text of our book in the Markdown file “body.md”, and we want to ultimately produce “book.pdf”. We produce “book.pdf” using pdflatex to process “book.tex”. The “book.tex” file wraps/includes (\input{.body.tex}) the file “body.tex”. We produce “body.tex” from “body.md” using pandoc. Our book contains a map, which we produce using Graphviz (dot).

We could do this using a shell script, like:

#!/bin/sh
dot -Tsvg map.dot > map.svg
pandoc --wrap=none -f markdown+pipe_tables -o body.tex body.md
pdflatex book.tex
pdflatex book.tex

…but this runs dot every time, even if when we didn’t change our map. Instead, using the following Makefile, make only run Graphviz when “map.dot” had changed.

book.pdf: book.tex body.tex img/map.svg
	pdflatex book.tex
	pdflatex book.tex

book.tex: body.md
	pandoc --wrap=none -f markdown+pipe_tables -o body.tex body.md

img/map.svg: map.dot
	dot -Tsvg map.dot > img/map.svg

The file must be named Makefile, and exist in the directory where we run make.

The general format of a rule is:

target: prerequisite …
	command
	…

The target is usually a file name, but doesn’t need to be. A prerequisite(s) must match the name of an existing file or a target in the Makefile. A rule does not require any prerequisites; zero prerequisites is valid. Any specified prerequisite must exist before the target can be created. For each prerequisite that matches a target, make evaluates the matching rule before proceeding (and so forth down the tree of prerequisites). For each target, if one of its prerequisites is newer, make runs its commands. Each command is passed to the shell (executed in its own sub-shell).

clean:
	rm book.pdf body.tex img/map.svg

We can specify a target on the command-line to limit what happens (e.g., make clean instead of simply make). By default, make runs the first rule in the Makefile (which make calls the “default goal”).

Long lines can be broken with backslashes (\).

Comment lines start with #.

Gotcha’s:

Preview the build process without executing anything with make -n or make --dry-run.

Variables

objects = main.o kbd.o command.o display.o \
	insert.o search.o files.o utils.o
…
clean :
	rm mybinary $(objects)

Phony Targets

A phony target is one that is not really the name of a file, like “clean”.

clean:
	rm *.o *.tmp

What if our project contains a file named “clean”? Explicitly tell make that “clean” is a phony target with the special target .PHONY, like:

.PHONY: clean
clean:
	rm *.o *.tmp

Errors in Recipes

Make checks the exit status of each shell return. By default, make abandons execution of the current rule if a command return a non-zero exit status. To ignore errors from a command, prefix it with a dash:

clean:
	-rm -f *.o

Debugging Makefiles

$  make --just-print
$  make --print-data-base
$  make --warn-undefined-variables
$  make --debug

Quiet!

By default, make echos its commands to STDOUT. To silence a particular command, prefix it with @, like:

clean:
	@rm -f *.o

To silence everything, add:

ifndef VERBOSE
.SILENT:
endif

We can override the above by invoking make like:

$  make VERBOSE=1

References