产品管道:多产品多配置构建

在前一节中,我们计算了一个简化的 conan graph build-order,我们没有考虑 mapviewer 产品,并且仅处理了一种配置。

在实际场景中,将需要管理多个产品,最常见的情况是每个产品都有多个配置。如果我们按顺序构建这些不同的情况,速度会非常慢且效率低下;如果我们尝试并行构建它们,则很容易出现许多重复且不必要的相同包的构建,从而浪费资源,甚至产生竞争条件或追溯问题等问题。

为了避免这个问题,可以计算一个统一的“构建顺序”,该顺序聚合了为不同产品和配置计算的所有不同构建顺序。

让我们像往常一样从清理本地缓存并定义正确的仓库开始

# 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

现在,我们将开始计算 game/1.0 的构建顺序,针对我们在本教程中构建的 2 种不同配置:debug 和 release

$ conan graph build-order --requires=game/1.0 --build=missing --order-by=recipe --format=json > game_release.json
$ conan graph build-order --requires=game/1.0 --build=missing --order-by=recipe -s build_type=Debug --format=json > game_debug.json

这些命令与上一节中的命令基本相同,每个命令都使用不同的配置,并创建不同的输出文件 game_release.jsongame_debug.json。这些文件将与之前的类似,但由于我们没有使用 --reduce 参数(这一点很重要!),它们实际上将包含图中所有元素的“构建顺序”,即使只有一些元素包含 binary: Build 定义,而其他元素将包含其他 binary: Download|Cache|etc

现在,让我们计算 mapviewer/1.0 的构建顺序

$ conan graph build-order --requires=mapviewer/1.0 --build=missing --order-by=recipe --format=json > mapviewer_release.json
$ conan graph build-order --requires=mapviewer/1.0 --build=missing --order-by=recipe -s build_type=Debug --format=json > mapviewer_debug.json

请注意,在生成的 mapviewer_xxx.json 构建顺序文件中,mapviewer/1.0 只会有一个元素,其中包含 binary: Download,因为实际上没有其他包需要构建,并且由于 mapviewer 是一个静态链接的应用程序,Conan 知道它可以“跳过”其依赖项的二进制文件。如果我们使用了 --reduce 参数,我们将获得一个空的 order。但这没问题,因为下一个最终步骤将真正计算出需要构建的内容。

让我们获取所有 4 个不同的“构建顺序”文件(2 个产品 x 2 个配置),并将它们合并在一起

$ conan graph build-order-merge --file=game_release.json --file=game_debug.json --file=mapviewer_release.json --file=mapviewer_debug.json --reduce --format=json > build_order.json

现在,我们应用了 --reduce 参数来生成最终的 build_order.json,该文件已准备好分发给构建代理,并且仅包含那些需要构建的特定包

{
    "order_by": "recipe",
    "reduced": true,
    "order": [
        [
            {
                "ref": "engine/1.0#fba6659c9dd04a4bbdc7a375f22143cb",
                "packages": [
                    [
                        {
                            "package_id": "de738ff5d09f0359b81da17c58256c619814a765",
                            "filenames": ["game_release"],
                            "build_args": "--requires=engine/1.0 --build=engine/1.0",
                        },
                        {
                            "package_id": "cbeb3ac76e3d890c630dae5c068bc178e538b090",
                            "filenames": ["game_debug"],
                            "build_args": "--requires=engine/1.0 --build=engine/1.0",

                        }
                    ]
                ]
            }
        ],
        [
            {
                "ref": "game/1.0#1715574045610faa2705017c71d0000e",
                "packages": [
                    [
                        {
                            "package_id": "bac7cd2fe1592075ddc715563984bbe000059d4c",
                            "filenames": ["game_release"],
                            "build_args": "--requires=game/1.0 --build=game/1.0",
                        },
                        {
                            "package_id": "01fbc27d2c156886244dafd0804eef1fff13440b",
                            "filenames": ["game_debug"],
                            "build_args": "--requires=game/1.0 --build=game/1.0",
                        }
                    ]
                ]
            }
        ]
    ],
    "profiles": {
        "game_release": {"args": ""},
        "game_debug": {"args": "-s:h=\"build_type=Debug\""},
        "mapviewer_release": {"args": ""},
        "mapviewer_debug": {"args": "-s:h=\"build_type=Debug\""}
    }
}

此构建顺序总结了必要的构建。首先,必须为 engine/1.0 构建所有不同的二进制文件。此配方包含 2 个不同的二进制文件,一个用于 Release,另一个用于 Debug。这些二进制文件属于 packages 列表中的同一元素,这意味着它们彼此不依赖,可以并行构建。每个二进制文件都使用 "filenames": ["game_release"], 跟踪其原始构建顺序文件,因此可以推断出需要应用于它的配置文件。build_order.json 文件包含一个 profiles 部分,该部分有助于恢复用于创建相应原始构建顺序文件的配置文件和设置命令行参数。

然后,在构建完 engine/1.0 的所有二进制文件后,可以继续构建 game/1.0 的不同二进制文件。它还包含其 debug 和 release 配置的 2 个不同二进制文件,可以并行构建。

实际上,这意味着类似这样

# This 2 could be executed in parallel
# (in different machines, or different Conan caches)
$ conan install --requires=engine/1.0 --build=engine/1.0
$ conan install --requires=engine/1.0 --build=engine/1.0 -s build_type=Debug

# Once engine/1.0 builds finish, it is possible
# to build these 2 binaries in parallel (in different machines or caches)
$ conan install --requires=game/1.0 --build=game/1.0
$ conan install --requires=game/1.0 --build=game/1.0 -s build_type=Debug

在本节中,我们仍然省略了一些重要的实现细节,这些细节将在后续章节中介绍。目标是关注 conan graph build-order-merge 命令,以及如何将不同的产品和配置合并到单个“构建顺序”中。下一节将更详细地展示如何在 CI 中真正分发此构建顺序,并使用锁定文件来保证依赖项的恒定性。