ChatGPT解决这个技术问题 Extra ChatGPT

你如何获得生成文件中的目标列表?

我使用了一点 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:
我很快就阅读了有趣的答案,但到目前为止,我更喜欢使用别名(在我的 .bashrc 中)保持简单、简单和可移植:alias makefile-targets='grep "^[^#[:space:]].*:" Makefile' 通常我只需要检查当前的 makefile,并且bash 完成扩展了我的别名。
只是:grep : Makefile 是否太琐碎?

U
Utku

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

make 空格制表符制表符


+1,但请注意,这不是 bash 功能,而是取决于您的 平台。平台分为三类:默认安装此补全的平台(例如,Debian 7、Fedora 20),您可以可选安装它的平台(例如,Ubuntu 14,带有 . /etc/bash_completion),以及根本不支持它的那些(例如,OSX 10.9)
它确实适用于 OS X 10.10(不是使用 bash,而是使用 zsh)。
很棒的提示,但也需要 bash-completion 包。发现于 Fedora、RHEL、Gentoo 等。
这不适用于以编程方式创建的 make 目标 AFAICT,例如 $(RUN_TARGETS): run-%: ...
这似乎也被 eval 和可能的 varname:=... 弄糊涂了——我的完成最终包括 $(foreach f, 等。现在我使用的是 make -Bnmake -Bn targetname,它会像执行默认或 targetname 一样干运行从头开始。结合(可选)打印当前目标及其依赖项的目标,这可以为您提供所需的所有信息。顺便说一句,make --print-data-base 显示了所有目标/先决条件/配方,包括通配符目标的每个扩展!
m
mklement0

这是对 Brent Bradburn's great approach 的改进尝试,如下所示:

使用更强大的命令来提取目标名称,这有望防止任何误报(并且还消除了不必要的 sh -c)

并不总是以当前目录中的 makefile 为目标;尊重使用 -f 明确指定的 makefile

排除隐藏目标 - 按照惯例,这些目标的名称既不以字母也不以数字开头

只用一个虚假的目标

在命令前加上 @ 以防止它在执行前被回显

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

将以下规则放入 GNU make 的 makefile 中以实现名为 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 个实际的制表符字符。 (空格不起作用)。

请注意,排序生成的目标列表是最好的选择,因为 not 排序不会产生有用的排序,因为目标在 makefile 中出现的顺序是保留。
此外,包含多个目标的规则的子目标总是单独输出,因此,由于排序,通常不< /em> 并排出现;例如,如果有其他目标,则以 a z: 开头的规则不会在输出中彼此相邻列出目标 az

规则解释:

.PHONY: list 将目标列表声明为虚假目标,即不引用文件的目标,因此应该无条件调用其配方

将目标列表声明为虚假目标,即不引用文件的目标,因此应无条件调用其配方

LC_ALL=C 确保 make 以英语输出,因为输出的解析依赖于此。向 Bastian Bittorf 致敬

$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null 再次调用 make 以打印和解析从 makefile 派生的数据库: -p 打印数据库 -Rr 禁止包含已构建-in 规则和变量 -q 仅测试目标的最新状态(不重新制作任何东西),但这本身并不能阻止在所有情况下执行配方命令;因此: -f $(lastword $(MAKEFILE_LIST)) 确保与原始调用中相同的 makefile 被定位,无论它是使用 -f ... 隐式或显式定位的。警告:如果您的 makefile 包含,这将中断包括指令;为了解决这个问题,在任何包含指令之前定义变量 THIS_FILE := $(lastword $(MAKEFILE_LIST)) 并使用 -f $(THIS_FILE) 代替。 : 是一个故意无效的目标,旨在确保不执行任何命令; 2>/dev/null 抑制生成的错误消息。注意:这仍然依赖于 -p 打印数据库,这是 GNU make 3.82 的情况。遗憾的是,GNU make 没有提供直接打印数据库的选项,而不执行默认(或给定)任务;如果您不需要针对特定的 Makefile,您可以按照手册页中的建议使用 make -p -f/dev/null。

再次调用 make 以打印和解析从 makefile 派生的数据库: -p 打印数据库 -Rr 禁止包含内置规则和变量 -q 仅测试目标的最新状态(不重新制作任何东西),但这本身并不能阻止在所有情况下执行配方命令;因此: -f $(lastword $(MAKEFILE_LIST)) 确保与原始调用中相同的 makefile 被定位,无论它是使用 -f ... 隐式或显式定位的。警告:如果您的 makefile 包含,这将中断包括指令;为了解决这个问题,在任何包含指令之前定义变量 THIS_FILE := $(lastword $(MAKEFILE_LIST)) 并使用 -f $(THIS_FILE) 代替。 : 是一个故意无效的目标,旨在确保不执行任何命令; 2>/dev/null 抑制生成的错误消息。注意:这仍然依赖于 -p 打印数据库,这是 GNU make 3.82 的情况。遗憾的是,GNU make 没有提供直接打印数据库的选项,而不执行默认(或给定)任务;如果您不需要针对特定的 Makefile,您可以按照手册页中的建议使用 make -p -f/dev/null。

-p 打印数据库

-Rr 禁止包含内置规则和变量

-q 仅测试目标的最新状态(不重新制作任何东西),但这本身并不能阻止在所有情况下执行配方命令;因此:

-f $(lastword $(MAKEFILE_LIST)) 确保与原始调用中相同的 makefile 被定位,无论它是使用 -f ... 隐式或显式定位的。警告:如果您的 makefile 包含包含指令,这将中断;为了解决这个问题,在任何包含指令之前定义变量 THIS_FILE := $(lastword $(MAKEFILE_LIST)) 并使用 -f $(THIS_FILE) 代替。

是一个故意无效的目标,旨在确保不执行任何命令; 2>/dev/null 抑制生成的错误消息。注意:这仍然依赖于 -p 打印数据库,这是 GNU make 3.82 的情况。遗憾的是,GNU make 没有提供直接打印数据库的选项,而不执行默认(或给定)任务;如果您不需要针对特定的 Makefile,您可以按照手册页中的建议使用 make -p -f/dev/null。

-v RS= 这是一个 awk 习惯用法,将输入分成连续的非空行块。

这是一个 awk 习惯用法,它将输入分成连续的非空行块。

/^# File/,/^# Finished Make database/ 匹配输出中包含所有目标的行范围(从 GNU make 3.82 开始为 true) - 通过将解析限制在此范围内,无需处理 false其他输出部分的积极因素。

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

if ($$1 !~ "^[#.]") 选择性地忽略块:# ... 忽略非目标,其块以 #Not a target: 开头。 ...忽略特殊目标所有其他块都应以仅包含显式定义目标名称的行开头,后跟:

选择性地忽略块:# ... 忽略非目标,其块以 #Not a target: 开头。 ...忽略特殊目标

... 忽略非目标,其块以 # Not a target 开头:

. ...忽略特殊目标

所有其他块都应以仅包含显式定义目标名称的行开头,后跟:

egrep -v -e '^[^[:alnum:]]' -e '^$@$$' 从输出中删除不需要的目标:'^[^[:alnum:]]' ... 排除隐藏目标,其中 - 按照惯例 - 是既不以字母也不以数字开头的目标。 '^$@$$' ... 不包括列表目标本身

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

'^$@$$' ... 不包括列表目标本身

运行 make list 然后打印所有目标,每个目标单独一行;您可以通过管道连接到 xargs 以创建一个以空格分隔的列表。


@Bulletmagnet:-n 选项的目的是“打印将要执行的命令......” - 换句话说:一个试运行功能。也不是说您的引用缺少单词 just 的斜体:make -p 本身 确实 打印数据库,但总是 也运行默认任务< /i>;手册建议使用笨重的 make -p -f/dev/null 来避免这种情况。
还将列出任何 .PHONY 目标,因此请确保您没有 .PHONY: *
可以使用 firstword 而不是 lastword 函数,然后能够在 list 规则之前 include 其他 makefile 而无需担心
我已经尝试过 awk 的 Big Sur 和 Gnu 版本,但我不断收到 make: invalid option -- F make: invalid option -- :
@mattalxndr,我没有看到这个问题。如果您有可重现的示例,我鼓励您提出一个新问题。
T
Timmmm

这在很多情况下显然不起作用,但如果您的 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,但我没有可用的伪帮助目标
没有。在 OSX 上至少使用最近的 CMake(我实际上正在使用 nightlies,但我相信当我写这篇文章时我使用的是 3.3),只需运行 touch CMakeLists.txt && cmake . && make help 就可以了。我只能猜测您使用的是古老的 cmake,还是不使用 Unix Makefiles 生成器?
我在 Windows 上使用 CMake 3.6.2 和 unix 生成器。我会四处寻找解释,因为它看起来很方便。
C
Community

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

我还(最近)发现 Bash 下的 tab-completion 会列出可用的目标。
+1 是一个很好的方法,但可以进行一些改进:(a)显式指定 sh -c "…" 是不必要的,因为这是 make 默认用于调用配方命令的内容; (b)您使用的命令过于简单,导致误报(如您所述); (c) 如果您在命令前加上 @,它不会在执行前回显到 stdout,从而避免在调用时使用 -s
您可以添加一个更简单的 make 目标: list: cat Makefile | grep "^[Az]" | awk '{打印 $$1}' | sed "s/://g | 排序"
cat/sed 方法在几个层面上都失败了。它不会扩展变量,也无法列出包含其他文件结果的目标。
@mklement0:我更喜欢使用 make -s(通过 Bash 别名)而不是在 makefile 中使用 @ 作为命令前缀。在某些情况下 @ 可能有用,特别是作为 echo 命令的前缀(命令的显示是多余的),但总的来说我不喜欢使用它。这样,我可以在需要时查看“食谱”(通过 not 使用 -s),但通常不会。以下是相关文档:GNU Make Manual, 5.2 Recipe Echoing
j
jsp

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

但是,原始帖子还提到了 rake,其 tasks 开关的作用与仅列出 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,并且您想要一种以一致的、自记录的方式生成帮助文本的方法,那么这种技术可能会很有用。


这是一项很棒的技术 - 既好又简单,但也提供了描述!
伟大的技术。我稍作修改,使用 echo 命令作为命令描述 (stackoverflow.com/a/52059487/814145)。
@PengheGeng 感谢您的帖子。我想明确 @echo "Target 1" 只是一个占位符,而不是“描述回显命令”。生成的帮助文本不是来自 @echo。在真正的 Makefile 中,这些 echo 将被构建命令或类似命令替换。我将编辑帖子以使其更清楚。
绝妙的解决方案!但是,我必须使用 grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1:\t\2/' | column -ts "$$(printf '\t')" 来获得漂亮的列表,因为 expand 对我来说无法呈现漂亮的输出。
@MikkoRantalainen 和其他人,从 StackOverflow 复制/粘贴时,空格被解释为空格。在我的例子中,expand -t20 正在寻找标签,因此我需要将 \1 \2 中的空间更改为标签以使其工作。
h
hek2mgl

如果您为 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

有帮助,但请注意,并非所有平台都有脚本 /etc/bash_completion 和/或函数 _make_target_extract_script - 您似乎使用的是 Ubuntu > 12. 如果您以 /usr/share/bash-completion/completions/make 为目标,您的方法将适用于其他平台,例如 Fedora 20。有(旧)平台具有此文件,但没有此功能(例如,Debian 7 和 Ubuntu 12);其他平台(例如 OSX)根本不提供 make 的制表符补全功能。也许发布实际的函数定义会有所帮助。
谢谢你的评论。这是非常感谢的。是的,我已经在 Ubuntu 14.04 上测试过了
M
Marc.2377

我最喜欢的答案是由 Chris Down 在 Unix & Linux 堆栈交换。我会引用。

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


R
Richard Kiefer

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

命令链说明:

首先,grep 所有目标及其前面的行,请参阅 https://unix.stackexchange.com/a/320709/223029。

然后,摆脱组分隔符,请参阅 https://stackoverflow.com/a/2168139/1242922。

然后,我们折叠每一对行以便稍后解析,请参阅 https://stackoverflow.com/a/9605559/1242922。

然后,我们解析有效行并删除那些不匹配的行,请参阅 https://stackoverflow.com/a/8255627/1242922,并给出我们想要的输出顺序:命令,然后是描述。

最后,我们像表格一样排列输出。


非常好的解决方案。我已经使用了几年了。:awk -F ':|##' '/^[^\t].+:.*##/ { printf "\033[36mmake %-28s\033[0m -%s\n", $$1, $$NF }' $(MAKEFILE_LIST) | sort ## 在与目标在同一行的评论之前。但我认为您的解决方案允许之前的可读性。
由前两行组成的生成文件在我的 OSX 10.15 机器上报告 grep: parentheses not balanced
@Crummy 对不起,我的错。感谢您指出。解决它。 make 需要转义不应以变量名开头的美元符号。
注意,StackOverflow 在显示上面的代码部分时会将制表符转换为空格,但 Makefile 需要制表符。您可以编辑帖子,然后复制部分,以便保留标签。
m
mario

将此目标添加到您的 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 '^[az].*:$$':搜索以小写开头并以“:”结尾的行

sed -e 's~:~~g': 删除“:”

然后运行:

make help

这对我有用😉

PD:更多信息在...

make --help

r
ronakg

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

示例生成文件:

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 help 目标的顶部与 mklement0 在此处发布的完全相同 - How do you get the list of targets in a makefile?

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

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


C
Community

@nobar's answer 有用地展示了如何使用制表符补全来列出 makefile 的目标

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

在其他平台(例如,Ubuntu)上,您必须显式加载此功能,正如@hek2mgl 的答案所暗示的那样:. /etc/bash_completion 安装了几个制表符补全功能,包括一个用于 make 或者,仅安装用于 make: 的制表符补全功能。 /usr/share/bash-completion/completions/make

/etc/bash_completion 安装了几个 tab-completion 函数,包括一个用于 make

或者,只为 make: 安装选项卡完成。 /usr/share/bash-completion/completions/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 为目标,即使命令行使用 -f 以不同的 makefile 为目标。


T
Tim Hughes

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

L
Lisael

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

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


g
ginkgoMZD

这里有很多可行的解决方案,但正如我喜欢说的,“如果值得做一次,那就值得再做一次。”我确实赞成使用 (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

P
Penghe Geng

这是对 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 目标,这可能适用于您的情况,也可能不适用于您的情况

此外,它仅列出那些具有 echo 或 : 命令作为配方的第一个命令的 PHONY 目标。 : 意思是“什么都不做”。我在这里将它用于那些不需要回显的目标,例如上面的所有目标。

帮助目标还有一个附加技巧,可以在 make 帮助输出中添加“:”。


j
jvriesem

对于 Bash 脚本

这是在 bash 中执行此操作的一种非常简单的方法 - 基于 @cibercitizen1 above 的评论:

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

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


D
David

要扩展 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)'

M
Mikko Rantalainen

make 默认情况下不支持此功能,其他答案已显示如何自动提取可能的目标列表。

但是,如果您希望在没有任何副作用的情况下对列表进行更多控制(例如使用 .PHONY 目标来标记文档,这会阻止使用目标名称作为 Make 用来决定哪些目标的实际文件的逻辑需要重建),您可以为文档发明自己的语法。我更喜欢这样使用 ###

CPUS ?= $(shell nproc)
MAKEFLAGS += -j $(CPUS) -l $(CPUS) -s

# Basic paths
PREFIX  ?= usr
BINDIR  ?= $(PREFIX)/bin
ETCDIR  ?= etc
MANDIR  ?= $(PREFIX)/share/man
# ...

### help: Show help message (default target)
# use "help" as the default target (first target in the Makefile)
.PHONY: help
help:
    @printf "%s\n\n" "make: List of possible targets:"
    @grep '^### .*:' $(lastword $(MAKEFILE_LIST)) | sed 's/^### \([^:]*\): \(.*\)/\1:\t\2/' | column -ts "$$(printf '\t')"

### install: Install all files in $PREFIX (used by debian binary package build scripts)
install:
    install -D -o root -g root -m 755 ...
    ...

### release: Increase package version number
release:
    debchange --release

(像往常一样,缩进的文件必须以一个制表符开头,但 stackoverflow 无法正确重现该细节。)

输出将如下所示:

$ make
make: List of possible targets:

help:      Show help message (default target)
install:   Install all files in $PREFIX (used by debian binary package build scripts)
release:   Increase package version number

这是因为只有以 ### 开头且具有 : 字符的行才被视为要输出的文档。请注意,这故意不会提取实际的目标名称,而是仅完全信任文档行。这也允许始终为非常复杂的 Makefile 技巧发出正确的输出。另请注意,这避免了需要将文档行放在相对于实际规则的任何特定位置。我还故意避免对输出进行排序,因为只需按首选顺序列出文档行,就可以从 Makefile 本身完全控制输出顺序。

您显然可以发明任何其他您喜欢的语法,甚至可以做类似的事情

### en: install: Install all files in $PREFIX
### fi: asennus: asenna kaikki tiedostot hakemistoon $PREFIX

并且只打印与当前语言环境匹配的行以支持多种语言并具有别名来本地化目标名称:

.PHONY: asennus
asennus: install

最重要的问题是为什么要列出目标?您想要实际的文档还是某种调试信息?


s
seanlum

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

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

C
Community

上面的另一个附加答案。

在 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/ 意味着当您“制作选项卡选项卡”时......它将完成基于一个衬里脚本的目标。


您也可以使用 make -qp 而不是 cat。所以它会变成 make -qp | awk '/:/ && !/:=/ && /^[Az]/ && !/PATH/ && !/DISPLAY/ && !/TERM_SESSION/ && !/USER_TEXT_ENCODING/ && !/Makefile/ {print substr($0, 1 , 长度($0)-1) | “排序-u”}'
H
Heider Sati

我通常这样做:

grep install_targets 生成文件

它会返回如下内容:

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

我希望这有帮助


G
Guss

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

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

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

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


t
thamster

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

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

previous answer 非常复杂,因为它涵盖了您的答案未涵盖的所有场景:(a) 包含通过 变量 定义的目标(例如 $(SHELLS): ...),(b) 包含目标以数字开头,(c) 不包含 变量定义,(d) 如果 make 使用显式生成文件路径指定,则定位正确的生成文件。 (a) 是一个关键的缺点:即使您可靠地定位了 right makefile,由于缺少变量扩展,在一般情况下解析 raw 文件将无法正常工作。
顺便说一句,再复杂一点:您的命令可以简化为以下 - 但主要的一点是它不够健壮awk -F: '/^[A-z]/ {print $$1}' Makefile。最后,一个主要的学术观点:使用 [A-z] 而不是 [:alpha:] 意味着您会错过以 á 等外来字符开头的目标。
@mklement0 您的第一个 awk 命令运行良好,并且是迄今为止最简单的命令,尽管使用 [:alpha:] 似乎由于某种原因破坏了它 - 它错过了测试文件中的一些目标。
@bburns.km:我的错:我应该说 [[:alpha:]] 而不是 [:alpha:][:alpha:] 表示字符类([...]内部的区域感知字母集([...]),因此double [[ / ]] 的有效需求。
为什么 this 的答案如此复杂?您设法在这个简短的片段中收集了几个 useless 罪。 awk -F: '/^[A-Za-z]/ { print $$1 }' Makefile 使用单个进程并希望使用更正确的正则表达式(尽管您当然也可以使用带有下划线、数字等的目标,如前面的评论中所述)。