代码之家  ›  专栏  ›  技术社区  ›  Brian Burns Yugansh

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

  •  159
  • Brian Burns Yugansh  · 技术社区  · 15 年前

    我已经使用了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制造中似乎没有这样做的选择。

    很明显,从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:
    
    13 回复  |  直到 7 年前
        1
  •  88
  •   mklement0    7 年前

    这是一种改进的尝试 @nobar's great approach 如下:

    • 使用一个更强大的命令来提取目标名称,这样可以防止任何误报(也可以消除不必要的 sh -c )
    • 并不总是以makefile为目标 现在的 目录;尊重用显式指定的makefiles -f <file>
    • 排除隐藏目标-按照惯例,这些目标的名称既不是字母也不是数字
    • 与…有关 单一的 伪目标
    • 在命令前面加上 @ 以防止在执行前回音

    奇怪的是,GNU make 没有只列出makefile中定义的目标名称的功能。这个 -p 选项生成包含所有目标的输出,但会将它们埋在许多其他信息中。

    将以下规则放入GNU的makefile中 制作 实现名为 list 只需按字母顺序列出所有目标名称-即: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 '^$@$$'
    

    重要 :粘贴时, 确保最后一行缩进量为 正好1个实际制表符字符。 (空格键 工作) .

    注意 排序 结果目标列表是最好的选择,因为 如果目标在makefile中的显示顺序是 保存。
    此外,包含多个目标的规则的子目标总是输出 分别地 因此,由于排序,通常 相邻出现;例如,以开头的规则 a z: 有目标 a z 上市的 彼此紧挨着 在输出中,如果有其他目标。

    规则的解释 :

    • . PHONY: list
      • 声明目标列表为假目标,即 指的是 文件 ,因此应调用其配方 无条件
    • $(MAKE) -prRn -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null

      • 调用 制作 同样,为了打印和分析从makefile派生的数据库:
        • - P 打印数据库
        • -Rr 禁止包含内置规则和变量
        • -q 只测试目标的最新状态(无需重新创建任何内容),但它本身并不妨碍在所有情况下执行配方命令;因此:
        • -f $(lastword $(MAKEFILE_LIST)) 确保将相同的makefile作为原始调用的目标,而不管它是以隐式还是显式方式 -f ...
          告诫 : 如果makefile包含 include 指令;要解决此问题,请定义变量 THIS_FILE := $(lastword $(MAKEFILE_LIST)) 之前 任何 包括 指示和使用 -f $(THIS_FILE) 相反。
        • : 是一个 故意使目标无效 这是为了 确保没有执行任何命令 ; 2>/dev/null 抑制产生的错误消息。注:这取决于 - P 尽管如此,打印数据库,从GNU的3.82版开始就是这样。遗憾的是,GNU make没有直接选择 只是 打印数据库,不打印 执行默认(或给定)任务;如果不需要以特定生成文件为目标,则可以使用 make -p -f/dev/null ,如 man 页。
    • -v RS=

      • 这是一个awk习语,它将输入分成连续的非空行块。
    • /^# File/,/^# Finished Make data base/
      • 匹配输出中包含所有目标的行的范围(从gnu make 3.82开始为真)-通过将解析限制到该范围,不需要处理来自其他输出部分的误报。
    • if ($$1 !~ "^[#.]")
      • 选择性地忽略块:
        • # …忽略非目标,其块以 # Not a target:
        • . …忽略特殊目标
      • 所有其他块都应该以一行开头,该行只包含显式定义的目标的名称,后跟 :
    • egrep -v -e '^[^[:alnum:]]' -e '^$@$$' 从输出中删除不需要的目标:
      • '^[^[:alnum:]]' …排除 隐藏的 按照惯例,目标是既不以字母开头也不以数字开头的目标。
      • '^$@$$' …排除 列表 目标本身

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

        2
  •  146
  •   Brent Bradburn    12 年前

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

    make(space)(tab)(tab)
    
        3
  •  24
  •   Community Mohan Dere    9 年前

    我把这两个答案结合起来: 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
    
        4
  •  19
  •   Timmmm    9 年前

    这在很多情况下显然不起作用,但是如果你 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
    
        5
  •  12
  •   hek2mgl    12 年前

    如果你的bash完成了 make 完成脚本将定义一个函数 _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
    
        6
  •  7
  •   jsp    7 年前

    AS mklement0 points out ,GNU make中缺少列出所有makefile目标的功能,而他的答案和其他方法提供了实现这一点的方法。

    然而,原文也提到 rake ,谁的 任务 switch执行的操作与只列出rakefile中的所有任务略有不同。Rake只会给你一个有相关描述的任务列表。没有说明的任务 will not be listed . 这使作者既可以提供自定义的帮助描述,也可以忽略某些目标的帮助。

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

    您可以将描述放在目标旁边,或者像我经常做的那样,放在目标上方虚假规范旁边,如下所示:

    .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 gist here 也是。

    同样,这并不能解决在makefile中列出所有目标的问题。例如,如果你有一个大的makefile,可能是生成的,或者是别人写的,并且你想要一个快速的方法列出它的目标而不需要挖掘它,那么这不会有帮助。

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

        7
  •  4
  •   Community Mohan Dere    9 年前

    @nobar's answer 很有帮助地演示如何 使用制表符完成列出makefile的目标 .

    • 这对提供此功能的平台非常有用 默认 (例如, 费多拉·迪班 )

    • 在其他平台上(例如, 乌邦图 )你必须 显式加载 此功能,如 @hek2mgl's answer :

      • . /etc/bash_completion 安装多个选项卡完成功能,包括用于 make
      • 或者,安装 只有 制表符完成 制作 :
        • . /usr/share/bash-completion/completions/make
    • 为了 完全不提供此功能的平台 ,例如 小精灵 ,你可以 源以下命令 (更新自 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发行版附带的选项卡完成功能那么复杂:最值得注意的是,它 总是以makefile为目标 现在的 目录 ,即使命令行以不同的makefile为目标, -f <file>
        8
  •  1
  •   seanlum    8 年前

    这个对我很有帮助,因为我希望看到make目标所需的构建目标(及其依赖项)。我知道make targets不能以“.”字符开头。我不知道支持哪些语言,所以我使用了Egrep的括号表达式。

    cat Makefile | egrep "^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$"
    
        9
  •  1
  •   Lisael    8 年前

    对我来说,这还远远不够干净,但却完成了任务。

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

        10
  •  1
  •   ginkgoMZD    8 年前

    这里有很多可行的解决方案,但正如我喜欢说的,“如果值得一次,就值得再做一次。” 我对要使用的建议(tab)(tab)进行了升级,但正如一些人所指出的,您可能没有完成支持,或者,如果您有许多包含文件,您可能需要一种更简单的方法来了解目标的定义位置。

    我还没有用Sub-makes测试下面的内容……我想它不会起作用。正如我们所知,递归使人认为是有害的。

    .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 := \)
    

    我喜欢是因为:

    • 按包含文件列出目标。
    • 输出原始动态目标定义(用modulo替换变量分隔符)
    • 在新行上输出每个目标
    • 似乎更清楚(主观意见)

    说明:

    • makefile_列表中的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
    
        11
  •  1
  •   Penghe Geng    8 年前

    这是对 jsp 很有帮助的回答( https://stackoverflow.com/a/45843594/814145 )我喜欢这样的想法,不仅得到目标的清单,而且还得到他们的描述。 JSP 的makefile将描述作为注释,我发现经常会在目标的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 答案是,只能列出虚假目标,这可能适用于您的案件,也可能不适用于您的案件。
    • 此外,它只列出那些假目标 echo : 命令作为配方的第一个命令。 : 意思是“什么都不做”。我用它来对付那些不需要回声的目标,比如 all 目标在上面。
    • 还有一个额外的技巧 help 将“:”添加到 帮助 输出。
        12
  •  0
  •   mirageglobe    7 年前

    另一个对上述问题的回答。

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

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

    生成文件的输出如下:

    目标1

    目标2

    目标3

    在makefile中,它应该是相同的语句,确保使用$$变量而不是$variable来转义变量。

    解释

    -把里面的东西吐出来

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

    AWK -运行regex,不包括“shell”,只接受“a-z”行,然后打印出$1第一列

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

    这是一个粗略的输出,你可以用锥子做更多的时髦的事情。尽量避免使用SED,因为它在BSDS变体中并不一致,例如,有些在*nix上工作,但在MacOSX等BSD上失败。

    更多

    您应该能够将此(经过修改)添加到 制作 ,转到默认的bash completion文件夹/usr/local/etc/bash-completion.d。/ 意思是当你“制造 选项卡选项卡 “…它将基于一行脚本完成目标。

        13
  •  -4
  •   thamster    11 年前

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

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