我使用过一些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。
不管怎样,我做了一个小hack来从makefile中提取目标,你可以将它包含在makefile中。
list:
@grep '^[^#[:space:]].*:' Makefile
它会给你一个已定义目标的列表。这只是一个开始——例如,它并没有过滤掉依赖关系。
> make list
list:
copy:
run:
plot:
turnin:
@nobar的回答很有帮助地展示了如何使用TAB补全来列出makefile的目标。
这对于默认提供此功能的平台(例如Debian、Fedora)非常有效。
在其他平台上(例如Ubuntu),你必须显式加载这个功能,正如@hek2mgl的回答所暗示的那样:
. /etc/bash_completion安装几个制表符补全函数,包括make函数
或者,为make只安装制表符补全:
. /usr/share/bash-completion /完成/
对于根本不提供这个功能的平台,比如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 <file>为目标的另一个makefile也是如此。
这个帮助目标只打印带有##后跟描述的目标。这允许同时记录公共目标和私有目标。使用. default_goal使帮助更容易被发现。
只使用sed, xargs和printf,这是非常常见的。
使用< $(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的输出。注意,私有目标没有得到输出,因为它的注释只有一个#。
$ make
make help # these help instructions
make lint # style, bug and quality checker
make test # run pytest with coverage
正如mklement0所指出的,GNU-make中缺少列出所有Makefile目标的功能,他的回答和其他回答提供了实现这一点的方法。
然而,最初的帖子也提到了rake,它的任务开关做的事情与仅仅在rakefile中列出所有任务略有不同。Rake只会给您一个有相关描述的任务列表。没有描述的任务将不会被列出。这使得作者既可以提供定制的帮助描述,也可以省略某些目标的帮助。
如果您想模拟rake的行为,为每个目标提供描述,有一个简单的技术可以做到这一点:在注释中嵌入您想列出的每个目标的描述。
你可以把描述放在目标旁边,或者像我经常做的那样,放在目标上面的PHONY规范旁边,就像这样:
.PHONY: target1 # Target 1 help text
target1: deps
[... target 1 build commands]
.PHONY: target2 # Target 2 help text
target2:
[... target 2 build commands]
...
.PHONY: help # Generate list of targets with descriptions
help:
@grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20
它会屈服
$ make help
target1 Target 1 help text
target2 Target 2 help text
...
help Generate list of targets with descriptions
你也可以在这里找到一个简短的代码示例。
同样,这不能解决在Makefile中列出所有目标的问题。例如,如果您有一个大的Makefile,它可能是生成的或由其他人编写的,并且您想要一种快速的方法来列出它的目标,而不需要深入研究它,那么这将没有帮助。
但是,如果您正在编写Makefile,并且希望以一致的、自记录的方式生成帮助文本,则此技术可能会有用。
专注于描述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
命令链的解释:
First, grep all targets and their preceeding line, see https://unix.stackexchange.com/a/320709/223029.
Then, get rid of the group separator, see https://stackoverflow.com/a/2168139/1242922.
Then, we collapse each pair of lines to parse it later, see https://stackoverflow.com/a/9605559/1242922.
Then, we parse for valid lines and remove those which do not match, see https://stackoverflow.com/a/8255627/1242922, and also give the output our desired order: command, then description.
Lastly, we arrange the output like a table.
这里有很多可行的解决方案,但正如我喜欢说的,“如果值得做一次,就值得再做一次。”
我确实赞成使用(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行,不缩进,没有注释,也不以句号开头
排除立即赋值表达式(:=)
切、排序、缩进和切规则依赖项(冒号后)
蒙格变量分隔符以防止扩展
样例输出:
= 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
我把这两个答案结合起来:https://stackoverflow.com/a/9524878/86967和https://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