My GNUmakefile template, with inspiration from:
- Victoria Drake’s “How to create a self-documenting Makefile
- Chris Wellon’s “A Tutorial on Portable Makefiles
As with all formulae, I may update this from time to time.
GNUmakefile
vs Makefile
I prefer to use GNUmakefile
as the filename to emphasize that this will not work with other make
implementations.
GNU make
will look for a Makefile
if GNUmakefile
is not present,
so you can use that name instead if you prefer.
Take care: GNU make
looks for Makefile
(capital M
) or GNUmakefile
(lower-case m
).
Template
Available directly at GNUmakefile (shortened).
# -*- mode: Makefile -*-
# A portable, self-documenting GNU Makefile for projects
#
# Suitable for projects where you write the Makefile by hand.
#
# Notes:
# - ".PHONY" targets do not generate files
# - Targets without .PHONY are assumed to generate files
# - See the help comment, after '##', for each target
# - For targets that do not have a '## comment', they are not shown in the help output
# - The help target is the first target, so it's also what gets run if you just run 'make' without a target
# Ignore built-in inference rules that determine eg how to build object files from C source code.
# You might not want this if you're writing a Makefile for a C project.
.SUFFIXES:
# Warn if any variables are undefined.
MAKEFLAGS += --warn-undefined-variables
# Fail if any command fails.
# Note that just because we're using GNU Make doesn't mean we're using Bash.
.SHELLFLAGS := -eu
# Show a nice table of Make targets.
# Generate it by grepping through the Makefile for targets with a ## after them,
# and treat everything following ## as the description.
.PHONY: help
help: ## Show this help
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
.PHONY: exsetenv
exsetenv: export FOO := "BAR"
exsetenv: export BAZ := ${BAT}
exsetenv: ## An example that involves setting an env var for the command
some-command ...
.PHONY: exdep1
exdep1:
do something ...
# This target depends on exdep1, which is .PHONY, so it runs it every time
.PHONY: exdepend
exdepend: exdep1
do something ...
build/generated.txt:
mkdir -p build/
echo "asdf" > build/generated.txt
# This target depends on the previous one, which is not .PHONY,
# which means it only reruns the previous target if necessary
# (helpful if building the previous target is time-intensive)
.PHONY: usegeneratedtxt
usegeneratedtxt: ## Show an example file that was generated in a different step
cat build/generated.txt
.PHONY: example-ripgrep
example-ripgrep: ## Run a command over the codebase to search for something
rg "search-term"
.PHONY: example-script
example-script: ## Run a script that is saved to the scripts/ directory
scripts/exscript asdf
Requires GNU Make
Unlike my shell script formulae, I require GNU Make rather than trying to stick with POSIX.
- There has been no way to assign a value from a shell command to a macro in POSIX Make, which made it really difficult to work with. (However, late in 2022, The Austin Group accepted a proposal to add it.)
- GNU make also supports macro definition like
ASDF := (shell echo 'value')
. The shell thing is described above. The:=
means the right side of the expression is evaluated immediately and stored. (Using just=
instead would mean the right side of the expression is executed every time theASDF
macro is referenced.) - Just because I am using GNU Make doesn’t mean I’m using bash. I do not set
SHELL := /bin/bash
.