How do you get the list of targets in a makefile?
Asked Answered
H

29

342

I've used rake a bit (a Ruby make program), and it has an option to get a list of all the available targets, eg

> rake --tasks
rake db:charset      # retrieve the charset for your data...
rake db:collation    # retrieve the collation for your da...
rake db:create       # Creates the databases defined in y...
rake db:drop         # Drops the database for your curren...
...

but there seems to be no option to do this in GNU make.

Apparently the code is almost there for it, as of 2007 - http://www.mail-archive.com/[email protected]/msg06434.html.

Anyway, I made little hack to extract the targets from a makefile, which you can include in a makefile.

list:
    @grep '^[^#[:space:]].*:' Makefile

It will give you a list of the defined targets. It's just a start - it doesn't filter out the dependencies, for instance.

> make list
list:
copy:
run:
plot:
turnin:
Hildebrandt answered 18/11, 2010 at 20:28 Comment(4)
I quickly read answers, which are interesting, but so far for myself I prefer to keep it plain, simple, and portable using an alias (in my .bashrc): alias makefile-targets='grep "^[^#[:space:]].*:" Makefile' Most often I just need to examine the current makefile, and bash completion expands my alias.Krohn
unix.stackexchange.com/a/230050/221416Niche
Is it too trivial just: grep : Makefile?Jewelfish
@Jewelfish That works for very simple 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.Chlorothiazide
P
239

Preface:

  • Per this answer, make versions post-4.4.1 will natively support a new --print-targets option, which makes the solution below unnecessary.

  • This other answer here builds on the solution below, and adds support for printing descriptions along with target names, assuming you've embedded @#-prefixed comments in your targets.


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 $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'
# IMPORTANT: The line above must be indented by (at least one) 
#            *actual TAB character* - *spaces* do *not* work.

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 that make's output in in English, as parsing of the output relies on that.Tip of the hat to Bastian Bittorf

  • $(MAKE) -pRrq -f $(firstword $(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 $(firstword $(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 ....
        (Since MAKEFILE_LIST also contains the list of included Makefiles, firstword is used to extract only the originally targeted file; the implication is that the targeted file name / path must not contain spaces, but that is unlikely to be the case in practice).

      • : 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 use make -p -f/dev/null, as recommended in the man page.

  • -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 in make'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 :
  • grep -E -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 the list 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.

Poi answered 13/10, 2014 at 12:22 Comment(5)
Is it possible to print out explanation of each target as well? I assume each target has its own explanation as a comment.Patrilineal
@Cemo: While that would be nice, you'd need a fundamentally different solution, because the output from make -pRrq that this solution parses doesn't contain the comments from the source file.Poi
your answer so good that it inspired others what I want.Patrilineal
https://mcmap.net/q/93193/-how-do-you-get-the-list-of-targets-in-a-makefile see pleasePatrilineal
Here is the one liner to perform this task. echo -e '\n.PHONY: list\nlist:\n\t@LC_ALL=C $(MAKE) -pRrp -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '"'"'/(^|\\n)# Files(\\n|$$)/,/(^|\\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}'"'"' | sort | grep -E -v -e '"'"'^[^[:alnum:]]'"'"' -e '"'"'^$@$$'"'" >> MakefileFagaceous
G
291

Under Bash (at least), this can be done automatically with tab completion:

make spacetabtab


If you're using a bare-bones distribution (maybe a container) you might need to install a package.

$ apt-get install bash-completion ## Debian/Ubuntu/etc.
$ . /etc/bash_completion ## or just launch bash again
Germane answered 15/5, 2014 at 0:20 Comment(16)
Tested: bash --version=4.2.45(1)-release. make --version=3.81.Germane
+1, but note that this is not a bash feature, but depends on your platform. Platforms fall into three categories: those where this completion is installed by default (e.g., Debian 7, Fedora 20), those where you can optionally install it (e.g., Ubuntu 14, with . /etc/bash_completion), and those that don't support it at all (e.g., OSX 10.9)Poi
it does work on OS X 10.10 (not with bash but with zsh).Should
Great tip but also needs bash-completion package. Found in Fedora, RHEL, Gentoo, ect.Constituent
This does not work with programmatically created make targets AFAICT, e.g. $(RUN_TARGETS): run-%: ...Bucolic
With my configuration (zsh 5.1.1) this dumps both targets and variables. Selecting a variable 'varname' adds 'varname=' to the command line arguments. This is certainly a useful feature, but not always what you want. I can also verify as @Matthias said that wildcard targets do not appear, although I'm not exactly surprised. A workaround is to define the target base case (run-: ...) to expose it to completion (although accepting the completion inserts a space, so it's not perfect.)Sverre
This also appears to be extremely confused by eval and potentially varname:=... - my completions end up including $(foreach f, etc. For now I'm using make -Bn or make -Bn targetname, which does a dry run of default or targetname as if from scratch. In combination with a target that (optionally) prints the current target and its dependencies, this could give you all the information you need. As an aside, make --print-data-base shows all of the targets/prerequisites/recipes, including each expansion of wildcard targets!Sverre
To install bash-completion properly on OS X see this tip from DavidWalters
simple and elegentBukovina
@FlorianOswald For me, in zsh on macOS 10.15, the completion lists the targets but after the files/directories! And with the layout being column after column, takes some effort to find where the targets begin.Pugilism
It's actually worse. Just noticed that the files/directories and targets are all alphabetically ordered put together! So they get interleaved. Initially appeared to be together due to a whole bunch of targets with a common prefix.Pugilism
This also does not show files under directories.N
slackware comes without it by default, but you just need slackpkg install bash-completionDiaphane
Whoa: This is how the bash completion module for make gets its listGermane
To install on Debian-based, if it isn't already: apt-get install bash-completionGermane
This is not an answer. The Bash completion actually runs some command in the background. And we still don't know what that command is.Beating
P
239

Preface:

  • Per this answer, make versions post-4.4.1 will natively support a new --print-targets option, which makes the solution below unnecessary.

  • This other answer here builds on the solution below, and adds support for printing descriptions along with target names, assuming you've embedded @#-prefixed comments in your targets.


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 $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'
# IMPORTANT: The line above must be indented by (at least one) 
#            *actual TAB character* - *spaces* do *not* work.

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 that make's output in in English, as parsing of the output relies on that.Tip of the hat to Bastian Bittorf

  • $(MAKE) -pRrq -f $(firstword $(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 $(firstword $(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 ....
        (Since MAKEFILE_LIST also contains the list of included Makefiles, firstword is used to extract only the originally targeted file; the implication is that the targeted file name / path must not contain spaces, but that is unlikely to be the case in practice).

      • : 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 use make -p -f/dev/null, as recommended in the man page.

  • -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 in make'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 :
  • grep -E -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 the list 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.

Poi answered 13/10, 2014 at 12:22 Comment(5)
Is it possible to print out explanation of each target as well? I assume each target has its own explanation as a comment.Patrilineal
@Cemo: While that would be nice, you'd need a fundamentally different solution, because the output from make -pRrq that this solution parses doesn't contain the comments from the source file.Poi
your answer so good that it inspired others what I want.Patrilineal
https://mcmap.net/q/93193/-how-do-you-get-the-list-of-targets-in-a-makefile see pleasePatrilineal
Here is the one liner to perform this task. echo -e '\n.PHONY: list\nlist:\n\t@LC_ALL=C $(MAKE) -pRrp -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '"'"'/(^|\\n)# Files(\\n|$$)/,/(^|\\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}'"'"' | sort | grep -E -v -e '"'"'^[^[:alnum:]]'"'"' -e '"'"'^$@$$'"'" >> MakefileFagaceous
S
83

Update!

As of Jan 8th, 2024, Make has a --print-targets option that should do this properly without hacky regexes. The current version is Make 4.4.1 so the next release after that will have this feature.


If your Makefile was created by CMake you might be able to run make help.

$ make help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... install
etc

If not, I wrote a patch to add proper support for this obviously useful feature to Make. This is way better than all the other answers here which all involve horrible hacks to grep the Makefiles. That obviously doesn't work if you include other Makefiles, use computed target names, etc.

The patch hasn't been merged so you have to build from source. It's not too bad but you do need some crusty old autoconf-related build tools:

git clone https://github.com/Timmmm/make
cd make
./bootstrap
./configure
make -j4

On Linux you can use this binary I built already.

Then you can use the -l flag to list targets:

./make -C /path/to/your/project -l
Speedway answered 5/1, 2017 at 16:39 Comment(5)
Do you have any option to turn on in your CMakeList to get that ? I got a makefile generated by CMake and I don't have the pseudo help target availablePhilpot
Nope. On OSX at least with a recent CMake (I actually am using nightlies but I believe when I wrote this I was using 3.3), just run touch CMakeLists.txt && cmake . && make help and it should work. I can only guess you're using an ancient cmake, or not using the Unix Makefiles generator?Speedway
I am using CMake 3.6.2 with unix generator on Windows. I'll dig around to look for an explanation because it seems handy.Philpot
@Poi the new flag was only added a few weeks ago, not 7 years ago. If you don't want to build make yourself you can download the prebuilt one I linked (but it uses -l instead of --print-targets).Speedway
I've added a link to your answer to a preface to mine (which is at least currently the accepted answer). With the availability of --print-targets it is a moot point, but note that said answer doesn't parse Makefiles, it parses make -p's output, which - leaving the brittleness of text parsing in general aside - shouldn't be subject to the limitations you mention, if I understand correctly.Poi
G
33

I combined these two answers: https://mcmap.net/q/94368/-list-goals-targets-in-gnu-make-that-contain-variables-in-their-definition and https://mcmap.net/q/94368/-list-goals-targets-in-gnu-make-that-contain-variables-in-their-definition and did some escaping so that this could be used from inside a makefile.

.PHONY: no_targets__ list
no_targets__:
list:
    sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | sort"

.

$ make -s list
build
clean
default
distclean
doc
fresh
install
list
makefile ## this is kind of extraneous, but whatever...
run
Germane answered 25/2, 2013 at 1:43 Comment(8)
I have also (recently) discovered that tab-completion under Bash will list available targets.Germane
Also of note: Some types of makefiles are more conducive than others to having their targets listed. When I add this to an old-style makefile, which explicitly lists every target and every intermediate, the list includes all of those files -- and their dependencies. Listing targets seems to work better with a makefile that uses generic targets and rules.Germane
+1 for a great approach, but a few improvements could be made: (a) explicitly specifying sh -c "…" is unnecessary, because that's what make by default uses to invoke recipe commands; (b) the command you use is too simplistic, resulting in false positives (as you note); (c) if you prefix the command with @, it will not be echoed to stdout before execution, obviating the need to use -s on invocation.Poi
there's a much simpler make target you could add: list: cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g | sort"Oilcan
The cat/sed approach fails on several levels. It does not expand variables, and also fails to list targets that are the result of including other files.Tipsy
@mklement0: I prefer to use make -s (via a Bash alias) rather than prefixing commands in the makefile with @. There are some cases where @ may be useful, notably as a prefix for echo commands (where the display of the command would be redundant), but in general I don't like to use it. This way, I can see the "recipes" when I need to (by not using -s), but not typically. Here's the related documentation: GNU Make Manual, 5.2 Recipe Echoing.Germane
@BrentBradburn tab completion is the simplest and therefore my preferred approach! I'd add that in your answer as it's easy to miss in a comment.Tropic
@Nagev, I wrote a different answer based on tab completion. :)Germane
L
25

As mklement0 points out, a feature for listing all Makefile targets is missing from GNU-make, and his answer and others provides ways to do this.

However, the original post also mentions rake, whose tasks switch does something slightly different than just listing all tasks in the rakefile. Rake will only give you a list of tasks that have associated descriptions. Tasks without descriptions will not be listed. This gives the author the ability to both provide customized help descriptions and also omit help for certain targets.

If you want to emulate rake's behavior, where you provide descriptions for each target, there is a simple technique for doing this: embed descriptions in comments for each target you want listed.

You can either put the description next to the target or, as I often do, next to a PHONY specification above the target, like this:

.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.PHONY: help # Generate list of targets with descriptions                                                                
help:                                                                                                                    
    @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20

Which will yield

$ make help
target1             Target 1 help text
target2             Target 2 help text

...
help                Generate list of targets with descriptions

You can also find a short code example in this gist and here too.

Again, this does not solve the problem of listing all the targets in a Makefile. For example, if you have a big Makefile that was maybe generated or that someone else wrote, and you want a quick way to list its targets without digging through it, this won't help.

However, if you are writing a Makefile, and you want a way to generate help text in a consistent, self-documenting way, this technique may be useful.

Lolalolande answered 23/8, 2017 at 15:16 Comment(9)
This is a great technique - nice and simple but gives descriptions also!Hildebrandt
Great technique. I modified this a bit to use echo command as the command description (https://mcmap.net/q/93193/-how-do-you-get-the-list-of-targets-in-a-makefile).Flinger
@PengheGeng Thanks for the post. I want to make clear the @echo "Target 1" is just a placeholder and not a "description echo command". The generated help text is not coming from the @echo's. In a real Makefile, these echo's would be replaced by a build command or some-such. I'll edit the post to make this more clear.Lolalolande
Superb solution! However, I had to use grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1:\t\2/' | column -ts "$$(printf '\t')" to get nice listing because the expand didn't work for me to render nice output.Joslyn
That that because this uses .PHONY it will prevent using this method for targets that actually need to create files matching the target names. One could invent a special syntax e.g. starting a line with triple ### to mark documentation lines instead.Joslyn
@MikkoRantalainen and others, the whitespace is interpreted as spaces when copy/pasting from StackOverflow. In my case, the expand -t20 was looking for tabs, so I needed to change the space in \1 \2 to a tab for it to work.Corpulent
If you use a Makefile that includes other Makefile, then a small alteration from Makefile to $(MAKEFILE_LIST) will include the .PHONY's from all the Makefiles. @grep '^.PHONY: .* #' $(MAKEFILE_LIST) | sed 's/^.*\.PHONY: \(.*\) # \(.*\)/\1 \2/' | sort | expand -t48Toname
Fantastic answer. Took this one step further to add sections. A section looks like #=Section above a group of targets and is included in output using grep -e '^.PHONY: .*#' -e '^#=' makefile | sed -r -e 's|#=(.*)|\n\1:|' -e 's|\.PHONY: (.*) # (.*)| \1 \2|' | expand -t20Creditor
Actually, grep isn't even necessary though it is easier to read if you split up the regex patterns on multiple lines sed -nr -e 's|^#=(.*)|\n\1:|p' -e 's|^\.PHONY: (.*) # (.*)| \1 \2|p' makefile | expand -t20Creditor
R
24

My favorite answer to this was posted by Chris Down at Unix & Linux Stack Exchange. I'll quote.

This is how the bash completion module for make gets its list:

make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'

It prints out a newline-delimited list of targets, without paging.

User Brainstone suggests piping to sort -u to remove duplicate entries:

make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u

Source: How to list all targets in make? (Unix&Linux SE)

Rooky answered 10/10, 2019 at 6:10 Comment(0)
K
19

If you have bash completion for make installed, the completion script will define a function _make_target_extract_script. This function is meant to create a sed script which can be used to obtain the targets as a list.

Use it like this:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile
Kisumu answered 27/6, 2014 at 13:44 Comment(2)
Helpful, but note that not all platforms have script /etc/bash_completion and/or function _make_target_extract_script - you seem to be on Ubuntu > 12. If you target /usr/share/bash-completion/completions/make instead, your approach will work on additional platforms, such as Fedora 20. There are (older) platforms that have this file, but not the function (e.g., Debian 7 and Ubuntu 12); other platforms, such as OSX, don't come with tab completion for make at all. Perhaps posting the actual function definition would be helpful.Poi
Thanks for your comment. This is much appreciated. Yes, I've tested this on Ubuntu 14.04Kisumu
T
12

Focusing on an easy syntax for describing a make target, and having a clean output, I chose this approach:

help:
    @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^\=]|$$)" Makefile \
     | grep -v -- -- \
     | sed 'N;s/\n/###/' \
     | sed -n 's/^#: \(.*\)###\(.*\):.*/\2###\1/p' \
     | column -t  -s '###'


#: Starts the container stack
up: a b
  command

#: Pulls in new container images
pull: c d 
    another command

make-target-not-shown:

# this does not count as a description, so leaving
# your implementation comments alone, e.g TODOs
also-not-shown:

So treating the above as a Makefile and running it gives you something like

> make help
up          Starts the container stack
pull        Pulls in new container images

Explanation for the chain of commands:

Thracophrygian answered 28/11, 2019 at 11:10 Comment(4)
Very nice solution. I have used this for a few years now.: awk -F ':|##' '/^[^\t].+:.*##/ { printf "\033[36mmake %-28s\033[0m -%s\n", $$1, $$NF }' $(MAKEFILE_LIST) | sort with the ## before the comment on the same line as the target. But I think your solution allows for before readability.Savell
A makefile comprised of the first two lines reports grep: parentheses not balanced on my OSX 10.15 machine.Eyestrain
@Crummy sorry, my mistake. Thanks for pointing it out. Fixed it. make requires to escape dollar signs that are not supposed to start variable names.Thracophrygian
Watch out, StackOverflow converts tabs to spaces when displaying the code sections above, but Makefiles require tabs. You can edit the post and then copy the sections, so tabs are retained.Thracophrygian
C
10

I took a few answers mentioned above and compiled this one, which can also generate a nice description for each target and it works for targets with variables too.

Example Makefile:

APPS?=app1 app2

bin: $(APPS:%=%.bin)
    @# Help: A composite target that relies only on other targets

$(APPS:%=%.bin): %.bin:
    @# Help: A target with variable name, value = $*

test:
    @# Help: A normal target without variables

# A target without any help description
clean:

# A hidden target
.hidden:

help:
    @printf "%-20s %s\n" "Target" "Description"
    @printf "%-20s %s\n" "------" "-----------"
    @make -pqR : 2>/dev/null \
        | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' \
        | sort \
        | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' \
        | xargs -I _ sh -c 'printf "%-20s " _; make _ -nB | (grep -i "^# Help:" || echo "") | tail -1 | sed "s/^# Help: //g"'

Example output:

$ make help
Target               Description
------               -----------
app1.bin             A target with variable name, value = app1
app2.bin             A target with variable name, value = app2
bin                  A composite target that relies only on other targets
clean
test                 A normal target without variables

How does it work:

The top part of the make help target works exactly as posted by mklement0 here - How do you get the list of targets in a makefile?.

After getting the list of targets, it runs make <target> -nB as a dry run for each target and parses the last line that starts with @# Help: for the description of the target. And that or an empty string is printed in a nicely formatted table.

As you can see, the variables are even expanded within the description as well, which is a huge bonus in my book :).

Cimbura answered 10/12, 2020 at 22:37 Comment(0)
M
5

For a Bash Script

Here's a very simple way to do this in bash -- based on the comment by @cibercitizen1 above:

grep : Makefile | awk -F: '/^[^.]/ {print $1;}'

See also the more authoritative answer by @Marc.2377, too, which says how the Bash completion module for make does it.

Molybdenous answered 21/5, 2020 at 21:52 Comment(1)
This is prefect for when you have a very large file and don't want to use tab completion or modify the make file!Mix
D
5

Add this target to your Makefile:

help:
    @echo "\nTARGETS:\n"
    @make -qpRr | egrep -e '^[a-z].*:$$' | sed -e 's~:~~g' | sort
    @echo ""

  • make -qpRr = make --question --print-data-base --no-builtin-variables --no-builtin-rules
  • egrep -e '^[a-z].*:$$': searches for lines which start with lowercase and ends with ":"
  • sed -e 's~:~~g': deletes the ":"

Then just run:

make help

This works for me 😉


PD: more info at...

make --help
Disaccredit answered 29/4, 2021 at 17:5 Comment(0)
P
4

@nobar's answer helpfully shows how to use tab completion to list a makefile's targets.

  • This works great for platforms that provide this functionality by default (e.g., Debian, Fedora).

  • On other platforms (e.g., Ubuntu) you must explicitly load this functionality, as implied by @hek2mgl's answer:

    • . /etc/bash_completion installs several tab-completion functions, including the one for make
    • Alternatively, to install only tab completion for make:
      • . /usr/share/bash-completion/completions/make
  • For platforms that don't offer this functionality at all, such as OSX, you can source the following commands (adapated from here) to implement it:
_complete_make() { COMPREPLY=($(compgen -W "$(make -pRrq : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | egrep -v '^[^[:alnum:]]' | sort | xargs)" -- "${COMP_WORDS[$COMP_CWORD]}")); }
complete -F _complete_make make
  • Note: This is not as sophisticated as the tab-completion functionality that comes with Linux distributions: most notably, it invariably targets the makefile in the current directory, even if the command line targets a different makefile with -f <file>.
Poi answered 13/10, 2014 at 12:47 Comment(0)
C
4

This help target will only print targets which have ## followed by a description. This allows for documenting both public and private targets. Using the .DEFAULT_GOAL makes the help more discoverable.

Only sed, xargs and printf used which are pretty common.

Using the < $(MAKEFILE_LIST) allows for the makefile to be called something other than Makefile for instance Makefile.github

You can customize the output to suit your preference in the printf. This example is set up to match the OP's request for rake style output

When cutting and pasting the below make file, don't forget to change the 4 spaces indentation to tabs.

# vim:ft=make
# Makefile

.DEFAULT_GOAL := help
.PHONY: test help

help:  ## these help instructions
    @sed -rn 's/^([a-zA-Z_-]+):.*?## (.*)$$/"\1" "\2"/p' < $(MAKEFILE_LIST) | xargs printf "make %-20s# %s\n"

lint: ## style, bug and quality checker
    pylint src test

private: # for internal usage only
    @true

test: private ## run pytest with coverage
    pytest --cov test


Here is the output from the Makefile above. Notice the private target doesn't get output because it only has a single # for it's comment.

$ make
make help                # these help instructions
make lint                # style, bug and quality checker
make test                # run pytest with coverage
Cressi answered 8/9, 2020 at 21:21 Comment(0)
P
3

This is far from clean, but did the job, for me.

make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1

I use make -p that dumps the internal database, ditch stderr, use a quick and dirty grep -A 100000 to keep the bottom of the output. Then I clean the output with a couple of grep -v, and finally use cut to get what's before the colon, namely, the targets.

This is enough for my helper scripts on most of my Makefiles.

EDIT: added grep -v Makefile that is an internal rule

Plagiarize answered 19/9, 2017 at 15:10 Comment(0)
F
3

This is a modification to jsp's very helpful answer (https://mcmap.net/q/93193/-how-do-you-get-the-list-of-targets-in-a-makefile). I like the idea of getting not only a list of targets but also their descriptions. jsp's Makefile puts the description as the comment, which I found often will be repeated in the target's description echo command. So instead, I extract the description from the echo command for each target.

Example Makefile:

.PHONY: all
all: build
    : "same as 'make build'"

.PHONY: build
build:
    @echo "Build the project"

.PHONY: clean
clean:
    @echo "Clean the project"

.PHONY: help
help:
    @echo -n "Common make targets"
    @echo ":"
    @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20

Output of make help:

$ make help
Common make targets:
    make all        same as 'make build'
    make build      Build the project
    make clean      Clean the project
    make help       Common make targets

Notes:

  • Same as jsp's answer, only PHONY targets may be listed, which may or may not work for your case
  • In addition, it only lists those PHONY targets that have a echo or : command as the first command of the recipe. : means "do nothing". I use it here for those targets that no echo is needed, such as all target above.
  • There is an additional trick for the help target to add the ":" in the make help output.
Flinger answered 28/8, 2018 at 13:48 Comment(0)
F
3

To expand on the answer given by @jsp, you can even evaluate variables in your help text with the $(eval) function.

The proposed version below has these enhanced properties:

  • Will scan any makefiles (even included)
  • Will expand live variables referenced in the help comment
  • Adds documentation anchor for real targets (prefixed with # TARGETDOC:)
  • Adds column headers

So to document, use this form:

RANDOM_VARIABLE := this will be expanded in help text

.PHONY: target1 # Target 1 help with $(RANDOM_VARIABLE)
target1: deps
    [... target 1 build commands]

# TARGETDOC: $(BUILDDIR)/real-file.txt # real-file.txt help text
$(BUILDDIR)/real-file.txt:
    [... $(BUILDDIR)/real-file.txt build commands]

Then, somewhere in your makefile:

.PHONY: help # Generate list of targets with descriptions
help:
    @# find all help in targets and .PHONY and evaluate the embedded variables
    $(eval doc_expanded := $(shell grep -E -h '^(.PHONY:|# TARGETDOC:) .* #' $(MAKEFILE_LIST) | sed -E -n 's/(\.PHONY|# TARGETDOC): (.*) # (.*)/\2  \3\\n/'p | expand -t40))
    @echo
    @echo ' TARGET   HELP' | expand -t40
    @echo ' ------   ----' | expand -t40
    @echo -e ' $(doc_expanded)'
Forsythe answered 20/7, 2020 at 18:37 Comment(0)
J
3

make doesn't support this by default and other answers have shown how to extract the list of possible targets automatically.

However, in case you want to have more control with the listing without any side-effects (such as using the .PHONY target to mark the documentation which prevents the logic of using the target names as actual files which Make uses to decide which targets needs to be rebuilt), you can invent your own syntax just for the documentation. I prefer to use ### like this:

CPUS ?= $(shell nproc)
MAKEFLAGS += -j $(CPUS) -l $(CPUS) -s

# Basic paths
PREFIX  ?= usr
BINDIR  ?= $(PREFIX)/bin
ETCDIR  ?= etc
MANDIR  ?= $(PREFIX)/share/man
# ...

### help: Show help message (default target)
# use "help" as the default target (first target in the Makefile)
.PHONY: help
help:
    @printf "%s\n\n" "make: List of possible targets:"
    @grep '^### .*:' $(lastword $(MAKEFILE_LIST)) | sed 's/^### \([^:]*\): \(.*\)/\1:\t\2/' | column -ts "$$(printf '\t')"

### install: Install all files in $PREFIX (used by debian binary package build scripts)
install:
    install -D -o root -g root -m 755 ...
    ...

### release: Increase package version number
release:
    debchange --release

(as usual, the indented files must start with exactly one tabulator but stackoverflow cannot reproduce that detail correctly.)

Output will look like this:

$ make
make: List of possible targets:

help:      Show help message (default target)
install:   Install all files in $PREFIX (used by debian binary package build scripts)
release:   Increase package version number

This works because only lines starting with ### and having a : character are considered as the documentation to output. Note that this intentionally does not extract the actual target name but fully trusts the documentation lines only. This allows always emitting correct output for very complex Makefile tricks, too. Also note that this avoids needing to put the documentation line on any specific position relative to actual rule. I also intentionally avoid sorting the output because the order of output can be fully controlled from the Makefile itself simply by listing the documentation lines in preferred order.

You could obviously invent any other syntax you like and even do something like

### en: install: Install all files in $PREFIX
### fi: asennus: asenna kaikki tiedostot hakemistoon $PREFIX

and only print lines that match the current locale to support multiple languages and having aliases to localize the target names, too:

.PHONY: asennus
asennus: install

The most important question is why do you want to list the targets? Do you want actual documentation or some kind of debugging information?

Joslyn answered 4/4, 2022 at 9:24 Comment(0)
D
2

Plenty of workable solutions here, but as I like saying, "if it's worth doing once, it's worth doing again." I did upvote the sugestion to use (tab)(tab), but as some have noted, you may not have completion support, or, if you have many include files, you may want an easier way to know where a target is defined.

I have not tested the below with sub-makes...I think it wouldn't work. As we know, recursive makes considered harmful.

.PHONY: list ls
ls list :
    @# search all include files for targets.
    @# ... excluding special targets, and output dynamic rule definitions unresolved.
    @for inc in $(MAKEFILE_LIST); do \
    echo ' =' $$inc '= '; \
    grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
    cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
    tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
; done

# to get around escaping limitations:
open_paren := \(
close_paren := \)

Which I like because:

  • list targets by include file.
  • output raw dynamic target definitions (replaces variable delimiters with modulo)
  • output each target on a new line
  • seems clearer (subjective opinion)

Explanation:

  • foreach file in the MAKEFILE_LIST
  • output the name of the file
  • grep lines containing a colon, that are not indented, not comments, and don't start with a period
  • exclude immediate assignment expressions (:=)
  • cut, sort, indent, and chop rule-dependencies (after colon)
  • munge variable delimiters to prevent expansion

Sample Output:

 = Makefile = 
  includes
  ls list
 = util/kiss/snapshots.mk = 
  rotate-db-snapshots
  rotate-file-snapshots
  snap-db
  snap-files
  snapshot
 = util/kiss/main.mk = 
  dirs
  install
   %MK_DIR_PREFIX%env-config.php
   %MK_DIR_PREFIX%../srdb
Dimmer answered 19/4, 2018 at 14:25 Comment(0)
O
2

This is a very simplified version of what the bash-completion script does.

make -npq : 2> /dev/null | \
      awk -v RS= -F: '$1 ~ /^[^#%]+$/ { print $1 }'

Explanation:

  • make -npq: Print the database without executing anything
  • -v RS=: Separate records by whole paragraphs
  • -F:: Separate fields by : (so the rule name is $1)
  • $1 ~ /^[^#%]+$/: Match rules that don't contain # or % (comments or pattern rules)
  • { print $1 }: Print the rule name

This is much simpler than mklement0's approach (which I fixed myself), and works better.

Ovalle answered 28/7, 2022 at 22:28 Comment(2)
Nice approach! How can I turn this into a custom command ? I tried editing my ~/.zshrc config file by adding make -npq : 2> /dev/null | \ awk -v RS= -F: '$1 ~ /^[^#%]+$/ { print $1 }' but it doesn't work saying zsh: command not found: awk: syntax error at source line 1 context is >>> ~ <<< awk: bailing out at source line 1Agone
@Agone Just put it inside a function: make_list() { make -npq : 2> /dev/null | awk -v RS= -F: '$1 ~ /^[^#%]+$/ { print $1 }' }.Ovalle
D
1

This one was helpful to me because I wanted to see the build targets required (and their dependencies) by the make target. I know that make targets cannot begin with a "." character. I don't know what languages are supported, so I went with egrep's bracket expressions.

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
Durfee answered 8/9, 2017 at 5:20 Comment(0)
P
1

Yet another additional answer to above.

tested on MacOSX using only cat and awk on terminal

cat Makefile | awk '!/SHELL/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'

will output of the make file like below:

target1

target2

target3

in the Makefile, it should be the same statement, ensure that you escape the variables using $$variable rather than $variable.

Explanation

cat - spits out the contents

| - pipe parses output to next awk

awk - runs regex excluding "shell" and accepting only "A-z" lines then prints out the $1 first column

awk - yet again removes the last character ":" from the list

this is a rough output and you can do more funky stuff with just AWK. Try to avoid sed as its not as consistent in BSDs variants i.e. some works on *nix but fails on BSDs like MacOSX.

More

You should be able add this (with modifications) to a file for make, to the default bash-completion folder /usr/local/etc/bash-completion.d/ meaning when you "make tab tab" .. it will complete the targets based on the one liner script.

Pantelleria answered 14/4, 2019 at 17:24 Comment(1)
you can also use make -qp rather than cat. so it will become make -qp | awk '/:/ && !/:=/ && /^[A-z]/ && !/PATH/ && !/DISPLAY/ && !/TERM_SESSION/ && !/USER_TEXT_ENCODING/ && !/Makefile/ {print substr($0, 1, length($0)-1) | "sort -u"}'Pantelleria
I
1

For AWK haters, and for simplicity, this contraption works for me:

help:
  make -qpRr $(lastword $(MAKEFILE_LIST)) | egrep -v '(^(\.|:|#|\s|$)|=)' | cut -d: -f1

(for use outside a Makefile, just remove $(lastword ...) or replace it with the Makefile path).

This solution will not work if you have "interesting" rule names but will work well for most simple setups. The main downside of a make -qp based solution is (as in other answers here) that if the Makefile defines variable values using functions - they will still be executed regardless of -q, and if using $(shell ...) then the shell command will still be called and its side effects will happen. In my setup often the side effects of running shell functions is unwanted output to standard error, so I add 2>/dev/null after the make command.

Instead answered 13/7, 2020 at 9:31 Comment(0)
C
1

I was inspired by @jsp's answer to adapt his file parsing technique and this version has the following benefits.

  • Automatically detects makefile name
  • Supports section headers
  • Places .PHONY below each section header for all targets within that section and places comments after the target so it's a little cleaner to look at.

Given a makefile like this

#=Default Command

.PHONY: help

help: # Shows the available make commands.
    @sed -nr \
        -e 's|^#=(.*)|\n\1:|p' \
        -e 's|^([a-zA-Z-]*):((.*?)# (.*))?|  \1 \4|p' \
        $(lastword $(MAKEFILE_LIST)) \
        | expand -t20

#=Project Commands

.PHONY: install build

install: # Installs the thing
    @echo install

build: install # Builds the thing
    @echo build

#=Other Commands

.PHONY: foo bar

foo:
    @echo foo

bar:
    @echo bar

You'll get output like this


Default Command:
  help              Shows the available make commands.

Project Commands:
  install           Installs the thing
  build             Builds the thing

Other Commands:
  foo
  bar

Regex explanations:
's|^#=(.*)|\n\1:|p' Captures everything in parens after section declaration #= and prints it \1 with a newline before and colon after it.

's|^([a-zA-Z-]*):((.*?)# (.*))?| \1 \4|p' Captures target name and includes an optional capture group ()? for the description. The optional group surrounds two more capture groups, the first one capturing everything between : and # and the second capturing the help text (aside: the regex is a bit complicated but it lets us output targets with and without comments. Open to better methods). Finally it outputs the target name \1 and description \4 indented 2 spaces to tuck it under the section header.

Creditor answered 2/8, 2023 at 18:40 Comment(0)
D
0

tl;dr I personally copy-paste the same help target for every Makefile I build.

.SILENT:

.PHONY: help
## This help screen
help:
    printf "Available targets\n\n"
    awk '/^[a-zA-Z\-\_0-9]+:/ { \
        helpMessage = match(lastLine, /^## (.*)/); \
        if (helpMessage) { \
            helpCommand = substr($$1, 0, index($$1, ":")-1); \
            helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
            printf "%-30s %s\n", helpCommand, helpMessage; \
        } \
    } \
    { lastLine = $$0 }' $(MAKEFILE_LIST)

I also maintain a copy of it in this Github gist: https://gist.github.com/Olshansk/689fc2dee28a44397c6e31a0776ede30

Dotation answered 30/7, 2022 at 21:13 Comment(0)
L
0

Very simple AWK solution:

all:
    @awk -F'[ :]' '!/^all:/ && /^([A-z_-]+):/ {print "make " $$1}' Makefile

(Note: This doesn't cover all the corner-cases as the accepted answer, as explained here.)

Lactic answered 22/11, 2022 at 13:1 Comment(0)
E
0

Try this one:

make -qp | awk -F':' '/^[^ \t.%][-A-Za-z0-9_]*:/ {split($0,A,/ /);for(i in A)if(match(A[i],/^[^.%][-A-Za-z0-9_]*/))print substr(A[i],1,RLENGTH)}' | sort -u
Enwomb answered 20/2, 2023 at 17:32 Comment(0)
E
0

Here is a version using cat, grep and cut.

cat Makefile | grep : | grep -v '^\t' | cut -d: -f1
Enmesh answered 6/9, 2023 at 18:11 Comment(0)
D
-1

I usually do:

grep install_targets Makefile

It would come back with something like:

install_targets = install-xxx1 install-xxx2 ... etc

I hope this helps

Dulci answered 3/10, 2019 at 9:29 Comment(0)
O
-5

not sure why the previous answer was so complicated:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g" 
Oilcan answered 16/10, 2014 at 19:25 Comment(5)
The previous answer is so complicated, because it covers all scenarios that your answer does NOT cover: (a) inclusion of targets defined via variables (e.g., $(SHELLS): ...), (b) inclusion of targets that start with a digit, (c) non-inclusion of variable definitions, (d) targeting the proper makefile if make was specified with an explicit makefile path. (a) is the crucial shortcoming: even if you reliably targeted the right makefile, parsing the raw file will not work in the general case, due to lack of variable expansion.Poi
As an aside, re complicated: your command can be simplified to the following - but the main point is that it's not robust enough: awk -F: '/^[A-z]/ {print $$1}' Makefile. Finally, a largely academic point: using [A-z] rather than [:alpha:] means that you'll miss targets that start with a foreign character such as á.Poi
@Poi your first awk command works nicely, and is the simplest so far, though using [:alpha:] seems to break it for some reason - it misses some targets in a test file.Hildebrandt
@bburns.km: My bad: I should have said [[:alpha:]] rather than [:alpha:] ([:alpha:] represents the locale-aware set of letters inside a character class ([...]), hence the effective need for double [[ / ]].Poi
Why is this answer so complicated? You manage to collect several of the useless sins in this short snippet. awk -F: '/^[A-Za-z]/ { print $$1 }' Makefile uses a single process and hopefully a more correct regular expression (though you could of course have targets with underscores, numbers etc as well, as noted in earlier comments).Liana

© 2022 - 2024 — McMap. All rights reserved.