Any solution that doesn't get its list from the output of make -p or similar, i.e. tries to parse the targets in the Makefile(s) itself, is going to miss and/or show extra targets. sed, grep, awk, etc., without a pipe from make -p will not be accurate.
Additionally, any solution which requires gnu extensions to sed or grep will likely fail on Mac OS.
Here's my solution for a list target/help target that works on Mac OS (tested on Sonoma and Sequoia with the make that ships with Mac OS, GNU Make 3.81, built for i386-apple-darwin11.3.0) and Linux (tested on AlmaLinux 9.6, GNU Make 4.3, Built for x86_64-redhat-linux-gnu). It's cobbled together from four or five different answers I've found on SO, various mailing lists, and AI answers, and tweaked for my particular style of help (two hashes after the target list), with added support for compound targets and if/ifdef-defined/undefined targets.
It doesn't support variable targets such as $(FOO): bar ## make $FOO from bar or %.o: %.c ## turn a C file into an object file.
It supports cascading/included Makefiles, Makefiles not called Makefile, target definitions with multiple targets in them (e.g.: foo bar: baz ## create either foo or bar from baz), removes hidden targets (.hidden: hidden-file.txt ## don't show this hidden target) and all of the builtin-targets (e.g. .PHONY), removes targets that are if or ifdeffed out (e.g.: ifdef INCLUDE_ME\nmore-stuff: my-stuff ## build more-stuff from my-stuff if INCLUDE_ME is defined\nendif), sorts and de-dups, gives you the user-friendly command to use (basename of the command called, e.g. make, not /Library/Developer/CommandLineTools/usr/bin/make), and will cook waffles for you while you wait. Assuming you have that recipe in your Makefile.
It uses xargs with grep to ensure that only the targets that are valid are shown in the output. It also does not show any target that is missing the ## comment goes here in the target definition. So if you haven't commented a target, it won't get shown with this.
Also if you have a compound target where one of the targets is hidden (e.g.: target .hidden-target: ## make this target), neither will be shown in the help list.
If you just want the list of targets without the help messages, remove everything after the { print $$1 }' . No need to pipe to xargs grep and search for commented targets. Note: if you make this change and have a compound target where one is hidden (e.g.: target .hidden-target: ## normal and hidden targets here), the one not hidden will be shown as a valid target.
If you remove everything after the { print $$1 }', you will also get your variable $(FOO): bar targets in the list, but still not your %.o: %.c targets.
.PHONY: help
help: ## Show this help message
@echo "$(notdir $(MAKE)) targets:"
@LC_ALL=C $(MAKE) -qp -f $(firstword $(MAKEFILE_LIST)) : 2> /dev/null | awk -v RS= -F: '$$1 ~ /^[^#%. ]+$$/ { print $$1 }' | xargs -I % grep -E '^%(:| [a-zA-Z_ -]+:).*?## .*$$' $(MAKEFILE_LIST) | sort -u | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Example output (ANSI color on a color terminal):
$ make help
make targets:
all run start up Run the docker containers, if already stopped. Don't restart if running.
build Build the custom docker image(s) needed for this app
composer-help Get details about calling composer
composer Run a composer command (composer-help for help)
debug-off Disable PHP debugging in the web container
debug-on Enable PHP debugging in the web container
drush-help Get details about calling drush
drush Run a drush command (drush-help for help)
help Show this help message
rb rebuild Recreate and restart the docker containers
rerun restart rr (Re)start the docker containers
sqlc Connect to the db container via the mysql CLI
ssh-db SSH into the db container
ssh SSH into the web container
stop Stop the docker containers
alias makefile-targets='grep "^[^#[:space:]].*:" Makefile'Most often I just need to examine the current makefile, and bash completion expands my alias.grep : Makefile?Makefiles, but in more advanced files your command would include special targets such as.PHONY, lines containing URLs, variable definitions that use:=, lines defining target-specific variables, and more.