可設定的建構屬性

回報問題 查看來源 Nightly · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

可設定的屬性 (通常稱為 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,根據指令列中的標記「選擇」其依附元件。具體來說,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 上執行時,這項作業不會造成混淆,因為兩個分支都會解析為「hello」。{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}* 其中一個人的 values 嚴格來說是其他所有人的超集。舉例來說,values = {"cpu": "x86", "compilation_mode": "dbg"}values = {"cpu": "x86"} 的明確特化版本。

如果沒有其他條件相符,系統就會自動比對內建條件 //conditions:default

雖然這個範例使用 deps,但 select() 也適用於 srcsresourcescmd 和大多數其他屬性。只有少數屬性無法設定,且這些屬性會清楚註記。舉例來說,config_setting 自己的 values 屬性無法設定。

select() 和依附元件

某些屬性會變更目標下所有遞移依附元件的建構參數。舉例來說,genruletools 會變更為執行 Bazel 的機器的 CPU (由於交叉編譯,這可能與目標建構的 CPU 不同)。--cpu這就是所謂的「設定轉換」

Given

#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 開發人員電腦上,將建構作業繫結至 g_arm.srctool1x86tool.cc。附加至 my_genrule 的兩個 select 都會使用 my_genrule 的建構參數,包括 --cpu=armtools 屬性會將 --cpu 變更為 x86,適用於 tool1 和遞移依附元件。tool1 上的 select 會使用 tool1 的建構參數,包括 --cpu=x86

設定條件

可設定屬性中的每個鍵都是 config_settingconstraint_value 的標籤參照。

config_setting 只是預期指令列旗標設定的集合。將這些條件封裝在目標中,使用者就能輕鬆維護「標準」條件,並從多個位置參照這些條件。

constraint_value 支援多平台行為

內建旗標

Bazel 內建 --cpu 等旗標,建構工具可原生瞭解所有專案中的所有建構項目。這些屬性會透過 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()'s 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 內的 ORing 條件:

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 分支在多個條件相符時相符,請使用 Skylib 巨集 config_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 無法直接在 selectAND。您必須明確將這些項目包裝在 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 路徑,雖然技術上可行,但缺乏一致的 UI。如要變更這項設定,必須進一步設計。

Bazel 查詢和 cquery

Bazel query 會在 Bazel 的載入階段運作。也就是說,由於這些旗標要到建構作業的稍後階段 (分析階段) 才會評估,因此 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

常見問題

為什麼 select() 無法在巨集中運作?

select() does work in rules! 詳情請參閱「規則相容性」一文。

這個問題通常表示 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({
        "//third_party/bazel_platforms/cpu:x86_32": "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({
        "//third_party/bazel_platforms/cpu:x86_32": "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({
        "//third_party/bazel_platforms/cpu:x86_32": 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 知道建構的指令列參數之前評估。他們至少可以讀取 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 = {
        "//third_party/bazel_platforms/cpu:x86_32": "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 規則,且會使用特定設定進行評估。

$ 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 可能會出現在同一建構版本的不同設定中。如要瞭解如何使用 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": [],
    }),
)

如果目標平台同時具有 @platforms//cpu:x86@platforms//os:linux 限制,但不是這裡定義的 :x86_linux_platform,則應使用哪個 select()BUILDBUILD 檔案的作者和定義個別平台的使用者可能會有不同想法。

我該怎麼做?

請改為定義符合任何平台的 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 團隊不建議這麼做,因為這會過度限制建構作業,且在預期條件不符時,會讓使用者感到困惑。