How do you get the list of targets in a makefile?
Under Bash (at least), this can be done automatically with tab completion:
make
spacetabtab
Note: This answer has been updated to still work as of GNU make
v4.3 - let us know if you come across something that breaks.
This is an attempt to improve on Brent Bradburn's great approach as follows:
- uses a more robust command to extract the target names, which hopefully prevents any false positives (and also does away with the unnecessary
sh -c
) - does not invariably target the makefile in the current directory; respects makefiles explicitly specified with
-f <file>
- excludes hidden targets - by convention, these are targets whose name starts neither with a letter nor a digit
- makes do with a single phony target
- prefixes the command with
@
to prevent it from being echoed before execution
Curiously, GNU make
has no feature for listing just the names of targets defined in a makefile. While the -p
option produces output that includes all targets, it buries them in a lot of other information and also executes the default target (which could be suppressed with -f/dev/null
).
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:
@LC_ALL=C $(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
Important: On pasting this, make sure that the last line is indented by exactly 1 actual tab char. (spaces do not work).
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
LC_ALL=C
makes sure thatmake
's output in in English, as parsing of the output relies on that.Tip of the hat to Bastian Bittorf$(MAKE) -pRrq -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, without also executing the default (or given) task; if you don't need to target a specific Makefile, you may usemake -p -f/dev/null
, as recommended in theman
page.
- Invokes
-v RS=
- This is an awk idiom that breaks the input into blocks of contiguous non-empty lines.
/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/
- Matches the range of lines in the output that contains all targets, across paragraphs - by limiting parsing to this range, there is no need to deal with false positives from other output sections.
- Note: Between
make
versions 3.x and 4.3, paragraph structuring inmake
's output changed, so(^|\n)
/(\n|$$)
ensures that the lines that identify the start and the end of the cross-paragraph range of lines of interest are detected irrespective of whether they occur at the start or inside / at the end of a paragraph.
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
Running make list
then prints all targets, each on its own line; you can pipe to xargs
to create a space-separated list instead.