在 GNU make 中列出定义中包含变量的目标/目标
Posted
技术标签:
【中文标题】在 GNU make 中列出定义中包含变量的目标/目标【英文标题】:List goals/targets in GNU make that contain variables in their definition 【发布时间】:2011-03-05 01:24:54 【问题描述】:我有一个相当大的 makefile,它通过计算变量的名称来动态创建许多目标。 (例如 foo$(VAR) : $(PREREQS))。有什么方法可以说服 gnu make 在扩展这些变量后吐出目标列表?
我希望能够获得任意生成文件的目标。我正在尝试为我的 shell 编写一个完成函数。
【问题讨论】:
【参考方案1】:make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ split($1,A,/ /);for(i in A)print A[i]'
取自 make arg 补全,它的作用就像一个魅力。
【讨论】:
我试图为此创建一个别名,将其放在双引号中,但这只会导致 awk 在第一个逗号上出现语法错误......关于如何正确转义这个的任何想法外壳(bash)别名? 我认为它与它的定义位置有关。我认为如果将其定义为别名,则其工作目录将成为定义它的目录,并且显然 awk 部分不喜欢隐藏目录中的点。定义一个函数是可行的。 我只是将它放在~/bin
的shell 脚本中,而不是使用别名。效果很好;谢谢!
别名 alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ split(\$1,A,/ /);for(i in A)print A[i]' | sort"
可以为您节省 2 分钟的引用游戏时间:-)
| sort -u
看起来更有用:)【参考方案2】:
你能解析来自make -pn
(即make --print-data-base --dry-run
)的输出吗?它打印出所有变量、规则、隐式规则以及哪些命令将被详细运行。
【讨论】:
这个答案看起来比另一个好。 我同意这样更好。仍然比我希望的要多,但我将把它标记为答案,因为似乎没有其他任何事情是合理的,而且 GNU make 没有我想要的方便标志。 请注意,如果您的 Makefile 动态生成目标,则输出可能取决于您指定的目标。输出也可能取决于环境变量的值或使用 make 命令指定的 make 变量。 我喜欢简洁。 +1 添加了我自己的旋转(在下面的答案中描述):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
建议make -pnr
。 -r
删除了隐式规则。【参考方案3】:
我不确定这是否只是一个 gnu 制作的东西,但这很好用:
make help
【讨论】:
这非常方便,比其他方法之一的自定义别名更容易记住。 没有真正在 GNU Make 3.81 上工作,可能是你的 makefile 中的一个目标:“make: *** No rule to make target `help'. Stop” 这只是调用 Makefile 中的“帮助”目标。如果存在,它将打印一些内容(不是完整目标列表),如果不存在(典型情况),它将退出。 好消息是 cmake 似乎生成了一个很好的易于阅读的“帮助”目标。 这太棒了! :) 去 cmake【参考方案4】:一些响应者建议使用make -pn
,它将打印规则数据库但不执行任何操作——或多或少。这种方法的问题是-n
仍然会调用所有递归make,并且它仍然做的工作比必要的要多得多,因为它会打印出它会在常规构建中调用的每个命令。更有效的解决方案是创建一个简单的 makefile,dummy.mk,包含以下内容:
__all_targets__: ; #no-op
现在调用 make 为 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.063 秒,第二个版本为 0.059 秒。
【讨论】:
好主意,每个使用大型构建系统的人都会喜欢它。 android Gingerbread 树的统计信息(不使用递归生成,因此节省的资金不多,只是很好):“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
。
好点。 make
的手册页似乎暗示了类似但友好的内容,即 make -p -f/dev/null
@Davide 不太有效:指定-f /dev/null
将阻止make
解析常规makefile,因此您将仅获得内置规则的打印输出。这就是我的解决方案指定-f Makefile -f dummy.mk
的原因。但这还不够,因为有了-n
,make
将继续执行并实际开始执行makefile 中的规则。不幸的是,我不知道有任何修改可以简化最初提出的解决方案。
你当然是对的。但是 make 手册页有一个错误,因为它指出:“要打印数据库而不尝试重新制作任何文件,请使用 make -p -f/dev/null”
对我之前的评论稍作修正:它应该是“...因为 without -n
...”【参考方案5】:
查看 GitHub 上的 bash completion for make。
【讨论】:
您提供的链接现已损坏 更新了当前版本的链接。【参考方案6】:编辑:仅供参考,另一个答案中的 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,如果由于文本的细微差别,此答案不完全符合所有政策,请在删除前发表评论。我会做出任何必要的合法调整,以便这个问题真正得到一个完整有效的答案。 作为一个快速测试,我获取了 gcc 源代码并运行了 ./configure && time ~/makecomp.sh | wc -l 其中 ~/makecomp.sh 是我的答案。 ...配置输出... config.status:创建Makefile 3312 real 0m0.401s user 0m0.412s sys 0m0.028s 作为另一个测试,我使用了codeshot.blogspot.co.uk/2012/08/… 的演示makefile,它生成了用于构建和检查单元测试通过的伪规则。这个例子比典型的跨平台 autoconf 项目使用了更多的 make 特性,所以这个答案中的脚本返回 14 个真实目标,而 ubuntu 上 make 的 bash 完成只返回两个有用的规则,然后是 5 个源文件。【参考方案7】:这是基于 Todd Hodes 解决方案的别名代码
alias mtargets='make -qp | awk -F":" "/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ split(\$1,A,/ /);for(i in A)print A[i]"'
【讨论】:
【参考方案8】:这将给出一个很好的输出:
make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
【讨论】:
【参考方案9】:这里有一个非常好的解决方案,其中包含一些 sed 和 egrep 魔法: https://gist.github.com/pvdb/777954
【讨论】:
【参考方案10】:我想我参加这个聚会有点晚了,但如果你正在寻找一个特定的命令,你可以试试
make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'
这主要是有效的,尽管它可能仍然包含一些非目标内容,如vpath
指令。如果您不依赖make
的内置规则,您可以使用make -qpR
作为管道中的第一个命令。
【讨论】:
我用 freetz (.org) 试过了。列出了很多包含的 makefile,但没有列出所有目标。【参考方案11】:Ruby 解决方案:
`make -qp`.split("\n").select|l| l =~ /^\w/ && l !~ /=/.map|l| l.sub(/:.*/,'')
【讨论】:
【参考方案12】:我正在开发 solaris 10 和 turbo C shell。 给定的解决方案不适用于我的 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
以及其他一些不重要的版本数据
【讨论】:
【参考方案13】:我去寻找同样的问题并想出了这个问题:
make -pn | sed -rn '/^[^# \t\.%].*:/p'
这将删除所有注释行、模式规则(以制表符开头的行)、所有内在函数(例如 .c.o
和 %.o: %.c
模式)。
【讨论】:
没有从接受的答案中阅读评论:+1 表示杰克的答案... gist.github.com/777954 – pvandenberk 1 月 13 日 14:47 不起作用,连同它打印所有环境变量的任务【参考方案14】:在另一个帖子中找到了这个解决方案:
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
。
【讨论】:
【参考方案15】:当然,但是你希望它什么时候吐出来?
要在运行规则时报告目标的名称,请在规则中添加一行:
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,例如涉及由eval
语句构造的规则,您必须编写一些接近 Make 模拟器的东西。不切实际。
要查看简单规则的目标,您可以编写一个充当生成文件扫描器的生成文件,对任意生成文件进行操作:
-
使用 sed 从 makefile 中获取所有目标名称。
`include` 生成文件以便使用它来扩展变量。
使用`show_%: ; echo $$*` 打印所有目标
这将是一件令人印象深刻的作品。您确定目标值得付出努力吗?
【讨论】:
不是我想要的。我希望能够让 make 为我可能拥有的任意 makefile 吐出目标列表。我正在尝试为我的 shell 编写一个参数完成函数,但我不能依赖 makefile 有任何明智的帮助。【参考方案16】:grep ^[a-z].*\:$ Makefile | sed s,:,,g
【讨论】:
-1:即使对于简单的变量赋值 (:=
),这种方法也会产生误报。
你应该先测试一下。不过,你尝试一下很好。以上是关于在 GNU make 中列出定义中包含变量的目标/目标的主要内容,如果未能解决你的问题,请参考以下文章