Jay Taylor's notes
back to listing indexHow do you get the list of targets in a makefile?
[web search]Curiously, GNU make
has no feature for listing just the names of targets defined in a makefile. The -p
option produces output that includes all targets, but buries them in a lot of other information.
Place the following rule in a makefile for GNU make
to implement a target named list
that simply lists all target names in alphabetical order - i.e.: invoke as make list
:
.PHONY: list
list:
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs
Note: On pasting this, make sure that the last line is indented by exactly 1 tab.
Note that sorting the resulting list of targets is the best option, since not sorting doesn't produce a helpful ordering in that the order in which the targets appear in the makefile is not preserved.
Also, the sub-targets of a rule comprising multiple targets are invariably output separately and will therefore, due to sorting, usually not appear next to one another; e.g., a rule starting with a z:
will not have targets a
and z
listed next to each other in the output, if there are additional targets.
Explanation of the rule:
- .
PHONY: list
- declares target list a phony target, i.e., one not referring to a file, which should therefore have its recipe invoked unconditionally
$(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null
- Invokes
make
again in order to print and parse the database derived from the makefile:-p
prints the database-Rr
suppresses inclusion of built-in rules and variables-q
only tests the up-to-date-status of a target (without remaking anything), but that by itself doesn't prevent execution of recipe commands in all cases; hence:-f $(lastword $(MAKEFILE_LIST))
ensures that the same makefile is targeted as in the original invocation, regardless of whether it was targeted implicitly or explicitly with-f ...
.
Caveat: this will break if your makefile containsinclude
directives; to address this, define variableTHIS_FILE := $(lastword $(MAKEFILE_LIST))
before anyinclude
directives and use-f $(THIS_FILE)
instead.:
is a deliberately invalid target that is meant to ensure that no commands are executed;2>/dev/null
suppresses the resulting error message. Note: This relies on-p
printing the database nonetheless, which is the case as of GNU make 3.82. Sadly, GNU make offers no direct option to just print the database.
- Invokes
-v RS=
- This is an awk idiom that breaks the input into blocks of contiguous non-empty lines.
/^# File/,/^# Finished Make data base/
- Matches the range of lines in the output that contains all targets (true as of GNU make 3.82) - by limiting parsing to this range, there is no need to deal with false positives from other output sections.
if ($$1 !~ "^[#.]")
- Selectively ignores blocks:
#
... ignores non-targets, whose blocks start with# Not a target:
.
... ignores special targets
- All other blocks should each start with a line containing only the name of an explicitly defined target followed by
:
- Selectively ignores blocks:
egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
removes unwanted targets from the output:'^[^[:alnum:]]'
... excludes hidden targets, which - by convention - are targets that start neither with a letter nor a digit.'^$@$$'
... excludes thelist
target itself
xargs
- Effectively converts the output lines to a single-line, space-separated list; omit this if you want each target name to appear on its own line.