如何在 GNU Make 中以编程方式定义目标?
Posted
技术标签:
【中文标题】如何在 GNU Make 中以编程方式定义目标?【英文标题】:How to programmatically define targets in 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
,则 确实 保证按顺序构建。
【参考方案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
的扩展:
$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,这是一个有用的答案,尤其是解决方法。定义多行宏的能力似乎特别相关。以上是关于如何在 GNU Make 中以编程方式定义目标?的主要内容,如果未能解决你的问题,请参考以下文章