Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active August 18, 2024 02:17
Show Gist options
  • Save ernstki/982edcfbf93c357bdb09878f7c724389 to your computer and use it in GitHub Desktop.
Save ernstki/982edcfbf93c357bdb09878f7c724389 to your computer and use it in GitHub Desktop.
Auto-generated 'make help' from comments on Makefile targets
TITLE = A Makefile that makes its own 'make help' target
ME = $(lastword $(MAKEFILE_LIST))
HOMEPAGE = https://gist.github.com/ernstki/982edcfbf93c357bdb09878f7c724389/edit
help: # prints this help
@bash -c "$$AUTOGEN_HELP_BASH" < $(ME)
define AUTOGEN_HELP_BASH
declare -A targets; declare -a torder
targetre='^([A-Za-z]+):.* *# *(.*)'
if [[ $$TERM && $$TERM != dumb && -t 1 ]]; then
ul=$$'\e[0;4m'; bbold=$$'\e[34;1m'; reset=$$'\e[0m'
fi
if [[ -n "$(TITLE)" ]]; then
printf "\n %sMakefile targets - $(TITLE)%s\n\n" "$$ul" "$$reset"
else
printf "\n %sMakefile targets%s\n\n" "$$ul" "$$reset"
fi
while read -r line; do
if [[ $$line =~ $$targetre ]]; then
target=$${BASH_REMATCH[1]}; help=$${BASH_REMATCH[2]}
torder+=("$$target")
targets[$$target]=$$help
if (( $${#target} > max )); then max=$${#target}; fi
fi
done
for t in "$${torder[@]}"; do
printf " %smake %-*s%s %s\n" "$$bbold" $$max "$$t" "$$reset" \
"$${targets[$$t]}"
done
if [[ -n "$(HOMEPAGE)" ]]; then
printf "\n Homepage:\n $(HOMEPAGE)\n\n"
else
printf "\n"
fi
endef
export AUTOGEN_HELP_BASH
# vim: ft=make
@ernstki
Copy link
Author

ernstki commented Aug 18, 2024

Screenshot of 'make help' as generated by this Makefile

This replaces an earlier Perl version I'd been using for years, but I started thinking, hey, Bash can do all that. It's about ten lines longer than the Perl version, but possibly more readable.

Usage

GIST=https://gist.githubusercontent.com/ernstki/982edcfbf93c357bdb09878f7c724389
THING=raw/Makefile_with_built_in_help
(set -x; curl -sL $GIST/$THING || wget -qO- $GIST/$THING) > Makefile

Add your targets after the provided help target, because they'll be printed in the order they appear in the file, not sorted. (Although if you wanted them sorted, to implement that would be straightforward.) Shove that bunch of Bash script to the bottom of the file and forget about it.

If you want help for a target, put a comment after it, on the same line, like this:

#

help:  # prints this help
	@bash -c "$$AUTOGEN_HELP_BASH" < $(ME)

build: dep1 dep2  # build all the things
	build-the-thing $<
#

Then run make help or just make (as long as you leave it at the top, it's the default target). Easy peasy.

If you don't want a target to appear in the output of make help, don't put a comment on the same line as the target name:

# this target won't show up in the output of 'make help'
dep1: dep3
	do-the-other-thing $< > $@

Put your comment above or below instead, if you need to document it internally.

Other tips

If you wanted, for example, build to be the default target instead of help, use .DEFAULT_GOAL for that:

#

.DEFAULT_GOAL = build

help:  # prints this help
	@bash -c "$$AUTOGEN_HELP_BASH" < $(ME)

build: dep1 dep2  # build the things
	whatever-build-thing --arg1 --arg2 $<
#

Bugs

The regexp is supposed to ignore any dependencies for the target, and it's a bug if it doesn't.

As written, would not support multiple targets in a single rule.

There are double quotes in the definition of AUTOGEN_HELP_BASH, which is itself double-quoted when passed to bash -c. Frankly I'm astonished that this worked. I suppose Make itself is taking care of the escaping under the hood, but I still have a feeling that somewhere this will eventually go wrong, and require some more attention. If you get in some kind of trouble with this, as long as the quotes are only for display purposes, just use curly quotes instead; e.g.

Option+{

for a US keyboard layout on macOS, or,

RAlt+Shift+<, Shift+'

assuming RAlt is your Compose key on Linux.

Feedback welcome.

License

WTFPL, or ISC at your discretion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment