产品管道:构建顺序

上一节使用 --build=missing 在同一台 CI 机器上构建所有必需的包。这并非总是希望的,甚至并非总是可能的,在许多情况下,最好进行分布式构建,以实现更快的构建并更好地利用 CI 资源。构建负载最自然的分布是将不同的包在不同的机器上构建。让我们看看如何使用 conan graph build-order 命令来实现这一点。

像往常一样,首先确保我们有一个干净的环境,并定义了正确的仓库

# First clean the local "build" folder
$ pwd  # should be <path>/examples2/ci/game
$ rm -rf build  # clean the temporary build folder
$ mkdir build && cd build # To put temporary files

$ conan remove "*" -c  # Make sure no packages from last run
# NOTE: The products repo is first, it will have higher priority.
$ conan remote enable products

我们暂时忽略 mapviewer/1.0 产品,并将本节重点放在 game/1.0 产品上。第一步是计算“构建顺序”,即需要构建的包列表以及它们的顺序。这可以通过以下 conan graph build-order 命令完成

$ conan graph build-order --requires=game/1.0 --build=missing --order-by=recipe --reduce --format=json > game_build_order.json

注意几个重要点

  • 必须使用 --build=missing,与上一节完全相同。如果未能提供预期的 --build 策略和参数,将导致构建顺序不完整或错误。

  • 参数 --reduce 会删除结果顺序中所有不包含 binary: Build 策略的元素。这意味着生成的“构建顺序”不能与其他构建顺序文件合并以将它们聚合成一个,这在存在多种配置和产品时很重要。

  • 参数 --order-by 允许定义不同的顺序,按“配方”或按“配置”。在这种情况下,我们使用 --order-by=recipe,这旨在按配方并行化构建,这意味着对于像 engine/1.0 这样的给定包,所有可能的不同二进制文件都应首先构建,然后才能构建 engine/1.0 的任何消费者。

生成的 game_build_order.json 看起来像

game_build_order.json
  {
      "order_by": "recipe",
      "reduced": true,
      "order": [
          [
              {
                  "ref": "engine/1.0#fba6659c9dd04a4bbdc7a375f22143cb",
                  "packages": [
                      [
                          {
                              "package_id": "de738ff5d09f0359b81da17c58256c619814a765",
                              "binary": "Build",
                              "build_args": "--requires=engine/1.0 --build=engine/1.0",
                          }
                      ]
                  ]
              }
          ],
          [
              {
                  "ref": "game/1.0#1715574045610faa2705017c71d0000e",
                  "depends": [
                      "engine/1.0#fba6659c9dd04a4bbdc7a375f22143cb"
                  ],
                  "packages": [
                      [
                          {
                              "package_id": "bac7cd2fe1592075ddc715563984bbe000059d4c",
                              "binary": "Build",
                              "build_args": "--requires=game/1.0 --build=game/1.0",
                          }
                      ]
                  ]
              }
          ]
      ]
  }

为了方便,与 conan graph info ... --format=html > graph.html 可以生成带有 HTML 交互式依赖图的文件一样,conan graph build-order ... --format=html > build_order.html 可以生成上述 json 文件的 HTML 可视化表示。

../../_images/build_order_simple.png

生成的 json 文件包含一个 order 元素,它是一个列表的列表。这种安排很重要,顶层列表中的每个元素是一组可以并行构建的包,因为它们之间没有关系。您可以将此列表视为“级别”列表,在级别 0 中,有对其他正在构建的包没有依赖关系的包,在级别 1 中,有仅依赖于级别 0 中元素的包,依此类推。

因此,最外层列表中元素的顺序很重要,必须遵守。直到一个列表项中的所有包构建完成,才能开始构建下一个“级别”。

使用 graph_build_order.json 文件中的信息,可以执行必要包的构建,其方式与上一节的 --build=missing 相同,但不由我们直接管理。

从 json 中获取参数,要执行的命令是

$ conan install --requires=engine/1.0 --build=engine/1.0
$ conan install --requires=game/1.0 --build=game/1.0

我们手动执行这些命令,但在实践中,这会是 CI 中针对 json 输出执行的 for 循环。稍后我们会看到一些 Python 代码。此时我们想重点关注 conan graph build-order 命令,但我们尚未真正解释构建是如何分布的。

另请注意,在每个元素内部都有一个列表的内层列表,即 "packages" 部分,用于特定配方在不同配置下必须构建的所有二进制文件。

现在让我们看看如何计算多产品、多配置的构建顺序。