产品流水线:多产品多配置构建

在上一节中,我们计算了一个具有多个简化的 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 的构建顺序,针对本教程中构建的两种不同配置: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 中真正分发此构建顺序,使用锁定文件来保证恒定的依赖项。