Atribut yang dapat dikonfigurasi, yang umumnya dikenal sebagai select(), adalah fitur Bazel yang memungkinkan pengguna mengalihkan nilai
atribut aturan build di command line.
Fitur ini dapat digunakan, misalnya, untuk library multiplatform yang secara otomatis memilih implementasi yang sesuai untuk arsitektur, atau untuk biner yang dapat dikonfigurasi fitur dan dapat disesuaikan pada waktu build.
Contoh
# 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",
},
)
Contoh ini mendeklarasikan cc_binary yang "memilih" dependensinya berdasarkan flag di command line. Secara khusus, deps menjadi:
| Perintah | 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() berfungsi sebagai placeholder untuk nilai yang akan dipilih berdasarkan
kondisi konfigurasi, yang merupakan label yang mereferensikan config_setting
target. Dengan menggunakan select() dalam atribut yang dapat dikonfigurasi, atribut tersebut secara efektif mengadopsi nilai yang berbeda saat kondisi yang berbeda berlaku.
Kecocokan harus tidak ambigu: jika beberapa kondisi cocok, maka
* Semuanya di-resolve ke nilai yang sama. Misalnya, saat berjalan di linux x86, ini tidak ambigu
{"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"} karena kedua cabang di-resolve ke "hello".
* values salah satunya adalah superhimpunan ketat dari semua yang lain. Misalnya, values = {"cpu": "x86", "compilation_mode": "dbg"}
adalah spesialisasi yang tidak ambigu dari values = {"cpu": "x86"}.
Kondisi bawaan //conditions:default otomatis cocok jika
tidak ada kondisi lain yang cocok.
Meskipun contoh ini menggunakan deps, select() berfungsi dengan baik di srcs, resources, cmd, dan sebagian besar atribut lainnya. Hanya sejumlah kecil atribut
yang tidak dapat dikonfigurasi, dan atribut ini diberi anotasi dengan jelas. Misalnya,
config_setting's sendiri
values atribut tidak dapat dikonfigurasi.
select() dan dependensi
Atribut tertentu mengubah parameter build untuk semua dependensi transitif di bawah target. Misalnya, tools genrule mengubah --cpu ke CPU mesin yang menjalankan Bazel (yang, berkat kompilasi silang, mungkin berbeda dengan CPU yang digunakan untuk membuat target). Hal ini dikenal sebagai a
transisi konfigurasi.
Mengingat
#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"],
}),
)
berjalan
$ bazel build //myapp:my_genrule --cpu=arm
di mesin developer x86 mengikat build ke g_arm.src, tool1, dan
x86tool.cc. Kedua select yang dilampirkan ke my_genrule menggunakan parameter build my_genrule, yang mencakup --cpu=arm. Atribut tools mengubah --cpu menjadi x86 untuk tool1 dan dependensi transitifnya. select di tool1 menggunakan parameter build tool1, yang mencakup --cpu=x86.
Kondisi konfigurasi
Setiap kunci dalam atribut yang dapat dikonfigurasi adalah referensi label ke
config_setting atau
constraint_value.
config_setting hanyalah kumpulan setelan flag command line yang diharapkan. Dengan mengenkapsulasi setelan ini dalam target, kondisi "standar" yang dapat direferensikan pengguna dari beberapa tempat dapat dipertahankan dengan mudah.
constraint_value memberikan dukungan untuk perilaku multiplatform.
Flag bawaan
Flag seperti --cpu dibuat ke dalam Bazel: alat build secara native memahaminya untuk semua build di semua project. Flag ini ditentukan dengan
config_setting's
values atribut:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN adalah nama flag (tanpa --, jadi "cpu" bukan "--cpu"). valueN
adalah nilai yang diharapkan untuk flag tersebut. :meaningful_condition_name cocok jika setiap entri di values cocok. Urutan tidak relevan.
valueN diuraikan seolah-olah ditetapkan di command line. Artinya:
values = { "compilation_mode": "opt" }cocok denganbazel build -c optvalues = { "force_pic": "true" }cocok denganbazel build --force_pic=1values = { "force_pic": "0" }cocok denganbazel build --noforce_pic
config_setting hanya mendukung flag yang memengaruhi perilaku target. Misalnya,
--show_progress tidak diizinkan karena
hanya memengaruhi cara Bazel melaporkan progres kepada pengguna. Target tidak dapat menggunakan flag tersebut untuk membuat hasilnya. Kumpulan flag yang didukung tidak didokumentasikan. Dalam praktiknya, sebagian besar flag yang "masuk akal" berfungsi.
Flag kustom
Anda dapat memodelkan flag khusus project Anda sendiri dengan setelan build Starlark. Tidak seperti flag bawaan, flag ini ditentukan sebagai target build, sehingga Bazel mereferensikannya dengan label target.
Flag ini dipicu dengan config_setting's
flag_values
atribut:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
Perilakunya sama seperti flag bawaan. Lihat di sini untuk contoh yang berfungsi.
--define
adalah sintaksis lama alternatif untuk flag kustom (misalnya
--define foo=bar). Hal ini dapat dinyatakan dalam atribut
nilai
(values = {"define": "foo=bar"}) atau atribut
define_values
(define_values = {"foo": "bar"}). --define hanya didukung untuk kompatibilitas
mundur. Sebaiknya gunakan setelan build Starlark jika memungkinkan.
values, flag_values, dan define_values dievaluasi secara independen. config_setting cocok jika semua nilai di seluruhnya cocok.
Kondisi default
Kondisi bawaan //conditions:default cocok jika tidak ada kondisi lain yang cocok.
Karena aturan "tepat satu kecocokan", atribut yang dapat dikonfigurasi tanpa kecocokan
dan tanpa kondisi default akan menampilkan error "no matching conditions". Hal ini dapat melindungi dari kegagalan senyap dari setelan yang tidak terduga:
# 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
Untuk error yang lebih jelas, Anda dapat menetapkan pesan kustom dengan select()'s
no_match_error atribut.
Platform
Meskipun kemampuan untuk menentukan beberapa flag di command line memberikan fleksibilitas, hal ini juga dapat membebani Anda untuk menetapkan setiap flag satu per satu setiap kali Anda ingin membuat target. Platform memungkinkan Anda menggabungkan flag ini ke dalam paket sederhana.
# 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",
],
)
Platform dapat ditentukan di command line. Platform ini mengaktifkan config_setting yang berisi subset constraint_values platform, sehingga config_setting tersebut dapat cocok dalam ekspresi select().
Misalnya, untuk menetapkan atribut srcs my_rocks ke calcite.sh, Anda cukup menjalankan
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Tanpa platform, hal ini mungkin terlihat seperti
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select() juga dapat langsung membaca 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"],
}),
)
Hal ini menghemat kebutuhan config_setting boilerplate jika Anda hanya perlu memeriksa nilai tunggal.
Platform masih dalam pengembangan. Lihat dokumentasi untuk mengetahui detailnya.
Menggabungkan select()
select dapat muncul beberapa kali dalam atribut yang sama:
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 tidak dapat muncul di dalam select lain. Jika Anda perlu menyarangkan selects dan atribut Anda menggunakan target lain sebagai nilai, gunakan target perantara:
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"],
...
}),
)
Jika Anda memerlukan select agar cocok saat beberapa kondisi cocok, pertimbangkan AND
perantaian.
Perantaian OR
Pertimbangkan hal berikut:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
Sebagian besar kondisi dievaluasi ke dep yang sama. Namun, sintaksis ini sulit dibaca dan dipertahankan. Sebaiknya jangan ulangi [":standard_lib"] beberapa
kali.
Salah satu opsinya adalah menentukan nilai sebelumnya sebagai variabel 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"],
}),
)
Hal ini memudahkan pengelolaan dependensi. Namun, hal ini masih menyebabkan duplikasi yang tidak perlu.
Untuk dukungan yang lebih langsung, gunakan salah satu hal berikut:
selects.with_or
Makro
with_or
di modul Skylib's
selects
mendukung kondisi ORlangsung di dalam select:
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
Makro
config_setting_group
di modul Skylib's
selects
mendukung OR beberapa config_settings:
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"],
}),
)
Tidak seperti selects.with_or, target yang berbeda dapat berbagi :config1_or_2 di berbagai atribut.
Error terjadi jika beberapa kondisi cocok kecuali jika salah satunya adalah "spesialisasi" yang tidak ambigu dari yang lain atau semuanya di-resolve ke nilai yang sama. Lihat di sini untuk mengetahui detailnya.
Perantaian AND
Jika Anda memerlukan cabang select agar cocok saat beberapa kondisi cocok, gunakan makro
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"],
}),
)
Tidak seperti perantaian OR, config_setting yang ada tidak dapat langsung di-AND di dalam select. Anda harus secara eksplisit menggabungkannya dalam config_setting_group.
Pesan error kustom
Secara default, jika tidak ada kondisi yang cocok, target yang dilampirkan ke select() akan gagal dengan error:
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
Hal ini dapat disesuaikan dengan no_match_error
atribut:
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
Kompatibilitas aturan
Implementasi aturan menerima nilai yang di-resolve dari atribut yang dapat dikonfigurasi. Misalnya, mengingat:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
Kode implementasi aturan melihat ctx.attr.some_attr sebagai [":foo"].
Makro dapat menerima klausa select() dan meneruskannya ke aturan native. Namun, makro tidak dapat memanipulasinya secara langsung. Misalnya, tidak ada cara bagi makro untuk mengonversi
select({"foo": "val"}, ...)
ke
select({"foo": "val_with_suffix"}, ...)
Hal ini karena dua alasan.
Pertama, makro yang perlu mengetahui jalur mana yang akan dipilih select tidak dapat berfungsi
karena makro dievaluasi dalam fase pemuatan Bazel,
yang terjadi sebelum nilai flag diketahui.
Ini adalah batasan desain inti Bazel yang kemungkinan tidak akan berubah dalam waktu dekat.
Kedua, makro yang hanya perlu melakukan iterasi pada semua jalur select, meskipun secara teknis memungkinkan, tidak memiliki UI yang koheren. Desain lebih lanjut diperlukan untuk mengubah hal ini.
Kueri Bazel dan cquery
Bazel query beroperasi selama fase pemuatan Bazel
.
Artinya, kueri ini tidak mengetahui flag command line yang digunakan target karena flag tersebut tidak dievaluasi hingga nanti dalam build (dalam fase analisis).
Jadi, kueri ini tidak dapat menentukan cabang select() mana yang dipilih.
Bazel cquery beroperasi setelah fase analisis Bazel, sehingga memiliki
semua informasi ini dan dapat me-resolve select() secara akurat.
Pertimbangkan:
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 mengestimasi dependensi :my_lib secara berlebihan:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
sedangkan cquery menampilkan dependensi yang tepat:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
FAQ
Mengapa select() tidak berfungsi di makro?
select() berfungsi dalam aturan. Lihat Kompatibilitas aturan untuk mengetahui detailnya.
Masalah utama yang biasanya dimaksud dalam pertanyaan ini adalah select() tidak berfungsi di makro. Makro berbeda dengan aturan. Lihat dokumentasi tentang aturan dan makro untuk memahami perbedaannya. Berikut contoh lengkapnya:
Menentukan aturan dan makro:
# 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)
Membuat instance aturan dan makro:
# 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",
}),
)
Build gagal karena sad_macro tidak dapat memproses 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.
Build berhasil saat Anda mengomentari 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.
Hal ini tidak mungkin diubah karena menurut definisi makro dievaluasi sebelum Bazel membaca flag command line build. Artinya, tidak ada cukup informasi untuk mengevaluasi select().
Namun, makro dapat meneruskan select() sebagai blob buram ke aturan:
# 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.
Mengapa select() selalu menampilkan nilai benar (true)?
Karena makro (tetapi bukan aturan) menurut definisi
tidak dapat mengevaluasi select()s, setiap upaya untuk melakukannya
biasanya menghasilkan error:
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().
Boolean adalah kasus khusus yang gagal secara diam-diam, jadi Anda harus sangat berhati-hati dengan boolean:
$ 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.
Hal ini terjadi karena makro tidak memahami konten select().
Jadi, yang sebenarnya dievaluasi adalah objek select() itu sendiri. Menurut
standar desain
Pythonic, semua objek selain sejumlah kecil pengecualian
otomatis menampilkan nilai benar (true).
Dapatkah saya membaca select() seperti dict?
Makro tidak dapat mengevaluasi select(s) karena makro dievaluasi sebelum
Bazel mengetahui parameter command line build. Dapatkah makro setidaknya membaca kamus select() untuk, misalnya, menambahkan akhiran ke setiap nilai?
Secara konseptual, hal ini mungkin dilakukan, tetapi belum menjadi fitur Bazel.
Hal yang dapat Anda lakukan saat ini adalah menyiapkan kamus langsung, lalu memasukkannya ke dalam 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
Jika ingin mendukung select() dan jenis native, Anda dapat melakukannya:
$ 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 + "> $@",
)
Mengapa select() tidak berfungsi dengan bind()?
Pertama-tama, jangan gunakan bind(). Fungsi ini tidak digunakan lagi dan digantikan dengan alias().
Jawaban teknisnya adalah bind() adalah aturan repo, bukan aturan BUILD.
Aturan repo tidak memiliki konfigurasi tertentu, dan tidak dievaluasi dengan cara yang sama seperti aturan BUILD. Oleh karena itu, select() dalam bind() sebenarnya tidak dapat dievaluasi ke cabang tertentu.
Sebagai gantinya, Anda harus menggunakan alias(), dengan select() di
atribut actual, untuk melakukan jenis penentuan waktu proses ini. Hal ini berfungsi dengan benar, karena alias() adalah aturan BUILD, dan dievaluasi dengan konfigurasi tertentu.
$ 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",
}),
)
Dengan penyiapan ini, Anda dapat meneruskan --define ssl_library=alternative, dan target apa pun
yang bergantung pada //:ssl atau //external:ssl akan melihat alternatif
yang berlokasi di @alternative//:ssl.
Namun, sebaiknya berhenti menggunakan bind().
Mengapa select() saya tidak memilih hal yang saya harapkan?
Jika //myapp:foo memiliki select() yang tidak memilih kondisi yang Anda harapkan,
gunakan cquery dan bazel config untuk melakukan debug:
Jika //myapp:foo adalah target tingkat atas yang Anda buat, jalankan:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Jika Anda membuat target lain //bar yang bergantung pada //myapp:foo di suatu tempat dalam subgrafiknya, jalankan:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
(12e23b9a2b534a) di samping //myapp:foo adalah hash konfigurasi yang me-resolve select() //myapp:foo. Anda dapat memeriksa nilainya dengan 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]
...
}
...
Kemudian, bandingkan output ini dengan setelan yang diharapkan oleh setiap config_setting.
//myapp:foo mungkin ada dalam konfigurasi yang berbeda dalam build yang sama. Lihat
dokumen cquery untuk panduan tentang penggunaan somepath guna mendapatkan yang tepat
one.
Mengapa select() tidak berfungsi dengan platform?
Bazel tidak mendukung atribut yang dapat dikonfigurasi yang memeriksa apakah platform tertentu adalah platform target karena semantiknya tidak jelas.
Misalnya:
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": [],
}),
)
Dalam file BUILD ini, select() mana yang harus digunakan jika platform target memiliki batasan @platforms//cpu:x86 dan @platforms//os:linux, tetapi bukan :x86_linux_platform yang ditentukan di sini? Penulis file BUILD dan pengguna yang menentukan platform terpisah mungkin memiliki ide yang berbeda.
Apa yang harus saya lakukan?
Sebagai gantinya, tentukan config_setting yang cocok dengan platform apa pun dengan batasan ini:
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": [],
}),
)
Proses ini menentukan semantik tertentu, sehingga lebih jelas bagi pengguna platform mana yang memenuhi kondisi yang diinginkan.
Bagaimana jika saya benar-benar ingin select di platform?
Jika persyaratan build Anda secara khusus mengharuskan pemeriksaan platform, Anda dapat membalik nilai flag --platforms di config_setting:
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": [],
}),
)
Tim Bazel tidak mendukung hal ini; hal ini terlalu membatasi build Anda dan membingungkan pengguna saat kondisi yang diharapkan tidak cocok.