Genellikle select()
olarak bilinen yapılandırılabilir özellikler, kullanıcıların komut satırında derleme kuralı özelliklerinin değerlerini değiştirmesine olanak tanıyan bir Bazel özelliğidir.
Bu, örneğin, mimari için 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, bağımlılıklarını komut satırındaki işaretlere göre "seçen" bir cc_binary
tanımlar. Özellikle, deps
şu şekilde dönüştürülür:
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çilen bir değer için yer tutucu görevi görür. Bu koşullar, config_setting
hedeflerine referans veren etiketlerdir. Yapılandırılabilir bir özellikte select()
kullanıldığında, bu özellik farklı koşullar geçerli olduğunda etkili bir şekilde farklı değerler benimser.
Eşleşmeler net olmalıdır: Birden fazla koşul eşleşirse şu durumlardan biri geçerlidir:
* Hepsi aynı değere çözümlenir. Örneğin, linux x86'te çalıştırıldığında bu durum açıktır çünkü her iki dal da "merhaba" olarak çözülür.
* Bir kullanıcının values
, diğer kullanıcıların values
'lerinin katı bir üst kümesidir. Örneğin, values = {"cpu": "x86", "compilation_mode": "dbg"}
values = {"cpu": "x86"}
'un net bir uzmanlığıdır.
Yerleşik //conditions:default
koşulu, 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 özelliklerin çoğunda aynı şekilde çalışır. Yalnızca az sayıda özellik yapılandırılamaz ve bunlara açıkça açıklama eklenir. Örneğin, config_setting
öğesinin kendi values
özelliği yapılandırılamaz.
select()
ve bağımlılıkları
Belirli özellikler, bir hedefin altındaki tüm geçişli bağımlılıkların derleme parametrelerini değiştirir. Örneğin, genrule
işlevinin tools
değeri, --cpu
değerini Bazel çalıştıran makinenin CPU'suyla değiştirir (çapraz derleme sayesinde 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, derlemeyi g_arm.src
, tool1
ve x86tool.cc
ile bağlar. my_genrule
'a bağlı her iki select
de my_genrule
'un --cpu=arm
içeren derleme parametrelerini kullanır. tools
özelliği, tool1
ve geçişli bağımlılıkları için --cpu
olarak x86
olarak değişir. tool1
üzerindeki select
, tool1
'ın --cpu=x86
içeren derleme parametrelerini kullanır.
Yapılandırma koşulları
Yapılandırılabilir bir özellikteki her anahtar, bir config_setting
veya constraint_value
için etiket referansıdır.
config_setting
, beklenen komut satırı işareti ayarlarından oluşan bir koleksiyondur. Bunları bir hedefe sarmalayarak, kullanıcıların birden fazla yerden referans verebileceği "standart" koşulları korumak kolaydır.
constraint_value
, çoklu platform 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 bunları doğal olarak anlar. Bunlar, config_setting
etiketinin values
özelliğiyle belirtilir:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
, işaret adı (--
olmadan, yani "--cpu"
yerine "cpu"
) bağımsız değişkenidir. valueN
, bu işaret için beklenen değerdir. values
öğesindeki her girişin eşleşmesi durumunda :meaningful_condition_name
eşleşir. Sıralama alakasız.
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ışı etkileyen işaretleri destekler. Örneğin, --show_progress
yalnızca Bazel'in ilerleme durumunu kullanıcıya bildirme şeklini etkilediği için buna izin verilmez. Hedefler, sonuçlarını oluşturmak için bu işareti kullanamaz. Desteklenen işaretlerin tam listesi belgede yer almıyor. Pratikte, "anlamlı" olan çoğu işaret çalışır.
Özel işaretler
Starlark derleme ayarlarıyla kendi projeye özel işaretlerinizi modelleyebilirsiniz. Yerleşik işaretlerin aksine, bunlar derleme hedefleri olarak tanımlandığından Bazel bunlara hedef etiketleriyle referans verir.
Bunlar, config_setting
öğesinin flag_values
özelliğiyle tetiklenir:
config_setting(
name = "meaningful_condition_name",
flag_values = {
"//myflags:flag1": "value1",
"//myflags:flag2": "value2",
...
},
)
Davranış, yerleşik işaretler ile aynıdır. Çalışan bir örnek için buraya bakın.
--define
, özel işaretler için alternatif bir eski söz dizimidir (ör. --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. Tüm değerlerdeki tüm değerler eşleşirse config_setting
eşleşir.
Varsayılan koşul
Yerleşik //conditions:default
koşulu, başka hiçbir 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
Daha net hatalar için select()
'ın no_match_error
özelliğini kullanarak özel mesajlar ayarlayabilirsiniz.
Platformlar
Komut satırında birden fazla işaret belirtme özelliği esneklik sağlasa da bir hedef oluşturmak istediğiniz zaman her birini ayrı ayrı ayarlamak da 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. Platformun constraint_values
alt kümesini içeren config_setting
'leri etkinleştirerek bu config_setting
'lerin select()
ifadelerinde eşleşmesine olanak tanır.
Ö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 aşağıdaki gibi görünebilir:
bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic
select()
, constraint_value
dosyalarını doğrudan 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ğerlerle kontrol etmeniz gerektiğinde config_setting
ortak metinlerine ihtiyaç duymazsınız.
Platformlar hâlâ geliştirme aşamasındadır. Ayrıntılar için belgeleri inceleyin.
select()
'leri birleştirme
select
aynı özellikte birden çok 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
içine 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 eşleşmesi için select
öğesine ihtiyacınız varsa VE zincirleme özelliğini 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ı dep ile değerlendirilir. Ancak bu söz dizimi okunması ve bakımı zordur. [":standard_lib"]
'yi birden çok kez tekrarlamak zorunda kalmamak güzel olur.
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, bağımlılığı yönetmeyi kolaylaştırır. Ancak yine de gereksiz kopyalara neden olur.
Daha doğrudan destek almak için aşağıdakilerden birini kullanın:
selects.with_or
Skylib'in selects
modülündeki with_or makrosu, doğrudan bir select
içinde OR
ing 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
'in OR
edilmesini 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
'ün aksine farklı hedefler :config1_or_2
'ı farklı özellikler arasında paylaşabilir.
Bir koşul diğerlerinin net bir "uzmanlığı" olmadığı veya tümü aynı değere çözüm üretmediği sürece birden fazla koşulun eşleşmesi hatadır. Ayrıntılı bilgi için buraya göz atın.
VE zincirleme
Birden fazla koşul eşleştiğinde eşleşmesi için bir select
dalına ihtiyacınız varsa 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 zincirlemesinin aksine mevcut config_setting
öğeleri doğrudan select
içinde AND
yerleştirilemez. Bunları açıkça bir config_setting_group
içine sarmanız gerekir.
Özel hata mesajları
Varsayılan olarak, hiçbir koşul eşleşmediğinde select()
öğesinin bağlı olduğu hedef aşağıdaki 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, 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
Kural uyumluluğu
Kural uygulamaları, yapılandırılabilir özelliklerin çözülmüş değerlerini alır. Örneğin:
# 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()
yan tümcelerini kabul edip bunları yerel kurallara iletebilir. Ancak doğrudan manipüle edemezler. Örneğin, bir makronun
hızla satılan içeriğe
select({"foo": "val"}, ...)
to
select({"foo": "val_with_suffix"}, ...)
Bunun iki nedeni vardır.
Öncelikle, select
'nin hangi yolu seçeceğini bilmesi gereken makrolar çalışamaz çünkü makrolar, işaret değerleri bilinmeden önce gerçekleşen Bazel'in yükleme aşamasında değerlendirilir.
Bu, yakın zamanda değişmesi pek olası olmayan temel bir Bazel tasarım kısıtlamasıdır.
İkinci olarak, tüm select
yollarında iterasyon yapması gereken 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 çalışması gerekir.
Bazel sorgusu ve cquery
Bazel query
, Bazel'in yükleme aşamasında çalışır.
Bu, bu işaretler derlemenin ilerleyen aşamalarında (analiz aşamasında) değerlendirilmediğinden, bir hedefin hangi komut satırı işaretlerini kullandığını bilmediği anlamına gelir.
Bu nedenle, hangi select()
dallarının seçildiğini belirlenemiyor.
Bazel cquery
, Bazel'in analiz aşamasından sonra çalıştığı için tüm bu bilgileri içerir 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
kullanıcısının bağımlılıklarına gereğinden fazla tahminde bulunuyor:
$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep
cquery
ise tam bağımlılıkları gösterir:
$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep
SSS
select() işlevi makrolarda neden çalışmıyor?
select() işlevi kurallarda çalışıyor. Ayrıntılar için Kural uyumluluğu bölümüne bakın.
Bu sorunun genellikle temel nedeni, select() işlevinin makrolarda çalışmamasıdır. Bunlar kurallar ile aynı değildir. Aradaki farkı anlamak için kurallar ve makrolar ile ilgili dokümanlara bakın. Aşağıda uçtan uca bir örnek verilmiştir:
Bir kural ve makro tanımlayın:
# 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 örneklendirin:
# 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ği için derleme başarısız oldu:
$ 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
'yi yoruma aldığınızda 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, derlemenin komut satırı işaretlerini okumadan önce değerlendirildiği için bu değeri değiştirmek mümkün değildir. Bu, select() işlevlerini değerlendirmek için yeterli bilgi olmadığı anlamına gelir.
Ancak makrolar, select()
öğelerini 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.
select() işlevi neden her zaman doğru sonucu döndürür?
Makrolar (kurallar değil) tanımı gereği select()
'leri değerlendiremediğinden bunu yapmaya yönelik her girişim genellikle hata oluşturur:
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'lar sessizce başarısız olan özel bir durum olduğundan ö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.
Yani aslında select()
nesnesi değerlendirilmektedir. Pythonic tasarım standartlarına göre, çok az sayıda istisna dışındaki tüm nesneler otomatik olarak doğru değerini döndürür.
select() işlevini bir sözlük gibi okuyabilir miyim?
Bazel, derlemenin komut satırı parametrelerini öğrenmeden önce makrolar değerlendirildiği için makrolar, seçimleri değerlendiremez. Örneğin, en azından select()
sözlüğünü okuyabilir mi? Örneğin, her değere bir son ek ekleyebilirler mi?
Kavramsal olarak bu mümkündür ancak henüz Bazel özelliği değildir.
Şu anda yapabilecekleriniz arasında, düz bir sözlük hazırlayıp bunu bir select()
'ye beslemek yer alıyor:
$ 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 + "> $@",
)
select() neden bind() ile çalışmıyor?
Öncelikle bind()
kullanmayın. Bu politika kullanımdan kaldırılarak yerine alias()
getirilmiştir.
Teknik olarak yanıt, bind()
'in BUILD kuralı değil, repo kuralı olmasıdır.
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 belirli bir şubeyle değerlendirilemez.
Bunun yerine, bu tür bir çalışma zamanında belirleme yapmak için actual
özelliğinde select()
ile alias()
kullanmalısınız. alias()
bir BUILD kuralı olduğu ve belirli bir yapılandırmayla değerlendirildiği için bu doğru şekilde çalışır.
Gerekirse bir bind()
'ün alias()
'u hedeflemesini 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
'ye bağlı tüm hedefler, @alternative//:ssl
adresindeki alternatifi görür.
Ancak bind()
'ü kullanmayı gerçekten bırakın.
select() işlevi neden beklediğimi seçmiyor?
//myapp:foo
, beklediğiniz koşulu seçmeyen bir select()
içeriyorsa hata ayıklamak için cquery ve bazel config
tuşlarını kullanın:
//myapp:foo
, oluşturduğunuz üst düzey hedefse ş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 şunları ç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
'un yanındaki (12e23b9a2b534a)
, //myapp:foo
'un select()
değerini çözen yapılandırmanın karmasıdır. Değerlerini bazel config
ile 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 config_setting
tarafından beklenen ayarlarla karşılaştırın.
//myapp:foo
aynı derlemede farklı yapılandırmalarda bulunabilir. Doğru sonucu almak için somepath
kullanma konusunda yardım almak üzere cquery belgelerine bakın.
select()
neden platformlarla çalışmıyor?
Bazel, anlamları net olmadığı için 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": [],
}),
)
Hedef platformda hem @platforms//cpu:x86
hem de @platforms//os:linux
kısıtlamaları varsa ancak burada tanımlanan :x86_linux_platform
değilse bu BUILD
dosyasında hangi select()
kullanılmalıdır? BUILD
dosyasının yazarı ile ayrı platformu tanımlayan kullanıcının farklı fikirleri olabilir.
Bunun yerine ne yapmalıyım?
Bunun yerine, aşağıdaki 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 anlamları tanımlayarak kullanıcıların hangi platformların istenen koşulları karşıladığını daha net bir şekilde görmesini sağlar.
Platformda select
yapmak istiyorum.
Derleme gereksinimleriniz özellikle platformun kontrol edilmesini gerektiriyorsa config_setting
içinde --platforms
işaretinin değerini 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 önermez. Derlemenizi aşırı şekilde kısıtlar ve beklenen koşul eşleşmediğinde kullanıcıların kafasını karıştırır.