可配置的 build 属性

7.3 · 7.2 · 7.1 · 7.0 · 6.5

可配置的属性(通常称为 select())是一项 Bazel 功能,可让用户在命令行中切换构建规则属性的值。

例如,这可用于自动为架构选择适当实现的多平台库,或可在构建时自定义的功能可配置的二进制文件。

示例

# myapp/BUILD

cc_binary(
    name = "mybinary",
    srcs = ["main.cc"],
    deps = select({
        ":arm_build": [":arm_lib"],
        ":x86_debug_build": [":x86_dev_lib"],
        "//conditions:default": [":generic_lib"],
    }),
)

config_setting(
    name = "arm_build",
    values = {"cpu": "arm"},
)

config_setting(
    name = "x86_debug_build",
    values = {
        "cpu": "x86",
        "compilation_mode": "dbg",
    },
)

这会声明一个 cc_binary,该 cc_binary 会根据命令行中的标志“选择”其依赖项。具体而言,deps 会变为:

命令 deps =
bazel build //myapp:mybinary --cpu=arm [":arm_lib"]
bazel build //myapp:mybinary -c dbg --cpu=x86 [":x86_dev_lib"]
bazel build //myapp:mybinary --cpu=ppc [":generic_lib"]
bazel build //myapp:mybinary -c dbg --cpu=ppc [":generic_lib"]

select() 是根据配置条件(即引用 config_setting 目标的标签)选择的值的占位符。通过在可配置属性中使用 select(),该属性在满足不同条件时会有效采用不同的值。

匹配必须明确无误:如果多个条件匹配,则: * 它们都解析为相同的值。例如,在 Linux x86 上运行时,这是一个明确的 {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"},因为这两个分支都会解析为“hello”。* 某个 values 是所有其他 values 的严格超集。例如,values = {"cpu": "x86", "compilation_mode": "dbg"}values = {"cpu": "x86"} 的明确特化。

当没有其他条件匹配时,系统会自动匹配内置条件 //conditions:default

虽然此示例使用了 deps,但 select() 同样适用于 srcsresourcescmd 和大多数其他属性。只有少数属性是不可配置的,并且这些属性会带有明确的注解。例如,config_setting 自己的 values 属性不可配置。

select() 和依赖项

某些属性会更改目标下所有传递依赖项的 build 参数。例如,genruletools--cpu 更改为运行 Bazel 的计算机的 CPU(由于交叉编译,这可能不同于为目标构建的 CPU)。这称为配置转换

给定

#myapp/BUILD

config_setting(
    name = "arm_cpu",
    values = {"cpu": "arm"},
)

config_setting(
    name = "x86_cpu",
    values = {"cpu": "x86"},
)

genrule(
    name = "my_genrule",
    srcs = select({
        ":arm_cpu": ["g_arm.src"],
        ":x86_cpu": ["g_x86.src"],
    }),
    tools = select({
        ":arm_cpu": [":tool1"],
        ":x86_cpu": [":tool2"],
    }),
)

cc_binary(
    name = "tool1",
    srcs = select({
        ":arm_cpu": ["armtool.cc"],
        ":x86_cpu": ["x86tool.cc"],
    }),
)

跑步

$ bazel build //myapp:my_genrule --cpu=arm

将 build 绑定到 g_arm.srctool1x86tool.ccx86附加到 my_genrule 的两个 select 都使用 my_genrule 的构建参数,其中包括 --cpu=armtools 属性会将 tool1 及其传递依赖项的 --cpu 更改为 x86tool1 上的 select 使用 tool1 的 build 参数,其中包括 --cpu=x86

配置条件

可配置属性中的每个键都是对 config_settingconstraint_value 的标签引用。

config_setting 只是一组预期的命令行标志设置。通过将这些条件封装在目标中,您可以轻松维护用户可从多个位置引用的“标准”条件。

constraint_value 支持多平台行为

内置标志

--cpu 等标志内置于 Bazel 中:构建工具对所有项目中的所有 build 都原生地理解这些标志。这些值使用 config_settingvalues 属性指定:

config_setting(
    name = "meaningful_condition_name",
    values = {
        "flag1": "value1",
        "flag2": "value2",
        ...
    },
)

flagN 是标志名称(不带 --,因此为 "cpu",而不是 "--cpu")。valueN 是该标志的预期值。如果 values 中的每个条目都匹配,则 :meaningful_condition_name 匹配。顺序无关紧要。

解析 valueN 时,就像是在命令行中设置的一样。这意味着:

  • values = { "compilation_mode": "opt" }与“bazel build -c opt”相匹配
  • values = { "force_pic": "true" }与“bazel build --force_pic=1”相匹配
  • values = { "force_pic": "0" }与“bazel build --noforce_pic”相匹配

config_setting 仅支持会影响目标行为的标志。例如,不允许使用 --show_progress,因为它只会影响 Bazel 向用户报告进度的方式。目标无法使用该标志来构建其结果。未记录确切的一组受支持标志。实际上,大多数“合理”的标志都有效。

自定义标志

您可以使用 Starlark build 设置,对自己的项目专用标志进行建模。与内置标志不同,这些标志被定义为构建目标,因此 Bazel 会使用目标标签引用它们。

这些事件是通过 config_settingflag_values 属性触发的:

config_setting(
    name = "meaningful_condition_name",
    flag_values = {
        "//myflags:flag1": "value1",
        "//myflags:flag2": "value2",
        ...
    },
)

行为与内置标志相同。如需查看实际示例,请点击此处

--define 是自定义标志(例如 --define foo=bar)的替代旧版语法。这可在 values 属性 (values = {"define": "foo=bar"}) 或 define_values 属性 (define_values = {"foo": "bar"}) 中表达。仅出于向后兼容性目的支持 --define。尽可能使用 Starlark build 设置。

valuesflag_valuesdefine_values 分别进行评估。如果所有项中的所有值都匹配,则 config_setting 会匹配。

默认条件

当没有其他条件匹配时,内置条件 //conditions:default 会匹配。

由于“完全匹配”规则,如果可配置属性没有匹配项且没有默认条件,则会发出 "no matching conditions" 错误。这可以防止因意外设置而导致静默失败:

# myapp/BUILD

config_setting(
    name = "x86_cpu",
    values = {"cpu": "x86"},
)

cc_library(
    name = "x86_only_lib",
    srcs = select({
        ":x86_cpu": ["lib.cc"],
    }),
)
$ bazel build //myapp:x86_only_lib --cpu=arm
ERROR: Configurable attribute "srcs" doesn't match this configuration (would
a default condition help?).
Conditions checked:
  //myapp:x86_cpu

如需更清楚地显示错误,您可以使用 select()no_match_error 属性设置自定义消息。

平台

虽然能够在命令行中指定多个标志会带来灵活性,但每次要构建目标时都单独设置每个标志也可能会很繁琐。 借助平台,您可以将这些内容整合到简单的软件包中。

# myapp/BUILD

sh_binary(
    name = "my_rocks",
    srcs = select({
        ":basalt": ["pyroxene.sh"],
        ":marble": ["calcite.sh"],
        "//conditions:default": ["feldspar.sh"],
    }),
)

config_setting(
    name = "basalt",
    constraint_values = [
        ":black",
        ":igneous",
    ],
)

config_setting(
    name = "marble",
    constraint_values = [
        ":white",
        ":metamorphic",
    ],
)

# constraint_setting acts as an enum type, and constraint_value as an enum value.
constraint_setting(name = "color")
constraint_value(name = "black", constraint_setting = "color")
constraint_value(name = "white", constraint_setting = "color")
constraint_setting(name = "texture")
constraint_value(name = "smooth", constraint_setting = "texture")
constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")

platform(
    name = "basalt_platform",
    constraint_values = [
        ":black",
        ":igneous",
    ],
)

platform(
    name = "marble_platform",
    constraint_values = [
        ":white",
        ":smooth",
        ":metamorphic",
    ],
)

您可以在命令行中指定平台。它会激活包含平台 constraint_values 的子集的 config_setting,以便这些 config_settingselect() 表达式中匹配。

例如,如需将 my_rockssrcs 属性设置为 calcite.sh,只需运行

bazel build //my_app:my_rocks --platforms=//myapp:marble_platform

如果没有平台,

bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic

select() 还可以直接读取 constraint_value

constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
    name = "my_rocks",
    srcs = select({
        ":igneous": ["igneous.sh"],
        ":metamorphic" ["metamorphic.sh"],
    }),
)

这样,当您只需针对单个值进行检查时,便无需使用样板 config_setting

平台仍处于开发阶段。如需了解详情,请参阅文档

组合 select()

select 可以在同一属性中出现多次:

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"] +
           select({
               ":armeabi_mode": ["armeabi_src.sh"],
               ":x86_mode": ["x86_src.sh"],
           }) +
           select({
               ":opt_mode": ["opt_extras.sh"],
               ":dbg_mode": ["dbg_extras.sh"],
           }),
)

select”不能出现在其他“select”中。如果您需要嵌套 selects,并且您的属性将其他目标用作值,请使用中间目标:

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":armeabi_mode": [":armeabi_lib"],
        ...
    }),
)

sh_library(
    name = "armeabi_lib",
    srcs = select({
        ":opt_mode": ["armeabi_with_opt.sh"],
        ...
    }),
)

如果您需要在多个条件匹配时匹配 select,请考虑使用 AND 串联

OR 链接

请考虑以下事项:

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1": [":standard_lib"],
        ":config2": [":standard_lib"],
        ":config3": [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)

大多数条件都会评估为相同的依赖项。但这种语法难以阅读和维护。如果不必多次重复 [":standard_lib"],那就太好了。

一种方法是将值预定义为 BUILD 变量:

STANDARD_DEP = [":standard_lib"]

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1": STANDARD_DEP,
        ":config2": STANDARD_DEP,
        ":config3": STANDARD_DEP,
        ":config4": [":special_lib"],
    }),
)

这样可以更轻松地管理依赖项。但它仍然会导致不必要的重复。

如需更直接的支持,请使用以下任一方式:

selects.with_or

Skylibselects 模块中的 with_or 宏支持直接在 select 内使用 OR 条件:

load("@bazel_skylib//lib:selects.bzl", "selects")
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = selects.with_or({
        (":config1", ":config2", ":config3"): [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)

selects.config_setting_group

Skylibselects 模块中的 config_setting_group 宏支持OR多个 config_setting

load("@bazel_skylib//lib:selects.bzl", "selects")
config_setting(
    name = "config1",
    values = {"cpu": "arm"},
)
config_setting(
    name = "config2",
    values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
    name = "config1_or_2",
    match_any = [":config1", ":config2"],
)
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1_or_2": [":standard_lib"],
        "//conditions:default": [":other_lib"],
    }),
)

selects.with_or 不同,不同的目标可以在不同的属性中共享 :config1_or_2

除非某个条件是其他条件的明确“特化”,或者它们都解析为相同的值,否则多个条件匹配是错误的。如需了解详情,请点击此处

AND 链接

如果您需要在多个条件匹配时匹配 select 分支,请使用 Skylibconfig_setting_group

config_setting(
    name = "config1",
    values = {"cpu": "arm"},
)
config_setting(
    name = "config2",
    values = {"compilation_mode": "dbg"},
)
selects.config_setting_group(
    name = "config1_and_2",
    match_all = [":config1", ":config2"],
)
sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1_and_2": [":standard_lib"],
        "//conditions:default": [":other_lib"],
    }),
)

与 OR 链接不同,现有 config_setting 不能直接 ANDselect 内执行。您必须将它们显式封装在 config_setting_group 中。

自定义错误消息

默认情况下,如果没有任何条件匹配,则 select() 所附加的目标会失败并出现以下错误:

ERROR: Configurable attribute "deps" doesn't match this configuration (would
a default condition help?).
Conditions checked:
  //tools/cc_target_os:darwin
  //tools/cc_target_os:android

可使用 no_match_error 属性对此进行自定义:

cc_library(
    name = "my_lib",
    deps = select(
        {
            "//tools/cc_target_os:android": [":android_deps"],
            "//tools/cc_target_os:windows": [":windows_deps"],
        },
        no_match_error = "Please build with an Android or Windows toolchain",
    ),
)
$ bazel build //myapp:my_lib
ERROR: Configurable attribute "deps" doesn't match this configuration: Please
build with an Android or Windows toolchain

规则兼容性

规则实现会接收可配置属性的已解析值。例如,假设:

# myapp/BUILD

some_rule(
    name = "my_target",
    some_attr = select({
        ":foo_mode": [":foo"],
        ":bar_mode": [":bar"],
    }),
)
$ bazel build //myapp/my_target --define mode=foo

规则实现代码将 ctx.attr.some_attr 视为 [":foo"]

宏可以接受 select() 子句并将其传递给原生规则。但无法直接操控它们。例如,宏应用不可能将宏

select({"foo": "val"}, ...)

select({"foo": "val_with_suffix"}, ...)

原因有二。

首先,需要知道 select 将选择哪个路径的宏无法正常运行,因为宏是在 Bazel 的加载阶段(发生在标志值已知之前)进行求值的。这是 Bazel 的核心设计限制,不太可能很快发生变化。

其次,虽然只需迭代 select 路径的宏在技术上可行,但缺少一致的界面。如需更改此情况,需要进行进一步设计。

Bazel 查询和 cquery

Bazel query 在 Bazel 的加载阶段运行。这意味着,它不知道目标使用哪些命令行标志,因为这些标志要到 build 的后续阶段(在分析阶段)才会进行评估。因此,它无法确定要选择哪些 select() 分支。

Bazel cquery 在 Bazel 的分析阶段之后运行,因此它拥有所有这些信息,并且可以准确解析 select()

请考虑以下因素:

load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
# myapp/BUILD

string_flag(
    name = "dog_type",
    build_setting_default = "cat"
)

cc_library(
    name = "my_lib",
    deps = select({
        ":long": [":foo_dep"],
        ":short": [":bar_dep"],
    }),
)

config_setting(
    name = "long",
    flag_values = {":dog_type": "dachshund"},
)

config_setting(
    name = "short",
    flag_values = {":dog_type": "pug"},
)

query 过度近似了 :my_lib 的依赖项:

$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep

cquery 会显示其确切依赖项:

$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep

常见问题解答

为什么 select() 在宏中不起作用?

select() 可以在规则中使用!如需了解详情,请参阅规则兼容性

此问题通常表示的关键问题是 select() 在中不起作用。这些与规则不同。如需了解二者的区别,请参阅有关规则的文档。下面是一个端到端示例:

定义规则和宏:

# myapp/defs.bzl

# Rule implementation: when an attribute is read, all select()s have already
# been resolved. So it looks like a plain old attribute just like any other.
def _impl(ctx):
    name = ctx.attr.name
    allcaps = ctx.attr.my_config_string.upper()  # This works fine on all values.
    print("My name is " + name + " with custom message: " + allcaps)

# Rule declaration:
my_custom_bazel_rule = rule(
    implementation = _impl,
    attrs = {"my_config_string": attr.string()},
)

# Macro declaration:
def my_custom_bazel_macro(name, my_config_string):
    allcaps = my_config_string.upper()  # This line won't work with select(s).
    print("My name is " + name + " with custom message: " + allcaps)

实例化规则和宏:

# myapp/BUILD

load("//myapp:defs.bzl", "my_custom_bazel_rule")
load("//myapp:defs.bzl", "my_custom_bazel_macro")

my_custom_bazel_rule(
    name = "happy_rule",
    my_config_string = select({
        "//tools/target_cpu:x86": "first string",
        "//third_party/bazel_platforms/cpu:ppc": "second string",
    }),
)

my_custom_bazel_macro(
    name = "happy_macro",
    my_config_string = "fixed string",
)

my_custom_bazel_macro(
    name = "sad_macro",
    my_config_string = select({
        "//tools/target_cpu:x86": "first string",
        "//third_party/bazel_platforms/cpu:ppc": "other string",
    }),
)

构建失败,因为 sad_macro 无法处理 select()

$ bazel build //myapp:all
ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
  (most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
  my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().
ERROR: error loading package 'myapp': Package 'myapp' contains errors.

注释掉 sad_macro 后,构建成功:

# Comment out sad_macro so it doesn't mess up the build.
$ bazel build //myapp:all
DEBUG: /myworkspace/myapp/defs.bzl:5:3: My name is happy_macro with custom message: FIXED STRING.
DEBUG: /myworkspace/myapp/hi.bzl:15:3: My name is happy_rule with custom message: FIRST STRING.

这无法更改,因为定义为宏会在 Bazel 读取 build 的命令行标志之前进行评估。这意味着没有足够的信息来评估 select()。

不过,宏可以将 select() 作为不透明 blob 传递给规则:

# myapp/defs.bzl

def my_custom_bazel_macro(name, my_config_string):
    print("Invoking macro " + name)
    my_custom_bazel_rule(
        name = name + "_as_target",
        my_config_string = my_config_string,
    )
$ bazel build //myapp:sad_macro_less_sad
DEBUG: /myworkspace/myapp/defs.bzl:23:3: Invoking macro sad_macro_less_sad.
DEBUG: /myworkspace/myapp/defs.bzl:15:3: My name is sad_macro_less_sad with custom message: FIRST STRING.

为什么 select() 始终返回 true?

由于(而非规则)按定义无法评估 select(),因此任何尝试执行此操作的操作通常都会产生错误:

ERROR: /myworkspace/myapp/BUILD:17:1: Traceback
  (most recent call last):
File "/myworkspace/myapp/BUILD", line 17
my_custom_bazel_macro(name = "sad_macro", my_config_stri..."}))
File "/myworkspace/myapp/defs.bzl", line 4, in
  my_custom_bazel_macro
my_config_string.upper()
type 'select' has no method upper().

布尔值是一种特殊情况,会静默失败,因此您应对其格外谨慎:

$ cat myapp/defs.bzl
def my_boolean_macro(boolval):
  print("TRUE" if boolval else "FALSE")

$ cat myapp/BUILD
load("//myapp:defs.bzl", "my_boolean_macro")
my_boolean_macro(
    boolval = select({
        "//tools/target_cpu:x86": True,
        "//third_party/bazel_platforms/cpu:ppc": False,
    }),
)

$ bazel build //myapp:all --cpu=x86
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.
$ bazel build //mypro:all --cpu=ppc
DEBUG: /myworkspace/myapp/defs.bzl:4:3: TRUE.

之所以会出现这种情况,是因为宏无法理解 select() 的内容。因此,它们真正评估的是 select() 对象本身。根据 Pythonic 设计标准,除了极少数例外情况外,所有对象都会自动返回 true。

我可以像读取字典一样读取 select() 吗?

无法评估选择,因为宏在 Bazel 知道 build 的命令行参数之前进行评估。它们至少能否读取 select() 的字典,例如为每个值添加后缀?

从概念上讲,这是可能的,但这还不是 Bazel 的功能。目前,您可以准备一个普通字典,然后将其馈送到 select()

$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
  for key in select_cmd.keys():
    select_cmd[key] += " WITH SUFFIX"
  native.genrule(
      name = name,
      outs = [name + ".out"],
      srcs = [],
      cmd = "echo " + select(select_cmd + {"//conditions:default": "default"})
        + " > $@"
  )

$ cat myapp/BUILD
selecty_genrule(
    name = "selecty",
    select_cmd = {
        "//tools/target_cpu:x86": "x86 mode",
    },
)

$ bazel build //testapp:selecty --cpu=x86 && cat bazel-genfiles/testapp/selecty.out
x86 mode WITH SUFFIX

如果您想同时支持 select() 和原生类型,可以执行以下操作:

$ cat myapp/defs.bzl
def selecty_genrule(name, select_cmd):
    cmd_suffix = ""
    if type(select_cmd) == "string":
        cmd_suffix = select_cmd + " WITH SUFFIX"
    elif type(select_cmd) == "dict":
        for key in select_cmd.keys():
            select_cmd[key] += " WITH SUFFIX"
        cmd_suffix = select(select_cmd + {"//conditions:default": "default"})

    native.genrule(
        name = name,
        outs = [name + ".out"],
        srcs = [],
        cmd = "echo " + cmd_suffix + "> $@",
    )

为什么 select() 无法与 bind() 配合使用?

首先,请勿使用 bind()。该 API 已废弃,取而代之的是 alias()

技术层面的答案是,bind() 是一个代码库规则,而非 BUILD 规则。

Repo 规则没有特定的配置,并且其评估方式与 BUILD 规则不同。因此,bind() 中的 select() 实际上无法评估为任何特定分支。

您应改用 alias() 以及 actual 属性中的 select(),来执行此类运行时确定。由于 alias() 是一个 BUILD 规则,并且会使用特定配置进行评估,因此此操作会正常运行。

您甚至可以根据需要让 bind() 目标指向 alias()

$ cat WORKSPACE
workspace(name = "myapp")
bind(name = "openssl", actual = "//:ssl")
http_archive(name = "alternative", ...)
http_archive(name = "boringssl", ...)

$ cat BUILD
config_setting(
    name = "alt_ssl",
    define_values = {
        "ssl_library": "alternative",
    },
)

alias(
    name = "ssl",
    actual = select({
        "//:alt_ssl": "@alternative//:ssl",
        "//conditions:default": "@boringssl//:ssl",
    }),
)

采用此设置后,您可以传递 --define ssl_library=alternative,并且任何依赖于 //:ssl//external:ssl 的目标都会看到位于 @alternative//:ssl 的替代项。

不过,请停止使用 bind()

为什么我的 select() 不会选择我预期的内容?

如果 //myapp:fooselect() 未选择您预期的条件,请使用 cquerybazel config 进行调试:

如果 //myapp:foo 是您要构建的顶级目标,请运行以下命令:

$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)

如果您要构建依赖于其子图中某处的 //myapp:foo 的其他目标 //bar,请运行以下命令:

$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar   (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)

//myapp:foo 旁边的 (12e23b9a2b534a) 是用于解析 //myapp:fooselect() 的配置的哈希。您可以使用 bazel config 检查其值:

$ bazel config 12e23b9a2b534a
BuildConfigurationValue 12e23b9a2b534a
Fragment com.google.devtools.build.lib.analysis.config.CoreOptions {
  cpu: darwin
  compilation_mode: fastbuild
  ...
}
Fragment com.google.devtools.build.lib.rules.cpp.CppOptions {
  linkopt: [-Dfoo=bar]
  ...
}
...

然后,将此输出与每个 config_setting 的预期设置进行比较。

//myapp:foo 可能存在于同一 build 中的不同配置中。如需有关使用 somepath 获取正确的命令的指导,请参阅 cquery 文档

为什么 select() 不适用于平台?

Bazel 不支持可配置的属性来检查给定平台是否为目标平台,因为语义不明确。

例如:

platform(
    name = "x86_linux_platform",
    constraint_values = [
        "@platforms//cpu:x86",
        "@platforms//os:linux",
    ],
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":x86_linux_platform": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

在此 BUILD 文件中,如果目标平台同时具有 @platforms//cpu:x86@platforms//os:linux 约束条件,但不是此处定义的 :x86_linux_platform,应使用哪个 select()BUILD 文件的作者和定义单独平台的用户可能有不同的想法。

我该怎么做?

而是定义一个 config_setting,使其与具有以下约束条件的任何平台匹配:

config_setting(
    name = "is_x86_linux",
    constraint_values = [
        "@platforms//cpu:x86",
        "@platforms//os:linux",
    ],
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":is_x86_linux": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

此过程定义了特定的语义,让用户更清楚地了解哪些平台符合所需条件。

如果我真的非常想在该平台上执行 select 操作,该怎么办?

如果您的构建要求特别要求检查平台,您可以翻转 config_setting--platforms 标志的值:

config_setting(
    name = "is_specific_x86_linux_platform",
    values = {
        "platforms": ["//package:x86_linux_platform"],
    },
)

cc_library(
    name = "lib",
    srcs = [...],
    linkopts = select({
        ":is_specific_x86_linux_platform": ["--enable_x86_optimizations"],
        "//conditions:default": [],
    }),
)

Bazel 团队不建议这样做;这会过度约束您的 build,并在预期条件不匹配时让用户感到困惑。