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

发布于 2024-10-03 14:32:35 字数 876 浏览 5 评论 0原文

我用过一点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/[电子邮件受保护]/msg06434.html

不管怎样,我做了一些小技巧来从 makefile 中提取目标,您可以将其包含在 makefile 中。

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

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

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

I've used rake a bit (a Ruby make program), and it has an option to get a list of all the available targets, eg

> 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...
...

but there seems to be no option to do this in GNU make.

Apparently the code is almost there for it, as of 2007 - http://www.mail-archive.com/[email protected]/msg06434.html.

Anyway, I made little hack to extract the targets from a makefile, which you can include in a makefile.

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

It will give you a list of the defined targets. It's just a start - it doesn't filter out the dependencies, for instance.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(29

梦旅人picnic 2024-10-10 14:32:36

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

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 这是一个内部规则

This is far from clean, but did the job, for me.

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

I use make -p that dumps the internal database, ditch stderr, use a quick and dirty grep -A 100000 to keep the bottom of the output. Then I clean the output with a couple of grep -v, and finally use cut to get what's before the colon, namely, the targets.

This is enough for my helper scripts on most of my Makefiles.

EDIT: added grep -v Makefile that is an internal rule

念三年u 2024-10-10 14:32:36

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

To expand on the answer given by @jsp, you can even evaluate variables in your help text with the $(eval) function.

The proposed version below has these enhanced properties:

  • Will scan any makefiles (even included)
  • Will expand live variables referenced in the help comment
  • Adds documentation anchor for real targets (prefixed with # TARGETDOC:)
  • Adds column headers

So to document, use this form:

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]

Then, somewhere in your 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)'
还在原地等你 2024-10-10 14:32:36

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

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

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

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

make doesn't support this by default and other answers have shown how to extract the list of possible targets automatically.

However, in case you want to have more control with the listing without any side-effects (such as using the .PHONY target to mark the documentation which prevents the logic of using the target names as actual files which Make uses to decide which targets needs to be rebuilt), you can invent your own syntax just for the documentation. I prefer to use ### like this:

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

(as usual, the indented files must start with exactly one tabulator but stackoverflow cannot reproduce that detail correctly.)

Output will look like this:

$ 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

This works because only lines starting with ### and having a : character are considered as the documentation to output. Note that this intentionally does not extract the actual target name but fully trusts the documentation lines only. This allows always emitting correct output for very complex Makefile tricks, too. Also note that this avoids needing to put the documentation line on any specific position relative to actual rule. I also intentionally avoid sorting the output because the order of output can be fully controlled from the Makefile itself simply by listing the documentation lines in preferred order.

You could obviously invent any other syntax you like and even do something like

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

and only print lines that match the current locale to support multiple languages and having aliases to localize the target names, too:

.PHONY: asennus
asennus: install

The most important question is why do you want to list the targets? Do you want actual documentation or some kind of debugging information?

层林尽染 2024-10-10 14:32:36

这是对 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 目标。食谱。 : 表示“不执行任何操作”。我在这里将它用于那些不需要 echo 的目标,例如上面的 all 目标。
  • help 目标还有一个额外的技巧,可以在 make help 输出中添加“:”。

This is a modification to jsp's very helpful answer (https://stackoverflow.com/a/45843594/814145). I like the idea of getting not only a list of targets but also their descriptions. jsp's Makefile puts the description as the comment, which I found often will be repeated in the target's description echo command. So instead, I extract the description from the echo command for each target.

Example 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

Output of 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

Notes:

  • Same as jsp's answer, only PHONY targets may be listed, which may or may not work for your case
  • In addition, it only lists those PHONY targets that have a echo or : command as the first command of the recipe. : means "do nothing". I use it here for those targets that no echo is needed, such as all target above.
  • There is an additional trick for the help target to add the ":" in the make help output.
风吹雨成花 2024-10-10 14:32:36

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

Plenty of workable solutions here, but as I like saying, "if it's worth doing once, it's worth doing again."
I did upvote the sugestion to use (tab)(tab), but as some have noted, you may not have completion support, or, if you have many include files, you may want an easier way to know where a target is defined.

I have not tested the below with sub-makes...I think it wouldn't work. As we know, recursive makes considered harmful.

.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:

  • list targets by include file.
  • output raw dynamic target definitions (replaces variable delimiters with modulo)
  • output each target on a new line
  • seems clearer (subjective opinion)

Explanation:

  • foreach file in the MAKEFILE_LIST
  • output the name of the file
  • grep lines containing a colon, that are not indented, not comments, and don't start with a period
  • exclude immediate assignment expressions (:=)
  • cut, sort, indent, and chop rule-dependencies (after colon)
  • munge variable delimiters to prevent expansion

Sample Output:

 = 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
暖伴 2024-10-10 14:32:36

这是 bash-completion 脚本功能的非常简化版本。

make -npq : 2> /dev/null | \
      awk -v RS= -F: '$1 ~ /^[^#%]+$/ { print $1 }'

说明:

  • make -npq:打印数据库而不执行任何操作
  • -v RS=:按整个段落分隔记录
  • -F::按:(因此规则名称为$1
  • $1 ~ /^[^#%]+$/:匹配不包含的规则#% (注释或模式规则)
  • { print $1 }:打印规则名称

这比 mklement0 的方法(我自己修复了),效果更好。

This is a very simplified version of what the bash-completion script does.

make -npq : 2> /dev/null | \
      awk -v RS= -F: '$1 ~ /^[^#%]+$/ { print $1 }'

Explanation:

  • make -npq: Print the database without executing anything
  • -v RS=: Separate records by whole paragraphs
  • -F:: Separate fields by : (so the rule name is $1)
  • $1 ~ /^[^#%]+$/: Match rules that don't contain # or % (comments or pattern rules)
  • { print $1 }: Print the rule name

This is much simpler than mklement0's approach (which I fixed myself), and works better.

脱离于你 2024-10-10 14:32:36

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

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

This one was helpful to me because I wanted to see the build targets required (and their dependencies) by the make target. I know that make targets cannot begin with a "." character. I don't know what languages are supported, so I went with egrep's bracket expressions.

cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
后知后觉 2024-10-10 14:32:36

对上面的另一个补充答案。

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

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

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

目标1

目标2

目标3

应该是相同的语句,确保使用 $$variable 而不是 $variable 对变量进行转义。

解释

cat - 吐出内容

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

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

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

这是一个粗略的输出,您可以用以下命令做更多时髦的事情AWK。尽量避免使用 sed,因为它在 BSD 变体中不一致,即某些在 *nix 上可以工作,但在 MacOSX 等 BSD 上却失败。

更多

您应该能够将其(经过修改)添加到 ma​​ke 的文件中,默认的 bash-completion 文件夹 /usr/local/等/bash-completion.d/
意思是当您“制作选项卡选项卡”时。它将根据单行脚本完成目标。

Yet another additional answer to above.

tested on MacOSX using only cat and awk on terminal

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

will output of the make file like below:

target1

target2

target3

in the Makefile, it should be the same statement, ensure that you escape the variables using $$variable rather than $variable.

Explanation

cat - spits out the contents

| - pipe parses output to next awk

awk - runs regex excluding "shell" and accepting only "A-z" lines then prints out the $1 first column

awk - yet again removes the last character ":" from the list

this is a rough output and you can do more funky stuff with just AWK. Try to avoid sed as its not as consistent in BSDs variants i.e. some works on *nix but fails on BSDs like MacOSX.

More

You should be able add this (with modifications) to a file for make, to the default bash-completion folder /usr/local/etc/bash-completion.d/
meaning when you "make tab tab" .. it will complete the targets based on the one liner script.

剩余の解释 2024-10-10 14:32:36

对于 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

For AWK haters, and for simplicity, this contraption works for me:

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

(for use outside a Makefile, just remove $(lastword ...) or replace it with the Makefile path).

This solution will not work if you have "interesting" rule names but will work well for most simple setups. The main downside of a make -qp based solution is (as in other answers here) that if the Makefile defines variable values using functions - they will still be executed regardless of -q, and if using $(shell ...) then the shell command will still be called and its side effects will happen. In my setup often the side effects of running shell functions is unwanted output to standard error, so I add 2>/dev/null after the make command.

感情废物 2024-10-10 14:32:36

我受到 @jspanswer 适应了他的文件解析技术,这个版本有以下好处。

  • 自动检测 makefile 名称
  • 支持节标题
  • 对于该节中的所有目标,将 .PHONY 放置在每个节标题下方,并将注释放在目标后面,以便看起来更清晰。

给定这样的 makefile,

#=Default Command

.PHONY: help

help: # Shows the available make commands.
    @sed -nr \
        -e 's|^#=(.*)|\n\1:|p' \
        -e 's|^([a-zA-Z-]*):((.*?)# (.*))?|  \1 \4|p' \
        $(lastword $(MAKEFILE_LIST)) \
        | expand -t20

#=Project Commands

.PHONY: install build

install: # Installs the thing
    @echo install

build: install # Builds the thing
    @echo build

#=Other Commands

.PHONY: foo bar

foo:
    @echo foo

bar:
    @echo bar

您将得到如下输出


Default Command:
  help              Shows the available make commands.

Project Commands:
  install           Installs the thing
  build             Builds the thing

Other Commands:
  foo
  bar

正则表达式解释:
's|^#=(.*)|\n\1:|p' 捕获节声明 #= 后括号中的所有内容并打印 \1 前面有一个换行符,后面有一个冒号。

's|^([a-zA-Z-]*):((.*?)# (.*))?| \1 \4|p' 捕获目标名称并包含用于描述的可选捕获组 ()?。可选组包围另外两个捕获组,第一个捕获 :# 之间的所有内容,第二个捕获帮助文本(旁白:正则表达式有点复杂,但它让我们输出带注释和不带注释的目标(开放更好的方法)。最后,它输出目标名称 \1 和描述 \4 缩进 2 个空格以将其塞到节标题下。

I was inspired by @jsp's answer to adapt his file parsing technique and this version has the following benefits.

  • Automatically detects makefile name
  • Supports section headers
  • Places .PHONY below each section header for all targets within that section and places comments after the target so it's a little cleaner to look at.

Given a makefile like this

#=Default Command

.PHONY: help

help: # Shows the available make commands.
    @sed -nr \
        -e 's|^#=(.*)|\n\1:|p' \
        -e 's|^([a-zA-Z-]*):((.*?)# (.*))?|  \1 \4|p' \
        $(lastword $(MAKEFILE_LIST)) \
        | expand -t20

#=Project Commands

.PHONY: install build

install: # Installs the thing
    @echo install

build: install # Builds the thing
    @echo build

#=Other Commands

.PHONY: foo bar

foo:
    @echo foo

bar:
    @echo bar

You'll get output like this


Default Command:
  help              Shows the available make commands.

Project Commands:
  install           Installs the thing
  build             Builds the thing

Other Commands:
  foo
  bar

Regex explanations:
's|^#=(.*)|\n\1:|p' Captures everything in parens after section declaration #= and prints it \1 with a newline before and colon after it.

's|^([a-zA-Z-]*):((.*?)# (.*))?| \1 \4|p' Captures target name and includes an optional capture group ()? for the description. The optional group surrounds two more capture groups, the first one capturing everything between : and # and the second capturing the help text (aside: the regex is a bit complicated but it lets us output targets with and without comments. Open to better methods). Finally it outputs the target name \1 and description \4 indented 2 spaces to tuck it under the section header.

静谧 2024-10-10 14:32:36

这是使用 cat、grep 和 cut 的版本。

cat Makefile | grep : | grep -v '^\t' | cut -d: -f1

Here is a version using cat, grep and cut.

cat Makefile | grep : | grep -v '^\t' | cut -d: -f1
隐诗 2024-10-10 14:32:36

tl;dr 我个人为我构建的每个 Makefile 复制粘贴相同的 help 目标。

.SILENT:

.PHONY: help
## This help screen
help:
    printf "Available targets\n\n"
    awk '/^[a-zA-Z\-\_0-9]+:/ { \
        helpMessage = match(lastLine, /^## (.*)/); \
        if (helpMessage) { \
            helpCommand = substr($1, 0, index($1, ":")-1); \
            helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
            printf "%-30s %s\n", helpCommand, helpMessage; \
        } \
    } \
    { lastLine = $0 }' $(MAKEFILE_LIST)

我还在这个 Github gist 中保留了它的副本:
https://gist.github.com/Olshansk/689fc2dee28a44397c6e31a0776ede30

tl;dr I personally copy-paste the same help target for every Makefile I build.

.SILENT:

.PHONY: help
## This help screen
help:
    printf "Available targets\n\n"
    awk '/^[a-zA-Z\-\_0-9]+:/ { \
        helpMessage = match(lastLine, /^## (.*)/); \
        if (helpMessage) { \
            helpCommand = substr($1, 0, index($1, ":")-1); \
            helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
            printf "%-30s %s\n", helpCommand, helpMessage; \
        } \
    } \
    { lastLine = $0 }' $(MAKEFILE_LIST)

I also maintain a copy of it in this Github gist:
https://gist.github.com/Olshansk/689fc2dee28a44397c6e31a0776ede30

半﹌身腐败 2024-10-10 14:32:36

非常简单的 AWK 解决方案:(

all:
    @awk -F'[ :]' '!/^all:/ && /^([A-z_-]+):/ {print "make " $1}' Makefile

注意:这并不涵盖 接受的答案 中的所有极端情况,如 此处解释。)

Very simple AWK solution:

all:
    @awk -F'[ :]' '!/^all:/ && /^([A-z_-]+):/ {print "make " $1}' Makefile

(Note: This doesn't cover all the corner-cases as the accepted answer, as explained here.)

笑红尘 2024-10-10 14:32:36

试试这个:

make -qp | awk -F':' '/^[^ \t.%][-A-Za-z0-9_]*:/ {split($0,A,/ /);for(i in A)if(match(A[i],/^[^.%][-A-Za-z0-9_]*/))print substr(A[i],1,RLENGTH)}' | sort -u

Try this one:

make -qp | awk -F':' '/^[^ \t.%][-A-Za-z0-9_]*:/ {split($0,A,/ /);for(i in A)if(match(A[i],/^[^.%][-A-Za-z0-9_]*/))print substr(A[i],1,RLENGTH)}' | sort -u
倚栏听风 2024-10-10 14:32:36

我通常这样做:

grep install_targets Makefile

它会返回类似以下内容:

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

我希望这有帮助

I usually do:

grep install_targets Makefile

It would come back with something like:

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

I hope this helps

清风不识月 2024-10-10 14:32:36

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

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

not sure why the previous answer was so complicated:

list:
    cat Makefile | grep "^[A-z]" | awk '{print $1}' | sed "s/://g" 
最单纯的乌龟 2024-10-10 14:32:35

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

make spacetabtab


如果您正在使用一个简单的发行版(可能是一个容器),您可能需要安装一个软件包。

$ apt-get install bash-completion ## Debian/Ubuntu/etc.
$ . /etc/bash_completion ## or just launch bash again

Under Bash (at least), this can be done automatically with tab completion:

make spacetabtab


If you're using a bare-bones distribution (maybe a container) you might need to install a package.

$ apt-get install bash-completion ## Debian/Ubuntu/etc.
$ . /etc/bash_completion ## or just launch bash again
つ低調成傷 2024-10-10 14:32:35

前言

  • 根据这个答案制作版本< em>post-4.4.1 将原生支持新的 --print-targets 选项,这使得下面的解决方案变得不必要。

  • 此处的另一个答案基于下面的解决方案,并添加了对打印描述的支持描述以及目标名称,假设您已在目标中嵌入了 @# 前缀注释。


注意:此答案已更新,自 GNU make v4.3 起仍然有效 - 如果您遇到问题,请告诉我们。

这是对 Brent Bradburn 的伟大方法的尝试,如下所示:

  • 使用更强大的命令来提取目标名称,这希望可以防止任何误报(并且消除不必要的 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 $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$)/,/(^|\n)# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$'
# IMPORTANT: The line above must be indented by (at least one) 
#            *actual TAB character* - *spaces* do *not* work.

重要:粘贴此内容时,确保最后一行缩进正好 1 个实际制表符。(空格不起作用

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

规则说明

  • .PHONY:列表

    • 将目标列表声明为虚假目标,即一个引用文件,因此应该无条件调用其配方
  • LC_ALL=C 确保make 的输出为英语,因为输出的解析依赖于Bastian Bittorf

    致敬

  • $(MAKE) -pRrq -f $(第一个字 $(MAKEFILE_LIST)) : 2>/dev/null

    • 再次调用 make 以打印和解析从 makefile 派生的数据库:
      • -p 打印数据库

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

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

      • -f $(firstword $(MAKEFILE_LIST)) 确保与原始调用中的目标相同的 makefile,无论它是使用 隐式还是显式定位的 - f...
        (由于 MAKEFILE_LIST 还包含 included Makefile 的列表,因此 firstword 用于仅提取原始目标文件;这意味着目标文件文件名/路径不得包含空格,但实际情况不太可能如此)。

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

  • <代码>-v RS=

    • 这是一个 awk 习惯用法,它将输入分成连续的非空行块。
  • /(^|\n)# 文件(\n|$$)/,/(^|\n)# 数据库制作完成/

    • 匹配输出中跨段落包含所有目标的行范围 - 通过将解析限制在此范围内,无需处理来自其他输出部分的误报。
    • 注意:在 make 版本 3.x 和 4.3 之间,make 输出中的段落结构发生了变化,因此 (^|\n) / (\n|$$) 确保识别感兴趣行的跨段落范围的开始和结束的行被检测到,无论它们出现在开始还是内部 /在段落末尾。
  • if ($$1 !~ "^[#.]")

    • 有选择地忽略块:
      • # ... 忽略非目标,其块以 # Not a target:
      • 开头

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

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

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

Preface:

  • Per this answer, make versions post-4.4.1 will natively support a new --print-targets option, which makes the solution below unnecessary.

  • This other answer here builds on the solution below, and adds support for printing descriptions along with target names, assuming you've embedded @#-prefixed comments in your targets.


Note: This answer has been updated to still work as of GNU make v4.3 - let us know if you come across something that breaks.

This is an attempt to improve on Brent Bradburn's great approach as follows:

  • uses a more robust command to extract the target names, which hopefully prevents any false positives (and also does away with the unnecessary sh -c)
  • does not invariably target the makefile in the current directory; respects makefiles explicitly specified with -f <file>
  • excludes hidden targets - by convention, these are targets whose name starts neither with a letter nor a digit
  • makes do with a single phony target
  • prefixes the command with @ to prevent it from being echoed before execution

Curiously, GNU make has no feature for listing just the names of targets defined in a makefile. While the -p option produces output that includes all targets, it buries them in a lot of other information and also executes the default target (which could be suppressed with -f/dev/null).

Place the following rule in a makefile for GNU make to implement a target named list that simply lists all target names in alphabetical order - i.e.: invoke as make list:

.PHONY: list
list:
	@LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$)/,/(^|\n)# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$'
# IMPORTANT: The line above must be indented by (at least one) 
#            *actual TAB character* - *spaces* do *not* work.

Important: On pasting this, make sure that the last line is indented by exactly 1 actual tab char. (spaces do not work).

Note that sorting the resulting list of targets is the best option, since not sorting doesn't produce a helpful ordering in that the order in which the targets appear in the makefile is not preserved.
Also, the sub-targets of a rule comprising multiple targets are invariably output separately and will therefore, due to sorting, usually not appear next to one another; e.g., a rule starting with a z: will not have targets a and z listed next to each other in the output, if there are additional targets.

Explanation of the rule:

  • .PHONY: list

    • declares target list a phony target, i.e., one not referring to a file, which should therefore have its recipe invoked unconditionally
  • LC_ALL=C makes sure that make's output in in English, as parsing of the output relies on that.Tip of the hat to Bastian Bittorf

  • $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null

    • Invokes make again in order to print and parse the database derived from the makefile:
      • -p prints the database

      • -Rr suppresses inclusion of built-in rules and variables

      • -q only tests the up-to-date-status of a target (without remaking anything), but that by itself doesn't prevent execution of recipe commands in all cases; hence:

      • -f $(firstword $(MAKEFILE_LIST)) ensures that the same makefile is targeted as in the original invocation, regardless of whether it was targeted implicitly or explicitly with -f ....
        (Since MAKEFILE_LIST also contains the list of included Makefiles, firstword is used to extract only the originally targeted file; the implication is that the targeted file name / path must not contain spaces, but that is unlikely to be the case in practice).

      • : is a deliberately invalid target that is meant to ensure that no commands are executed; 2>/dev/null suppresses the resulting error message. Note: This relies on -p printing the database nonetheless, which is the case as of GNU make 3.82. Sadly, GNU make offers no direct option to just print the database, without also executing the default (or given) task; if you don't need to target a specific Makefile, you may use make -p -f/dev/null, as recommended in the man page.

  • -v RS=

    • This is an awk idiom that breaks the input into blocks of contiguous non-empty lines.
  • /(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/

    • Matches the range of lines in the output that contains all targets, across paragraphs - by limiting parsing to this range, there is no need to deal with false positives from other output sections.
    • Note: Between make versions 3.x and 4.3, paragraph structuring in make's output changed, so (^|\n) / (\n|$$) ensures that the lines that identify the start and the end of the cross-paragraph range of lines of interest are detected irrespective of whether they occur at the start or inside / at the end of a paragraph.
  • if ($$1 !~ "^[#.]")

    • Selectively ignores blocks:
      • # ... ignores non-targets, whose blocks start with # Not a target:
      • . ... ignores special targets
    • All other blocks should each start with a line containing only the name of an explicitly defined target followed by :
  • grep -E -v -e '^[^[:alnum:]]' -e '^$@$$' removes unwanted targets from the output:

    • '^[^[:alnum:]]' ... excludes hidden targets, which - by convention - are targets that start neither with a letter nor a digit.
    • '^$@$$' ... excludes the list target itself

Running make list then prints all targets, each on its own line; you can pipe to xargs to create a space-separated list instead.

空‖城人不在 2024-10-10 14:32:35

更新!

1 月 8 日起, 2024,Make 有一个 --print-targets 选项,应该可以在不使用 hacky 正则表达式的情况下正确执行此操作。当前版本是 Make 4.4.1,因此之后的下一个版本将具有此功能。


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

如果没有,我编写了一个补丁来为Make添加对这个明显有用的功能的适当支持。这比这里的所有其他答案都要好得多,这些答案都涉及 grep Makefiles 的可怕黑客行为。如果您包含其他 Makefile、使用计算的目标名称等,这显然不起作用。

补丁尚未合并,因此您必须从源代码构建。这还不错,但您确实需要一些与 autoconf 相关的旧的构建工具:

git clone https://github.com/Timmmm/make
cd make
./bootstrap
./configure
make -j4

在 Linux 上,您可以使用 我已经构建了这个二进制文件

然后您可以使用 -l 标志来列出目标:

./make -C /path/to/your/project -l

Update!

As of Jan 8th, 2024, Make has a --print-targets option that should do this properly without hacky regexes. The current version is Make 4.4.1 so the next release after that will have this feature.


If your Makefile was created by CMake you might be able to run 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

If not, I wrote a patch to add proper support for this obviously useful feature to Make. This is way better than all the other answers here which all involve horrible hacks to grep the Makefiles. That obviously doesn't work if you include other Makefiles, use computed target names, etc.

The patch hasn't been merged so you have to build from source. It's not too bad but you do need some crusty old autoconf-related build tools:

git clone https://github.com/Timmmm/make
cd make
./bootstrap
./configure
make -j4

On Linux you can use this binary I built already.

Then you can use the -l flag to list targets:

./make -C /path/to/your/project -l
明媚殇 2024-10-10 14:32:35

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

I combined these two answers: https://stackoverflow.com/a/9524878/86967 and https://stackoverflow.com/a/7390874/86967
and did some escaping so that this could be used from inside a 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
黎夕旧梦 2024-10-10 14:32:35

正如 mklement0 指出的,GNU-make 中缺少列出所有 Makefile 目标的功能,他的答案和其他人提供了方法来做到这一点。

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

As mklement0 points out, a feature for listing all Makefile targets is missing from GNU-make, and his answer and others provides ways to do this.

However, the original post also mentions rake, whose tasks switch does something slightly different than just listing all tasks in the rakefile. Rake will only give you a list of tasks that have associated descriptions. Tasks without descriptions will not be listed. This gives the author the ability to both provide customized help descriptions and also omit help for certain targets.

If you want to emulate rake's behavior, where you provide descriptions for each target, there is a simple technique for doing this: embed descriptions in comments for each target you want listed.

You can either put the description next to the target or, as I often do, next to a PHONY specification above the target, like this:

.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

Which will yield

$ make help
target1             Target 1 help text
target2             Target 2 help text

...
help                Generate list of targets with descriptions

You can also find a short code example in this gist and here too.

Again, this does not solve the problem of listing all the targets in a Makefile. For example, if you have a big Makefile that was maybe generated or that someone else wrote, and you want a quick way to list its targets without digging through it, this won't help.

However, if you are writing a Makefile, and you want a way to generate help text in a consistent, self-documenting way, this technique may be useful.

我只土不豪 2024-10-10 14:32:35

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

这就是 make 的 bash 补全模块获取其列表的方式:

make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /); for(i in A)打印 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)

My favorite answer to this was posted by Chris Down at Unix & Linux Stack Exchange. I'll quote.

This is how the bash completion module for make gets its list:

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

It prints out a newline-delimited list of targets, without paging.

User Brainstone suggests piping to sort -u to remove duplicate entries:

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

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

红焚 2024-10-10 14:32:35

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

If you have bash completion for make installed, the completion script will define a function _make_target_extract_script. This function is meant to create a sed script which can be used to obtain the targets as a list.

Use it like this:

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

# List targets from Makefile
sed -nrf <(_make_target_extract_script --) Makefile
任性一次 2024-10-10 14:32:35

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

命令链的解释:

Focusing on an easy syntax for describing a make target, and having a clean output, I chose this approach:

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:

So treating the above as a Makefile and running it gives you something like

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

Explanation for the chain of commands:

橪书 2024-10-10 14:32:35

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

示例 Makefile:

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 在此发布的完全一样 - 如何获取 makefile 中的目标列表?

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

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

I took a few answers mentioned above and compiled this one, which can also generate a nice description for each target and it works for targets with variables too.

Example Makefile:

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"'

Example output:

$ 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

How does it work:

The top part of the make help target works exactly as posted by mklement0 here - How do you get the list of targets in a makefile?.

After getting the list of targets, it runs make <target> -nB as a dry run for each target and parses the last line that starts with @# Help: for the description of the target. And that or an empty string is printed in a nicely formatted table.

As you can see, the variables are even expanded within the description as well, which is a huge bonus in my book :).

攒一口袋星星 2024-10-10 14:32:35

对于 Bash 脚本

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

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

另请参阅@Marc.2377的更权威的答案,它说明了Bash如何完成make 的模块可以做到这一点。

For a Bash Script

Here's a very simple way to do this in bash -- based on the comment by @cibercitizen1 above:

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

See also the more authoritative answer by @Marc.2377, too, which says how the Bash completion module for make does it.

浅唱々樱花落 2024-10-10 14:32:35

将此目标添加到您的 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

这对我有用

Add this target to your 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].*:$$': searches for lines which start with lowercase and ends with ":"
  • sed -e 's~:~~g': deletes the ":"

Then just run:

make help

This works for me ????


PD: more info at...

make --help
苏别ゝ 2024-10-10 14:32:35

@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 <文件>

@nobar's answer helpfully shows how to use tab completion to list a makefile's targets.

  • This works great for platforms that provide this functionality by default (e.g., Debian, Fedora).

  • On other platforms (e.g., Ubuntu) you must explicitly load this functionality, as implied by @hek2mgl's answer:

    • . /etc/bash_completion installs several tab-completion functions, including the one for make
    • Alternatively, to install only tab completion for make:
      • . /usr/share/bash-completion/completions/make
  • For platforms that don't offer this functionality at all, such as OSX, you can source the following commands (adapated from here) to implement it:
_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
  • Note: This is not as sophisticated as the tab-completion functionality that comes with Linux distributions: most notably, it invariably targets the makefile in the current directory, even if the command line targets a different makefile with -f <file>.
蓝戈者 2024-10-10 14:32:35

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

仅使用 sedxargsprintf,这些都是非常常见的。

使用 < $(MAKEFILE_LIST) 允许调用除 Makefile 之外的其他内容,例如 Makefile.github

您可以在 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

This help target will only print targets which have ## followed by a description. This allows for documenting both public and private targets. Using the .DEFAULT_GOAL makes the help more discoverable.

Only sed, xargs and printf used which are pretty common.

Using the < $(MAKEFILE_LIST) allows for the makefile to be called something other than Makefile for instance Makefile.github

You can customize the output to suit your preference in the printf. This example is set up to match the OP's request for rake style output

When cutting and pasting the below make file, don't forget to change the 4 spaces indentation to tabs.

# 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


Here is the output from the Makefile above. Notice the private target doesn't get output because it only has a single # for it's comment.

$ make
make help                # these help instructions
make lint                # style, bug and quality checker
make test                # run pytest with coverage
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文