可配置的 build 属性

报告问题 查看源代码 每夜版 · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

可配置的属性(通常称为 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 = {"cpu": "x86", "compilation_mode": "dbg"}values = {"cpu": "x86"} 的明确特例。

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

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

select() 和依赖项

某些属性会更改目标下所有传递依赖项的 build 参数。例如,genruletools 会将运行 Bazel 的机器的 CPU 更改为 --cpu(由于交叉编译,这可能与目标 build 所用的 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 设置来为自己的项目特定标志建模。与内置标志不同,这些标志被定义为 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_valuesconfig_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"}, ...)

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

这是出于以下两个原因。

首先,需要知道 select 将选择哪个路径的宏无法正常运行,因为宏是在 Bazel 的加载阶段进行评估的,而该阶段发生在标志值已知之前。 这是 Bazel 的一项核心设计限制,短期内不太可能发生变化。

其次,仅需遍历所有 select 路径的宏在技术上可行,但缺乏连贯的界面。需要进一步设计才能更改此设置。

Bazel query 和 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() 吗?

无法评估 select(s),因为宏在 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() 是一个 repo 规则,而不是 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:foo 具有未选择预期条件的 select(),请使用 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,并且在预期条件不匹配时让用户感到困惑。