【问题标题】:make rule that invokes another rule several times with different values for a variable使用不同的变量值多次调用另一个规则的规则
【发布时间】:2016-01-14 00:41:07
【问题描述】:

我有一个规则something,它适用于变量VAR。我还有另一条规则something-all,需要运行something,并将VAR 设置为vars 中的每个值。

vars = hello world

something:
    echo $(VAR)

something-all:
    $(foreach VAR,$(vars),something)

这不太行,我明白了

noob@work:~/Desktop$ make something-all
something something
make: something: No such file or directory
make: *** [something-all] Error 1

它可能应该打印hello\nworld

我曾经通过从% 检索VAR 来使用通配符规则来执行此操作,但我觉得这样做的方法是错误的。看起来像这样:

vars = hello world

all: $(foreach VAR,$(vars),something-$(VAR))

something-%:
    echo $*

【问题讨论】:

  • 您能否详细说明“通配符规则”的方法?您可能正在描述我想到的解决方案,但您可能不会。
  • 谢谢@Beta 我已经添加了这个。
  • @Beta 立即查看,谢谢
  • 所以你不想使用模式规则。你所说的“规范”是什么意思?
  • @Beta:我会接受 GNU Make 的花哨/高级用户会使用的任何规范,例如 automake,和/但仍然是人类可读的。

标签: makefile gnu-make


【解决方案1】:

以下内容应该可以解决您的问题

使用 foreach(在 sparc-solaris 2.8 和 windows 上的 GNU Make 3.80 上试用过)

vars = hello world

something:
    echo $(VAR)

something-all:
    $(foreach i, $(vars), $(MAKE) something VAR=$i || exit 1;)

使用 shell for-loop(在 GNU Make 3.80 和 cc make 在 sparc-solaris 2.8 上尝试过)

vars = hello world

something:
    echo $(VAR)

something-all:
    for i in $(vars); do $(MAKE) something VAR=$$i || exit 1; done

【讨论】:

  • 在数字2中,需要在make调用后加上|| exit 1,这样子make的错误才会传播。另请参阅我对 Jens 回答的评论:在数字 1 中,您忽略了除最后一个以外的所有错误。
  • @Dummy00001 谢谢。我已经更新了答案。由于 OP 没有要求,因此我没有将其包含在样本中;但 OP 可能只是忽略了它。
【解决方案2】:

TL;DR:如果您想编写 make,请放弃 GNU Make,转而使用 BSD Make。

这是个人建议。虽然 BSD Make 似乎比 GNU Make 更受限制,因为它提供的编程工具较少,但它是 much easier to programfew unique killer features。这就是为什么我提出一个使用 GNU Make 的解决方案和另一个用于 BSD Make 的解决方案:

在 GNU Make 中实现

使用 GNU Make,您可以write a macro 定义一个目标。在 Makefile 中定义序列的规范方法是将序列的步骤作为依赖项添加到目标,如下面的 sn-p 所示:

vars= hello world

define something_t =
something: something-$(1)
something-$(1):
    @echo $(1)
endef

$(foreach _,$(vars),$(eval $(call something_t,$_)))

建议使用这种组织方式(而不是只定义一个目标),因为如果您中断序列,您可以使用它使任务轻松恢复。一个 Makefile describes a job,其进展完全由文件系统的状态描述。如果每个步骤都与一个文件相关联,则任务很容易恢复,通常是一个编译对象,但有时也是一个空文件,被触摸以表明重要的检查点已通过。

使用辅助宏是一种灵活的解决方案,可以适应更复杂的任务,而不仅仅是呼应名称。请注意,这适用于最新版本的 GNU Make (4.1)。在 GNU Make 3.81 上,您应该从宏定义中删除等号。

使您的示例适应 BSD Make

如果这对你来说是一个选项,我建议放弃使用 GNU Make 并用 BSD Make 替换它,即way easier to program:它是has a short and to the point documentation,而 GNU Make 的文档非常冗长且有些不清楚, BSD Make 具有工业级的复杂规则集示例(FreeBSD Build systemBSD Owl),并且它具有简单且可预测的宏语言。

vars= hello world

something:
.for _var in ${vars}
    echo ${_var}
.endfor

这可以演变为支持更复杂的任务,只需将 echo 替换为适应的命令,或使用中间步骤。

允许用户覆盖一些任务,也在 BSD Make 中

在这个稍微高级一点的变体中,我们允许用户覆盖我们自己的配方来构建目标something-hellosomething-world。 对于我们列表中的每个项目,如果目标 something-* 尚不存在,则创建它,并将其添加到 something 的依赖项中。只有在未定义something 时,才会发生定义这些目标的整个操作。因此,这些宏的用户可以:

  1. 覆盖something-hellosomething-world 的配方
  2. 覆盖绑定到something 的完整过程。

如果我们想为 Make 编写有用的、可重用的宏,则必须实现这种自定义可能性。不幸的是,这种自定义是nearly impossible in GNU Make

vars = hello world

.if!target(depend)
.for _var in ${vars}
.if!target(something-${_var})
something-${_var}:
    echo ${_var}
.endif
something: something-${_var}
.endfor
.endif

【讨论】:

  • IIRC 从语法中删除 = 将使其也适用于旧版本的 GNU make。
  • @hkBst 我刚刚试了一下。你是对的,确实!
  • 补充一点:如果使用小众软件不是问题,omake 是最先进的 make 实用程序之一。 omake.metaprl.org
  • 如果我没记错的话,在基于 Debian 的系统上,BSD 可以使用 bmake(以前是 pmake)。
  • “在 GNU Make 中实现”。我已经测试了这个技巧,但它是无用的,因为$(eval) 强制过早评估变量,如$@/$</等。为了使其可用,必须添加额外的递归 make 调用层(这会更加混乱)。
【解决方案3】:

这是一种方法:

VARS := hello world
THINGS := $(addprefix something-, $(VARS))

allthings: $(THINGS)

something-%:
    echo $*

【讨论】:

  • ...您是否只是抄袭了一个问题并将其作为您自己的答案? OP已经有了,他问有没有更好的。
  • 不,我很确定这是我说我不想要的解决方案。
【解决方案4】:

这应该不足为奇

vars := hello world
something-all:
    $(foreach VAR,$(vars),something)

尝试运行something something。这正是 foreach 扩展的内容,因为您没有在第三个表达式中引用 VAR

您需要做的就是引用VAR 并使用命令,例如echo

vars := hello world
something-all:
    $(foreach VAR,$(vars),echo $(VAR);)

$ make
echo hello; echo world;
hello
world

注意用分号链接命令如何避免分叉多个 shell 或 -- GASP! -- 递归 make 调用。没有比这更高效的了。

或者,如果您的命令接受几个东西作为参数,

vars := hello world
something-all:
    echo $(foreach VAR,$(vars),$(VAR))

$ make
echo hello world
hello world

但这相当于超级简单的echo $(vars)。因此,跳出框框思考并尝试更改您的要求以使这个简单的解决方案发挥作用可能会有所回报。

【讨论】:

  • 在第一个代码中,将; 替换为&& 并在最后添加true - 这几乎是我自己经常使用的。问题在于它否定了并行化。
  • @Dummy00001 阅读您的评论就像阅读自己的思想一样。 :)
猜你喜欢
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 2011-03-12
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多