首页 文章

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

提问于
浏览
137

我已经使用了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:

12 回答

  • 24

    在Bash(至少)下,这可以通过选项卡完成自动完成:

    make(space)(tab)(tab)
    
  • 3

    这是为了改进@nobar's great approach如下:

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

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

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

    • 使用单个虚假目标

    • 使用 @ 作为前缀,以防止在执行前回显该命令


    奇怪的是,GNU make 没有列出makefile中定义的目标名称的功能 . -p 选项生成包含所有目标的输出,但将其隐藏在许多其他信息中 .

    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:
        @$(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 '^$@$$' | xargs
    

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

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

    Explanation of the rule

    • . PHONY: list

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

    • $(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null

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

    • -p 打印数据库

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

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

    • -f $(lastword $(MAKEFILE_LIST)) 确保与原始调用中的目标相同的makefile,无论它是使用 -f ... 隐式还是显式定向 .
      CaveatThis will break if your makefile contains include directives; to address this, define variable THIS_FILE := $(lastword $(MAKEFILE_LIST)) before any include directives and use -f $(THIS_FILE) instead.

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

    • -v RS=

    • 这是一个awk成语,它将输入分解为连续的非空行块 .

    • /^# File/,/^# Finished Make data base/

    • 匹配输出中包含所有目标的行范围(GNU make 3.82时为true) - 通过将解析限制到此范围,不需要处理来自其他输出节的误报 .

    • if ($$1 !~ "^[#.]")

    • 有选择地忽略块:

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

    • . ...忽略特殊目标

    • 所有其他块应各自以一行开头,该行仅包含明确定义的目标的名称,后跟 :

    • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' 从输出中删除不需要的目标:

    • '^[^[:alnum:]]' ...排除隐藏目标,按惯例,这些目标既不是字母也不是数字 .

    • '^$@$$' ...排除 list 目标本身

    • xargs

    • 有效地将输出行转换为单行,以空格分隔的列表;如果您希望每个目标名称显示在其自己的行上,请省略此项 .

  • 10

    这里有很多可行的解决方案,但正如我喜欢说的那样,“如果值得做一次,那就值得再做一次 . ”我做了upvote sugestion使用(tab)(tab),但有些人已经注意到,你可能没有完成支持,或者,如果你有很多包含文件,你可能想要一种更简单的方法来知道目标的定义 .

    我没有测试下面的子品牌...我认为它不会起作用 . 众所周知,递归会被认为是有害的 .

    .PHONY: list ls
    ls list :
        @# search all include files for targets.
        @# ... excluding special targets, and output dynamic rule definitions unresolved.
        @for inc in $(MAKEFILE_LIST); do \
        echo ' =' $$inc '= '; \
        grep -Eo '^[^\.#[:blank:]]+.*:.*' $$inc | grep -v ':=' | \
        cut -f 1 | sort | sed 's/.*/  &/' | sed -n 's/:.*$$//p' | \
        tr $$ \\\ | tr $(open_paren) % | tr $(close_paren) % \
    ; done
    
    # to get around escaping limitations:
    open_paren := \(
    close_paren := \)
    

    我喜欢哪个因为:

    • 按包含文件列出目标 .

    • 输出原始动态目标定义(用模数替换变量分隔符)

    • 在新行上输出每个目标

    • 似乎更清楚(主观意见)

    说明:

    MAKEFILE_LIST中的

    • foreach文件

    • 输出文件名
      包含冒号的

    • grep行,不缩进,不是注释,也不以句点开头

    • 排除立即赋值表达式(:=)

    • 剪切,排序,缩进和切断规则依赖(冒号后)

    • munge变量分隔符以防止扩展

    样本输出:

    = Makefile = 
      includes
      ls list
     = util/kiss/snapshots.mk = 
      rotate-db-snapshots
      rotate-file-snapshots
      snap-db
      snap-files
      snapshot
     = util/kiss/main.mk = 
      dirs
      install
       %MK_DIR_PREFIX%env-config.php
       %MK_DIR_PREFIX%../srdb
    
  • 5

    我将这两个答案结合起来: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
    
  • 17

    这是对jsp非常有用的答案(https://stackoverflow.com/a/45843594/814145)的修改 . 我喜欢不仅要获得目标列表而且还要获取它们的描述 . jsp 's Makefile puts the description as the comment, which I found often will be repeated in the target' s description echo命令 . 因此,我从每个目标的echo命令中提取描述 .

    示例Makefile:

    .PHONY: all
    all: build
            : "same as 'make build'"
    
    .PHONY: build
    build:
            @echo "Build the project"
    
    .PHONY: clean
    clean:
            @echo "Clean the project"
    
    .PHONY: help
    help:
            @echo -n "Common make targets"
            @echo ":"
            @cat Makefile | sed -n '/^\.PHONY: / h; /\(^\t@*echo\|^\t:\)/ {H; x; /PHONY/ s/.PHONY: \(.*\)\n.*"\(.*\)"/    make \1\t\2/p; d; x}'| sort -k2,2 |expand -t 20
    

    输出 make help

    $ make help
    Common make targets:
        make all        same as 'make build'
        make build      Build the project
        make clean      Clean the project
        make help       Common make targets
    

    笔记:

    • jsp的答案相同,只列出PHONY目标,这可能适用于您的案例,也可能不适用

    • 此外,它仅列出那些具有 echo: 命令作为配方的第一个命令的PHONY目标 . : 表示"do nothing" . 我在这里用它来表示那些不需要回声的目标,比如上面的 all 目标 .

    • help 目标还有一个额外的技巧是在 make help 输出中添加":" .

  • 1

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

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

  • 80

    @nobar's answer帮助显示如何 use tab completion to list a makefile's targets .

    • 这适用于提供此功能的平台 by default (例如, Debian, Fedora ) .

    • 在其他平台(例如 Ubuntu )上,您必须 explicitly load 此功能,如@hek2mgl's answer所示:

    • . /etc/bash_completion 安装了几个制表符完成功能,包括 make 的功能

    • 或者,仅为 make 安装选项卡完成:

    • . /usr/share/bash-completion/completions/make

    • 对于 platforms that don't offer this functionality at all ,例如 OSX ,您可以 source the following commands (从here调整)来实现它:

    _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发行版附带的制表符完成功能那样复杂:最明显的是它 invariably targets the makefile in the current directory ,即使命令行使用 -f <file> 定位不同的makefile .
  • 1

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

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

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

    作为mklement0 points out,GNU-make缺少列出所有Makefile目标的功能,他的回答和其他提供了实现此目的的方法 .

    但是,原帖还提到rake,其任务切换与仅列出rakefile中的所有任务略有不同 . Rake只会为您提供具有相关描述的任务列表 . 没有描述的任务will not be listed . 这使作者既能提供自定义帮助描述,又能省略某些目标的帮助 .

    如果你想模仿rake的行为,你提供了descriptions for each target,有一种简单的方法可以做到这一点:在你想要列出的每个目标的注释中嵌入描述 .

    您可以将描述放在目标旁边,或者像往常一样,将其放在目标上方的PHONY规范旁边,如下所示:

    .PHONY: target1 # Target 1 help text
    target1: deps
        [... target 1 build commands]
    
    .PHONY: target2 # Target 2 help text
    target2:
        [... target 2 build commands]
    
    ...                                                                                                         
    
    .PHONY: help # Generate list of targets with descriptions                                                                
    help:                                                                                                                    
        @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20
    

    哪个会产生

    $ make help
    target1             Target 1 help text
    target2             Target 2 help text
    
    ...
    help                Generate list of targets with descriptions
    

    您也可以在this gisthere中找到一个简短的代码示例 .

    同样,这并没有解决在Makefile中列出所有目标的问题 . 例如,如果你有一个可能生成的大型Makefile或其他人写的,你想要一个快速的方法来列出它的目标而不需要挖掘它,这将无济于事 .

    但是,如果您正在编写Makefile,并且希望以一致的自我文档方式生成帮助文本,则此技术可能很有用 .

  • 129

    不确定为什么以前的答案是如此复杂:

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

相关问题