如何在 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 用户的角度来看:
- 默认情况下,所有目标都应该可以针对所有配置构建
- 在添加配置时到处添加“客户配置 X”是不行的
- 特定于配置的组件应该可以明确说明它特定于哪个配置
- 我认为这就是 constraint_value 的目的
- 规则应根据配置添加标志
- 例如
-DVARIANT_POINT_CONNECTED_ETH
用于以太网连接,-DVARIANT_POINT_CONNECTED_USB
用于 USB 连接
- 我想要一个用于 building/testing/running 单个配置的简单构建命令
- 类似于
bazel build //my/target --//:configuration=debughw
- 认为它有些东西in the skylark config docs,但没有看到如何从示例转到在规则
中使用 constraint_values 和 select()
您正在寻找 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_1
和 customer_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_value
s。所以你可以这样写:
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
一起使用。
一遍又一遍地阅读 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 用户的角度来看:
- 默认情况下,所有目标都应该可以针对所有配置构建
- 在添加配置时到处添加“客户配置 X”是不行的
- 特定于配置的组件应该可以明确说明它特定于哪个配置
- 我认为这就是 constraint_value 的目的
- 规则应根据配置添加标志
- 例如
-DVARIANT_POINT_CONNECTED_ETH
用于以太网连接,-DVARIANT_POINT_CONNECTED_USB
用于 USB 连接
- 例如
- 我想要一个用于 building/testing/running 单个配置的简单构建命令
- 类似于
bazel build //my/target --//:configuration=debughw
- 认为它有些东西in the skylark config docs,但没有看到如何从示例转到在规则 中使用 constraint_values 和
select()
- 类似于
您正在寻找 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_1
和 customer_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_value
s。所以你可以这样写:
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
一起使用。