如何在 Bazel 中进行配置和变体处理?

How to do configuration and variant handling in Bazel?

一遍又一遍地阅读 Bazel 文档,以至于没有什么新鲜的地方,但我似乎无法掌握如何为本机以外的变体设置配置和变体,例如--cpu--compilation_mode.

为了解释我对配置和变体的意思,请考虑这个 table 的配置行、变体点列和变体单元格(制作这个 table,希望它至少使抽象感):

Has hardware module X Connected on interface Fail fast
Customer configuration 1 Yes Ethernet Yes
Customer configuration 2 Yes USB Yes
Customer configuration 3 No USB No
Debug hw configuration No UART Yes

从 Bazel 用户的角度来看:

您正在寻找 platforms. Build settings(您首先找到的)更适合独立的配置部分,在这种情况下,将它们设置为任何排列都是有用的,而不是平台将所有部分捆绑在一起。然后可以使用生成的设置和约束来驱动编译器标志并将目标限制为某些配置。

对于你的例子,我认为这样的事情是有道理的:

constraint_setting(name = "has_x")

constraint_value(
    name = "x_v1",
    constraint_setting = ":has_x",
)

constraint_value(
    name = "x_v2",
    constraint_setting = ":has_x",
)

constraint_value(
    name = "x_none",
    constraint_setting = ":has_x",
)

constraint_setting(name = "interface")

constraint_value(
    name = "interface_usb",
    constraint_setting = ":interface",
)

constraint_value(
    name = "interface_ethernet",
    constraint_setting = ":interface",
)

constraint_value(
    name = "interface_uart",
    constraint_setting = ":interface",
)

constraint_setting(name = "failure_reporting")

constraint_value(
    name = "fail_fast",
    constraint_setting = ":failure_reporting",
)

constraint_value(
    name = "fail_bugreport",
    constraint_setting = ":failure_reporting",
)

platform(
    name = "customer_1",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":x_v1",
        ":interface_ethernet",
        ":fail_fast",
    ],
)

platform(
    name = "customer_2",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":x_v1",
        ":interface_usb",
        ":fail_fast",
    ],
)

platform(
    name = "customer_3",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":x_none",
        ":interface_usb",
        ":fail_bugreport",
    ],
)

platform(
    name = "debug_hw",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
        ":x_none",
        ":interface_uart",
        ":fail_fast",
    ],
)

注意每个平台如何为每个约束指定设置。给定的编译器命令(由configured target) will use exactly one platform. Also, each file will be compiled separately for every platform even if the outputs are identical. Platforms are intended to limit which permutations of the constraints are actually built. If you want to deduplicate the constraints, platform.parents lets you build an inheritance tree平台创建,不能同时使用两个平台。

target_compatible_with attribute limits targets to specific constraint values. By default, every target is considered compatible with all platforms. If you use target_compatible_with to limit this, then incompatible targets will produce errors if you try to build them explicitly or just be skipped 如果包含在通配符中。对于您的用例,此目标将为任何平台构建:

cc_library(
    name = "generic_linked_list",
    <etc>
)

但是这个只会为 customer_1customer_2 构建:

cc_library(
    name = "hardware_x_driver",
    target_compatible_with = [":x_v1", ":x_v2"],
    <etc>
)

cc_library(
    name = "ethernet_driver",
    # Or you could have a separate constraint for "has ethernet hardware",
    # to use for some platforms which have the hardware but don't use it
    # as the primary interface.
    target_compatible_with = [":interface_ethernet"],
    <etc>
)

针对不同平台使用不同的编译器标志、依赖项或大多数其他规则属性是通过 select 完成的。 Select可以直接读取constraint_values。所以你可以这样写:

cc_library(
    name = "selected_connection",
    copts = select({
        ":interface_usb": [ "-DVARIANT_POINT_CONNECTED_USB" ],
        ":interface_ethernet": ["-DVARIANT_POINT_CONNECTED_ETH" ],
        ":interface_uart": ["-DVARIANT_POINT_CONNECTED_UART" ],
    }),
    deps = select({
        ":interface_ethernet": [ ":ethernet_driver" ],
        "//conditions:default": [],
    }),
    <etc>
)

请注意,如果您希望定义传播到相关目标,使用 cc_library.defines instead of copts. Also, using cc_library.local_defines 将提供相同的非继承语义,并且还与为此使用不同命令行标志的编译器兼容。

指定平台的最简单方法是使用 --platforms. In this case, build with something like --platforms=//:customer_1. You can specify that directly on the command line or put it a .bazelrc config section,如下所示:

build:customer_1 --platforms=//some/long/and/annoying/package:customer_1

您将与 --config=customer_1 一起使用。