Yapılandırılabilir Derleme Özellikleri

Sorun bildirin Kaynağı göster

Yapılandırılabilir özellikler, genellikle select() olarak bilinen, kullanıcıların komut satırında derleme kuralı özelliklerinin değerlerini değiştirebilmesini sağlayan bir Bazel özelliğidir.

Bu özellik, örneğin mimari için uygun uygulamayı otomatik olarak seçen bir çoklu platform kitaplığı veya derleme sırasında özelleştirilebilen, özelliğe göre 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 işlem, komut satırındaki işaretlere göre başlangıç noktalarını "seçen" bir cc_binary bildirir. Daha ayrıntılı olarak belirtmek gerekirse, deps şu şekilde ifade edilir:

Komut gidilecek yer =
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 açık olmalıdır: Birden fazla koşul eşleşirse biri * Hepsi aynı değere çözümlenir. Örneğin, linux x86'da çalışırken her iki dal da "hello" olarak çözümlendiğinden bu {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}, açık bir şekilde ifade edilir. * Birinin values özelliği, diğer tüm kullanıcıların katı bir üst kümesidir. Örneğin, values = {"cpu": "x86", "compilation_mode": "dbg"}, values = {"cpu": "x86"} konusunda açık bir uzmanlık alanıdır.

//conditions:default yerleşik koşulu, başka hiçbir şey eşleşmediğ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. 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 hedef altındaki tüm geçişli bağımlılıklar için 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"],
    }),
)

koşu

$ bazel build //myapp:my_genrule --cpu=arm

x86 geliştirici makinesi, derlemeyi g_arm.src, tool1 ve x86tool.cc uygulamalarına bağlar. my_genrule öğesine ekli selectların her ikisi de my_genrule derleme parametrelerini (--cpu=arm) 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 ürününün derleme parametrelerini (--cpu=x86) 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ının yalnızca bir koleksiyonudur. Bunları bir hedef içine alarak, kullanıcıların birden çok yerden başvurabileceği "standart" koşulları sağlamak kolaydır.

constraint_value, çok platformlu davranış için destek sağlar.

Yerleşik işaretler

--cpu gibi işaretlemeler Bazel'da 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ı (-- olmadığı için "--cpu" yerine "cpu"). Bu işaret için beklenen değer valueN. values öğesindeki her girişin eşleşmesi durumunda :meaningful_condition_name eşleşir. Sipariş alakasız.

valueN, komut satırında ayarlanmış gibi ayrıştırılır. Bunun anlamı şudur:

  • values = { "compilation_mode": "opt" } boyutu bazel build -c opt ifadesiyle eşleşiyor
  • values = { "force_pic": "true" } boyutu bazel build --force_pic=1 ifadesiyle eşleşiyor
  • values = { "force_pic": "0" } boyutu bazel 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ğinden --show_progress'e izin verilmez. Hedefler, sonuçlarını oluşturmak için bu işareti kullanamaz. Desteklenen işaretlerin tam grubu belgelenmemiştir. Pratikte, "anlamlı" çoğu işaret işe yarıyor.

Özel işaretler

Starlark derleme ayarlarıyla kendi projeye özel flag'lerinizi modelleyebilirsiniz. Yerleşik işaretlerin aksine bunlar derleme hedefleri olarak tanımlanır. Bu nedenle Bazel, hedef etiketlerle bunlara 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 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 koşul (//conditions:default) başka hiçbir koşul eşleşmediğinde eşleşir.

"Tam olarak bir eşleşme" kuralı nedeniyle, eşleşmesi olmayan 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 da belirgin hatalar için select() no_match_error özelliğiyle özel mesajlar oluşturabilirsiniz.

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 paketlerde 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 öğelerinin select() ifadelerinde 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 şuna benzer şekilde 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() birleştiriliyor

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 öğesini iç içe yerleştirmeniz gerekiyorsa ve özelliğiniz diğer hedefleri değer olarak alıyorsa bir 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ı farklılıkla değerlendirilir. Ancak bu söz diziminin okunması ve sürdürülmesi zordur. [":standard_lib"] öğesini birden çok kez tekrarlamak zorunda kalmamak güzel olurdu.

Seçeneklerden biri, değeri bir 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 tekrara 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 ORing koşullarını doğrudan bir select içinde 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 ORbirden fazla config_setting öğesini 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 öğesini farklı özelliklerde paylaşabilir.

Biri diğerlerinin açık bir "uzmanlığı" değilse veya hepsi aynı değere çözümleniyorsa birden fazla koşulun eşleşmesi hataya neden olur. Ayrıntılı bilgi için buraya göz atın.

VE zincirleme

Birden fazla koşul eşleştiğinde 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 bir şekilde config_setting_group içinde sarmalamanız gerekir.

Özel hata mesajları

Varsayılan olarak, hiçbir koşul eşleşmediğinde select() öğesine eklenen hedef, şu hatayla karşılaşır:

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 öğesini [":foo"] olarak görür.

Makrolar select() ifadelerini kabul edebilir ve yerel kurallara iletebilir. Ancak doğrudan manipüle edemezler. Örneğin, bir makronun hızla satılan içeriğe

select({"foo": "val"}, ...)

-

select({"foo": "val_with_suffix"}, ...)

Bunun iki nedeni vardır.

Öncelikle, select işlevinin hangi yolu seçeceğini bilmesi gereken makrolar çalışmaz. Bunun nedeni, makrolar işaret değerleri bilinmeden önce gerçekleşen Bazel'in yükleme aşamasında değerlendirilmesidir. Bu, yakın zamanda değişmesi muhtemel olmayan temel bir Bazel tasarım kısıtlamasıdır.

İkinci olarak, yalnızca tüm select yollarında yinelemesi gereken makrolar teknik olarak uygun olsa da tutarlı bir kullanıcı arayüzüne sahip değildir. Bunu değiştirmek için daha fazla tasarım gerekir.

Bazel sorgusu ve cquery

Bazel query, Bazel'in yükleme aşamasında çalışır. Bu işaret, derlemenin sonraki aşamalarına (analiz aşamasında) kadar değerlendirilmediğinden, hedefin hangi komut satırı işaretlerini kullandığını bilemez. 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 yüksek tahmini bir değer verir:

$ 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() çalışıyor! Ayrıntılar için Kural uyumluluğu bölümüne bakın.

Bu sorunun genellikle en önemli sorunu, select() makrolarda çalışmamasıdır. Bunlar kurallardan farklıdır. Farkı anlamak için kurallar ve makrolar ile ilgili belgeleri inceleyin. 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({
        "//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 yapı 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 yorumu yaptığınızda derleme 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, 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()'leri değerlendirmek için yeterli bilgi olmadığı anlamına gelir.

Ancak makrolar select()'leri kurallara opak blob'lar 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() neden her zaman doğru sonucunu döndürüyor?

Tanımı gereği makrolar (kurallar değil) select()'leri değerlendiremediğinden, bunu yapmaya yönelik 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 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({
        "//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 değerlendirdikleri şey select() nesnesinin kendisi. 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.

select() öğesini bir dikt gibi okuyabilir miyim?

Makrolar, derlemenin komut satırı parametrelerinin ne olduğunu bilmeden önce 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 bu henüz bir Bazel özelliği değildir. Bugün düz bir sözlük hazırlayıp ardından select() içine besleyebilirsiniz:

$ 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, bir WORKSPACE kuralıdır.

Çalışma alanı kuralları belirli bir yapılandırmaya sahip değildir ve BUILD kurallarıyla aynı şekilde değerlendirilmez. Bu nedenle, bind() içindeki select() öğesi, gerçekte belirli bir dalla değerlendirilemez.

Bunun yerine, bu tür bir çalışma zamanı belirlemeyi gerçekleştirmek için actual özelliğinde select() ile alias() kullanmanız gerekir. alias() bir DERLE kuralı olduğu ve belirli bir yapılandırmayla değerlendirildiği için bu yöntem doğru şekilde çalışır.

Gerekirse alias() için bind() hedef puanınız bile olabilir.

$ 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 ayarını iletebilirsiniz. //:ssl veya //external:ssl öğesine bağlı olan tüm hedefler, @alternative//:ssl adresinde bulunan alternatifi görür.

select() işlevim 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şturmakta olduğ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'a 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() öğesini çö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]
  ...
}
...

Daha sonra bu çıkışı her config_setting tarafından beklenen ayarlarla karşılaştırın.

//myapp:foo, aynı derlemede farklı yapılandırmalarda bulunabilir. Doğru sorguyu bulmak için somepath kullanımıyla ilgili yönergeler için cquery belgelerine bakın.

select() neden platformlarla çalışmıyor?

Anlamları belirsiz olduğundan Bazel, 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ı ve 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 işlem, belirli anlamları tanımlayarak hangi platformların istenen koşulları karşıladığını kullanıcılar için daha açık hale getirir.

Platformda gerçekten select olmasını istiyorsam ne yapmalıyım?

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.