可設定的建構屬性

回報問題 查看來源

可設定的屬性 (一般稱為 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」。* One 的 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_genruleselect 都會使用 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 模組中的 with_or 巨集直接在 select 內支援 ORing 條件:selects

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

除非其他條件的「專業」不明確,或是全都解析為相同的值,否則多項條件都應相符。詳情請參閱這篇文章

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 規則,並以特定設定評估。

$ 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 預期的設定。

//myapp:foo」可能存在於相同版本的不同設定中。請參閱 cquery 說明文件,瞭解如何使用 somepath 取得正確的資料。

為什麼 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在平台上,該怎麼辦?

如果您的建構需求特別需要檢查平台,您可以翻轉 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 團隊不會為此背書,會過度限制建構作業,並在符合預期條件不符時讓使用者感到困惑。