Yapılandırılabilir özellikler (genellikle select()
), kullanıcıların komut satırında derleme kuralı özelliklerinin değerlerini değiştirmelerine 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ılabilen bir ikili program 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 tanımlarını "seçen" bir cc_binary
tanımlar. Daha ayrıntılı olarak belirtmek gerekirse deps
şu şekilde olur:
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()
, config_setting
hedeflerine referans veren etiketler olan yapılandırma koşullarına göre seçilecek bir değer için yer tutucu işlevi görür. Yapılandırılabilir bir özellikte select()
kullanıldığında özellik, farklı koşullar geçerli olduğunda farklı değerleri etkili şekilde benimser.
Eşleşmeler açık olmalıdır: Ya tam olarak bir koşul eşleşmelidir ya da birden fazla koşul eşleşirse bir koşulun values
değeri diğer tüm koşulların katı üst kümesi olmalıdır. Örneğin values = {"cpu": "x86", "compilation_mode": "dbg"}
, values = {"cpu": "x86"}
için açıkça belirtilmiş bir uzmanlıktır. Yerleşik koşul
//conditions:default
, başka hiçbir şey
eşleştiğinde otomatik olarak eşleşir.
Bu örnekte deps
kullanılsa da select()
, srcs
, resources
, cmd
ve diğer birçok özellikte de iyi performans gösterir. Az sayıda özellik yapılandırılabilir değildir ve bu özellikler açıkça ek açıklamalarla gösterilir. Örneğin, config_setting
özelliğinin 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 için derleme parametrelerini değiştirir. Örneğin, genrule
'in tools
öğesi, --cpu
değerini Bazel'i çalıştıran makinenin CPU'su ile değiştirir (çapraz derleme sayesinde bu işlem, hedefin oluşturulduğu CPU'dan farklı olabilir). Bu, 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
bağlar. my_genrule
öğesine ekli select
öğelerinin ikisi de --cpu=arm
dahil olmak üzere my_genrule
derleme parametrelerini kullanır. tools
özelliği, tool1
ve geçişli bağımlılıkları için --cpu
değerini x86
olarak değiştirir. tool1
sitesindeki select
, --cpu=x86
dahil olmak üzere tool1
derleme parametrelerini kullanır.
Yapılandırma koşulları
Yapılandırılabilir özellikteki her anahtar, bir config_setting
veya constraint_value
etiketine referanstır.
config_setting
, beklenen komut satırı işareti ayarlarının bir koleksiyonudur. Bunları bir hedefe kapsülleyerek, kullanıcıların birden çok yerden başvurabileceği "standart" koşulların sağlanması kolaydır.
constraint_value
, çok platformlu davranışı destekler.
Yerleşik işaretler
--cpu
gibi işaretler Bazel'de yerleşiktir: Derleme aracı, bunları tüm projelerdeki tüm derlemeler için yerel olarak anlar. Bunlar config_setting
values
özelliğiyle belirtilir:
config_setting(
name = "meaningful_condition_name",
values = {
"flag1": "value1",
"flag2": "value2",
...
},
)
flagN
bir işaret adıdır (--
olmadan, bu nedenle "--cpu"
yerine "cpu"
). valueN
bu işaret için beklenen değerdir. values
içindeki her giriş eşleşirse :meaningful_condition_name
eşleşir. Sıra 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, yalnızca Bazel'in kullanıcıya ilerleme durumunu bildirme şeklini etkilediği için --show_progress
öğesine izin verilmez. Hedefler, sonuçlarını oluşturmak için
bu işareti kullanamaz. Desteklenen işaret gruplarının tamamı belgelenmemiştir. Pratikte, "anlamlı" olan çoğu işaret işe yarar.
Özel işaretler
Starlark derleme ayarlarıyla kendi projeye özgü işaretlerinizi modelleyebilirsiniz. Yerleşik işaretlerin aksine, bunlar derleme hedefi olarak tanımlanır. Bu nedenle, Bazel bunlara hedef etiketlerle referans verir.
Bunlar, config_setting
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 olan eski bir 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. Hepsindeki tüm değerler eşleşiyorsa config_setting
eşleşir.
Varsayılan koşul
Başka hiçbir koşul eşleşmediğinde //conditions:default
yerleşik koşulu eşleşir.
"Tam bir eşleşme" kuralı nedeniyle, eşleşmesi olmayan ve varsayılan koşulu olmayan yapılandırılabilir özellikler "no matching conditions"
hatası verir. Bu, beklenmeyen ayarlardan kaynaklanan sessiz hatalara karşı korunabilir:
# 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 da net hatalar için select()
özelliğinin no_match_error
özelliğiyle özel mesajlar ayarlayabilirsiniz.
Platformlar
Komut satırında birden fazla işaret belirtebilme olanağı esneklik sağlasa da, her hedef oluşturmak istediğinizde her birini ayrı ayrı ayarlamak külfetli olabilir. Platformlar bunları basit gruplar halinde birleştirmenizi sağlar.
# 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
öğelerini etkinleştirerek bu config_setting
'lerin select()
ifadeleriyle eşleşmesini sağlar.
Örneğin, my_rocks
için srcs
özelliğini calcite.sh
olarak ayarlamak üzere
bazel build //my_app:my_rocks --platforms=//myapp:marble_platform
Platformlar olmadan şöyle 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 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 tekli değerlerle kontrol yapmanız gerektiğinde standart config_setting
ihtiyacını ortadan kaldırır.
Platformlar hâlâ geliştirme aşamasındadır. Ayrıntılar için dokümanları inceleyin.
select()
birleştiriliyor
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 diğer hedefleri değer olarak alırsa 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şme için bir select
öğesine ihtiyacınız varsa VE zincirlemeyi kullanmayı düşünün.
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ı davranışı değerlendirir. Ancak bu söz diziminin okunması ve kullanılması zordur. [":standard_lib"]
öğesini birden fazla kez tekrarlamak zorunda kalmamak iyi olur.
Seçeneklerden biri, 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ığı daha kolay bir şekilde yönetebilirsiniz. Ancak yine de gereksiz yinelemelere 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 bir select
içinde OR
oluşturma 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, OR
birden fazla config_setting
'yı 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
işlevinin aksine farklı hedefler, :config1_or_2
özelliğini farklı özellikler arasında paylaşabilir.
Bir koşul diğerlerinin açık bir "uzmanlığı" değilse birden fazla koşulun eşleşmesi hataya neden olur. Ayrıntılar için buraya göz atın.
VE zincirleme
Birden fazla koşul eşleştiğinde bir select
dalına ihtiyacınız varsa Skylib makrosunu 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 zincirinin aksine, mevcut config_setting
öğeleri doğrudan bir select
içinde AND
kıslanamaz. Bunları açıkça bir config_setting_group
ile sarmalamanız gerekir.
Özel hata mesajları
Varsayılan olarak, hiçbir koşul eşleşmediğinde select()
öğesinin eklendiği 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, 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 uyumluluğu
Kural uygulamaları, yapılandırılabilir özelliklerin çözülmüş değerlerini alır. Örneğin, şu iç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()
ifadelerini kabul edebilir ve bunları yerel kurallara iletebilir. Ancak bilgileri doğrudan manipüle edemezler. Örneğin, bir makronun
elde etmek istediğiniz
select({"foo": "val"}, ...)
-
select({"foo": "val_with_suffix"}, ...)
Bunun iki nedeni vardır.
Öncelikle, makrolar Bazel'in işaret değerleri bilinmeden önce gerçekleşen yükleme aşamasında değerlendirildiğinden bir select
öğesinin hangi yolu seçeceğini bilmesi gereken makrolar çalışamaz.
Bu, yakın zamanda değişmesi muhtemel olmayan temel Bazel tasarım kısıtlamasıdır.
İkinci olarak, teknik olarak uygun olsa da yalnızca tüm select
yollarında yinelemesi gereken makrolar tutarlı bir kullanıcı arayüzüne sahip değildir. Bunu değiştirmek için daha fazla tasarım
gereklidir.
Bazel sorgusu ve cquery
Bazel query
, Bazel'in yükleme aşamasında çalışır.
Yani bir hedefin hangi komut satırı işaretlerini kullandığını bilemez, çünkü bu işaretlemeler derlemenin sonraki aşamalarına kadar (analiz aşamasında) değerlendirilmez.
Bu nedenle hangi select()
dalının seçildiği belirlenemez.
Bazel cquery
, Bazel'in analiz aşamasından sonra çalıştığı için tüm bu bilgileri içerir ve select()
sorunlarını doğru bir şekilde çözebilir.
Seçenek:
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
bağımlılarının fazla yakınına denk geliyor:
$ 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
Select() neden 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 anlamına geldiğ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 belgelere bakın. Aşağıda uçtan uca bir örnek verilmiştir:
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 somutlaştırın:
# 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()
öğ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
adlı kullanıcıya yorum yaptığınızda oluşturma işlemi 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ği için bu değiştirilemez. Bu, select() işlevini değerlendirmek için yeterli bilgi olmadığı anlamına gelir.
Ancak makrolar select()
'leri 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üyor?
Makrolar (kurallar değil) tanımı gereği select()
'ları değerlendiremediğinden, bu amaçla yapılan herhangi bir girişim genellikle bir 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'lar, sessizce başarısız olan özel bir durumlardır. Bu nedenle, Boole'lar konusunda özellikle dikkatli olmalısınız:
$ 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.
Bunun nedeni, makroların select()
içeriğini anlamamasıdır.
Aslında select()
nesnesini değerlendiriyorlar. Python 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 dikt gibi okuyabilir miyim?
Makrolar, derlemenin komut satırı parametrelerinin ne olduğunu anlamadan önce makrolar değerlendirildiği için seçili öğeleri değerlendiremez. Örneğin, her değere son ek eklemek için en azından select()
sözlüğünü okuyabilirler mi?
Kavramsal olarak bu mümkündür, ancak henüz bir Bazel özelliği değildir.
Bugün yapabilirsiniz: düz bir sözlük hazırlayıp bunu bir select()
dosyasına ekleyebilirsiniz:
$ 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
Hem select()
hem de yerel türleri desteklemek istiyorsanız şunu 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?
Çünkü bind()
bir BUILD kuralı değil, WORKSPACE kuralıdır.
Workspace kurallarının belirli bir yapılandırması yoktur ve DERLEME kurallarıyla aynı şekilde değerlendirilmez. Bu nedenle, bir bind()
içindeki select()
öğesi belirli bir dalı değerlendiremez.
Bunun yerine, bu tür bir çalışma zamanı belirlemesi yapmak için actual
özelliğinde select()
ile birlikte alias()
kullanmanız gerekir. alias()
bir DERLEME kuralı olduğu ve belirli bir yapılandırmayla değerlendirildiği için bu yöntem doğru şekilde çalışır.
Gerekirse bir alias()
için bind()
hedef noktası bile oluşturabilirsiniz.
$ 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 atlayabilirsiniz. Bu durumda, //:ssl
veya //external:ssl
öğesine bağlı tüm hedefler @alternative//:ssl
adresinde bulunan alternatifi görür.
Select() işlevim beklediğimi neden 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:
Derlediğiniz ü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 öğesine bağlı başka bir hedef //bar
oluşturuyorsanız şu komutu ç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
öğesinin yanındaki (12e23b9a2b534a)
, //myapp:foo
select()
özelliğini çözümleyen 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 çıkışı, her bir config_setting
için beklenen ayarlarla karşılaştırın.
//myapp:foo
aynı derlemede farklı yapılandırmalarda mevcut olabilir. Doğru olanı bulmak için somepath
kullanmayla ilgili yol gösterici bilgiler için cquery belgelerine bakın.
select()
neden platformlarla çalışmıyor?
Bazel, anlamlar 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 platformun hem @platforms//cpu:x86
hem de @platforms//os:linux
kısıtlamaları varsa ancak burada tanımlı :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.
Onun yerine ne yapmalıyım?
Bunun yerine, aşağıdaki kısıtlamalarla 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 hangi platformların istenen koşulları karşıladığını kullanıcılara daha net bir şekilde açıklar.
Ya gerçekten platformda select
.
Derleme ihtiyaçlarınız özellikle platformun kontrol edilmesini gerektiriyorsa config_setting
içindeki --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 yapmayı desteklemez. Bu yaklaşım derlemenizi fazlasıyla kısıtlar ve beklenen koşul eşleşmediğinde kullanıcıların kafasını karıştırır.