可設定的建構屬性

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,該函式會根據命令列中的標記「選擇」其依附元件。具體來說,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() 和依附元件

某些屬性會變更目標下所有傳遞依附元件的建構參數。舉例來說,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 開發人員機器上的版本會將建構繫結至 g_arm.srctool1x86tool.cc。附加至 my_genrule 的兩個 select 都使用 my_genrule 的建構參數,包括 --cpu=armtools 屬性會將 tool1 及其遞移依附元件的 --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 = {"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

Skylib selects 模組中的 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

Skylib selects 模組中的 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

除非其中一個條件是其他條件的明確「專門化」(specialization),或是所有條件都解析為相同的值,否則多個條件無法相符。詳情請參閱這篇文章

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 的載入階段中運作。這表示它不知道目標使用哪些指令列旗標,因為這些旗標會在建構作業後期 (分析階段) 才評估。因此無法判斷選擇哪些 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({
        "//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() 嗎?

宏會在 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() 是存放區規則,而非 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 可能會出現在同一個版本的不同設定中。如要瞭解如何使用 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,那麼在這個 BUILD 檔案中,應使用哪個 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 團隊不建議這麼做,因為這會過度限制建構作業,並在預期條件不相符時讓使用者感到困惑。