Create a gist now

Instantly share code, notes, and snippets.

@prwhite /Makefile
Last active Sep 23, 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

benjick commented Aug 15, 2017 edited

@nowox (20 - length $$_->[0]) always return 20 for me

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