具有动态依赖性的细粒度构建?

Fine-grained builds with dynamic dependencies?

我有兴趣了解 bazel 是否可以处理“两阶段构建”,其中依赖项是根据文件内容发现的依赖项必须在依赖的代码之前编译它们(与 C/C++ 不同,其中依赖项主要是未单独编译的头文件)。具体来说,我正在构建类似于 Ocaml 的 Coq 语言。

我创建构建计划的直觉是使用一个(现有的)工具(称为 coqdep)读取一个 .v 文件和 returns 它的所有 直接 依赖项。这是我想到的算法:

  1. 在目标文件上调用 coqdep 并(传递地)在它的每个依赖文件上,
  2. 计算目标的传递依赖关系后,添加规则以从包含传递依赖关系的 .v 构建 .vo

理想情况下,对 coqdep 的调用(在步骤 1 中)将在构建之间进行缓存,因此只需要在文件更改时重新计算。并且依赖信息的传递闭包也会被缓存。

是否可以在 bazel 中实现?是否有任何指示可以为此类语言设置构建?天真地,它似乎是一个两阶段构建,我不确定这如何适合 bazel 的编译模型。当我查看 Ocaml 的规则时,它似乎依赖 ocamlbuild 来满足构建顺序和依赖性要求,而不是在 bazel 中“本地”执行。

感谢任何指点或见解。

我正在研究类似的问题,因为我想用 Bazel 构建 ReasonML。

Bazel 根据存储库中的 BUILD 文件计算 Bazel 目标之间的依赖关系,而无需访问源文件。在此分析阶段,您可以与文件系统进行的唯一交互是通过在规则调用中使用 glob 列出目录内容。

目前,我看到了四个使用 Bazel 进行细粒度增量构建的选项:

  1. 在手写 BUILD 文件中阐明细粒度的依赖关系。
  2. 使用工具生成 BUILD 文件。您不能直接将该工具包装在 Bazel 规则中以在 bazel build 期间使其 运行 因为生成的 BUILD 文件将位于输出文件夹中,而不是源文件夹中。但是您可以 运行 在构建期间调用 coqdep 的规则,并提供一个可执行文件,根据 [=16= 的(可缓存)结果编辑源文件夹中的 BUILD 文件] 来电。由于您可以在构建期间同时读取源文件夹和输出文件夹,如果用户必须再次 运行 可执行文件,您甚至可以向用户打印一条消息。无论如何,完整的构建过程将 bazel run //tools/update-coq-build-files && bazel build 达到固定点。
  3. BUILD 文件中有粗粒度的依赖关系,但持久的工作人员会逐步重建单个目标。
  4. BUILD 文件中具有同粒度的依赖关系,但为每个目标文件生成一个单独的操作,并使用 ctx.actions.rununused_inputs_list 参数与 Bazel 通信,哪些依赖关系实际上在哪里未使用。

虽然我不太确定 3 和 4 是否真的有效,或者需要付出多少努力。

(还没有足够的代表发表评论,所以这是一个答案)

第 2 个 可能是最规范的。

gazelle 是 Golang 的一个例子,它在同一条船上:Golang 文件的依赖关系是通过读取源文件的导入语句在 Bazel 上下文之外确定的。 gazelle 是 writes/rewrites Golang 根据 Bazel 工作区源文件中的导入在 BUILD 文件中规则的工具。可以为遵循此模式的其他语言创建类似的工具。

but the generated BUILD file will be in the output folder, not in the source folder. So you also have to provide an executable that copies the files back into the source folder.

请注意,通过 bazel run 的二进制文件 运行 将环境变量 BUILD_WORKSPACE_DIRECTORY 设置为 Bazel 工作区的根目录(参见 the docs),因此如果您的工具使用此环境变量,它可以就地编辑 BUILD 文件,而不是生成并复制回去。

(事实上,生成和复制回策略可能不可行,因为纯生成的文件将只包含 Coq 规则,而不包含任何其他类型的规则。使用 Coq 生成 BUILD 文件来自一个规则与其他类型规则的规则,必须将 BUILD 文件本身添加为依赖项——这会造成相当大的混乱!)