Skip to content

Instantly share code, notes, and snippets.

@ruittenb
Last active October 9, 2025 07:48
Show Gist options
  • Select an option

  • Save ruittenb/5d2d281237385276f49652b9b9f6d5a1 to your computer and use it in GitHub Desktop.

Select an option

Save ruittenb/5d2d281237385276f49652b9b9f6d5a1 to your computer and use it in GitHub Desktop.
Makefile target for automatically generating help
############################################################################
#
# makefile-autohelp
#
# This file is at : https://tinyurl.com/makefile-autohelp
# also known as : https://gist.github.com/ruittenb/5d2d281237385276f49652b9b9f6d5a1
############################################################################
#
# DESCRIPTION
#
# This code adds a 'help' target to your Makefile, that automatically
# creates a table of your Makefile's targets and short descriptions.
#
############################################################################
#
# INSTALLATION: copy this block over to your own Makefile:
.DEFAULT_GOAL:=help
.PHONY: help # See https://tinyurl.com/makefile-autohelp
help: ## Print help for each target
@awk -v tab=10 'BEGIN { FS = ":.*## "; buffer = ""; color = "\033[36m"; nocolor = "\033[0m"; indent = " "; usage(); } function trim(str) { gsub(/[ \t]+$$/, "", str); gsub(/^[ \t]+/, "", str); return str; } function spout(target, desc) { split(trim(target), fields, " "); for (i in fields) printf "%s%s%-" tab "s%s%s\n", indent, color, trim(fields[i]), nocolor, desc; } function usage() { printf "\nUsage:\n%smake %s<target>%s\n\nRecognized targets:\n", indent, color, nocolor; } /\\$$/ { gsub(/\\$$/, ""); buffer = buffer $$0; next; } buffer { $$0 = buffer $$0; buffer = ""; } /^[-a-zA-Z0-9*/%_. ]+:.*## / { pad = sprintf("\n%" tab "s" indent, ""); gsub(/\\n/, pad); spout($$1, $$2); } /^##@ / { gsub(/\\n/, "\n"); printf "\n%s\n", substr($$0, 5) } END { print "" }' $(MAKEFILE_LIST) # v1.54
############################################################################
#
# USAGE
#
# The awk option '-v tab=10' can be changed to change the indentation of the output.
#
# Descriptions for each target should be added in the Makefile itself:
#
# '##' after a target is used for target descriptions
# '##@' at the start of a line is used for header lines.
############################################################################
#
# EXAMPLES
#
##@ This is heading text, which is shown on a separate line.
##@ Heading text may \
span multiple lines.
##@ Heading text may contain\n- newlines\n- by specifying them with\n- backslash-n\n
# This is just any comment and its continuation. \
It will not be parsed by 'make help'.
##@ Examples of targets:
double: prereq ## This will not be shown ## Sequential comments only show the latter
newline: ## Comments may\ncontain newlines,\nentered as "backslash-n"
target1 target2: ## Multiple targets will be split across lines
show1: ## This is a description for show1, no prerequisites
show2: prereq1 prereq2 ## This is a description for show2, with compact prerequisites
show3: prereq1 \
prereq2 ## This is a description for show3, with prerequisites spanning lines
show4: ## This is a description for show4 \
and its continuation, no prerequisites
show5: prereq1 prereq2 ## This is a description for show5 \
and its continuation, with compact prerequisites
show6: prereq1 \
prereq2 ## This is a description for show6 \
and its continuation, with prerequisites spanning lines
##@ Nothing should be picked up from these lines:
noshow1: # This is just any comment, which will not be parsed by 'make help'
noshow2: prereq1 prereq2 # This is just any comment, which will not be parsed by 'make help'
noshow3: prereq1 \
prereq2 # This is just any comment, which will not be parsed by 'make help'
noshow4: # This is just any comment and its continuation. \
It will not be parsed by 'make help'.
noshow5: prereq1 prereq2 # This is just any comment and its continuation. \
It will not be parsed by 'make help'.
noshow6: prereq1 \
prereq2 # This is just any comment and its continuation. \
It will not be parsed by 'make help'.
############################################################################
#
# Readable version of code, with '$' replaced with makefile-style '$$', but no trailing backslashes:
#
# @awk -v tab=10 '
# BEGIN {
# FS = ":.*## ";
# buffer = "";
# color = "\033[36m";
# nocolor = "\033[0m";
# indent = " ";
# usage();
# }
# function trim(str) {
# gsub(/[ \t]+$$/, "", str);
# gsub(/^[ \t]+/, "", str);
# return str;
# }
# function spout(target, desc) {
# split(trim(target), fields, " ");
# for (i in fields) printf "%s%s%-" tab "s%s%s\n", indent, color, trim(fields[i]), nocolor, desc;
# }
# function usage() {
# printf "\nUsage:\n%smake %s<target>%s\n\nRecognized targets:\n", indent, color, nocolor;
# }
# /\\$$/ {
# gsub(/\\$$/, "");
# buffer = buffer $$0;
# next;
# }
# buffer {
# $$0 = buffer $$0;
# buffer = "";
# }
# /^[-a-zA-Z0-9*/%_. ]+:.*## / {
# pad = sprintf("\n%" tab "s" indent, "");
# gsub(/\\n/, pad);
# spout($$1, $$2);
# }
# /^##@ / {
# gsub(/\\n/, "\n");
# printf "\n%s\n", substr($$0, 5);
# }
# END {
# print "";
# }
# ' $(MAKEFILE_LIST) # v1.54
#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment