ChatGPT解决这个技术问题 Extra ChatGPT

在 GNU make 中列出定义中包含变量的目标/目标

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

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


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

取自 make arg 完成,它就像一个魅力。


我试图为此创建一个别名,将其放在双引号中,但这只会导致 awk 在第一个逗号上出现语法错误......关于如何正确地为 shell(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 看起来更有用 :)
J
Jack Kelly

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


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

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

make help


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

一些响应者建议使用 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 解析常规生成文件,因此您将仅获得内置规则的打印输出。这就是我的解决方案指定 -f Makefile -f dummy.mk 的原因。但这还不够,因为使用 -nmake 将继续执行并实际开始执行 makefile 中的规则。不幸的是,我不知道有任何修改可以简化最初提出的解决方案。
你当然是对的。但是 make 手册页有一个错误,因为它指出:“要打印数据库而不尝试重新制作任何文件,请使用 make -p -f/dev/null”
对我之前的评论稍作更正:它应该是“...因为 without -n ...”
j
js.

查看 GitHub 上的 bash completion for make


您提供的链接现在已损坏
更新了当前版本的链接。
c
codeshot

编辑:仅供参考,另一个答案中的 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 真实 0m0.401s 用户 0m0.412s sys 0m0.028s
作为另一个测试,我在 codeshot.blogspot.co.uk/2012/08/… 使用了演示 makefile,它生成用于构建和检查单元测试通过的伪规则。此示例使用比典型的跨平台 autoconf 项目更多的 make 功能,因此此答案中的脚本返回 14 个实际目标,而 ubuntu 上 make 的 bash 完成仅返回两个有用的规则,然后返回 5 个源文件。
N
Nguyễn Minh Vũ

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

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

A
Andor

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

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

A
Atilla Filiz

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


D
DGrady

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

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

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


我用freetz(.org)试过了。列出了很多包含的 makefile 并没有列出所有目标。
g
gdub

红宝石解决方案:

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

A
Aviv

我正在研究solaris 10 anda 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

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


d
dwj

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

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

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


没有从接受的答案中阅读评论:杰克的答案 +1... gist.github.com/777954 – pvandenberk 1 月 13 日 14:47
不起作用,以及它打印所有环境变量的任务
t
thisismydesign

在另一个线程中找到了这个解决方案:

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


B
Beta

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

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

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 模拟器的东西。不切实际的。

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

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

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


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

-1:即使对于简单的变量分配 (:=),这种方法也会产生误报。
你应该先测试一下。不过,你尝试一下很好。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅