Create a gist now

Instantly share code, notes, and snippets.

@prwhite /Makefile
Last active Jul 12, 2017

What would you like to do?
Add a help target to a Makefile that will allow all targets to be self documenting
# Add the following 'help' target to your Makefile
# And add help text after each target name starting with '\#\#'
help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
# Everything below is an example
target00: ## This message will show up when typing 'make help'
@echo does nothing
target01: ## This message will also show up when typing 'make help'
@echo does something
# Remember that targets can have multiple entries (if your target specifications are very long, etc.)
target02: ## This message will show up too!!!
target02: target00 target01
@echo does even more
Owner

prwhite commented Dec 29, 2013

Output will be:

help: Show this help.
target00: This message will show up when typing 'make help'
target01: This message will also show up when typing 'make help'
target02: This message will show up too!!!

amussey commented Oct 6, 2014

This is great! Thanks for sharing it.

Here's a modified help command I've put together so the messages are pushed out to the same point:

help: ## This help dialog.
    @IFS=$$'\n' ; \
    help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//'`); \
    for help_line in $${help_lines[@]}; do \
        IFS=$$'#' ; \
        help_split=($$help_line) ; \
        help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
        help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
        printf "%-30s %s\n" $$help_command $$help_info ; \
    done

# Everything below is an example

target00: ## This message will show up when typing 'make help'
    @echo does nothing

target01: ## This message will also show up when typing 'make help'
    @echo does something

# Remember that targets can have multiple entries (if your target specifications are very long, etc.)
target02:  ## This message will show up too!!!
target02: target00 target01
    @echo does even more

will respond with:

help:                          This help dialog.
target00:                      This message will show up when typing 'make help'
target01:                      This message will also show up when typing 'make help'
target02:                      This message will show up too!!!

Thanks to the both of you for providing this.

I've made a version using only awk (tested on OS X, but possible works on gawk too). I also moved the help message just before the task - so you don't need to declare the target multiple times:

https://gist.github.com/rcmachado/af3db315e31383502660

nowox commented Feb 16, 2015

Very nice idea. I modified it with a small Perl script that support categories:

 # Add the following 'help' target to your Makefile
 # And add help text after each target name starting with '\#\#'
 # A category can be added with @category

 HELP_FUN = \
         %help; \
         while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^(\w+)\s*:.*\#\#(?:@(\w+))?\s(.*)$$/ }; \
         print "usage: make [target]\n\n"; \
     for (keys %help) { \
         print "$$_:\n"; $$sep = " " x (20 - length $$_->[0]); \
         print "  $$_->[0]$$sep$$_->[1]\n" for @{$$help{$$_}}; \
         print "\n"; }     

 help:           ##@miscellaneous Show this help.
     @perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)

 # Everything below is an example

 target00:       ##@foo This message will show up when typing 'make help'
     @echo does nothing

 target01:       ##@foo This message will also show up when typing 'make help'
     @echo does something

 # Remember that targets can have multiple entries (if your target specifications are very long, etc.)
 target02:       ## This message will show up too!!!
 target02: target00 target01
     @echo does even more

The output is this:

 $ make help
 usage: make [target]

 options:
   target02                    This message will show up too!!!

 foo:
   target00                    This message will show up when typing 'make help'
   target01                    This message will also show up when typing 'make help'

 miscellaneous:
   help                    Show this help.

muhmi commented Mar 25, 2015

This is awesome! I wanted ANSI colors so ...

help: ## This help dialog.
    @IFS=$$'\n' ; \
    help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \
    printf "%-30s %s\n" "target" "help" ; \
    printf "%-30s %s\n" "------" "----" ; \
    for help_line in $${help_lines[@]}; do \
        IFS=$$':' ; \
        help_split=($$help_line) ; \
        help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
        help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \
        printf '\033[36m'; \
        printf "%-30s %s" $$help_command ; \
        printf '\033[0m'; \
        printf "%s\n" $$help_info; \
    done

target: ## Does absolutely nothing!
    exit 0

This is great stuff. Can you declare a licence, so I can use it in good conscience?

@nowox I know this gist is quite old but I liked your solution. I just had to make a small modification and wanted to share

  • Modified the regex to match rules that have special chars in them
  • Sort help hash before generating messages for consistent output
  • Use printf with a fixed width column for consistent output. (There where alignment issues in your initial implementation)
HELP_FUNC = \
    %help; \
    while(<>) { \
        if(/^([a-z0-9_-]+):.*\#\#(?:@(\w+))?\s(.*)$$/) { \
            push(@{$$help{$$2}}, [$$1, $$3]); \
        } \
    }; \
    print "usage: make [target]\n\n"; \
    for ( sort keys %help ) { \
        print "$$_:\n"; \
        printf("  %-20s %s\n", $$_->[0], $$_->[1]) for @{$$help{$$_}}; \
        print "\n"; \
    }

Here's my take:

  • Colored target names
  • Used column to have reasonable spacing between the target and help message
  • Help comments can be on the same line as target dependencies
# Needed SHELL since I'm using zsh
SHELL := /bin/bash
.PHONY: help

# Add the following 'help' target to your Makefile
# And add help text after each target name starting with '\#\#'

help: ## This help message
    @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"

# Everything below is an example

target00:       ## This message will show up when typing 'make help'
    @echo does nothing

target01:       ## This message will also show up when typing 'make help'
    @echo does something

# Remember that targets can have multiple entries (if your target specifications are very long, etc.)
target02:       ## This message will show up too!!!
target02: target00 target01
    @echo does even more

If anyone is interested, I wrote a package for that: make-help

The following Makefile:

# generate all assets
build: scripts styles

# show some help
help:
    echo
    echo '  Usage:'
    echo '    make <target>'
    echo
    echo '  Targets:'
    make-help -p 4 "$(lastword $(MAKEFILE_LIST))"
    echo

# generete scripts
scripts:
    ...

# generete styles
styles:
    ...

Will output this when make help is called:

  Usage:
    make <target>

  Targets:
    build    generate all assets
    help     show some help
    scripts  generete scripts
    styles   generete styles

@nowox version with colors and repair targets with - and separator length:

#COLORS
GREEN  := $(shell tput -Txterm setaf 2)
WHITE  := $(shell tput -Txterm setaf 7)
YELLOW := $(shell tput -Txterm setaf 3)
RESET  := $(shell tput -Txterm sgr0)

# Add the following 'help' target to your Makefile
# And add help text after each target name starting with '\#\#'
# A category can be added with @category
HELP_FUN = \
    %help; \
    while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \
    print "usage: make [target]\n\n"; \
    for (sort keys %help) { \
    print "${WHITE}$$_:${RESET}\n"; \
    for (@{$$help{$$_}}) { \
    $$sep = " " x (32 - length $$_->[0]); \
    print "  ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
    }; \
    print "\n"; }

help: ##@other Show this help.
    @perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)

Any way to make this support targets created with templates?
https://www.gnu.org/software/make/manual/html_node/Eval-Function.html

@caylorme you need to modify fragment [a-zA-Z\-] to like this [a-zA-Z\-\$\(\)]

I'm starting to use the following:

.PHONY: help
help: ## Show this help message.
    echo 'usage: make [target] ...'
    echo
    echo 'targets:'
    egrep '^(.+)\:\ ##\ (.+)' ${MAKEFILE_LIST} | column -t -c 2 -s ':#'

Would somebody help on how to make the entire help that is in multiple lines show up aligned with first line of help associated with the target?

Example: For below make file content


target00: ## This message will show up when typing 'make help'
    @echo does nothing

target01: ## This message will also show up when typing 'make help'
    @echo does something

# Remember that targets can have multiple entries (if your target specifications are very long, etc.)
target02:  ## This message will show up too!!!
## This help message is way too long so it is in multiple line
target02: target00 target01
    @echo does even more

Desired output:
help:                     This help dialog.
target00:              This message will show up when typing 'make help'
target01:              This message will also show up when typing 'make help'
target02:              This message will show up too!!!
                              This help message is way too long so it is in multiple line

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