NinjaBuild Order-only dependencies 到底是什么

@vrqq  November 30, 2024
官方文档令人误入歧途

假设我们有如下build.ninja

build B : cxx b.cpp
build A : cxx a.cpp

假设我们的编译过程, 都要在命令行中按顺序执行: ninja B && ninja A, 那么 我们就可以改写以上ninja为

build B : cxx b.cpp
build A : cxx a.cpp || B

这样, 我们就可以只运行 ninja A, 就可以触发 ninja内部先自动执行 ninja B, 然后才真正运行 ninja A.

换句话说 AB两个独立的target, 只是我们不想每次打两条ninja编译命令, 而想用一条ninja命令 依次触发两个target的编译.

符合官方文档对order-only的解释. 我们在此拆解下官方文档

Order-only dependencies, expressed with the syntax || dep1 dep2 on the end of a build line.
When these are out of date, : this means "|| dep1 dep2"
the output is not rebuilt until they are built,: output means "build xxx"
but changes in order-only dependencies alone do not cause the output to be rebuilt.: changes in order-only dependencies means "|| dep1 dep2"

重点在最后一句"changes in order-only dependencies alone". 上文的例子, 正因为A和B是两个不同的target, 所以 当我们仅修改了b.cpp时 拆解成两条cmdline后:

ninja B  # 会实际执行 `cxx b.cpp`
ninja A  # 返回 Ninja: No work to do. (不会执行 `cxx a.cpp`)

那它能干啥?
官方文档是生成头文件, 但注意 严格来说头文件是 implicit dependency, 这里官方文档没说的是: "头文件的implicit dependency" 是通过 "/showInclude" 引入的.

我目前尚未想到只能用order only dependency 而不能用 implicit dependency的例子.
在我看来 The constraint of order-only dependency is more loose than implicit dependency.
在官方文档中, 亦是仅有配合"dyndep"和"/showInclude自动解析头文件"的例子.

例如以下两个文件

rule cxx
  deps = msvc
  command = cl.exe /showIncludes $in

build X.stamp : cmake big_project | big_project/x.cpp big_project/other.cpp
build Y : cxx y.cpp || X.stamp
// y.cpp
#include "big_project/x.h"

这个例子中, X是个语焉不详的大工程, 会产生不止x.h一个文件 例如他有很多头文件, 在ninja.build中不便全部列出, 我们在此只写了X.stamp作为代表. (如对应文档中"generate a header file before starting a subsequent compilation step")
即使全部列出了, 也要在使用者(Y)的implicit input列表里列出来, 增加了难度, 不如用/showIncludes方便.
Y对 "x.h" 的依赖由/showIncludes引入.
若我们写成 build Y : cxx y.cpp | X.stamp 亦能形成结果正确的依赖, 但代价是: 只修改"big_project/x.cpp", 也会触发对Y的重新编译, 而这完全没必要.
实际上只有修改了 "big_project/x.h" , 才有必要重新编译Y.


仅有一条评论

  1. 《弦动我心》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/72551.html

添加新评论