【问题标题】:How to programmatically define targets in GNU Make?如何在 GNU Make 中以编程方式定义目标?
【发布时间】:2015-03-02 16:44:52
【问题描述】:

我不知道在 GNU Make 中以编程方式定义目标的任何方法。这怎么可能?

有时可以离开with alternate methods。然而,在 Makefile 中以编程方式定义目标的能力对于使用 make 编写和组织复杂的生产规则非常重要。在 FreeBSD 的构建系统或 Makefile 库如BSD Owl中可以找到复杂生产规则的示例

shell 脚本和 Makefile 之间的main differences 是:

  • 在 Makefile 中,程序的状态由命令行和文件系统给出,因此可以在作业中断后恢复作业。当然,这需要正确编写 Makefile,但即使这相当困难,也比使用 shell 脚本实现类似的效果要容易得多。

  • 在 Makefile 中,用建议或钩子装饰过程非常容易,而这在 shell 脚本中基本上是不可能的。

例如,一个非常简单且有用的模式如下:

build: pre-build
build: do-build
build: post-build

这将build 目标呈现为三个目标的组合,一个包含实际指令do-build,另外两个是挂钩,在do-build 之前和之后执行。许多为 BSD Make 编写的构建系统都使用这种模式,顺便说一句,它允许对目标进行编程定义,因此可以批量编写:

.for _target in configure build test install
.if !target(${_target})
${_target}: pre-${_target}
${_target}: do-${_target}
${_target}: post-${_target}
.endif
.endfor

.if/.endif 块引入的条件允许用户使用它自己对任何${_target} 的定义。

那个 sn-p 对 GNU Make 的翻译是什么?

【问题讨论】:

  • 应该注意的是,在 GNU 中,您不会在目标先决条件之间获得有序保证(尽管没有-j,我相信假设您通常是安全的做)所以你的“钩子”不一定做你想做的事。
  • @EtanReisner 当然,但这与问题没有直接关系,为了简单起见,我省略了这一点。例如,在 BSD Make 中,有人说 .ORDER: pre-build do-build post-build 强制对上述目标进行串行处理。
  • 无论有没有-j,make 都会以相同的顺序遍历先决条件列表。只是使用-j,在开始新的先决条件之前,make 不会等待以前的(非依赖的)先决条件完成(当然,由于先决条件未完成,这可能会导致某些作业以不同的顺序运行)。如果没有-j,则 确实 保证按顺序构建。

标签: makefile gnu-make bsdmake


【解决方案1】:

这里的 FWIW 是 make 的等效语法

.for _target in configure build test install
.if !target(${_target})
${_target}: pre-${_target}
${_target}: do-${_target}
${_target}: post-${_target}
.endif
.endfor

基本上,您希望 make 看到类似 sn-p 的内容:

build: pre-build
build: do-build
build: post-build

configure、test 和 install 也是如此。这表明在某处带有eval 的循环:

define makerule =
  $1: pre-$1
  $1: do-$1
  $1: post-$1
endef

targets := configure build test install

$(foreach _,${targets},$(eval $(call makerule,$_)))

(要玩这个,请将eval 更改为info)。小心那些关闭!

FWIW,这是foreach 的扩展:

  • make 扩展要迭代的列表
    • ${targets} 变为 configure、build、test 和 install
    • 我们有$(foreach _,configure build test install,$(eval $(call makerule,$_)))
  • _ 设置为第一个值 configure。
  • make 扩展 $(eval $(call makerule,configure))
  • 为了评估eval,make 扩展$(call makerule,configure)
    • 它通过将1 设置为configure 并扩展${makerule} 来生成3 行文本:
      configure: pre-configure
      configure: do-configure
      configure: post-configure
  • $(eval) 开始工作,以 make 语法阅读此文本
  • 注意$(eval) 的扩展是空的!它的所有工作都是作为副作用完成的。 洗涤、起泡、冲洗、重复。

请注意:我必须同意所有其他评论者的观点:您的模式是糟糕。如果您的 makefile 不是 -j 安全的,那么它是损坏的(缺少依赖项)。

【讨论】:

  • 感谢您的有用回答!正如我在其他一些 cmets 中所说,我很清楚并行性问题,并在我的问题中省略了它们,因为我只对编程方面感兴趣。但是初学者阅读您的并行横幅很好! :D
【解决方案2】:

首先,如果您想支持并行构建,则此结构无效;如果您使用-j 选项调用make,它将同时运行所有三个先决条件规则,因为虽然它们都必须在build 之前完成,但它们都不相互依赖,因此没有定义排序(即, 你不是说pre-build 必须在do-build 运行之前完成)。

其次,GNU make 为programmatically defining rules 提供了许多工具。 GNU make 目前没有的一件事是能够搜索已定义的目标,因此与 .if !target(...) 没有直接的类比。

但是,您可以使用.VARIABLES 变量来搜索变量是否已定义。因此,一种解决方法是,如果您想要自己的目标,则定义一个变量,然后让您的规则生成器进行检查。

【讨论】:

  • 谢谢@MadScientist,这是一个有用的答案,尤其是解决方法。定义多行宏的能力似乎特别相关。
猜你喜欢
  • 1970-01-01
  • 2011-03-05
  • 2014-04-11
  • 2013-06-13
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多