如何获得makefile中的目标列表?
我已经使用了一些rake(一个Ruby make程序),并且有一个选项来获得所有可用的目标列表,例如
> 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... ...
但在GNU make中似乎没有这样做的select。
显然,代码几乎就在这里,截至2007年 – http://www.mail-archive.com/help-make@gnu.org/msg06434.html 。
无论如何,我做了一些小技巧来从makefile中提取目标,你可以在makefile中包含目标。
list: @grep '^[^#[:space:]].*:' Makefile
它会给你一个定义的目标列表。 这只是一个开始 – 例如,它不会过滤掉依赖关系。
> make list list: copy: run: plot: turnin:
这是为了改进@ nobar的伟大做法 ,如下所示:
- 使用更强大的命令来提取目标名称,希望能够防止任何误报(并且也消除了不必要的
sh -c
) - 并不总是以当前目录中的makefile为目标; 尊重使用
-f <file>
显式指定的makefile - 排除隐藏的目标 – 按照惯例,这些目标的名字既不以字母也不以数字开头
- 使一个单一的假目标
- 用
@
作为前缀,以防止在执行前被回显
奇怪的是,GNU make
没有列出makefile中定义的目标名称的function。 -p
选项会生成包含所有目标的输出,但会将其embedded许多其他信息中。
将下面的规则放在GNU make
的makefile中,以实现一个目标命名list
,它简单地按字母顺序列出所有目标名称,即:作为make list
调用 :
.PHONY: list list: @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs
注意:在粘贴时,确保最后一行缩进一个制表符 。
请注意, sorting结果列表的目标是最好的select,因为不sorting不会产生一个有用的sorting,目标出现在生成文件中的顺序不会保留。
而且,包含多个目标的规则的子目标总是单独输出,因此,由于sorting,通常不会彼此相邻; 例如,以az:
开始的规则将不会有输出中彼此相邻的目标a
和z
(如果有其他目标)。
规则说明 :
- 。
PHONY: list
- 将目标列表声明为假目标,也就是说不指向文件 ,因此无条件调用其配方
-
$(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null
- 再次调用
make
来打印和parsing从makefile派生的数据库:-
-p
打印数据库 -
-Rr
禁止包含内置的规则和variables -
-q
只testing目标的最新状态(不重做任何东西),但是它本身并不妨碍在所有情况下执行配方命令。 因此: -
-f $(lastword $(MAKEFILE_LIST))
确保相同的生成文件的目标位置与原始调用的目标位置相同,无论该目标是隐式还是明确使用-f ...
警告 :如果你的makefile包含include
指令,这将会中断。 为了解决这个问题, 在任何include
指令之前定义variablesTHIS_FILE := $(lastword $(MAKEFILE_LIST))
,并使用-f $(THIS_FILE)
。 -
:
是一个故意无效的目标 ,旨在确保没有命令执行 ;2>/dev/null
禁止生成的错误消息。 注意:这依赖于-p
打印数据库,这是GNU make 3.82的情况。 不幸的是,GNU make没有提供直接打印数据库的选项。
-
- 再次调用
-
-v RS=
- 这是一个awk成语,将input分成连续的非空行块。
-
/^# File/,/^# Finished Make data base/
- 匹配包含所有目标(GNU make 3.82为true)的输出行的范围 – 通过将parsing限制在此范围内,不需要处理来自其他输出节的误报。
-
if ($$1 !~ "^[#.]")
- select性忽略块:
-
#
…忽略非目标,其块以# Not a target:
开始# Not a target:
-
.
忽略特殊的目标
-
- 所有其他块都应该以一行仅包含明确定义的目标名称的行开始,后跟
:
- select性忽略块:
-
egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
从输出中删除不想要的目标:-
'^[^[:alnum:]]'
…排除隐藏的目标,按照惯例,这些隐藏目标是既不以字母也不以数字开始的目标。 -
'^$@$$'
…排除list
目标本身
-
-
xargs
- 有效地将输出行转换为单行,空格分隔的列表; 如果您希望每个目标名称都出现在自己的行上,请省略此项。
在Bash(至less)下,这可以通过tab完成自动完成:
make(space)(tab)(tab)
我结合了这两个答案: https : //stackoverflow.com/a/9524878/86967和https://stackoverflow.com/a/7390874/86967 ,并做了一些转义,以便这可以从一个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
如果你安装了bash完成,完成脚本将定义一个函数_make_target_extract_script
。 这个函数是为了创build一个sed
脚本,可以用来获取列表中的目标。
像这样使用它:
# Make sure bash completion is enabled source /etc/bash_completion # List targets from Makefile sed -nrf <(_make_target_extract_script --) Makefile
@ nobar的回答有助于显示如何使用制表符完成来列出makefile的目标 。
-
这对默认情况下提供此function的平台(例如, Debian,Fedora )非常有用。
-
在其他平台上(例如Ubuntu ),你必须明确加载这个function,正如@ hek2mgl的答案所暗示的那样 :
-
. /etc/bash_completion
. /etc/bash_completion
安装了几个tab完成function,包括make
- 或者,要仅安装
make
制表符完成:-
. /usr/share/bash-completion/completions/make
-
-
- 对于根本不提供此function的平台 ,例如OSX ,可以使用以下命令来实现它:
_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
- 注意:这不像Linux发行版附带的制表符完成function那样复杂:最明显的是,它始终以当前目录中的makefile为目标 ,即使命令行使用
-f <file>
定位到不同的生成-f <file>
。
这在许多情况下显然不起作用,但是如果你的Makefile
是由CMake创build的,你可能可以运行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
正如mklement0所指出的那样 ,GNU-make中没有列出所有Makefile目标的function,他的答案和其他方法提供了这种方法。
但是,原帖也提到了rake ,其中的任务切换与rakefile中的所有任务略有不同。 耙将只会给你一个有相关描述的任务列表。 没有描述的任务将不会被列出 。 这使作者既能够提供定制的帮助描述,也能省略某些目标的帮助。
如果你想模拟耙子的行为,你为每个目标提供描述 ,这里有一个简单的方法:在你想要列出的每个目标的注释中embedded描述。
您可以将描述放在目标旁边,或者像往常一样,在目标上方的PHONY规范旁边,如下所示:
.PHONY: target1 # Target 1 help text target1: deps @echo "Target 1" .PHONY: target2 # Target 2 help text target2: @echo "Target 2" ... .PHONY: help # Generate list of targets with descriptions help: @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20
哪个会屈服
$ make help target1 Target 1 help text target2 Target 2 help text ... help Generate list of targets with descriptions
你也可以在这里find一个简短的代码示例。
同样,这并不能解决在Makefile中列出所有目标的问题。 例如,如果你有一个大的Makefile可能被生成或者别人写了,你想要一个快速的方式来列出它的目标而不用挖掘它,这将无济于事。
但是,如果你正在编写一个Makefile文件,并且你想要一种以一致的,自我logging的方式生成帮助文本的方法,那么这种技术可能是有用的。
这个对我来说很有帮助,因为我希望通过make目标来看到所需的构build目标(及其依赖关系)。 我知道,制定目标不能以“。”开头。 字符。 我不知道支持什么语言,所以我使用了egrep的括号expression式。
cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
这不是干净的,而是为我做的工作。
make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1
我使用make -p
转储内部数据库,沟渠stderr,使用快速和脏grep -A 100000
来保持输出的底部。 然后我用一对grep -v
清理输出,最后使用cut
来得到冒号之前的内容,即目标。
这对我的大多数Makefiles的帮手脚本来说已经足够了。
编辑:添加grep -v Makefile
这是一个内部的规则
不知道为什么以前的答案是如此复杂:
list: cat Makefile | grep "^[Az]" | awk '{print $$1}' | sed "s/://g"