My colleagues often ask me, "Dude could you remind me what I have to type to build this target?". So I eventually implemented a nice workaround in my Makefiles.
I wanted my Makefiles to have a help target that give some usage clues to the users. This is achieved with an embedded Perl script and some markers.
Wherever I want a target to be documented, I simply add this trailing comment:
##@<category> <description>
Here is an example:
SCRIPT_VERSION=v1.0
SCRIPT_AUTHOR=John Doe
all: ##@Build Build all the project
echo "Hello World!" > all
clean: ##@Cleaning Remove all intermediate objects
rm all
mrproper: clean ##@Cleaning Remove all output and interemediate objects
HELP_FUN = \
%help; while(<>){push@{$$help{$2ドル//'options'}},[$1,ドル$3ドル] \
if/^(\w+)\s*:.*\#\#(?:@(\w+))?\s(.*)$$/}; \
print"$$_:\n", map" $$_->[0]".(" "x(20-length($$_->[0])))."$$_->[1]\n",\
@{$$help{$$_}},"\n" for keys %help; \
help: ##@Miscellaneous Show this help
@echo -e "Usage: make [target] ...\n"
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)
@echo -e "Written by $(SCRIPT_AUTHOR), version $(SCRIPT_VERSION)"
@echo -e "Please report any bug or error to the author."
Here what I get if I type make help
:
$ make help
Usage: make [target] ...
Miscellaneous:
help Show this help
Build:
all Build all the project
Cleaning:
clean Remove all intermediate objects
mrproper Remove all output and interemediate objects
Written by John Doe, version v1.0
Please report any bug or error to the author.
In the case the Makefile takes time to parse the dependencies we can bypass the file for the help
target:
ifeq (,$(filter help,$(MAKECMDGOALS)))
# Core of the makefile
endif
What do you think of this technique. Is it a good approach? Can I improve it?
3 Answers 3
It looks pretty good to me, but I see a few minor things that might be improved.
Specify which perl
to run
I'd recommend not leaving it to chance as to which instance of perl
is run. Instead, for both safety and consistency, it might be better to do this:
PERL=/usr/bin/perl
##...
@$(PERL) -e '$(HELP_FUN)' $(MAKEFILE_LIST)
Allow multiword category tags
With a slight change in your regex, multiword categories could be supported. If the second line is changed to this:
if/^(\w+)\s*:.*\#\#(?:@([^@]*)@)?\s(.*)$$/}; \
It will support multiword categories, delimited with @
,
nothing: ##@Odds & ends@ Do nothing, but gracefully
Sort the keys for printing
On my machine, I get a different ordering each time I run make help
which is a little confusing. To fix that, sort the keys, rendering the last line:
@{$$help{$$_}},"\n" for sort keys %help
Note that you shouldn't have a line continuation there, and you don't need the trailing semicolon (although you may prefer one for style reasons).
Omit declaration of hash
Because %help
is unambiguously used, you don't need to predeclare it. It will automatically be created as an empty hash, so the first line can become:
while(<>){push@{$$help{$2ドル//'options'}},[$1,ドル$3ドル] \
This is a minor point, but you should declare all non-file targets as phony. Otherwise make
will behave strangely if you have a file named named "all", "clean" etc in your directory. make
will only run the command when the file is updated, which is not the desired behaviour.
.PHONY: all clean mrproper help
See What is the purpose of .PHONY in a Makefile? and the GNU documentation.
You can do this in pure make if you'd like (that is, without requiring an external tool like Perl). John Graham-Cumming has a detailed explanation of it on his blog, but here's the short version.
You can add documentation to a make target by declaring it like this:
include help-system.mk
all: $(call print-help,all,Builds everything)
# commands go here
lib: $(call print-help,lib,Builds only the static library)
# commands go here
clean: $(call print-help,clean,Removes intermediate and output files)
# commands go here
Invoking make help
will print out something that looks like:
Makefile:6: all -- Builds everything
Makefile:14: lib -- Builds only the static library
Makefile:20: clean -- Removes intermediate and output files
The contents of "help-system.mk" are listed at the link given above.
Note: the macro "print-help" expands to an empty string so this won't interfere with listing prerequisites for a target.