Yapılandırılabilir Derleme Özellikleri

Sorun bildir Kaynağı görüntüleyin 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 işlem, "seçen" bir cc_binary bildirir bayraklara göre, komut satırından erişebilirsiniz. Daha ayrıntılı olarak belirtmek gerekirse, deps şu şekilde ifade edilir:

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 atıfta bulunan etiketler olan yapılandırma koşullarına göre seçilecek bir değer için yer tutucu görevi görür. 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ği için {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"}. * 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"} , açık bir values = {"cpu": "x86"} uzmanlığıdır.

Yerleşik koşul //conditions:default başka hiçbir şey yapmaz.

Bu örnekte deps kullanılsa da select(), srcs üzerinde de aynı performansı gösterir. resources, cmd ve diğer özelliklerin çoğu. Az sayıda özellik yapılandırılabilir değildir ve bunlar açık bir şekilde açıklamalıdır. Örneğin, config_setting adlı kullanıcıya ait values özelliği yapılandırılamıyor.

select() ve bağımlılıkları

Belirli özellikler, tüm geçişli bağımlılıklar için derleme parametrelerini değiştirir olduğunu görebilirsiniz. Ö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. Bu bir yapılandırma geçişi.

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. my_genrule öğesine ekli selectların her ikisi de my_genrule derleme parametrelerini (--cpu=arm) içerir. tools özelliği, tool1 ve onun geçişli bağımlılıkları için --cpu değerini x86 olarak değiştirir. select açık tool1, tool1 ürününün --cpu=x86 derleme parametrelerini kullanıyor.

Yapılandırma koşulları

Yapılandırılabilir bir özellikteki her anahtar, bir config_setting veya constraint_value.

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, çok platformlu davranış için destek sağlar.

Yerleşik işaretler

--cpu gibi işaretler Bazel'e yerleştirilmiştir: Derleme aracı, tüm projelerdeki tüm derlemeler için 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, bir işaret adıdır (-- içermeyen, bu nedenle "--cpu" yerine "cpu"). valueN bu işaret için beklenen değerdir. Şu durumda :meaningful_condition_name eşleşir: values eşleşmelerinde her giriş. 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 belirtilmemiştir. Pratikte, "anlamlı" olan çoğu işaret çalışır.

Özel işaretler

Kendi projenize özel işaretlerinizi Starlark derleme ayarları Yerleşik işaretlerin aksine, bunlar derleme hedefleri olarak tanımlandığından Bazel bunlara hedef etiketleriyle referans verir.

Bunlar config_setting ile tetiklenir flag_values özellik:

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 (örneğin, --define foo=bar) tıklayın. Bu, values özelliği (values = {"define": "foo=bar"}) veya define_values özelliği (define_values = {"foo": "bar"}). --define yalnızca geriye dönük olarak desteklenir uyumluluk. 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 ayarlara karşı sessiz hatalara karşı koruma sağlar:

# 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 bayrak belirtme olanağı, ancak her seferinde her birini ayrı ayrı ayarlamak zahmetli olabilir. bir hedef oluşturmanız gerekir. 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. Bu özellik config_settingplatformun constraint_values alt kümesini içeren bu config_setting'lerin select() ifadelerde eşleşmesine izin verir.

Ö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'leri doğrudan da okuyabilir:

constraint_setting(name = "type")
constraint_value(name = "igneous", constraint_setting = "type")
constraint_value(name = "metamorphic", constraint_setting = "type")
sh_binary(
    name = "my_rocks",
    srcs = select({
        ":igneous": ["igneous.sh"],
        ":metamorphic" ["metamorphic.sh"],
    }),
)

Böylece, ortak bir metin kullanmanız gerektiğinde config_setting standartlarından tasarruf edebilirsiniz. tek değerlerle karşılaştırmalı olarak görebilirsiniz.

Platformlar hâlâ geliştirme aşamasındadır. Bkz. dokümanları 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 içine yerleştirmeniz gerekiyorsa ve özelliğiniz değerler olarak başka hedefler alıyorsa ara hedef kullanın:

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":armeabi_mode": [":armeabi_lib"],
        ...
    }),
)

sh_library(
    name = "armeabi_lib",
    srcs = select({
        ":opt_mode": ["armeabi_with_opt.sh"],
        ...
    }),
)

Birden fazla koşul eşleştiğinde select değerinin eşleşmesi gerekiyorsa VE operatörünü kullanabilirsiniz. zincirleme.

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"] öğesini birden fazla kez tekrarlamak zorunda olmasaydım kez.

Değeri BUILD değişkeni olarak önceden tanımlamak da bir seçenektir:

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

İlgili içeriği oluşturmak için kullanılan with_or makrosu Skylib selects modülü doğrudan bir select içinde ORkoşulları 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

İlgili içeriği oluşturmak için kullanılan config_setting_group makrosu Skylib selects modül ORbirden çok 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'ün aksine farklı hedefler, :config1_or_2'yi farklı özelliklerde paylaşabilir.

Biri net değilse birden fazla koşulun eşleşmesi için hatadır "uzmanlık" yoksa hepsi aynı değere çözümlenir. 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:

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 AND yapılamaz bir select içinde olmalıdır. 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() öğesinin eklendiği hedef şu hata mesajını vererek başarısız oluyor:

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 ile özelleştirilebilir özellik:

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 yapılandırmanın çözülmüş değerlerini alır özellikleri hakkında daha fazla bilgi edinin. Ö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() yan tümcelerini kabul edip bunları yerel kurallara iletebilir. Ancak bunları doğrudan değiştiremezler. Örneğin, para kazanma bir makronun

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

to

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

Bunun iki nedeni vardır.

Öncelikle, select öğesinin hangi yolu seçeceğini bilmesi gereken makrolar çalışmaz çünkü makrolar Bazel'in yükleme aşamasında değerlendirildiğinden Bunlar işaret değerleri bilinmeden önce gerçekleşir. 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 tutarlı bir kullanıcı arayüzüne sahip değil. 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. Yani, bu komutlar hedefin hangi komut satırı işaretlerini kullandığını bilemez, işaretleri, derlemenin sonraki aşamalarına kadar değerlendirilmez ( analiz aşamasında) da görebilirsiniz. Bu nedenle, hangi select() dallarının seçildiğini belirlenemiyor.

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 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() 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 sorunu, select() yönteminin çalışmamasıdır. makrolar. 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:

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 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, dönüşümden önce Bazel, derlemenin komut satırı işaretlerini okur. Demek ki yeterli select() işlevini değerlendirmek için bilgi kullanır.

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 durumlar olduğundan ihtiyatlı davranan

$ 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. Aslında değerlendirdikleri şey select() nesnesinin kendisi. 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. En azından okuyabilirler mi? select()'ın sözlüğünü nasıl eklersiniz?

Bu kavramsal olarak mümkün olsa da henüz Bazel'de bir özellik 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 ş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?

Öncelikle bind() kullanmayın. Bu politika kullanımdan kaldırılarak yerine alias() getirilmiştir.

Teknik yanıt, bind()'in bir depo olduğudur kuralından yararlanmanız gerekir.

Depo kurallarının belirli bir yapılandırması yoktur ve BUILD kurallarıyla aynı şekilde değerlendirilmez. Bu nedenle, bind() içindeki select() değerlendirebilirsiniz.

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.

$ 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 öğesinin yanındaki (12e23b9a2b534a), şu öğenin karmasıdır: //myapp:foo ürününün select() sorununu çözümleyen yapılandırma. Buradaki bazel config içeren değerler:

$ 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. Bkz. doğru verileri almak için somepath kullanma konusunda rehberlik için cquery docs bir.

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

Bazel, belirli bir platformun olup olmadığını kontrol eden yapılandırılabilir özellikleri desteklemiyor hedef platformdur. Çünkü anlamları belirsizdir.

Ö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ı ile ayrı platformu tanımlayan kullanıcının farklı fikirleri olabilir.

Bunun yerine ne yapmalıyım?

Bunun yerine, şununla herhangi bir platformla eşleşen bir config_setting tanımlayın: şu kısıtlamaları dikkate almanız gerekir:

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 bunu onaylamaz; yapınızı aşırı derecede sınırlandırır. Beklenen koşul eşleşmediğinde kullanıcıların kafasını karıştırır.