5

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

我对创建构建计划的直觉是使用一个(现有的)工具(称为coqdep)来读取.v文件并返回其所有直接依赖项的列表。这是我想到的算法:

  1. coqdep在目标文件和(传递地)其每个依赖文件上调用,
  2. 一旦计算了目标的传递依赖关系,就添加一个规则来构建.vo包含.v传递依赖关系的规则。

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

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

感谢您的任何指示或见解。

4

2 回答 2

3

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

Toraxis 的#2答案可能是最规范的。

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

但生成的 BUILD 文件将在输出文件夹中,而不是在源文件夹中。因此,您还必须提供将文件复制回源文件夹的可执行文件。

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

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

于 2020-12-21T07:56:12.700 回答
2

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

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

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

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

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

于 2020-12-20T21:44:31.727 回答