类数据库文件作为 Makefile 中的目标和先决条件

Database-like files as targets and prerequisites in a Makefile

假设我有一个简单的 Makefile。

a1: some_script b
    some_command $< b
a2: some_other_script b
    some_command $< b
b: c
    touch $@
c:
    touch $@

其中 b 是生成 a1a2 所需的一些类似数据库的文件。但是,每次访问 b 时(即使未更改)修改日期也会更改。因此,无论何时执行 a2 的规则,Make 都认为 a1 需要重新创建,因为使用了数据库 b (即使 c 没有改变并且 b保持不变)。我只想更新 a1a2 如果 c 更新(因此 b 需要实际重新编译)。

我可以简单地让 a1a2 直接依赖于 c,但这歪曲了真实的工作流程。

我不想删除 b,所以将它作为中间文件是行不通的。

我也试过将 b 包含为仅订单依赖项,但 a1a2 除非被迫,否则永远不会重新制作。

注意:Makefile 旨在自动执行脚本并跟踪研究项目(而不是软件项目)的依赖关系。也许 Make 不是解决此问题的正确工具。类似数据库的文件是 GeoPackages。

如果您不能依赖 b 的时间戳是准确的,那么您不需要在您的 makefile 中使用它。你可以这样做:

a1: some_script .buildc
        some_command $< b

a2: some_other_script .buildc
        some_command $< b

.buildc: c
        command to update b
        touch $@

c:
        touch $@

仅当 c 比每次调用此命令时设置的 .buildc 更新时才会 运行 command to update b,而不是当 b 更新时使用过。

您也许可以防止对 b:

的时间戳进行无用的更改
a1: some_script b
    touch -r b .b.a1
    some_command $< b
    touch -r .b.a1 b && rm .b.a1

a2: some_other_script b
    touch -r b .b.a2
    some_command $< b
    touch -r .b.a2 b && rm .b.a2

但请注意:如果您运行以并行模式制作 (make -j),a1a2 食谱可能会 运行 并行潜在的竞争条件。因此,最好使用 .NOTPARALLEL: 或在食谱中使用 flock 来序列化它们。

我认为“order-only prerequisite”可能会成功:

a1: some_script | b
    some_command $< b
a2: some_other_script | b
    some_command $< b
b: c
    touch $@
c:
    touch $@