268

我使用了一点 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:
4

22 回答 22

246

在 Bash 下(至少),这可以通过制表符完成自动完成:

make spacetabtab

于 2014-05-15T00:20:29.800 回答
184

这是对布伦特·布拉德本 (Brent Bradburn) 伟大方法的改进尝试,如下所示:

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

奇怪的是,GNUmake没有列出 makefile 中定义的目标名称的功能。虽然该-p选项生成包含所有目标的输出,但它会将它们隐藏在许多其他信息中,并且还执行默认目标(可以使用 来抑制-f/dev/null)。

将以下规则放在 GNU 的 makefile 中make以实现一个名为的目标,该目标list仅按字母顺序列出所有目标名称 - 即:调用为make list

.PHONY: list
list:
    @LC_ALL=C $(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

    • 将目标列表声明为虚假目标,即引用文件的目标,因此应无条件调用其配方
  • LC_ALL=C确保make' 以English输出,因为输出的解析依赖于此。巴斯蒂安·比特托夫致敬

  • $(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 make 没有提供直接打印数据库的选项,而不执行默认(或给定)任务;如果您不需要针对特定​​的 Makefile,您可以按照页面中的建议使用。make -p -f/dev/nullman
  • -v RS=

    • 这是一个 awk 习惯用法,它将输入分成连续的非空行块。
  • /^# File/,/^# Finished Make data base/

    • 匹配包含所有目标的输出中的行范围(从 GNU make 3.82 开始为真) - 通过将解析限制在此范围内,无需处理来自其他输出部分的误报。
  • if ($$1 !~ "^[#.]")

    • 选择性地忽略块:
      • #...忽略非目标,其块以# Not a target:
      • ....忽略特殊目标
    • 所有其他块都应以仅包含显式定义目标名称的行开头,后跟:
  • egrep -v -e '^[^[:alnum:]]' -e '^$@$$'从输出中删除不需要的目标:

    • '^[^[:alnum:]]'... 不包括隐藏目标,按照惯例,隐藏目标是既不以字母也不以数字开头的目标。
    • '^$@$$'...不包括list目标本身

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

于 2014-10-13T12:22:41.907 回答
48

这在很多情况下显然不起作用,但如果你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
于 2017-01-05T16:39:43.243 回答
28

我结合了这两个答案: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
于 2013-02-25T01:43:24.487 回答
18

正如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

您还可以在这个 gist这里找到一个简短的代码示例。

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

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

于 2017-08-23T15:16:44.137 回答
15

如果您安装make了 bash 补全,补全脚本将定义一个 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
于 2014-06-27T13:44:46.987 回答
13

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

这是 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)

于 2019-10-10T06:10:53.503 回答
8

专注于描述 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

命令链说明:

于 2019-11-28T11:10:41.460 回答
4

@nobar 的回答很有帮助地展示了如何使用制表符完成来列出生成文件的目标

  • 这对于默认提供此功能的平台(例如,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 发行版附带的 tab-completion 功能那么复杂:最值得注意的是,它总是针对当前目录中的 makefile ,即使命令行以不同的 makefile 为目标-f <file>
于 2014-10-13T12:47:02.990 回答
4

help目标将仅打印##带有描述的目标。这允许记录公共和私人目标。使用.DEFAULT_GOAL使帮助更容易被发现。

只有sed,xargsprintfused 很常见。

使用< $(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
于 2020-09-08T21:21:00.777 回答
4

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

示例生成文件:

APPS?=app1 app2

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

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

test:
    @# Help: A normal target without variables

# A target without any help description
clean:

# A hidden target
.hidden:

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 helpmklement0 在这里发布的完全一样 -你如何在生成文件中获取目标列表?.

获取目标列表后,它会make <target> -nB作为每个目标的试运行,并解析以@# Help:目标描述开头的最后一行。并且那个或一个空字符串被打印在一个格式很好的表格中。

正如你所看到的,变量甚至在描述中也被扩展了,这在我的书中是一个巨大的好处:)。

于 2020-12-10T22:37:39.363 回答
3

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

make -p 2&>/dev/null | grep -A 100000 "# Files" | grep -v "^$" | grep -v "^\(\s\|#\|\.\)" | grep -v "Makefile:" | cut -d ":" -f 1

我使用make -p它转储内部数据库,抛弃标准错误,使用快速而肮脏grep -A 100000的方法来保持输出的底部。然后我用几个 , 来清理输出grep -v,最后cut用来获取冒号之前的内容,即目标。

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

编辑:补充grep -v Makefile说这是一个内部规则

于 2017-09-19T15:10:42.930 回答
3

将此目标添加到您的 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
于 2021-04-29T17:05:58.953 回答
2

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

示例生成文件:

.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 目标,这可能适用于您的情况,也可能不适用于您的情况
  • 此外,它仅列出具有echoor:命令作为配方的第一个命令的那些 PHONY 目标。:意思是“什么都不做”。我在这里将它用于那些不需要回显的目标,例如all上面的目标。
  • 目标还有一个额外的技巧,可以在输出help中添加“:” 。make help
于 2018-08-28T13:48:39.953 回答
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
于 2018-04-19T14:25:50.283 回答
2

要扩展@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)'
于 2020-07-20T18:37:35.507 回答
2

对于 Bash 脚本

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

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

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

于 2020-05-21T21:52:42.110 回答
1

这对我很有帮助,因为我想查看 make 目标所需的构建目标(及其依赖项)。我知道 make 目标不能以“。”开头。特点。我不知道支持哪些语言,所以我使用了 egrep 的括号表达式。

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
于 2017-09-08T05:20:52.773 回答
1

上面的另一个附加答案。

在 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 转义变量。

解释

cat - 吐出内容

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

awk - 运行正则表达式,不包括“shell”并仅接受“Az”行,然后打印出 $1 第一列

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

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

更多的

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

于 2019-04-14T17:24:54.533 回答
0

对于 AWK 的仇恨者,为了简单起见,这个装置对我有用:

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

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

如果您有“有趣的”规则名称,则此解决方案将不起作用,但对于大多数简单设置都适用。基于解决方案的主要缺点make -qp是(如此处的其他答案),如果 Makefile 使用函数定义变量值 - 它们仍将被执行而不管-q,如果使用$(shell ...)则仍然会调用 shell 命令并且它的副作用会发生. 在我的设置中,运行 shell 函数的副作用通常是对标准错误的不需要的输出,所以我2>/dev/null在 make 命令之后添加。

于 2020-07-13T09:31:49.220 回答
0

我通常这样做:

grep install_targets 生成文件

它会返回如下内容:

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

我希望这有帮助

于 2019-10-03T09:29:16.830 回答
-5

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

list:
    cat Makefile | grep "^[A-z]" | awk '{print $$1}' | sed "s/://g" 
于 2014-10-16T19:25:17.453 回答