A re-usable help command for Makefiles
Supporting 'help' as a target within Makefiles for self-documentation
Table Of Contents
Today I Explained
Makefiles are a solid approach for having a lightweight task runner within a repository. Other tools for task runners exist that can be used for running common actions or useful scripts, but Makefiles have an appealing labyrinthine simplicity to them. One of these benefits is just familiarity with the syntax:
action:
# do an action
rm .env
One of the rough points about using Makefiles is making accessible the list of tasks that can be run. As make
doesn’t support any make help
or built-in docgen, it results in various patterns of parsing the Makefile to generate a help document from comments within the Makefile. The approach that I tend to adopt within repositories is a single line comment following the rule declaration (init: # Initialize the repositoriy resources
).
The make help
rule is then left up to the development environment to be defined. The Makefile
of the repository is just responsible for loading in any global makefile rules from the directory GLOBAL_MAKEFILE_LIBRARY
, defined at the root of the Makefile
like so:
ifdef GLOBAL_MAKEFILE_LIBRARY
include $(wildcard $(GLOBAL_MAKEFILE_LIBRARY)/*.mk)
endif
The definition of the help
rule is then a file named help.mk
located within a GLOBAL_MAKEFILE_LIBRARY
directory typically encoded into a development container, like such:
.DEFAULT_GOAL := help
NC = \033[0m
ERR = \033[31;1m
TAB := '%-20s'
clr_cmd := $(shell tput setaf 202)
clr_header := $(shell tput setaf 250)
clr_make := $(shell tput setaf 240)
clr_desc := $(shell tput setaf 250)
bold := $(shell tput bold)
underline := $(shell tput smul)
reset := $(shell tput sgr0)
.PHONY: help
help:
@printf '$(clr_header)$(shell cat docs/title.txt)$(reset)\n'
@printf '$(clr_desc) > $(shell cat docs/tagline.txt)$(reset)\n\n'
@printf ' $(underline)$(clr_header)Commands:$(reset)\n\n'
@grep -E '^([a-zA-Z0-9_-]+\.?)+:.+#.+$$' $(MAKEFILE_LIST) \
| cut -d':' -f2- \
| grep -v '^check-' \
| grep -v '^env-' \
| grep -v '^arg-' \
| sed 's/:.*#/: #/g' \
| awk 'BEGIN {FS = "[: ]+#[ ]+"}; \
{printf " $(clr_make) make $(reset)$(clr_cmd)$(bold)$(TAB) $(reset)$(clr_desc)# %s$(reset)\n", \
$$1, $$2}'
@printf '\n'
@grep -E '^([a-zA-Z0-9_-]+\.?)+:(\s+|\w+)*$$' $(MAKEFILE_LIST) \
| cut -d':' -f2- \
| (grep -v help || true) \
| awk 'BEGIN {FS = ":"}; \
{printf " $(clr_make) make $(reset)$(clr_cmd)$(bold)$(TAB)$(reset)\n", \
$$1}'
A Makefile rule definition for
help
that emits the contents of the filesdocs/title.txt
,docs/tagline.txt
, then emits a list of commands in the Makefile excluding anycheck-
,env-
, orarg-
prefixed rules
If a GLOBAL_MAKEFILE_LIBRARY
doesn’t exist, then the Makefile behaves just as any other Makefile without a built-in make help
, while if the GLOBAL_MAKEFILE_LIBRARY
exists with a defined help
rule, it will be generally customized to the standards of the development containers in-use.