100

I have a fairly large makefile that creates a number of targets on the fly by computing names from variables. (eg foo$(VAR) : $(PREREQS)). Is there any way that gnu make can be convinced to spit out a list of targets after it has expanded these variables?

I'd like to be able to get the targets for an aribitrary makefile. I'm trying to write a completion function for my shell.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
BitShifter
  • 1,208
  • 2
  • 10
  • 9

16 Answers16

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

Taken from the make arg completion, which works like a charm.

todd hodes
  • 1,199
  • 1
  • 7
  • 2
  • 2
    i tried to make an alias for this, putting it in double quotes, but it just results in awk having a syntax error on the first comma... any thoughts on how to correctly escape this for a shell (bash) alias? – drfrogsplat Oct 25 '12 at 02:06
  • 3
    I think it ha something to do with where it's defined. I think if it's defined as an alias its working directory becomes the directory where it's defined, and apparently the awk part doesn't like the dot in hidden directories. Defining a function works. – Ibrahim Dec 03 '12 at 10:00
  • I just put it in a shell script in `~/bin` rather than using an alias. Works great; thanks! – mpontillo Feb 13 '15 at 22:13
  • 3
    The alias `alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"` which will save you 2 minutes of quoting games :-) – Ciro Santilli OurBigBook.com May 13 '15 at 20:14
  • 1
    `| sort -u` looks more useful :) – sherpya Oct 30 '15 at 14:38
80

Can you parse the output from make -pn (i.e. make --print-data-base --dry-run)? It prints out all the variables, rules, implicit rules and which commands will be run in laborious detail.

Jack Kelly
  • 18,264
  • 2
  • 56
  • 81
  • This answer looks much nicer than the other. – Matt Joiner Sep 06 '10 at 03:43
  • I agree that this is much nicer. Still more work than I hoped, but I'm going to mark it as the answer since nothing else seems to be reasonable and GNU make doesn't have a convenient flag for what I want. – BitShifter Sep 08 '10 at 14:47
  • 6
    Be aware that the output may depend on the target you specify, if your Makefile generates targets dynamically. The output may also depend on the values of environment variables or make variables specified with the make command. – reinierpost Mar 07 '11 at 08:57
  • 6
    I like the brevity. +1 Added my own spin (described in an answer below): `make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'` – Jamie Sep 12 '11 at 14:06
  • Suggest `make -pnr`. The `-r` removes implicit rules. – sjbx Jun 15 '15 at 13:01
15

I'm not sure if this is only a gnu make thing, but this works well:

make help

Zdenik
  • 406
  • 1
  • 4
  • 13
  • This is pretty handy, much easier to remember than a custom alias for one of the other methods. – Ibrahim Dec 03 '12 at 10:01
  • 8
    Not really working on GNU Make 3.81, probably a target in you makefiles: "make: *** No rule to make target `help'. Stop" – a1an Jan 22 '13 at 22:39
  • 9
    This just invokes "help" target in Makefile. If it exists, it will print something (not the *complete* lists of targets), if it doesn't exist (typical situation), it will bail out. – pfalcon Feb 11 '13 at 17:35
  • 4
    Good news is cmake seems to generates a nice easy to read "help" target. – Stéphane Sep 12 '14 at 00:15
  • This is awesome! :) Go cmake – Jeef Dec 23 '19 at 16:33
10

Several responders have suggested using make -pn, which will print the database of rules but not execute anything -- more or less. The problem with this approach is that -n does still invoke all recursive makes, and it does still do a lot more work than necessary, because it prints out every command that it would have invoked in a regular build. A more efficient solution would be to create a trivial makefile, dummy.mk, with these contents:

__all_targets__: ; #no-op

Now invoke make as make -p -f Makefile -f dummy.mk __all_targets__. On any substantial build, the difference in the amount of output generated by make is significant. For example:

$ gmake -pn | wc
 138985 2632330 69612711
$ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
  21673   93437  878985

Execution time was dramatically better as well -- 2.063s for the first version, 0.059s for the second.

Eric Melski
  • 16,432
  • 3
  • 38
  • 52
  • 1
    Neat idea, everyone who works with huge build systems will appreciate it. Stats for Android Gingerbread tree (doesn't use recursive make, so savings not huge, just good): "`time make -pn | wc -l`": `1338738 real 0m47.754s.` "`time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l`": `938621 real 0m40.219s`. – pfalcon Feb 11 '13 at 17:51
  • Good point. The man page for `make` seems to suggest something similar but friendly, namely `make -p -f/dev/null` – Davide Aug 30 '16 at 21:35
  • @Davide that won't quite work: specifying `-f /dev/null` will prevent `make` from parsing the regular makefiles, so you'll get a printout only of the built-in rules. That's why my solution specifies `-f Makefile -f dummy.mk`. But that's not enough either, because with `-n`, `make` will go ahead and actually start executing the rules in the makefile as well. Unfortunately I'm not aware of any modifications that can simplify the solution as presented originally. – Eric Melski Aug 31 '16 at 04:57
  • Of course you are right. However the make man page has a bug, because it states: "To print the data base without trying to remake any files, use make -p -f/dev/null" – Davide Sep 01 '16 at 19:10
  • Slight correction to my previous comment: it should read "... because _without_ `-n` ..." – Eric Melski Sep 03 '16 at 05:19
9

Check out bash completion for make on GitHub.

js.
  • 1,787
  • 19
  • 22
7

Edit: FYI the debian bash completion git repository in another answer now incorporates an enhanced version of this script tailored to bash completion use cases.

#!/bin/bash

SCRIPT='
  /^# Make data base/,/^# Files/d             # skip until files section
  /^# Not a target/,+1          d             # following target isnt
  /^\.PHONY:/                   d             # special target
  /^\.SUFFIXES:/                d             # special target
  /^\.DEFAULT:/                 d             # special target
  /^\.PRECIOUS:/                d             # special target
  /^\.INTERMEDIATE:/            d             # special target
  /^\.SECONDARY:/               d             # special target
  /^\.SECONDEXPANSION/          d             # special target
  /^\.DELETE_ON_ERROR:/         d             # special target
  /^\.IGNORE:/                  d             # special target
  /^\.LOW_RESOLUTION_TIME:/     d             # special target
  /^\.SILENT:/                  d             # special target
  /^\.EXPORT_ALL_VARIABLES:/    d             # special target
  /^\.NOTPARALLEL:/             d             # special target
  /^\.ONESHELL:/                d             # special target
  /^\.POSIX:/                   d             # special target
  /^\.NOEXPORT:/                d             # special target
  /^\.MAKE:/                    d             # special target

# The stuff above here describes lines that are not
#  explicit targets or not targets other than special ones
# The stuff below here decides whether an explicit target
#  should be output.

  /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
    h                                         # hold target
    d                                         # delete line
  }
  /^# File is an intermediate prerequisite/ { # nope
    s/^.*$//;x                                # unhold target
    d                                         # delete line
  }
  /^([^#]|$)/ {                               # end of target block
    s/^.*$//;x                                # unhold target
    s/:.*$//p                                 # write current target
    d                                         # hide any bugs
  }
'

make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
  | sort | uniq

This is a much more complete script than the debian bash completion script because it provides results for generated rules which is what the question asks for and the top voted answer (debian bash completion script at the debian git server) doesn't go far enough.

This is not the original script that I linked to but it is much simpler and is a touch faster.

codeshot
  • 1,183
  • 1
  • 9
  • 20
  • BTW, mods, if this answer isn't fully in line with all policies due to nuance of it's text, please comment before deleting. I will make any lawful adjustments necessary so that this question actually gets a complete and valid answer. – codeshot Aug 26 '12 at 16:52
  • As a quick test I grabbed the gcc source code and ran ./configure && time ~/makecomp.sh | wc -l where ~/makecomp.sh is my answer. ... configure output ... config.status: creating Makefile 3312 real 0m0.401s user 0m0.412s sys 0m0.028s – codeshot Aug 26 '12 at 19:35
  • As another test I used the demo makefile at http://codeshot.blogspot.co.uk/2012/08/this-is-program-that-lists-targets-in.html which generates pseudo-rules for building and checking unit-test passes. This example uses more features of make than a typical cross-platform autoconf project so the script in this answer returns 14 real targets while bash completion for make on ubuntu returns only two useful rules and then 5 source files. – codeshot Aug 27 '12 at 03:26
4

This is the code for alias based on Todd Hodes solution

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

There is a very nice solution with some sed and egrep magic here: https://gist.github.com/pvdb/777954

Atilla Filiz
  • 2,383
  • 8
  • 29
  • 47
3

This will give a nice output:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
Andor
  • 5,523
  • 5
  • 26
  • 24
2

Guess I'm a little late to this party, but if you're looking for a specific command you could try

make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'

This mostly works, although it might still include some non-target stuff like a vpath directive. If you don't depend on make's built-in rules, you can use make -qpR as the first command in the pipeline.

DGrady
  • 1,065
  • 1
  • 13
  • 23
1

Ruby solution:

`make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}
gdub
  • 11
  • 2
1

Im working on solaris 10 anda turbo C shell. The given solution doesn´t work for my makefile project. even after altering the command line above to tcsh syntax. However, I found out you can get an easy solution using

remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´

remake version:

remake --version

is

GNU Make 3.82+dbg0.8
Built for sparc-sun-sparc2-9

and some other unimportant version data

Aviv
  • 414
  • 4
  • 16
0

Found this solution in another thread:

sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\""

You can also add it to your Makefile:

list:
    sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""

And execute make list.

thisismydesign
  • 21,553
  • 9
  • 123
  • 126
0

I went looking for the same question and came up with this spin:

make -pn | sed -rn '/^[^# \t\.%].*:/p'

This removes all comment lines, pattern rules (lines beginning with tabs), all the intrinsics (example .c.o and %.o: %.c patterns).

dwj
  • 3,443
  • 5
  • 35
  • 42
Jamie
  • 7,075
  • 12
  • 56
  • 86
  • didn't read the comment from the accepted answer: **+1 for Jack's answer... gist.github.com/777954 – pvandenberk Jan 13 at 14:47** – Jamie Sep 12 '11 at 14:35
  • Doesn't work, along with the tasks it prints all the environment variables – Mariano Ruiz Jul 05 '21 at 19:44
-1

Sure, but when do you want it to spit them out?

To report the name of the target when it runs the rule, put a line in the rule:

foo$(VAR): $(PREREQS)
    @echo now making the foo target: $@
    do_other_stuff...

To spit them all out at once, you could make a separate PHONY target:

.PHONY: show_vars
show_vars:
    @echo foo$(VAR)
    @echo bar$(PARAM) blah$(FLAG)
    # and so on

And this can be made a prerequisite of your default target:

all: show_vars
    ...

EDIT:
You want a way to show all possible targets of an arbitrary makefile, which I suppose means non-intrusively. Well...

To do it exactly, and be able to cope with sophisticated makefiles, e.g. involving rules constructed by eval statements, you'd have to write something close to a Make emulator. Impractical.

To see the targets of the simple rules, you could write a makefile that would act as a makefile scanner, operating on an arbitrary makefile:

  1. Get all the target names from the makefile using sed.
  2. `include` the makefile in order to use it to expand variables.
  3. Use `show_%: ; echo $$*` to print all the targets

This would be an impressive piece of work. Are you sure the goal is worth the effort?

Beta
  • 96,650
  • 16
  • 149
  • 150
  • Not quite what I want. I would like to be able to make make spit out a list of targets for an arbitrary makefile that I may have. I am trying to write an argument completion function for my shell and I can't rely on the makefiles having any sensible help. – BitShifter Jun 22 '10 at 17:29
-4
grep ^[a-z].*\:$ Makefile | sed s,:,,g
animuson
  • 53,861
  • 28
  • 137
  • 147