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

时间:2010-11-18 20:28:46

标签: makefile gnu-make targets

我曾经使用过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:

22 个答案:

答案 0 :(得分:149)

在Bash(至少)下,这可以通过标签完成自动完成:

make(space)(tab)(tab)

答案 1 :(得分:89)

这是尝试改进@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个实际标签字符。(空格 work)

请注意,排序生成的目标列表是最佳选择,因为 not 排序不会产生有用的排序,因为目标的顺序出现在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来创建以空格分隔的列表。

答案 2 :(得分: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

答案 3 :(得分: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

答案 4 :(得分:12)

如果您安装了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

答案 5 :(得分:8)

作为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,并且想要以一致的自我文档方式生成帮助文本,则此技术可能很有用。

答案 6 :(得分:4)

@nobar's answer有助于展示如何使用制表符完成列出制作文件的目标

  • 这适用于提供此功能的平台默认(例如, Debian,Fedora )。

  • 在其他平台上(例如 Ubuntu ),您必须明确加载此功能,如@hek2mgl's answer所暗示的那样:

    • . /etc/bash_completion安装了多个制表符完成功能,包括make
    • 的功能
    • 或者,要为make安装标签页:
      • . /usr/share/bash-completion/completions/make
  • 对于完全不提供此功能的平台,例如 OSX ,您可以获取以下命令(改编自{ {3}})实施它:
_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。

答案 7 :(得分:2)

对于Bash脚本

bash中,这是一种非常简单的方法-基于@ cibercitizen1 above的评论:

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

另请参见@ Marc.2377的more authoritative answer,其中说明了make的Bash完成模块是如何做到的。

答案 8 :(得分:2)

我最喜欢的答案是Chris Down在Unix&Linux Stack Exchange上发布的。我会报价。

  

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

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

来源:How to list all targets in make?(Unix&Linux SE)

答案 9 :(得分:1)

将此目标添加到您的 Makefile:

help:
    @echo "\nTARGETS:\n"
    @make -qpRr | egrep -e '^[a-z].*:$$' | sed -e 's~:~~g' | sort
    @echo ""

  • make -qpRr = make --question --print-data-base --no-builtin-variables --no-builtin-rules
  • egrep -e '^[a-z].*:$$':搜索以小写开头并以“:”结尾的行
  • sed -e 's~:~~g':删除“:”

然后运行:

make help

这对我有用?


PD:更多信息在...

make --help

答案 10 :(得分:1)

我接受了上面提到的几个答案并编译了这个答案,它也可以为每个目标生成一个漂亮的描述,它也适用于带有变量的目标。

示例Makefile:

APPS?=app1 app2

bin: $(APPS:%=%.bin)
    @# Help: A composite target that relies only on other targets

$(APPS:%=%.bin): %.bin:
    echo "build binary"
    @# Help: A target with variable name, value = $*

test:
    echo $(MAKEFLAGS)
    echo "starting test"
    @# Help: A normal target without variables

# A target without any help description
clean:
    echo $(MAKEFLAGS)
    echo "Cleaning..."

MAKEOVERRIDES =
help:
    @printf "%-20s %s\n" "Target" "Description"
    @printf "%-20s %s\n" "------" "-----------"
    @make -pqR : 2>/dev/null \
        | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' \
        | sort \
        | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' \
        | xargs -I _ sh -c 'printf "%-20s " _; make _ -nB | (grep -i "^# Help:" || echo "") | tail -1 | sed "s/^# Help: //g"'

示例输出:

$ make help
Target               Description
------               -----------
app1.bin             A target with variable name, value = app1
app2.bin             A target with variable name, value = app2
bin                  A composite target that relies only on other targets
clean
test                 A normal target without variables

它如何工作:

make help目标的顶部与mklement0在此处发布的How do you get the list of targets in a makefile?完全一样。

获取目标列表后,它会make <target> -nB作为每个目标的空运行,并分析以@# Help:开头的最后一行以描述目标。然后在格式正确的表格中打印出该字符串或空字符串。

如您所见,变量甚至在描述中也进行了扩展,这在我的书中是非常有用的:)。

答案 11 :(得分:1)

help目标仅打印带有##后跟说明的目标。这样可以记录公共目标和私有目标。使用.DEFAULT_GOAL可以使帮助更容易发现。

仅使用sedxargsprintf这两个很常见的

使用< $(MAKEFILE_LIST)可以使makefile被调用为Makefile以外的其他Makefile.github

您可以在printf中自定义输出以适合您的偏好。设置此示例以匹配OP对rake样式输出的请求

剪切和粘贴下面的make文件时,请不要忘记将4个空格的缩进更改为制表符。

# vim:ft=make
# Makefile

.DEFAULT_GOAL := help
.PHONY: test help

help:  ## these help instructions
    @sed -rn 's/^([a-zA-Z_-]+):.*?## (.*)$$/"\1" "\2"/p' < $(MAKEFILE_LIST) | xargs printf "make %-20s# %s\n"

lint: ## style, bug and quality checker
    pylint src test

private: # for internal usage only
    @true

test: private ## run pytest with coverage
    pytest --cov test


这是上面Makefile的输出。请注意,private目标没有得到输出,因为它的注释只有一个#

$ make
make help                # these help instructions
make lint                # style, bug and quality checker
make test                # run pytest with coverage

答案 12 :(得分:1)

要扩展answer given by @jsp,甚至可以使用$(eval)函数在帮助文本中求值。

下面的建议版本具有以下增强的属性:

  • 将扫描所有makefile(甚至包括在内)
  • 将展开帮助注释中引用的实时变量
  • 为实际目标添加文档锚点(以# TARGETDOC:开头)
  • 添加列标题

要记录,请使用以下表格:

RANDOM_VARIABLE := this will be expanded in help text

.PHONY: target1 # Target 1 help with $(RANDOM_VARIABLE)
target1: deps
    [... target 1 build commands]

# TARGETDOC: $(BUILDDIR)/real-file.txt # real-file.txt help text
$(BUILDDIR)/real-file.txt:
    [... $(BUILDDIR)/real-file.txt build commands]

然后,在您的makefile中的某个位置:

.PHONY: help # Generate list of targets with descriptions
help:
    @# find all help in targets and .PHONY and evaluate the embedded variables
    $(eval doc_expanded := $(shell grep -E -h '^(.PHONY:|# TARGETDOC:) .* #' $(MAKEFILE_LIST) | sed -E -n 's/(\.PHONY|# TARGETDOC): (.*) # (.*)/\2  \3\\n/'p | expand -t40))
    @echo
    @echo ' TARGET   HELP' | expand -t40
    @echo ' ------   ----' | expand -t40
    @echo -e ' $(doc_expanded)'

答案 13 :(得分:1)

我选择一种简单的语法来描述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:

给你类似的东西

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

命令链说明:

答案 14 :(得分: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输出中添加“:”。

答案 15 :(得分: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 := \)

我喜欢因为:

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

说明:

  • 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

答案 16 :(得分:1)

这远非干净,但为我做了工作。

KTable<Long, ArrayList<Long>> sample = builder.stream(TOPIC)
    .groupByKey()
    .aggregate(
        () -> new ArrayList<Long>(),
        (key, val, agg) -> agg.add(val),
        longSerde
    );

我使用make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1 转储内部数据库,ditch stderr,使用快速而脏的make -p来保持输出的底部。然后我用几个grep -A 100000清理输出,最后使用grep -v来获取冒号之前的内容,即目标。

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

编辑:添加cut这是一项内部规则

答案 17 :(得分:1)

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

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

答案 18 :(得分:0)

我通常这样做:

  

grep install_targets Makefile

它会返回类似以下内容:

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

我希望这对您有帮助

答案 19 :(得分:0)

对上面还有另一个答案。

仅在终端机上使用cat和awk在MacOSX上进行了测试

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

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

  

target1

     

target2

     

target3

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

说明

-吐出内容

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

awk -运行除“ shell”外的正则表达式,仅接受“ A-z”行,然后打印出$ 1第一列

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

这是一个粗略的输出,您仅需AWK就可以做更多时髦的事情。尝试避免sed,因为sed在BSD变体中的一致性不高,即某些功能在* nix上有效,但在MacOSX等BSD上却不起作用。

更多

您应该能够将此文件(带有修改)添加到 make 的文件中,并添加到默认的bash-completion文件夹/ usr / local / etc / bash-completion .d / 意思是当您“制作 选项卡选项卡 ”时。.它将根据一个衬纸脚本完成目标。

答案 20 :(得分:0)

对于AWK仇恨者和为简单起见,此配置对我有效:

help:
  make -qpRr $(lastword $(MAKEFILE_LIST)) | egrep -v '(^(\.|:|#|\s|$)|=)' | cut -d: -f1

(要在Makefile外部使用,只需删除$(lastword ...)或将其替换为Makefile路径)。

如果您具有“有趣的”规则名称,则此解决方案将不起作用,但适用于大多数简单设置。基于make -qp的解决方案的主要缺点是(如此处的其他答案),如果Makefile使用函数定义变量值-无论-q以及是否使用{{1} },仍然会调用shell命令,并且会发生副作用。在我的设置中,运行shell函数的副作用通常是不必要的输出到标准错误,因此我在make命令后添加$(shell ...)

答案 21 :(得分:-4)

不确定为什么以前的答案太复杂了:

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