Yapılandırılabilir Derleme Özellikleri

Sorun bildirme Kaynağı görüntüleme Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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 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, farklı koşullar geçerli olduğunda özellik etkili bir şekilde farklı değerler alır.

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'da çalışırken her iki dal da "hello" olarak çözümlendiğinden bu {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}, belirsiz değildir. * 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"}'un net bir uzmanlığıdır.

Yerleşik koşul //conditions:default, 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 iyi performans gösterir. Yalnızca az sayıda özellik yapılandırılamaz ve bunlar açıkça belirtilir. Örneğin, config_setting'nin 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'nin tools, --cpu değerini Bazel'i çalıştıran makinenin CPU'suna (çapraz derleme sayesinde hedefin derlendiği CPU'dan farklı olabilir) değiştirir. Buna yapılandırma geçişi denir.

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 onun geçişli bağımlılıkları için --cpu değerini x86 olarak değiştirir. 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 etiketine referans verir.

config_setting, beklenen komut satırı işareti ayarlarının yalnızca bir koleksiyonudur. 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şaretlemeler Bazel'da yerleşiktir. Derleme aracı, bunları tüm projelerdeki tüm derlemeler için yerel 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'daki her giriş eşleşirse :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" } 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ğ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 projeye özel kendi işaretlerinizi modelleyebilirsiniz. Yerleşik flag'lerin aksine bunlar derleme hedefleri olarak tanımlanır. Bu nedenle Bazel, hedef etiketlerle bunlara 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 config_setting değerleri eşleşirse eşleşme gerçekleşir.

Varsayılan durum

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ğlar ancak hedef oluşturmak istediğinizde 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'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 ş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 karşılaştırmanız gerektiğinde standart config_setting'lere ihtiyaç duymazsınız.

Platformlar hâlâ geliştirme aşamasındadır. Ayrıntılar için dokümanları 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 bir select değerine ihtiyacınız varsa VE zincirleme işlemini 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"]'yi birden çok kez tekrarlamak zorunda kalmamak güzel olur.

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 kopyalara 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 ORing 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 işlevinin aksine, farklı hedefler :config1_or_2 öğesini farklı özelliklerde 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ılar 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 zincirleme işleminin aksine, mevcut config_setting'ler bir select içinde doğrudan AND edilemez. 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ümlenmiş 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 bunları doğrudan değiştiremezler. Örneğin, bir makronun

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 muhtemel 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 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 belirleyemez.

Bazel cquery, Bazel'in analiz aşamasından sonra çalışır. Bu nedenle, tüm bu bilgilere sahiptir ve select()'ları 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'un 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ı 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 kurallardan farklıdır. Aradaki farkı anlamak için kurallar ve makrolar ile ilgili dokümanlara bakın. Aşağıda uçtan uca bir örnek verilmiştir:

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 ö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 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.

Bazel, derlemenin komut satırı işaretlerini okumadan önce tanım gereği makroları değerlendirdiği için bu durum değiştirilemez. Bu, select() işlevlerini 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() işlevi neden her zaman doğru sonucu döndürür?

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. 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() öğesini bir dikt gibi okuyabilir miyim?

Bazel, derlemenin komut satırı parametrelerini öğrenmeden önce makrolar değerlendirildiği için makrolar, seçimleri değerlendiremez. Örneğin, her değere son ek eklemek için en azından select() sözlüğünü okuyabilir mi?

Kavramsal olarak bu mümkündür ancak henüz 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 ş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ı belirlemeyi gerçekleştirmek için actual özelliğinde select() ile alias() kullanmanız gerekir. alias() bir BUILD kuralı olduğu ve belirli bir yapılandırmayla değerlendirildiği için bu 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 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ş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ş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 öğ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]
  ...
}
...

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?

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": [],
    }),
)

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, 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 --platforms işaretinin değerini config_setting olarak 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 bu yöntemi önermez. Bu yöntem, derlemenizi aşırı derecede kısıtlar ve beklenen koşul eşleşmediğinde kullanıcıları şaşırtır.