将外部构建系统与 (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
。
- 当我触摸
test-data
时,我不想 运行 intermediate
目标。
- 当我接触go源代码时,我想运行所有步骤。
考虑的选项:
可以将 go 源文件列为 my-exe
的依赖项。但是,我的 go 源文件夹包含多个目标的文件,我将不得不以某种方式在我的 Makefile 中列出正确的 files/folders。我还可以超越并将所有 go 源文件列为 Makefile 中的依赖项。
如果我将 my-exe
变成虚假目标,则要求 2. 已满足但 1. 已被破坏。
如果我们将外部构建步骤变成一个虚假目标,它将始终被构建。但是,任何依赖于虚假目标的目标都是 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.
有两种使用方法:
- 使外部构建步骤依赖于虚假目标:
.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
的时间戳。如果目标没有更改时间戳,则后续步骤(中间、测试)不是 运行.
- 引入一个虚拟目标来移除“虚假”属性:
.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 个目标稍微抽象一下:always
和 expensive
。我们希望始终构建 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 将按照您想要的方式工作。
如何将 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
。
- 当我触摸
test-data
时,我不想 运行intermediate
目标。 - 当我接触go源代码时,我想运行所有步骤。
考虑的选项:
可以将 go 源文件列为
my-exe
的依赖项。但是,我的 go 源文件夹包含多个目标的文件,我将不得不以某种方式在我的 Makefile 中列出正确的 files/folders。我还可以超越并将所有 go 源文件列为 Makefile 中的依赖项。如果我将
my-exe
变成虚假目标,则要求 2. 已满足但 1. 已被破坏。
如果我们将外部构建步骤变成一个虚假目标,它将始终被构建。但是,任何依赖于虚假目标的目标都是 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.
有两种使用方法:
- 使外部构建步骤依赖于虚假目标:
.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
的时间戳。如果目标没有更改时间戳,则后续步骤(中间、测试)不是 运行.
- 引入一个虚拟目标来移除“虚假”属性:
.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 个目标稍微抽象一下:always
和 expensive
。我们希望始终构建 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 将按照您想要的方式工作。