HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Built-in help in a Makefile

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
builtmakefilehelp

Problem

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:

##@ 


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?

Solution

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] \

Code Snippets

PERL=/usr/bin/perl
##...
@$(PERL) -e '$(HELP_FUN)' $(MAKEFILE_LIST)
if/^(\w+)\s*:.*\#\#(?:@([^@]*)@)?\s(.*)$$/}; \
nothing:         ##@Odds & ends@ Do nothing, but gracefully
@{$$help{$$_}},"\n" for sort keys %help
while(<>){push@{$$help{$$2//'options'}},[$$1,$$3] \

Context

StackExchange Code Review Q#94307, answer score: 5

Revisions (0)

No revisions yet.