如何以编程方式在 GNU Make 中定义目标?

How to programmatically define targets in GNU Make?

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

有时候一个人可以走开with alternate methods. The ability to define programatically targets in Makefiles is however a very important to write and organise complex production rules with make. Examples of complex production rules are found in the build system of FreeBSD or in Makefile libraries such as BSD Owl

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

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

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}

GNU Make 的该片段的翻译是什么?

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

其次,GNU make 有许多 programmatically defining rules 的功能。 GNU make 目前没有的一件事是搜索已经定义的目标的能力,所以没有直接类比 .if !target(...).

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

FWIW 是

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

基本上,您希望 make 看到类似以下片段的内容:

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

configuretestinstall 也类似。这表明在某处带有 eval 的循环:

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

targets := configure build test install

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

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

FWIW,这是 foreach 的扩展:

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

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