可配置的 build 属性

报告问题 查看源代码 每夜 build · 8.0 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

可配置的属性(通常称为 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,该 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 可能与目标平台的构建目标 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 的 build 参数,其中包括 --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 宏支持直接在 selectOR条件:

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"}, ...)

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 规则。

代码库规则没有特定的配置,并且评估方式与 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)

如果您要构建其他目标 //bar,并且该目标在其子图的某个位置依赖于 //myapp:foo,请运行以下命令:

$ 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 操作,该怎么办?

如果您的 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,并在预期条件不匹配时让用户感到困惑。