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


202

我使用过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/help-make@gnu.org/msg06434.html

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

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

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

> make list
list:
copy:
run:
plot:
turnin:

2
我很快就读了一些有趣的答案,但是到目前为止,对于我自己来说,我更喜欢使用别名(在我的.bashrc中)使它简单明了,可移植: alias makefile-targets='grep "^[^#[:space:]].*:" Makefile' 大多数情况下,我只需要检查当前的makefile,bash的完成范围就会扩大我的别名。
RockyRoad


4
只是太琐碎了grep : Makefile吗?
cibercitizen1

Answers:


130

这是尝试改进@nobar的出色方法,如下所示:

  • 使用更强大的命令来提取目标名称,从而有望防止任何误报(并消除了不必要的sh -c
  • 不会始终将目标放在当前目录中的makefile中;尊重明确指定的makefile-f <file>
  • 排除隐藏目标-按照惯例,这些目标的名称均不以字母或数字开头
  • 品牌做一个单一的假目标
  • 给命令加上前缀,@以防止在执行前被回显

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

将以下规则放在用于GNU的makefile中,make以实现一个名为目标的目标list,该目标只是按字母顺序列出所有目标名称-即:invoke asmake 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个实际制表符。(空格也没有工作)

注意,对目标结果列表进行排序是最好的选择,因为不进行排序不会产生有用的排序,因为不会保留目标在makefile中出现的顺序。
同样,包含多个目标的规则的子目标始终不变地单独输出,因此,由于排序,通常不会彼此相邻出现;例如,如果有其他目标,则以开头的规则a z:没有目标,a并且在输出中彼此相邻z列出。

规则说明

  • PHONY: list
    • 将目标列表声明为伪目标,即引用文件的目标,因此应无条件调用其配方
  • $(MAKE) -pRrq -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要约没有直接的选项,只是打印数据库,不执行默认(或给出)任务; 如果您不需要定位特定的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创建一个以空格分隔的列表。


这是一个很好的答案,但是,我如何获得每个任务的描述?目前,我将其存储为任务定义上方的注释。不确定是否有可能以与之类似的方式将其包括在列表中rake --tasks
Piotr Kuczynski

1
@PiotrKuczynski:有趣的问题,但是我建议您将其发布为问题(指向该问题以供参考)。
mklement0

“不幸的是,GNU make没有提供直接打印数据库的直接选择。” 有一个-n选择。
子弹头

@Bulletmagnet:该-n选项的目的是“打印将要执行的命令...”-换句话说:空运行功能。也并不是说,你的报价缺少字斜体make -p本身确实打印数据库,但总是还运行默认任务 ; 手册建议笨拙的make -p -f/dev/null人避免这种情况。
mklement0

1
还将列出任何.PHONY目标,因此请确保您没有.PHONY: *
tekumara

189

在Bash(至少)下,可以使用制表符自动完成:

make spacetabtab


1
经过测试:bash --version = 4.2.45(1)-发布。使--version = 3.81。
2014年

53
+1,但请注意,这不是bash功能,但取决于您的平台。平台分为三类:默认情况下安装了此功能的平台(例如Debian 7,Fedora 20),可以选择安装的平台(例如Ubuntu 14,带有. /etc/bash_completion)以及根本不支持它的平台。 (例如OSX 10.9)
mklement0 2014年

8
它确实适用于OS X 10.10(不适用于bash但适用于zsh)。
弗洛里安·奥斯瓦尔德

4
大提示,但也需要bash-completion包装。在Fedora,RHEL,Gentoo等处找到。
NoelProf '16

4
这不适用于以编程方式创建的制造目标AFAICT,例如$(RUN_TARGETS): run-%: ...
Matthias

31

这显然在许多情况下不起作用,但是如果您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

您是否有任何选择要在CMakeList中打开以获取该选项?我有一个由CMake生成的makefile,但没有可用的伪帮助目标
Xavier T.17年

不。至少在OSX上使用最近的CMake(我实际上正在使用Nightnight,但是我相信当我写这篇文章时我正在使用3.3),只要运行touch CMakeLists.txt && cmake . && make help就可以了。我只能猜测您使用的是古老的cmake,还是未使用Unix Makefiles生成器?
Timmmm

我在Windows上将CMake 3.6.2与unix生成器一起使用。我会四处寻找解释,因为它似乎很方便。
Xavier T.

26

我结合了这两个答案: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

10
我还(最近)发现,Bash下的制表符补全将列出可用的目标。
nobar 2014年

1
+1是一种很好的方法,但可以进行一些改进:(a)显式指定sh -c "…"是不必要的,因为make默认情况下这就是调用配方命令的方法;(b)您使用的命令过于简单,导致误报(如您所述);(c)如果在命令前加上@,则在执行之前不会将其回显到stdout,从而避免了-s在调用时使用它。
mklement0 2014年

1
您可以添加一个更简单的make目标:list:cat Makefile | grep“ ^ [Az]” | awk'{print $$ 1}'| sed“ s /:// g | sort”
thamster 2014年

2
cat / sed方法在多个级别上均失败。它不会展开变量,也不会列出由于包含其他文件而导致的目标。
Troy Daniels

1
@ mklement0:我更喜欢使用make -s(通过Bash别名)而不是在makefile中给命令加上前缀@。在某些情况下@可能有用,特别是作为echo命令的前缀(命令的显示将是多余的),但是总的来说我不喜欢使用它。这样,我可以在需要时(使用-s)看到“食谱” ,但是通常不需要。以下是相关文档:GNU Make手册5.2配方回显
nobar 2015年

14

如果要安装bash补全make,则补全脚本将定义一个function _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

3
有用,但请注意,并非所有平台都具有脚本/etc/bash_completion和/或功能_make_target_extract_script-您似乎在Ubuntu> 12上。如果您以目标为目标/usr/share/bash-completion/completions/make,则您的方法将在其他平台上使用,例如Fedora20。有些(较旧的)平台具有该文件,而不是功能(例如Debian 7和Ubuntu 12);其他平台(例如OSX)根本不带有制表符完成功能make。也许发布实际的函数定义会有所帮助。
mklement0 2014年

1
谢谢你的评论。非常感谢。是的,我已经在Ubuntu 14.04上进行了测试
hek2mgl 2014年

11

正如mklement0指出的那样,GNU-make缺少列出所有Makefile目标的功能,他的回答和其他提供了实现此目的的方法。

但是,原始帖子还提到了rake,其任务切换与只列出rakefile中的所有任务有一点不同。Rake仅会为您提供具有相关描述的任务列表。没有描述的任务将不会列出。这使作者既可以提供自定义的帮助说明,又可以省略某些目标的帮助。

如果要模拟rake的行为,可以在其中为每个目标提供描述,这是一种简单的方法:将描述嵌入要列出的每个目标的注释中。

您可以将描述放在目标旁边,或者像我通常所做的那样,将描述放在目标上方的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

您也可以在此要点此处找到一个简短的代码示例。

同样,这不能解决在Makefile中列出所有目标的问题。例如,如果您有一个可能已生成的大型Makefile或由其他人编写的Makefile,并且想要一种快速的方法来列出其目标而不进行深入挖掘,则无济于事。

但是,如果您正在编写一个Makefile,并且想要一种以一致的自记录方式生成帮助文本的方法,那么该技术可能会很有用。


这是一种很棒的技术-美观而简单,但也给出了说明!
布莱恩·伯恩斯

很棒的技术。我对此进行了一些修改,以使用echo命令作为命令描述(stackoverflow.com/a/52059487/814145)。
Penghe Geng

@PengheGeng感谢您的帖子。我想说明@echo "Target 1"的只是一个占位符,而不是“描述回显命令”。生成的帮助文本不是来自@echo的。在真实的Makefile中,这些echo将被构建命令或类似命令替换。我将编辑该帖子以使其更加清楚。
jsp

4

@nobar的答案有助于显示如何使用制表符补全列出makefile的目标

  • 这对于默认情况下提供此功能平台(例如Debian,Fedora)非常有用。

  • 在其他平台(例如Ubuntu)上,您必须显式加载此功能,如@ hek2mgl的答案所暗示:

    • . /etc/bash_completion 安装几个制表符完成功能,其中一个用于 make
    • 或者,安装以下选项卡的完成make
      • . /usr/share/bash-completion/completions/make
  • 对于根本不提供此功能的平台(例如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发行版附带的制表符完成功能那样复杂:最值得注意的是,即使命令行使用来指定另一个makefile ,它也始终以当前目录中的makefile为目标-f <file>

4

我最喜欢的答案是Unix&Linux Stack Exchange的Chris Down发布了。我会引用。

这是bash完成模块make获取其列表的方式:

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

它打印出以换行符分隔的目标列表,而不进行分页。

用户Brainstone建议使用管道sort -u来删除重复的条目:

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

来源:如何列出make中的所有目标?(Unix&Linux SE)


4

我专注于描述make目标的简单语法并获得干净的输出,我选择了这种方法:

help:
    @grep -B1 -E "^[a-zA-Z0-9_-]+\:([^\=]|$$)" Makefile \
     | grep -v -- -- \
     | sed 'N;s/\n/###/' \
     | sed -n 's/^#: \(.*\)###\(.*\):.*/\2###\1/p' \
     | column -t  -s '###'


#: Starts the container stack
up: a b
  command

#: Pulls in new container images
pull: c d 
    another command

make-target-not-shown:

# this does not count as a description, so leaving
# your implementation comments alone, e.g TODOs
also-not-shown:

因此,将上述内容作为Makefile并运行它会为您提供类似

> make help
up          Starts the container stack
pull        Pulls in new container images

命令链说明:


非常好的解决方案。我已经使用了几年了:awk -F ':|##' '/^[^\t].+:.*##/ { printf "\033[36mmake %-28s\033[0m -%s\n", $$1, $$NF }' $(MAKEFILE_LIST) | sort ##之前的注释在同一行。但是我认为您的解决方案需要先阅读。
托罗克

grep: parentheses not balanced在我的OSX 10.15计算机上,由前两行组成的Makefile报告。
马尔科姆·克鲁姆

@Crummy对不起,我的错。感谢您指出。解决它。make要求转义不应该以变量名开头的美元符号。
理查德·基弗

请注意,显示上面的代码部分时,StackOverflow会将选项卡转换为空格,但是Makefiles需要选项卡。您可以编辑帖子,然后复制各节,因此保留选项卡。
理查德·基弗

2

这里有很多可行的解决方案,但是正如我想说的那样,“如果值得一次做,就值得再次做。” 我确实建议不要使用(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 := \)

我喜欢的原因是:

  • 按包含文件列出目标。
  • 输出原始动态目标定义(用模替换变量定界符)
  • 在新行上输出每个目标
  • 看起来更清晰(主观意见)

说明:

  • 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

这对我很有帮助,因为我想查看make目标所需的构建目标(及其依赖关系)。我知道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它来转储内部数据库,ditch stderr,使用快速而肮脏的方法grep -A 100000来保持输出的底部。然后,我用几个清除输出grep -v,最后使用cut来获取冒号之前的内容,即目标。

这足以满足我大多数Makefile上的帮助脚本。

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


1

这是对jsp非常有用的答案(https://stackoverflow.com/a/45843594/814145)的修改。我喜欢不仅获得目标列表而且获得目标描述的想法。jsp的Makefile将描述作为注释,我经常在目标的description 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

目标2

目标3

在Makefile中,它应该是同一条语句,请确保使用$$ variable而不是$ variable对变量进行转义。

说明

-吐出内容

| -管道将输出解析到下一个awk

awk-运行除“ shell”外的正则表达式,仅接受“ Az”行,然后输出$ 1第一栏

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

这是一个粗略的输出,您仅需AWK就可以做更多时髦的事情。尽量避免使用sed,因为sed在BSD变体中的一致性不高,例如,某些可以在* nix上运行,但在MacOSX等BSD上却无法运行。

更多

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


您还可以使用make -qp而不是cat。因此它将成为make -qp | awk'/:/ &&!/:= / && / ^ [Az] / &&!/ PATH / &&!/ DISPLAY / &&!/ TERM_SESSION / &&!/ USER_TEXT_ENCODING / &&!/ Makefile / {print substr($ 0,1 ,length($ 0)-1)| “ sort -u”}'
Jimmy MG Lim,

0

我通常这样做:

grep install_targets Makefile

它将返回类似以下内容:

install_targets = install-xxx1 install-xxx2 ... etc

我希望这有帮助


0

对于Bash脚本

这是一个非常简单的方法bash-根据上面 @ cibercitizen1的评论:

grep : Makefile | awk -F: '/^[^.]/ {print $1;}'

另请参阅@ Marc.2377 的更为权威的答案,该答案说明了Bash完成模块是如何make做到的。


-4

不知道为什么先前的答案如此复杂:

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

8
以前的答案是如此的复杂,因为它涵盖了所有的场景,你的答案不包括:(一)列入通过定义的目标变量(如$(SHELLS): ...),(二)列入以数字开头的目标,(三)非包含变量定义,(d)如果make使用显式makefile路径指定,则以正确的makefile 为目标。(a)的主要缺点是:即使您可靠地定位了正确的 Makefile ,由于缺乏变量扩展,在一般情况下,解析原始文件也无法正常工作。
mklement0 2014年

6
顺便说一句,再复杂:你的命令可以简化为以下-但主要的一点是,它不是足够强大的awk -F: '/^[A-z]/ {print $$1}' Makefile。最后,在很大程度上是学术观点:使用[A-z]而不是[:alpha:]意味着您会错过以开头的异物(例如)的目标á
mklement0 2014年

@ mklement0您的第一个awk命令可以很好地工作,并且到目前为止是最简单的命令,尽管使用[:alpha:]似乎由于某些原因而破坏了它-它错过了测试文件中的某些目标。
Brian Burns

@ bburns.km:我的坏:我应该说[[:alpha:]],而不是[:alpha:][:alpha:]表示语言环境感知字母集的字符类([...]),因此有效的需求 [[ / ]]
mklement0

4
为什么这个答案这么复杂?您可以在此简短的代码段中收集一些无用的罪过。awk -F: '/^[A-Za-z]/ { print $$1 }' Makefile使用单个过程,并希望使用更正确的正则表达式(尽管您当然也可以使用带有下划线,数字等的目标,如前面的注释中所述)。
6
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.