【问题标题】:List goals/targets in GNU make that contain variables in their definition在 GNU make 中列出定义中包含变量的目标/目标
【发布时间】:2011-03-05 01:24:54
【问题描述】:

我有一个相当大的 makefile,它通过计算变量的名称来动态创建许多目标。 (例如 foo$(VAR) : $(PREREQS))。有什么方法可以说服 gnu make 在扩展这些变量后吐出目标列表?

我希望能够获得任意生成文件的目标。我正在尝试为我的 shell 编写一个完成函数。

【问题讨论】:

    标签: makefile gnu-make


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

    取自 make arg 补全,它的作用就像一个魅力。

    【讨论】:

    • 我试图为此创建一个别名,将其放在双引号中,但这只会导致 awk 在第一个逗号上出现语法错误......关于如何正确转义这个的任何想法外壳(bash)别名?
    • 我认为它与它的定义位置有关。我认为如果将其定义为别名,则其工作目录将成为定义它的目录,并且显然 awk 部分不喜欢隐藏目录中的点。定义一个函数是可行的。
    • 我只是将它放在~/bin 的shell 脚本中,而不是使用别名。效果很好;谢谢!
    • 别名 alias mkl="make -qp | awk -F':' '/^[a-zA-Z0-9][^\$#\/\t=]*:([^=]|\$)/ {split(\$1,A,/ /);for(i in A)print A[i]}' | sort" 可以为您节省 2 分钟的引用游戏时间:-)
    • | sort -u 看起来更有用:)
    【解决方案2】:

    你能解析来自make -pn(即make --print-data-base --dry-run)的输出吗?它打印出所有变量、规则、隐式规则以及哪些命令将被详细运行。

    【讨论】:

    • 这个答案看起来比另一个好。
    • 我同意这样更好。仍然比我希望的要多,但我将把它标记为答案,因为似乎没有其他任何事情是合理的,而且 GNU make 没有我想要的方便标志。
    • 请注意,如果您的 Makefile 动态生成目标,则输出可能取决于您指定的目标。输出也可能取决于环境变量的值或使用 make 命令指定的 make 变量。
    • 我喜欢简洁。 +1 添加了我自己的旋转(在下面的答案中描述):make -pn | sed -rn '/^[^# \t\.%].*:[^=]?/p'
    • 建议make -pnr-r 删除了隐式规则。
    【解决方案3】:

    我不确定这是否只是一个 gnu 制作的东西,但这很好用:

    make help

    【讨论】:

    • 这非常方便,比其他方法之一的自定义别名更容易记住。
    • 没有真正在 GNU Make 3.81 上工作,可能是你的 makefile 中的一个目标:“make: *** No rule to make target `help'. Stop”
    • 这只是调用 Makefile 中的“帮助”目标。如果存在,它将打印一些内容(不是完整目标列表),如果不存在(典型情况),它将退出。
    • 好消息是 cmake 似乎生成了一个很好的易于阅读的“帮助”目标。
    • 这太棒了! :) 去 cmake
    【解决方案4】:

    一些响应者建议使用make -pn,它将打印规则数据库但不执行任何操作——或多或少。这种方法的问题是-n 仍然会调用所有递归make,并且它仍然做的工作比必要的要多得多,因为它会打印出它会在常规构建中调用的每个命令。更有效的解决方案是创建一个简单的 makefile,dummy.mk,包含以下内容:

    __all_targets__: ; #no-op
    

    现在调用 make 为 make -p -f Makefile -f dummy.mk __all_targets__。在任何实质性构建中,make 生成的输出量差异很大。例如:

    $ gmake -pn | wc
     138985 2632330 69612711
    $ gmake -f Makefile -f /tmp/dummy.mk -pn __all_targets__ | wc
      21673   93437  878985
    

    执行时间也明显缩短——第一个版本为 2.063 秒,第二个版本为 0.059 秒。

    【讨论】:

    • 好主意,每个使用大型构建系统的人都会喜欢它。 Android Gingerbread 树的统计信息(不使用递归生成,因此节省的资金不多,只是很好):“time make -pn | wc -l”:1338738 real 0m47.754s.time make -f Makefile -f dummy.mk -pn __all_targets__ | wc -l”:938621 real 0m40.219s
    • 好点。 make 的手册页似乎暗示了类似但友好的内容,即 make -p -f/dev/null
    • @Davide 不太有效:指定-f /dev/null 将阻止make 解析常规makefile,因此您将仅获得内置规则的打印输出。这就是我的解决方案指定-f Makefile -f dummy.mk 的原因。但这还不够,因为有了-nmake 将继续执行并实际开始执行makefile 中的规则。不幸的是,我不知道有任何修改可以简化最初提出的解决方案。
    • 你当然是对的。但是 make 手册页有一个错误,因为它指出:“要打印数据库而不尝试重新制作任何文件,请使用 make -p -f/dev/null”
    • 对我之前的评论稍作修正:它应该是“...因为 without -n ...”
    【解决方案5】:

    查看 GitHub 上的 bash completion for make

    【讨论】:

    • 您提供的链接现已损坏
    • 更新了当前版本的链接。
    【解决方案6】:

    编辑:仅供参考,另一个答案中的 debian bash 完成 git 存储库现在包含了针对 bash 完成用例量身定制的此脚本的增强版本。

    #!/bin/bash
    
    SCRIPT='
      /^# Make data base/,/^# Files/d             # skip until files section
      /^# Not a target/,+1          d             # following target isnt
      /^\.PHONY:/                   d             # special target
      /^\.SUFFIXES:/                d             # special target
      /^\.DEFAULT:/                 d             # special target
      /^\.PRECIOUS:/                d             # special target
      /^\.INTERMEDIATE:/            d             # special target
      /^\.SECONDARY:/               d             # special target
      /^\.SECONDEXPANSION/          d             # special target
      /^\.DELETE_ON_ERROR:/         d             # special target
      /^\.IGNORE:/                  d             # special target
      /^\.LOW_RESOLUTION_TIME:/     d             # special target
      /^\.SILENT:/                  d             # special target
      /^\.EXPORT_ALL_VARIABLES:/    d             # special target
      /^\.NOTPARALLEL:/             d             # special target
      /^\.ONESHELL:/                d             # special target
      /^\.POSIX:/                   d             # special target
      /^\.NOEXPORT:/                d             # special target
      /^\.MAKE:/                    d             # special target
    
    # The stuff above here describes lines that are not
    #  explicit targets or not targets other than special ones
    # The stuff below here decides whether an explicit target
    #  should be output.
    
      /^[^#\t:=%]+:([^=]|$)/ {                    # found target block
        h                                         # hold target
        d                                         # delete line
      }
      /^# File is an intermediate prerequisite/ { # nope
        s/^.*$//;x                                # unhold target
        d                                         # delete line
      }
      /^([^#]|$)/ {                               # end of target block
        s/^.*$//;x                                # unhold target
        s/:.*$//p                                 # write current target
        d                                         # hide any bugs
      }
    '
    
    make -npq .DEFAULT 2>/dev/null | sed -n -r "$SCRIPT" \
      | sort | uniq
    

    这是一个比 debian bash 完成脚本更完整的脚本,因为它为生成的规则提供结果,这是问题所要求的,并且投票最多的答案(debian git 服务器上的 debian bash 完成脚本)没有够远了。

    这不是我链接到的原始脚本,但它更简单,而且速度更快。

    【讨论】:

    • 顺便说一句,mods,如果由于文本的细微差别,此答案不完全符合所有政策,请在删除前发表评论。我会做出任何必要的合法调整,以便这个问题真正得到一个完整有效的答案。
    • 作为一个快速测试,我获取了 gcc 源代码并运行了 ./configure && time ~/makecomp.sh | wc -l 其中 ~/makecomp.sh 是我的答案。 ...配置输出... config.status:创建Makefile 3312 real 0m0.401s user 0m0.412s sys 0m0.028s
    • 作为另一个测试,我使用了codeshot.blogspot.co.uk/2012/08/… 的演示makefile,它生成了用于构建和检查单元测试通过的伪规则。这个例子比典型的跨平台 autoconf 项目使用了更多的 make 特性,所以这个答案中的脚本返回 14 个真实目标,而 ubuntu 上 make 的 bash 完成只返回两个有用的规则,然后是 5 个源文件。
    【解决方案7】:

    这是基于 Todd Hodes 解决方案的别名代码

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

    【讨论】:

      【解决方案8】:

      这将给出一个很好的输出:

      make -pn | perl -F: -ane 'print "$F[0]\n" if /^\w+\s*:/' | sort
      

      【讨论】:

        【解决方案9】:

        这里有一个非常好的解决方案,其中包含一些 sed 和 egrep 魔法: https://gist.github.com/pvdb/777954

        【讨论】:

          【解决方案10】:

          我想我参加这个聚会有点晚了,但如果你正在寻找一个特定的命令,你可以试试

          make -qp | grep -v '^# ' | grep -v '^[[:space:]]' | grep --only-matching '^.*:'
          

          这主要是有效的,尽管它可能仍然包含一些非目标内容,如vpath 指令。如果您不依赖make 的内置规则,您可以使用make -qpR 作为管道中的第一个命令。

          【讨论】:

          • 我用 freetz (.org) 试过了。列出了很多包含的 makefile,但没有列出所有目标。
          【解决方案11】:

          Ruby 解决方案:

          `make -qp`.split("\n").select{|l| l =~ /^\w/ && l !~ /=/}.map{|l| l.sub(/:.*/,'')}
          

          【讨论】:

            【解决方案12】:

            我正在开发 solaris 10 和 turbo C shell。 给定的解决方案不适用于我的 makefile 项目。即使将上面的命令行更改为 tcsh 语法。 但是,我发现您可以使用

            获得简单的解决方案
            remake --tasks | grep -v "clean some static output or just grep tabs on start of line" | awk ´{print $1}´
            

            重制版:

            remake --version
            

            GNU Make 3.82+dbg0.8
            Built for sparc-sun-sparc2-9
            

            以及其他一些不重要的版本数据

            【讨论】:

              【解决方案13】:

              我去寻找同样的问题并想出了这个问题:

              make -pn | sed -rn '/^[^# \t\.%].*:/p'
              

              这将删除所有注释行、模式规则(以制表符开头的行)、所有内在函数(例如 .c.o%.o: %.c 模式)。

              【讨论】:

              • 没有从接受的答案中阅读评论:+1 表示杰克的答案... gist.github.com/777954 – pvandenberk 1 月 13 日 14:47
              • 不起作用,连同它打印所有环境变量的任务
              【解决方案14】:

              在另一个帖子中找到了这个解决方案:

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

              您也可以将其添加到您的Makefile

              list:
                  sh -c "cat Makefile | egrep \"^[[:alnum:][:punct:]]{0,}:[[:space:]]{0,}[[:alnum:][:punct:][:space:]]{0,}$\\""
              

              并执行make list

              【讨论】:

                【解决方案15】:

                当然,但是你希望它什么时候吐出来?

                要在运行规则时报告目标的名称,请在规则中添加一行:

                foo$(VAR): $(PREREQS)
                    @echo now making the foo target: $@
                    do_other_stuff...
                

                要一次将它们全部吐出,您可以创建一个单独的 PHONY 目标:

                .PHONY: show_vars
                show_vars:
                    @echo foo$(VAR)
                    @echo bar$(PARAM) blah$(FLAG)
                    # and so on
                

                这可以作为您的默认目标的先决条件:

                all: show_vars
                    ...
                

                编辑:
                您想要一种显示任意生成文件的所有可能目标的方法,我认为这意味着非侵入性。嗯……

                要做到这一点,并且能够处理复杂的 makefile,例如涉及由eval 语句构造的规则,您必须编写一些接近 Make 模拟器的东西。不切实际。

                要查看简单规则的目标,您可以编写一个充当生成文件扫描器的生成文件,对任意生成文件进行操作:

                1. 使用 sed 从 makefile 中获取所有目标名称。
                2. `include` 生成文件以便使用它来扩展变量。
                3. 使用`show_%: ; echo $$*` 打印所有目标

                这将是一件令人印象深刻的作品。您确定目标值得付出努力吗?

                【讨论】:

                • 不是我想要的。我希望能够让 make 为我可能拥有的任意 makefile 吐出目标列表。我正在尝试为我的 shell 编写一个参数完成函数,但我不能依赖 makefile 有任何明智的帮助。
                【解决方案16】:
                grep ^[a-z].*\:$ Makefile | sed s,:,,g
                

                【讨论】:

                • -1:即使对于简单的变量赋值 (:=),这种方法也会产生误报。
                • 你应该先测试一下。不过,你尝试一下很好。
                猜你喜欢
                • 2014-04-11
                • 1970-01-01
                • 2013-12-19
                • 2012-10-03
                • 2014-07-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多