将外部构建系统与 (GNU) make 集成

Integrating an external build system with (GNU) make

如何将 go build 这样的外部黑盒构建系统集成到 Make 中?

我不认为这个问题是围棋特有的,但我会用它作为例子。 go build 系统在内部跟踪输入和输出(= 依赖关系)之间的关系,并避免在没有输入更改的情况下重建输出。

我有一个 Makefile,其中包含基于 shell 脚本的目标和基于调用 go build 的目标,例如:

my-exe:
    go build <some-url>

intermediate: my-exe
    <expensive shell script>

test: intermediate test-data
    <some test>

要求:

设定目标为 test

  1. 当我触摸 test-data 时,我不想 运行 intermediate 目标。
  2. 当我接触go源代码时,我想运行所有步骤。

考虑的选项:

如果我们将外部构建步骤变成一个虚假目标,它将始终被构建。但是,任何依赖于虚假目标的目标都是 also 始终构建的。 GNU make documentation:

A phony target should not be a prerequisite of a real target file; if it is, its recipe will be run every time make goes to update that file.

有两种使用方法:


  1. 使外部构建步骤依赖于虚假目标:
.PHONY: always-rebuild

my-exe: always-rebuild
    go build -o my-exe <some-url> # creates my-exe

intermediate: my-exe
    <expensive shell script>

test: intermediate test-data
    <some test>

(GNU) make 在目标 my-exe 具有 运行 之后评估 my-exe 的时间戳。如果目标没有更改时间戳,则后续步骤(中间、测试)不是 运行.


  1. 引入一个虚拟目标来移除“虚假”属性:
.PHONY: external_my-exe
external_my-exe:
    go build -o my-exe <some-url> # creates my-exe

my-exe: external_my-exe
    # do nothing!
    true

intermediate: my-exe
    <expensive shell script>

test: intermediate test-data
    <some test>
  • external_my-exe 始终构建(如果它出现在 make 目标的依赖树中)。
  • my-exe 总是被构建,因为它依赖于一个虚假的目标。
  • intermediate 取决于实际文件 (my-exe),因此只有 运行 如果文件的时间戳发生变化。

让我们用 2 个目标稍微抽象一下:alwaysexpensive。我们希望始终构建 always 因为它很便宜并且依赖于外部构建系统。但我们只想在构建 always 改变某些东西时构建 expensive。解决方案包括将 always 声明为假的,这样它总是被重新制作,但不将其声明为 expensive 的先决条件,否则它也将始终被重新制作。因此,我们需要第三个目标,relay,它不是假的,它也很便宜,只有当 always 做了什么才会真正改变。

在下文中,GO make 变量用于模拟外部构建系统。如果设置为非空字符串 always 更改,否则保持不变。

expensive: relay
    @echo "$@"
    @touch "$@"

relay: always
    @echo "$@"
    @if ! [ -f "$@" ] || [ "$<" -nt "$@" ]; then touch "$@"; fi

.PHONY: always

always:
    @echo "$@"
    @if ! [ -f "$@" ] || [ -n "$(GO)" ]; then touch "$@"; fi

就是这样:

$ make
always
relay
expensive

$ make
always
relay

$ make GO=1
always
relay
expensive

Renaud 的解决方案将起作用。但是正如我在上面的评论中所说,我认为您需要做的就是利用此处描述的 FORCE 目标技巧:https://www.gnu.org/software/make/manual/html_node/Force-Targets.html

将您的 makefile 更改为:

FORCE:

my-exe: FORCE
        go build <some-url>

intermediate: my-exe
        <expensive shell script>

test: intermediate test-data
        <some test>

通过向 my-exe 添加一个永远无法满足的先决条件,您将强制它始终被构建。假设 go build ... 命令在没有任何变化时实际上并没有更新 my-exe 目标,而是在有变化时更新它,这个 makefile 将按照您想要的方式工作。