Blog Recipes Links

Self-Documented Makefile V1.1

I like Makefiles: The syntax is quite simple, it's easy to use and it's useful to group commands in one place. In 2016, Marmelab published an article on documenting a Makefile. With some shell script voodoo, you can run make help and it will print all the available targets and their descriptions. It works and it is simple. I've been using this trick for years and I'm happy with it.

Today I would like to go one step further: Let's improve the help target so it also outputs the variables and their values. To do so, we can read the internal database thanks to make -p. This command prints a lot of information: recipes, prerequisites, environment variables, variables and so on.

bash
make -pn hello=there # -n for a dry run
# environment
XAUTHORITY = /run/user/1000/gdm/Xauthority
# environment
GDMSESSION = ubuntu
# environment
XMODIFIERS = @im=ibus
# makefile (from 'Makefile', line 3)
NPROC := 12
# command line
hello = there
# ....

A quick lookup to the output later, I can extract the relevant information with the command, nothing fancy here:

bash
make -pn | awk '/^# (makefile |command)/{getline; print}'

Finally, let's format and print the result. Makefile looks like this:

Makefile
.DEFAULT_GOAL = help
NPROC := $(shell nproc)
HOST ?= 127.0.0.1

.PHONY: help

help: ## Show this help
	@echo "Variables:"
	@make -pnf $(MAKEFILE_LIST) | awk '/^# (makefile |command)/{getline; print}' | grep -v "^MAKEFILE_LIST" | sort | uniq | awk 'BEGIN {FS = ":?= "}; {printf "  \033[36m%-30s\033[0m %s\n", $$1, $$2}'
	@echo "\nTargets:"
	@grep -E '^[/%a-zA-Z0-9_-]+: .*?## .*$$' $(MAKEFILE_LIST) | sort | awk  'BEGIN {FS = ": .*?## "}; {printf "  \033[36m%-30s\033[0m %s\n", $$1, $$2}'
bash
make help hello=world
Variables:
  .DEFAULT_GOAL                  help
  HOST                           127.0.0.1
  NPROC                          8
  hello                          world

Targets:
  help                           Show this help