Genellikle select()
olarak bilinen yapılandırılabilir özellikler, kullanıcıların derleme kuralı özelliklerinin değerlerini komut satırında değiştirmesine olanak tanıyan bir Bazel özelliğidir.
Örneğin, mimariye uygun uygulamayı otomatik olarak seçen çok platformlu bir kitaplık veya derleme sırasında özelleştirilebilen, özellik yapılandırılabilir bir ikili dosya için kullanılabilir.
Örnek
# 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",
},
)
Bu, komut satırındaki işaretlere göre bağımlılarını "seçen" bir cc_binary
bildirir. Özellikle deps
şu hale gelir:
Komut | 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()
, yapılandırma koşullarına göre seçilecek bir değerin yer tutucusu olarak kullanılır. Yapılandırma koşulları, config_setting
hedeflerine referans veren etiketlerdir. Yapılandırılabilir bir özellikte select()
kullanıldığında, farklı koşullar geçerli olduğunda özellik farklı değerler alır.
Eşleşmeler net olmalıdır: Birden fazla koşul eşleşiyorsa:
* Tümü aynı değere çözümlenir. Örneğin, linux x86'da çalışırken her iki dal da "hello" olarak çözümlendiğinden {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}
belirsiz değildir.
* Birinin values
, diğerlerinin hepsinin mutlak üst kümesidir. Örneğin, values = {"cpu": "x86", "compilation_mode": "dbg"}
values = {"cpu": "x86"}
öğesinin net bir uzmanlık alanıdır.
Yerleşik koşul //conditions:default
, başka hiçbir koşul eşleşmediğinde otomatik olarak eşleşir.
Bu örnekte deps
kullanılsa da select()
, srcs
, resources
, cmd
ve diğer birçok özellik için de aynı şekilde çalışır. Yalnızca az sayıda özellik yapılandırılamaz ve bunlar açıkça belirtilir. Örneğin, config_setting
'nın kendi values
özelliği yapılandırılamaz.
select()
ve bağımlılıklar
Belirli özellikler, bir hedef altındaki tüm geçişli bağımlılıkların derleme parametrelerini değiştirir. Örneğin, genrule
'nın tools
, Bazel'in çalıştığı makinenin CPU'sunda --cpu
değişiklikler yapar (çapraz derleme sayesinde bu, hedefin oluşturulduğu CPU'dan farklı olabilir). Bu işlem, yapılandırma geçişi olarak bilinir.
O dönemin
#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"],
}),
)
çalışıyor
$ bazel build //myapp:my_genrule --cpu=arm
x86
geliştirici makinesinde oluşturulan derleme, g_arm.src
, tool1
ve x86tool.cc
ile ilişkilendirilir. my_genrule
'ye eklenen her iki select
de my_genrule
'nin --cpu=arm
dahil olmak üzere derleme parametrelerini kullanır. tools
özelliği, tool1
ve geçişli bağımlılıkları için --cpu
olarak değişir.x86
select
üzerinde tool1
, --cpu=x86
dahil olmak üzere tool1
'nin derleme parametrelerini kullanır.
Yapılandırma koşulları
Yapılandırılabilir bir özellikteki her anahtar, config_setting
veya constraint_value
için bir etiket referansıdır.
config_setting
, yalnızca beklenen komut satırı işareti ayarlarından oluşan bir koleksiyondur. Bunları bir hedefte kapsülleyerek, kullanıcıların birden fazla yerden referans alabileceği "standart" koşulları kolayca koruyabilirsiniz.
constraint_value
, çok platformlu davranış için destek sağlar.
Yerleşik işaretler
--cpu
gibi işaretler Bazel'e yerleştirilmiştir: Derleme aracı, tüm projelerdeki tüm derlemeler için bu işaretleri doğal olarak anlar. Bunlar, config_setting
öğesinin values
özelliğiyle belirtilir:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
, işaret adıdır (--
olmadan, yani "--cpu"
yerine "cpu"
). valueN
İlgili işaret için beklenen değerdir. :meaningful_condition_name
, values
içindeki her giriş eşleşirse eşleşir. Sıra önemli değildir.
valueN
, komut satırında ayarlanmış gibi ayrıştırılır. Bunun anlamı şudur:
values = { "compilation_mode": "opt" }
boyutubazel build -c opt
ifadesiyle eşleşiyorvalues = { "force_pic": "true" }
boyutubazel build --force_pic=1
ifadesiyle eşleşiyorvalues = { "force_pic": "0" }
boyutubazel build --noforce_pic
ifadesiyle eşleşiyor
config_setting
yalnızca hedef davranışını etkileyen işaretleri destekler. Örneğin,
--show_progress
yalnızca Bazel'in ilerleme durumunu kullanıcıya nasıl bildirdiğini etkilediği için
izin verilmez. Hedefler, sonuçlarını oluşturmak için bu işareti kullanamaz. Desteklenen işaretlerin tam kümesi belgelenmemiştir. Pratikte, "mantıklı" olan çoğu işaretleme işe yarar.
Özel işaretler
Starlark derleme ayarları ile projeye özel kendi işaretlerinizi modelleyebilirsiniz. Yerleşik işaretlerin aksine bunlar derleme hedefleri olarak tanımlanır. Bu nedenle Bazel, bunlara hedef etiketleriyle referans verir.
Bunlar, config_setting
'ın
flag_values
özelliğiyle tetiklenir:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
Davranış, yerleşik işaretlerdeki davranışla aynıdır. Çalışan bir örnek için buraya bakın.
--define
özel işaretler için alternatif bir eski söz dizimidir (örneğin
--define foo=bar
). Bu, values özelliğinde
(values = {"define": "foo=bar"}
) veya define_values özelliğinde
(define_values = {"foo": "bar"}
) ifade edilebilir. --define
yalnızca geriye dönük uyumluluk için desteklenir. Mümkün olduğunda Starlark derleme ayarlarını tercih edin.
values
, flag_values
ve define_values
bağımsız olarak değerlendirilir. config_setting
, tüm değerler eşleşirse eşleşir.
Varsayılan koşul
Yerleşik koşul //conditions:default
, başka koşul eşleşmediğinde eşleşir.
"Tam olarak bir eşleşme" kuralı nedeniyle, eşleşmesi ve varsayılan koşulu olmayan yapılandırılabilir bir özellik "no matching conditions"
hatası verir. Bu, beklenmedik ayarlardan kaynaklanan sessiz hatalara karşı koruma sağlayabilir:
# 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
Hataları daha net bir şekilde görmek için select()
no_match_error
özelliğiyle özel mesajlar ayarlayabilirsiniz.
Platformlar
Komut satırında birden fazla işaret belirtme özelliği esneklik sağlasa da her hedef oluşturmak istediğinizde her birini ayrı ayrı ayarlamak zahmetli olabilir. Platformlar, bunları basit paketler halinde birleştirmenize olanak tanır.
# 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, komut satırında belirtilebilir. Bu işlev, platformun constraint_values
alt kümesini içeren config_setting
'ları etkinleştirerek bu config_setting
'ların select()
ifadelerinde eşleşmesini sağlar.
Örneğin, my_rocks
öğesinin srcs
özelliğini calcite.sh
olarak ayarlamak için
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Platformlar olmadan bu durum şöyle görünebilir:
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
, constraint_value
'leri doğrudan da okuyabilir:
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"],
}),
)
Bu sayede, yalnızca tek değerlere göre kontrol yapmanız gerektiğinde standart config_setting
'lere gerek kalmaz.
Platformlar hâlâ geliştirme aşamasındadır. Ayrıntılar için belgeleri inceleyin.
select()
birleştirme
select
aynı özellikte birden fazla kez görünebilir:
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
, başka bir select
içinde görünemez. selects
öğesini iç içe yerleştirmeniz gerekiyorsa ve özelliğiniz değer olarak başka hedefler alıyorsa ara hedef kullanın:
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"],
...
}),
)
Birden fazla koşul eşleştiğinde select
eşleşmesi gerekiyorsa VE zincirlemeyi kullanabilirsiniz.
VEYA zincirleme
Aşağıdakileri göz önünde bulundurun:
sh_binary(
name = "my_target",
srcs = ["always_include.sh"],
deps = select({
":config1": [":standard_lib"],
":config2": [":standard_lib"],
":config3": [":standard_lib"],
":config4": [":special_lib"],
}),
)
Çoğu koşul aynı bağımlılıkla değerlendirilir. Ancak bu söz diziminin okunması ve bakımı zordur. [":standard_lib"]
işlemini birden fazla kez tekrarlamak zorunda kalmamak güzel olurdu.
Bir seçenek, değeri BUILD değişkeni olarak önceden tanımlamaktır:
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"],
}),
)
Bu sayede bağımlılığı yönetmek kolaylaşır. Ancak yine de gereksiz kopyalamaya neden olur.
Daha doğrudan destek için aşağıdakilerden birini kullanın:
selects.with_or
Skylib'in selects
modülündeki with_or makrosu, doğrudan select
içinde OR
koşullarını destekler:
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'in selects
modülündeki config_setting_group makrosu, birden fazla config_setting
'ın OR
lenmesini destekler:
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
aksine, farklı hedefler farklı özellikler arasında :config1_or_2
paylaşabilir.
Bir koşul diğerlerinin net bir "uzmanlığı" olmadığı veya tüm koşullar aynı değere çözümlenmediği sürece birden fazla koşulun eşleşmesi hatadır. Ayrıntılar için buraya bakın.
AND zincirleme
Birden fazla koşul eşleştiğinde select
dalının eşleşmesi gerekiyorsa Skylib makrosu config_setting_group'u kullanın:
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"],
}),
)
VEYA zincirlemenin aksine, mevcut config_setting
'ler bir select
içinde doğrudan AND
yapılamaz. Bunları açıkça bir config_setting_group
içine sarmalısınız.
Özel hata mesajları
Varsayılan olarak, hiçbir koşul eşleşmediğinde select()
öğesinin bağlı olduğu hedef şu hatayla başarısız olur:
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
Bu ayar, no_match_error
özelliğiyle özelleştirilebilir:
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
Kuralların uyumluluğu
Kural uygulamaları, yapılandırılabilir özelliklerin çözümlenmiş değerlerini alır. Örneğin, şu ifade verildiğinde:
# myapp/BUILD
some_rule(
name = "my_target",
some_attr = select({
":foo_mode": [":foo"],
":bar_mode": [":bar"],
}),
)
$ bazel build //myapp/my_target --define mode=foo
Kural uygulama kodu, ctx.attr.some_attr
değerini [":foo"]
olarak görür.
Makrolar, select()
koşullarını kabul edip yerel kurallara iletebilir. Ancak bu verileri doğrudan değiştiremezler. Örneğin, bir makronun
select({"foo": "val"}, ...)
to
select({"foo": "val_with_suffix"}, ...)
Bunun iki nedeni vardır.
İlk olarak, select
'nın hangi yolu seçeceğini bilmesi gereken makrolar, işaret değerleri bilinmeden önce gerçekleşen Bazel'in yükleme aşamasında değerlendirildikleri için çalışamaz.
Bu, Bazel'in temel tasarım kısıtlamasıdır ve yakın zamanda değişmesi beklenmemektedir.
İkincisi, tüm select
yolları yinelemekten başka bir işlevi olmayan makrolar. Bu makrolar teknik olarak mümkün olsa da tutarlı bir kullanıcı arayüzüne sahip değildir. Bunu değiştirmek için daha fazla tasarım yapılması gerekir.
Bazel query ve cquery
Bazel query
, Bazel'in yükleme aşamasında çalışır.
Bu, hedefin hangi komut satırı işaretlerini kullandığını bilmediği anlamına gelir. Çünkü bu işaretler, derlemenin ilerleyen aşamalarında (analiz aşamasında) değerlendirilir.
Bu nedenle, hangi select()
dallarının seçildiğini belirleyemez.
Bazel cquery
, Bazel'in analiz aşamasından sonra çalışır. Bu nedenle, tüm bu bilgilere sahiptir ve select()
'leri doğru şekilde çözebilir.
Aşağıdakileri göz önünde bulundurun:
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
'nin bağımlılıklarını fazla tahmin ediyor:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
cquery
ise tam bağımlılıklarını gösterir:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
SSS
Neden select() makrolarda çalışmıyor?
select() kurallarda çalışır. Ayrıntılar için Kural uyumluluğu bölümüne bakın.
Bu sorunun genellikle işaret ettiği temel sorun, select() işlevinin makrolarda çalışmamasıdır. Bunlar kurallardan farklıdır. Farkı anlamak için kurallar ve makrolar ile ilgili dokümanlara bakın. Uçtan uca bir örnek:
Kural ve makro tanımlama:
# 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)
Kuralı ve makroyu oluşturun:
# 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()
öğesini işleyemediğinden oluşturma işlemi başarısız oluyor:
$ 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
yorum satırı haline getirildiğinde derleme başarılı olur:
# 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.
Tanım gereği makrolar, Bazel derlemenin komut satırı işaretlerini okumadan önce değerlendirildiğinden bu durum değiştirilemez. Bu, select() işlevlerini değerlendirmek için yeterli bilgi olmadığı anlamına gelir.
Ancak makrolar, select()
değerlerini kurallara opak blob olarak iletebilir:
# 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.
Neden select() her zaman doğru sonucu döndürüyor?
Makrolar (kurallar değil) tanım gereği select()
'ları değerlendiremediğinden, bunu yapmaya yönelik herhangi bir girişim genellikle hataya neden olur:
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().
Boole değerleri, sessizce başarısız olan özel bir durumdur. Bu nedenle, Boole değerleri konusunda özellikle dikkatli olmanız gerekir:
$ 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.
Bunun nedeni, makroların select()
içeriğini anlamamasıdır.
Bu nedenle, aslında select()
nesnesinin kendisi değerlendirilir. Pythonic tasarım standartlarına göre, çok az sayıda istisna dışında tüm nesneler otomatik olarak doğru değerini döndürür.
Can I read select() like a dict?
Makrolar, Bazel derlemenin komut satırı parametrelerinin ne olduğunu bilmeden önce değerlendirme yaptığından select() işlevini değerlendiremez. En azından select()
sözlüğünü okuyarak her değere son ek ekleyebilirler mi?
Bu kavramsal olarak mümkündür ancak henüz bir Bazel özelliği değildir.
Bugün yapabileceğiniz şey, düz bir sözlük hazırlayıp select()
'ye aktarmaktır:
$ 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
Hem select()
hem de yerel türleri desteklemek istiyorsanız şunları yapabilirsiniz:
$ 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 + "> $@",
)
Neden select() işlevi bind() işleviyle çalışmıyor?
Öncelikle bind()
kullanmayın. Bu politika kullanımdan kaldırılarak yerine alias()
getirilmiştir.
Teknik yanıt, bind()
öğesinin BUILD kuralı değil, depo kuralı olduğudur.
Depo kurallarının belirli bir yapılandırması yoktur ve BUILD kurallarıyla aynı şekilde değerlendirilmez. Bu nedenle, bind()
içindeki bir select()
aslında herhangi bir belirli dala değerlendirilemez.
Bunun yerine, bu tür bir çalışma zamanı belirleme işlemini gerçekleştirmek için actual
özelliğinde select()
ile birlikte alias()
kullanmanız gerekir. alias()
bir BUILD kuralı olduğundan ve belirli bir yapılandırmayla değerlendirildiğinden bu işlem doğru şekilde çalışır.
Gerekirse bind()
hedefinin alias()
işaret etmesini bile sağlayabilirsiniz.
$ 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",
}),
)
Bu kurulumla --define ssl_library=alternative
değerini iletebilirsiniz. //:ssl
veya //external:ssl
değerine bağlı olan tüm hedefler, @alternative//:ssl
konumundaki alternatifi görür.
Ancak bind()
kullanmayı bırakmanız gerekir.
Neden select() işlevi beklediğim öğeyi seçmiyor?
//myapp:foo
, beklediğiniz koşulu seçmeyen bir select()
içeriyorsa hata ayıklamak için cquery ve bazel config
kullanın:
Oluşturduğunuz üst düzey hedef //myapp:foo
ise şunu çalıştırın:
$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)
Alt grafiğinde bir yerde //myapp:foo'ya bağlı başka bir hedef //bar
oluşturuyorsanız şunu çalıştırın:
$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)
//myapp:foo
'nin yanındaki (12e23b9a2b534a)
, //myapp:foo
'nin select()
sorununu çözen yapılandırmanın karma değeridir. bazel config
ile değerlerini inceleyebilirsiniz:
$ 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]
...
}
...
Ardından bu çıktıyı her bir config_setting
tarafından beklenen ayarlarla karşılaştırın.
//myapp:foo
, aynı derlemede farklı yapılandırmalarda bulunabilir. Doğru somepath
değerini almak için cquery belgelerini inceleyin.
select()
neden platformlarla çalışmıyor?
Bazel, semantik net olmadığından belirli bir platformun hedef platform olup olmadığını kontrol eden yapılandırılabilir özellikleri desteklemez.
Örneğin:
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": [],
}),
)
Bu BUILD
dosyasında, hedef platformda hem @platforms//cpu:x86
hem de @platforms//os:linux
kısıtlamaları varsa ancak burada tanımlanan :x86_linux_platform
değilse hangi select()
kullanılmalıdır? BUILD
dosyasının yazarı ve ayrı platformu tanımlayan kullanıcının farklı fikirleri olabilir.
Bunun yerine ne yapmalıyım?
Bunun yerine, şu kısıtlamalara sahip herhangi bir platformla eşleşen bir config_setting
tanımlayın:
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": [],
}),
)
Bu süreç, belirli bir semantik tanımlar ve hangi platformların istenen koşulları karşıladığını kullanıcılar için daha net hale getirir.
Platformda gerçekten, gerçekten select
yapmak istiyorsam ne olur?
Derleme koşullarınız özellikle platformun kontrol edilmesini gerektiriyorsa --platforms
işaretinin değerini config_setting
içinde değiştirebilirsiniz:
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 ekibi bunu yapmanızı önermez. Bu, derlemenizi aşırı derecede kısıtlar ve beklenen koşul eşleşmediğinde kullanıcıların kafasını karıştırır.