你如何获得makefile中的目标列表?

问题描述 投票:160回答:13

我已经使用了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中执行此操作。

显然,代码几乎就在那里,截至2007年 - http://www.mail-archive.com/[email protected]/msg06434.html

无论如何,我几乎没有从makefile中提取目标,你可以将其包含在makefile中。

list:
    @grep '^[^#[:space:]].*:' Makefile

它将为您提供已定义目标的列表。这只是一个开始 - 例如,它不会过滤掉依赖项。

> make list
list:
copy:
run:
plot:
turnin:
makefile gnu-make targets
13个回答
95
投票

这是尝试改进@nobar's great approach如下:

  • 使用更强大的命令来提取目标名称,这有望防止任何误报(并且还可以消除不必要的sh -c
  • 并不总是以当前目录中的makefile为目标;尊重使用-f <file>明确指定的makefile
  • 排除隐藏目标 - 按惯例,这些目标的名称既不是字母也不是数字
  • 使用单个虚假目标
  • 使用@为命令添加前缀,以防止它在执行前被回显

奇怪的是,GNU make没有列出makefile中定义的目标名称的功能。 -p选项生成包含所有目标的输出,但将其隐藏在许多其他信息中。

将以下规则放在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 '^$@$$'

重要说明:粘贴此内容时,请确保最后一行缩进1个实际的tab char。 (空格不起作用)。

请注意,对结果目标列表进行排序是最佳选择,因为不排序不会产生有用的排序,因为不保留目标在makefile中出现的顺序。 此外,包含多个目标的规则的子目标总是单独输出,因此,由于分类,通常不会彼此相邻;例如,如果有其他目标,则以a z:开头的规则将不会在输出中将目标az列在彼此旁边。

规则说明:

  • .PHONY: list 声明目标列表伪目标,即不引用文件的目标,因此应无条件地调用其配方
  • $(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null 再次调用make以打印和解析从makefile派生的数据库: -p打印数据库 -Rr禁止包含内置规则和变量 -q只测试目标的最新状态(不重新制作任何东西),但这本身并不妨碍在所有情况下执行配方命令;因此: -f $(lastword $(MAKEFILE_LIST))确保在原始调用中定位相同的makefile,无论它是使用-f ...隐式还是显式定向。 警告:如果你的makefile包含include指令,这将会中断;为了解决这个问题,在任何THIS_FILE := $(lastword $(MAKEFILE_LIST))指令之前定义变量include并使用-f $(THIS_FILE)代替。 :是故意无效的目标,旨在确保不执行任何命令; 2>/dev/null抑制产生的错误消息。注意:这依赖于-p打印数据库,这是GNU make 3.82的情况。遗憾的是,GNU make没有提供直接打印数据库的选项,也没有执行默认(或给定)任务;如果您不需要定位特定的Makefile,可以按照make -p -f/dev/null页面中的建议使用man
  • -v RS= 这是一个awk成语,它将输入分解为连续的非空行块。
  • /^# File/,/^# Finished Make data base/ 匹配输出中包含所有目标的行范围(GNU make 3.82时为true) - 通过将解析限制到此范围,不需要处理来自其他输出节的误报。
  • if ($$1 !~ "^[#.]") 选择性地忽略块: # ...忽略非目标,其阻挡以# Not a target:开头 . ......无视特殊目标 所有其他块应各自以一行开头,该行仅包含明确定义的目标的名称,后跟:
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$'从输出中删除不需要的目标: '^[^[:alnum:]]' ......排除隐藏的目标,按照惯例,这些目标既不是字母也不是数字。 '^$@$$' ......排除了list目标本身

然后运行make list打印所有目标,每个目标都在自己的行上;您可以通过管道传输到xargs来创建一个以空格分隔的列表。


1
投票

这里有很多可行的解决方案,但正如我喜欢说的那样,“如果它值得做一次,那就值得再做一次。”我做了upvote sugestion使用(tab)(tab),但有些人已经注意到,你可能没有完成支持,或者,如果你有很多包含文件,你可能想要一种更简单的方法来知道目标的定义。

我没有测试下面的副产品......我认为它不会起作用。众所周知,递归会被认为是有害的。

.PHONY: list ls
ls list :
    @# search all include files for targets.
    @# ... excluding special targets, and output dynamic rule definitions unresolved.
    @for inc in $(MAKEFILE_LIST); do \
    echo ' =' $$inc '= '; \
    grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
    cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
    tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
; done

# to get around escaping limitations:
open_paren := \(
close_paren := \)

Which I like because:

  • 按包含文件列出目标。
  • 输出原始动态目标定义(用模数替换变量分隔符)
  • 在新线上输出每个目标
  • 似乎更清楚(主观意见)

Explanation:

  • MAKEFILE_LIST中的foreach文件
  • 输出文件的名称
  • 包含冒号的grep行,不缩进,不是注释,并且不以句点开头
  • 排除立即赋值表达式(:=)
  • 剪切,排序,缩进和切断规则依赖(冒号后)
  • munge变量分隔符以防止扩展

样本输出:

 = Makefile = 
  includes
  ls list
 = util/kiss/snapshots.mk = 
  rotate-db-snapshots
  rotate-file-snapshots
  snap-db
  snap-files
  snapshot
 = util/kiss/main.mk = 
  dirs
  install
   %MK_DIR_PREFIX%env-config.php
   %MK_DIR_PREFIX%../srdb

1
投票

这是对jsp非常有用的答案(https://stackoverflow.com/a/45843594/814145)的修改。我喜欢不仅获得目标列表而且还获取其描述的想法。 jsp的Makefile将描述作为注释,我发现它经常会在目标的描述echo命令中重复。因此,我从每个目标的echo命令中提取描述。

示例Makefile:

.PHONY: all
all: build
        : "same as 'make build'"

.PHONY: build
build:
        @echo "Build the project"

.PHONY: clean
clean:
        @echo "Clean the project"

.PHONY: help
help:
        @echo -n "Common make targets"
        @echo ":"
        @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20

make help的输出:

$ make help
Common make targets:
    make all        same as 'make build'
    make build      Build the project
    make clean      Clean the project
    make help       Common make targets

笔记:

  • jsp的答案相同,只列出了PHONY目标,这可能适用于您的情况,也可能不适用
  • 此外,它仅列出那些具有echo:命令的PHONY目标作为配方的第一个命令。 :的意思是“什么都不做”。我在这里用它来表示那些不需要回声的目标,例如上面的all目标。
  • help目标还有一个额外的技巧,即在make help输出中添加“:”。

0
投票

上面的另一个补充答案。

在MacOSX上仅使用cat和awk在终端上测试

cat Makefile | awk '!/SHELL/ && /^[A-z]/ {print $1}' | awk '{print substr($0, 1, length($0)-1)}'

将输出make文件,如下所示:

目标1

TARGET2

target3

在Makefile中,它应该是相同的语句,确保使用$$变量而不是$ variable来转义变量。

说明

猫 - 吐出内容

| - pipe将输出解析为下一个awk

awk - 运行正则表达式,不包括“shell”,只接受“A-z”行,然后输出$ 1第一列

awk - 再次从列表中删除最后一个字符“:”

这是一个粗略的输出,你可以用AWK做更多时髦的东西。尽量避免使用sed,因为它在BSD变体中并不一致,即一些在* nix上工作但在像MacOSX这样的BSD上失败。

更多

您应该能够将此(带有修改)添加到make文件中,默认为bash-completion文件夹/usr/local/etc/bash-completion.d/,这意味着当您“制作选项卡选项卡”时,它将完成基于一个班轮脚本的目标。


-4
投票

不确定为什么以前的答案是如此复杂:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g" 

156
投票

在Bash(至少)下,这可以通过选项卡完成自动完成:

make(space)(tab)(tab)

24
投票

我将这两个答案结合起来:https://stackoverflow.com/a/9524878/86967https://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

19
投票

这显然在许多情况下不起作用,但如果你的Makefile是由CMake创建的,你可能能够运行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

13
投票

如果你安装了make的bash完成,则完成脚本将定义一个函数_make_target_extract_script。此函数用于创建sed脚本,该脚本可用于将目标作为列表获取。

像这样使用它:

# Make sure bash completion is enabled
source /etc/bash_completion 

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile

9
投票

作为mklement0 points out,GNU-make缺少列出所有Makefile目标的功能,他的回答和其他提供了实现此目的的方法。

然而,原帖还提到了rake,其任务切换与仅列出rakefile中的所有任务略有不同。 Rake只会为您提供具有相关描述的任务列表。没有描述的任务will not be listed。这使作者既能提供自定义帮助描述,又能省略某些目标的帮助。

如果你想模仿rake的行为,你提供descriptions for each target,有一个简单的技巧:在你想要列出的每个目标的注释中嵌入描述。

您可以将描述放在目标旁边,或者像往常一样,将其放在目标上方的PHONY规范旁边,如下所示:

.PHONY: target1 # Target 1 help text
target1: deps
    [... target 1 build commands]

.PHONY: target2 # Target 2 help text
target2:
    [... target 2 build commands]

...                                                                                                         

.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

您还可以在this gisthere中找到一个简短的代码示例。

同样,这并没有解决在Makefile中列出所有目标的问题。例如,如果你有一个可能生成的大型Makefile或其他人写的,你想要一个快速的方法来列出它的目标而不需要深入研究它,这将无济于事。

但是,如果您正在编写Makefile,并且希望以一致的自我文档方式生成帮助文本,则此技术可能很有用。


4
投票

@nobar's answer帮助显示如何使用制表符完成列出makefile的目标。

  • 这适用于默认情况下提供此功能的平台(例如,Debian,Fedora)。
  • 在其他平台(例如,Ubuntu)上,您必须显式加载此功能,如@hek2mgl's answer所暗示的: . /etc/bash_completion安装了几个tab-completion函数,包括make的函数 或者,要仅安装make的制表符完成: . /usr/share/bash-completion/completions/make
  • 对于根本不提供此功能的平台,例如OSX,您可以使用以下命令(从here调整)来实现它:
_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发行版附带的制表符完成功能那么复杂:最值得注意的是,它总是以当前目录中的makefile为目标,即使命令行使用-f <file>定位不同的makefile也是如此。

1
投票

这个对我有帮助,因为我想通过make目标看到所需的构建目标(及其依赖项)。我知道制作目标不能以“。”开头。字符。我不知道支持哪种语言,所以我选择了egrep的括号表达式。

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

1
投票

这对我来说远非干净,但是做了这项工作。

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来获取冒号之前的内容,即目标。

这对我的大多数Makefile上的助手脚本来说已经足够了。

编辑:添加grep -v Makefile这是一个内部规则

© www.soinside.com 2019 - 2024. All rights reserved.