可設定屬性,通常稱為 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()
同樣適用於 srcs
、resources
、cmd
和大多數其他屬性。只有少數屬性是不可設定的,且這些屬性會清楚標註。舉例來說,config_setting
本身的 values
屬性無法設定。
select()
和依附元件
某些屬性會變更目標下所有傳遞依附元件的建構參數。舉例來說,genrule
的 tools
會將 --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.src
、tool1
和 x86tool.cc
。附加至 my_genrule
的兩個 select
都會使用 my_genrule
的建構參數,包括 --cpu=arm
。tools
屬性會將 --cpu
變更為 x86
,適用於 tool1
及其遞移依附元件。tool1
上的 select
會使用 tool1
的建構參數,包括 --cpu=x86
。
設定條件
可設定屬性中的每個鍵都是 config_setting
或 constraint_value
的標籤參照。
config_setting
只是預期的指令列旗標設定集合。將這些項目封裝在目標中,即可輕鬆維護「標準」條件,讓使用者可從多個位置參照。
constraint_value
支援多平台行為。
內建旗標
--cpu
等旗標已內建於 Bazel 中:建構工具會原生瞭解所有專案的所有建構作業。這些參數是透過 config_setting
的 values
屬性指定:
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_setting
的 flag_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 建構設定。
values
、flag_values
和 define_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_setting
在 select()
運算式中相符。
舉例來說,如要將 my_rocks
的 srcs
屬性設為 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
中支援 OR
ing 條件:
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
無法直接在 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
路徑的巨集,但技術上可行,缺少連貫的 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({
"//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 讀取版本的指令列標記之前評估,所以這項變更不會改變。這表示資訊不足,無法評估 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()
,因此任何嘗試評估 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 知道建構命令列參數為何之前進行評估,因此無法評估選取項目。使用者至少能否讀取 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()
是存放區規則,而非 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()
未選擇預期的條件,請使用 cquery 和 bazel 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:foo
的 select()
設定的雜湊。您可以使用 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 團隊不建議這麼做,因為這會過度限制建構作業,並在預期條件不相符時讓使用者感到困惑。