Thanks to Jacob David-Hansson's blog post.
/bin/sh
could be symlinked to any shell on foreign systems, so explicitly specify Bash instead (SHELL :=
).- Enable strict mode in Bash (
.SHELLFLAGS := -eu -o pipefail -c
). - Specify that each rule should run in a single shell environment, rather than a shell per line (
.ONESHELL:
). - Delete (do not save) target/output files if the recipe/script results in an error (
.DELETE_ON_ERROR:
). - Show a warning if undefined Makefile variables are referenced (
MAKEFLAGS += --warn-undefined-variables
). - Disable the built-in rules for Make, as we don't want magic only what we explicitly specify (
MAKEFLAGS += --no-builtin-rules
). - Use
>
instead of tabs to avoid IDE's from messing up whitespace (.RECIPEPREFIX
) and show an error if this is not supported.
Add the following as the first rule of your Makefile
:
usage:
> @grep -E '(^[a-zA-Z_-]+:\s*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.?## "}; {printf "\033[32m %-30s\033[0m%s\n", $$1, $$2}' | sed -e 's/\[32m ## /[33m/'
Then add a line beginning with ##<space>
for a section heading, and a line <command_name>: ##<space><command_comment>
to describe command usage.
Application Setup
install Install project dependencies
Testing
unit Run unit tests
For bash scripts, not Makefiles
Thanks to Egor Kovetskiy's blog post.
Add the following to the top of your script:
usage() {
sed -rn 's/^### ?//;T;p' "$0"
exit 0
}
if [ $# -gt 0 ]; then
for ARG in "$@"; do
if [ "$ARG" = "--help" ] || [ "$ARG" = "-h" ]; then
usage
fi
done
fi
Then add a file comment where each line is prepended with three hash symbols (###
). The section will be ignored as a script comment, but will be parsed and printed by the usage()
function.