可配置的 build 属性

报告问题 查看源代码

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

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

示例

# 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,用于根据命令行中的标志“选择”其依赖项。具体来说,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 = {"cpu": "x86", "compilation_mode": "dbg"}values = {"cpu": "x86"} 的明确特化。

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

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

select() 和依赖项

某些属性会更改目标下所有传递依赖项的构建参数。例如,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

x86 开发者机器上将 build 绑定到 g_arm.srctool1x86tool.cc。附加到 my_genrule 的两个 select 都使用 my_genrule 的构建参数,其中包括 --cpu=arm。对于 tool1 及其传递依赖项,tools 属性会将 --cpu 更改为 x86tool1 上的 select 使用 tool1 的构建参数,其中包括 --cpu=x86

配置条件

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

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

constraint_value 支持多平台行为

内置标志

--cpu 等标记内置于 Bazel 中:构建工具会针对所有项目中的所有构建以原生方式理解这些标志。这些属性使用 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 构建设置为特定于项目的标志建模。与内置标志不同,这些标志被定义为构建目标,因此 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 构建设置。

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 无法直接在 select 内执行 AND 操作。您必须明确将其封装在 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"}, ...)

to

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

原因有两个。

首先,需要知道 select 将选择哪个路径的宏不起作用,因为宏是在 Bazel 的加载阶段进行评估的,加载阶段发生在已知标志值之前。这是 Bazel 设计的核心限制,近期内不太可能改变。

其次,在技术上可行的情况下,只需遍历所有 select 路径的宏缺乏一致的界面。为了改变这种情况,需要进一步的设计。

Bazel 查询和 cquery

Bazel query 会在 Bazel 的加载阶段上运行。这意味着它不知道目标使用了哪些命令行标志,因为系统等到构建后期(在分析阶段)才会评估这些标志。因此它无法确定选择的是哪些 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

FAQ

为什么 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 读取构建的命令行标志进行评估。这意味着没有足够的信息来评估 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()。它已废弃,取而代之的是 alias()

从技术层面来讲,bind() 是代码库规则,而不是 build 规则。

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

应改用 alias(),并在 actual 属性中使用 select() 来执行此类运行时确定。这样就可以正常运行,因为 alias() 是 BUILD 规则,并且使用特定配置进行评估。

$ 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 预期的设置进行比较。

同一 build 中的不同配置中可能存在 //myapp:foo。如需了解如何使用 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,该怎么办?

如果您的 build 要求特别需要检查平台,您可以翻转 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,并在预期条件不符合时让用户感到困惑。