如何获得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:开始的规则将不会有输出中彼此相邻的目标az (如果有其他目标)。

规则说明

  • 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:
      • . 忽略特殊的目标
    • 所有其他块都应该以一行仅包含明确定义的目标名称的行开始,后跟:
  • 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"