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

时间:2020-12-19 04:17:28

标签: coq bazel bazel-rules

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

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

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

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

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

感谢您的指点或见解。

2 个答案:

答案 0 :(得分:3)

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

Toraxis' answer 中的 #2 可能是最规范的。

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

<块引用>

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

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

(实际上,生成并复制回策略可能不可行,因为纯生成的文件将只包含 Coq 规则,而不包含任何其他类型的规则。要生成带有 Coq 规则的 BUILD 文件来自如果使用其他类型的规则,则必须将 BUILD 文件本身添加为依赖项 - 这会造成相当混乱!)

答案 1 :(得分:1)

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

Bazel 根据您存储库中的 BUILD 文件计算 Bazel 目标之间的依赖关系,而无需访问您的源文件。在此分析阶段,您可以与文件系统进行的唯一交互是通过在规则调用中使用 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 文件中具有粗粒度的依赖项,但为每个目标文件生成单独的操作,并使用 ctx.actions.rununused_inputs_list 参数与 Bazel 通信哪些依赖项实际上未使用。

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

相关问题