Skip to content

Instantly share code, notes, and snippets.

@prwhite
Last active Aug 14, 2022
Embed
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
@jcwren
Copy link

jcwren commented Feb 9, 2021

This updated expression still fails to find targets with numbers in the name. I think you need 0-9 in front of the a-zA-Z.

.PHONY: jflash
jflash: ## Program xxx using last J-Link specified (600112147 if none)
ifeq (,$(wildcard $(LAST_SEGGER_FILE)))
  @echo "600112147" > $(LAST_SEGGER_FILE)
endif
  $(JFLASH) -min -openprjsxxx_l`cat $(LAST_CPU_MODEL_FILE)`.jflash -open$(COMBINEDHEX) -usb`cat $(LAST_SEGGER_FILE)` -eliminate -auto -startapp -exit

.PHONY: jflash_147
jflash_147: ## Program xxx using J-Link 600112147
  echo "600112147" > $(LAST_SEGGER_FILE)
  $(JFLASH) -min -openprjsxxx_l`cat $(LAST_CPU_MODEL_FILE)`.jflash -open$(COMBINEDHEX) -usb`cat $(LAST_SEGGER_FILE)` -eliminate -auto -startapp -exit

.PHONY: jflash_977
jflash_977: ## Program xxx using J-Link 600108977
  echo "600108977" > $(LAST_SEGGER_FILE)
  $(JFLASH) -min -openprjxxx_l`cat $(LAST_CPU_MODEL_FILE)`.jflash -open$(COMBINEDHEX) -usb`cat $(LAST_SEGGER_FILE)` -eliminate -auto -startapp -exit

.PHONY: jflash_756
jflash_756: ## Program xxx using J-Link 600103756
  echo "600103756" > $(LAST_SEGGER_FILE)
  $(JFLASH) -min -openprjsxxx_l`cat $(LAST_CPU_MODEL_FILE)`.jflash -open$(COMBINEDHEX) -usb`cat $(LAST_SEGGER_FILE)` -eliminate -auto -startapp -exit

.PHONY: fix_crlf
fix_crlf: ## Convert all .c, .h, and .s files to Unix EOL, set 0x644 permissions
  chown -R $(USERNAME):$(GROUPNAME) *
  find . -type f -name \*.c -exec chmod 644 {} \;
  find . -type f -name \*.h -exec chmod 644 {} \;
  find . -type f -name \*.c -exec dos2unix -q {} \;
  find . -type f -name \*.h -exec dos2unix -q {} \;
  find . -type f -name \*.s -exec dos2unix -q {} \;
$ make help

Usage:
  make
  jflash           Program xxx using last J-Link specified (600112147 if none)
  fix_crlf         Convert all .c, .h, and .s files to Unix EOL, set 0x644 permissions

@nothub
Copy link

nothub commented Jun 18, 2021

This updated expression still fails to find targets with numbers in the name. I think you need 0-9 in front of the a-zA-Z.

Yeah, this works:

help: ## show help message
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m\033[0m\n"} /^[$$()% 0-9a-zA-Z_-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
$ make help

Usage:
  make
  help             show help message
  jflash           Program xxx using last J-Link specified (600112147 if none)
  jflash_147       Program xxx using J-Link 600112147
  jflash_977       Program xxx using J-Link 600108977
  jflash_756       Program xxx using J-Link 600103756
  fix_crlf         Convert all .c, .h, and .s files to Unix EOL, set 0x644 permissions

@alexandregv
Copy link

alexandregv commented May 8, 2022

I had a case where my targets names contain semicolons (escaped with a backslash), to create "namespaces":

sw\:provision:  ## Provision machine "SW"
	@vagrant provision SW
$ make help

Usage:
  make 
  help              Show help message
  provision         Provision all machines
  s:provision       Provision machine "S"
  sw:provision      Provision machine "SW"

Here is the modified version to make it work:

help:  ## Show help message
	@awk 'BEGIN {FS = ": .*##"; printf "\nUsage:\n  make \033[36m\033[0m\n"} /^[$$()% 0-9a-zA-Z_-]+(\\:[$$()% 0-9a-zA-Z_-]+)*:.*?##/ { gsub(/\\:/,":", $$1); printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

And what I changed (based on the latest version just above):
image

@mathieu-aubin
Copy link

mathieu-aubin commented May 9, 2022

Here is how cargo-quickinstall does it in it's current Makefile

.PHONY: help
help: ## Display this help screen
	@grep -E '^[a-z.A-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Simple and efficient, here is the result

Resulting_help_output

@maxixcom
Copy link

maxixcom commented Aug 6, 2022

Here is how cargo-quickinstall does it in it's current Makefile

.PHONY: help
help: ## Display this help screen
	@grep -E '^[a-z.A-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Simple and efficient, here is the result

Resulting_help_output

If your makefile has something like:

-include .env

your proposal will display:

Makefile                       Display this help screen

@mathieu-aubin
Copy link

mathieu-aubin commented Aug 7, 2022

Here is how cargo-quickinstall does it in it's current Makefile

.PHONY: help
help: ## Display this help screen
	@grep -E '^[a-z.A-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Simple and efficient, here is the result
Resulting_help_output

If your makefile has something like:

-include .env

your proposal will display:

Makefile                       Display this help screen

really?

But... assuming you write your own stuff and no try to retrofit someone else's code... why wouldnt you just use (per example....)

.INCLUDEDIRS : /usr/blah/blah
.INCLUDE : somefile
.INCLUDE .IGNORE : another_file /etc/yetanotherfile

Wouldn't NOT using the double ## hashtags make them be ignored by the grep rule?

Can you post an example so i can replicate? i guess its also good practice when making such affirmatiin... i mean, it helps everyone now and in the future be able to replicate and understand fast, too

Thanks!

@prwhite
Copy link
Author

prwhite commented Aug 9, 2022

Here is how cargo-quickinstall does it in it's current Makefile

.PHONY: help
help: ## Display this help screen
	@grep -E '^[a-z.A-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Simple and efficient, here is the result
Resulting_help_output

If your makefile has something like:

-include .env

your proposal will display:

Makefile                       Display this help screen

Here's my fix for that:

.PHONY: help
help:	## Show this help.
	@grep -hE '^[A-Za-z0-9_ \-]*?:.*##.*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

Added -h to hide the file name and tweaked the regex to include numbers and possible whitespace before the target's :, although it still doesn't cover all of the possible characters in a target identifier.

@nothub
Copy link

nothub commented Aug 14, 2022

To golf a little bit more, the grep expression of the previous post can be shrinked down to grep -hP '^[\w \-]*?:.*##.*$$' with the perl regex flag and \w that is an alias for [a-zA-Z0-9_].

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