6
\$\begingroup\$

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?

Edward
67.2k4 gold badges120 silver badges284 bronze badges
asked Jun 22, 2015 at 8:06
\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

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ドル] \
answered Jun 22, 2015 at 13:34
\$\endgroup\$
3
\$\begingroup\$

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.

answered Jul 30, 2015 at 22:20
\$\endgroup\$
3
\$\begingroup\$

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.

answered Dec 7, 2015 at 17:48
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.