列出GNU make中包含在其定义中包含变量的目标/目标


100

我有一个相当大的makefile,它通过计算变量的名称来动态创建多个目标。(例如foo $(VAR):$(PREREQS))。gnu make在扩展了这些变量之后,是否可以说服他们吐出目标列表?

我希望能够获取任意makefile的目标。我正在尝试为我的shell写一个完成函数。

Answers:


79

您可以解析make -pnmake --print-data-base --dry-run)的输出吗?它打印出所有变量,规则,隐式规则以及要费力的细节运行哪些命令。


这个答案看起来比另一个更好。
马特·乔纳

我同意这要好得多。仍然比我希望的要多得多,但是我将其标记为答案,因为似乎没有其他合理的选择,并且GNU make并没有方便的标记来代表我想要的东西。
BitShifter 2010年

6
请注意,如果Makefile动态生成目标,则输出可能取决于您指定的目标。输出还可能取决于环境变量的值或使用make命令指定的make变量。
reinierpost 2011年

6
我喜欢简洁。+1添加了我自己的旋转(在下面的答案中描述):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
Jamie

建议make -pnr。该-r删除隐含规则。
sjbx

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

取自make arg补全,其工作原理像一个魅力。


2
我试图为此创建一个别名,将其放在双引号中,但这只会导致awk在第一个逗号处出现语法错误...关于如何正确地为shell(bash)别名转义的任何想法?
drfrogsplat

3
我认为这与定义位置有关。我认为,如果将其定义为别名,则其工作目录将成为其定义的目录,而且awk部分显然不喜欢隐藏目录中的点。定义功能有效。
Ibrahim 2012年

我只是将其放在shell脚本中,~/bin而不是使用别名。效果很好;谢谢!
mpontillo

3
别名alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort"将为您节省2分钟的报价游戏时间:-)
西罗(Ciro Santilli)郝海东冠状病六四事件法轮功

1
| sort -u看起来更有用:)
sherpya 2015年

14

我不确定这是否只是一个gnu make东西,但是效果很好:

make help


这很方便,比其他方法之一的自定义别名更容易记住。
Ibrahim 2012年

8
不能真正在GNU Make 3.81上工作,可能是您的makefile中的目标:“ make:***没有使目标“帮助”的规则。停止”
a1an

8
这只是调用Makefile中的“帮助”目标。如果存在,它将打印某些内容(不是目标的完整列表),如果不存在(典型情况),则将退出紧急状态。
pfalcon

2
好消息是,cmake似乎生成了一个易于阅读的“帮助”目标。
斯特凡

这太棒了!:) Go cmake
Jeef

10

一些响应者建议使用make -pn,它将打印规则数据库,但不执行任何操作-或多或少。这种方法的问题在于,-n它仍然会调用所有递归的make,并且仍然做比必要的事情更多的工作,因为它会打印出它将在常规构建中调用的每个命令。一个更有效的解决方案是使用以下内容创建一个普通的makefile:dummy.mk:

__all_targets__: ; #no-op

现在调用make as make -p -f Makefile -f dummy.mk __all_targets__。在任何实质性构建上,make生成的输出量之间的差异都很大。例如:

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

执行时间也明显更好-第一个版本为2.063s,第二个版本为0.059s。


1
简洁的主意,使用大型构建系统的每个人都会喜欢它。Android的姜饼树统计(不使用递归的化妆,所以储蓄并不大,刚刚好):“ 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

好点子。的手册页make似乎暗示了类似但友好的内容,即make -p -f/dev/null
Davide

@Davide不太有效:指定-f /dev/null将阻止make对常规makefile的解析,因此您将仅获得内置规则的打印输出。这就是我的解决方案指定的原因-f Makefile -f dummy.mk。但这还不够,因为有了-nmake将继续并实际上也开始执行makefile中的规则。不幸的是,我不知道有任何修改可以简化最初介绍的解决方案。
埃里克·梅尔斯基

你当然是对的。但是,make手册页有一个错误,因为它指出:“要打印数据库而不尝试重新制作任何文件,请使用make -p -f / dev / null”
Davide

略微修正了我的先前评论:它应显示为“ ...因为没有 -n ...”
埃里克·梅尔斯基


7

编辑:仅供参考,另一个答案中的debian bash完成git存储库现在结合了针对bash完成用例量身定制的此脚本的增强版本。

#!/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

这是一个比debian bash完成脚本更完整的脚本,因为它提供了生成规则的结果,这是问题所要的,并且投票最高的答案(debian git服务器上的debian bash完成脚本)还远远不够。

这不是我链接到的原始脚本,但是它更简单并且触感也更快。


顺便说一句,mods,如果由于文本的细微差别,此答案与所有​​政策都不完全一致,请在删除前发表评论。我将进行必要的合法调整,以使此问题真正得到完整有效的答案。
2012年

作为快速测试,我获取了gcc源代码并运行了./configure && time〜/ makecomp.sh | wc -l其中〜/ makecomp.sh是我的答案。...配置输出... config.status:创建Makefile 3312真实0m0.401s用户0m0.412s sys 0m0.028s
截图

作为另一项测试,我使用了位于codeshot.blogspot.co.uk/2012/08/…的演示生成文件,该文件生成用于构建和检查单元测试通过的伪规则。这个示例比典型的跨平台autoconf项目使用了更多的make功能,因此此答案中的脚本返回14个实际目标,而在ubuntu上进行make的bash完成仅返回两个有用的规则,然后返回5个源文件。
2012年

4

这是基于Todd Hodes解决方案的别名代码

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

3

这将提供一个不错的输出:

make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort


2

猜猜我参加这个聚会有点晚了,但是如果您正在寻找特定的命令,可以尝试

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

尽管它可能仍然包括一些非目标内容(例如vpath指令),但这大部分都可行。如果您不依赖make的内置规则,则可以make -qpR用作管道中的第一个命令。


我尝试用freetz(.org)。列出许多包含的makefile,但未列出所有目标。
罗伯特·西默

1

Ruby解决方案:

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

1

我正在研究solaris 10和turbo C外壳。给定的解决方案不适用于我的makefile项目。即使将上面的命令行更改为tcsh语法也是如此。但是,我发现您可以使用以下方法获得简单的解决方案

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

重制版本:

remake --version

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

和其他一些不重要的版本数据


0

我去寻找相同的问题,并提出了这个旋转:

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

这将删除所有注释行,模式规则(以制表符开头的行),所有内在函数(示例.c.o%.o: %.c模式)。


没有阅读已接受答案的评论:杰克的答案+1 ... gist.github.com/777954 – pvandenberk 1月13日在14:47
Jamie

0

在另一个线程中找到了此解决方案:

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

您也可以将其添加到您的Makefile

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

并执行make list


-1

当然可以,但是您什么时候要它吐出来呢?

要在目标运行规则时报告目标名称,请在规则中添加一行:

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

要一次吐出所有内容,您可以设置一个单独的PHONY目标:

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

这可以成为默认目标的先决条件:

all: show_vars
    ...

编辑:
您想要一种显示任意makefile的所有可能目标的方法,我想这是非侵入性的。好...

为了做到这一点,并能够处理复杂的makefile,例如涉及由eval语句构造的规则,您必须编写接近Make仿真器的内容。不切实际的。

要查看简单规则的目标,您可以编写一个可以作为makefile扫描程序的makefile,并在任意makefile上运行:

  1. 使用sed从makefile获取所有目标名称。
  2. 包括makefile以便使用它来扩展变量。
  3. 使用`show_%:; 回显$$ *`以打印所有目标

这将是一项令人印象深刻的工作。您确定目标值得努力吗?


不完全是我想要的。我希望能够使make吐出我可能拥有的任意makefile的目标列表。我试图为我的外壳编写一个参数完成函数,但是我不能依赖makefile的任何明智帮助。
BitShifter

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.