Atribut Build yang Dapat Dikonfigurasi

Laporkan masalah Lihat sumber Per Malam · 7,2 · 7,1 · 7,0 · 6,5 · 6,4

Atribut yang dapat dikonfigurasi, umumnya dikenal sebagai select(), adalah fitur Bazel yang memungkinkan pengguna mengubah nilai atribut aturan build di command line.

Ini dapat digunakan, misalnya, untuk library multiplatform yang secara otomatis memilih implementasi yang sesuai untuk arsitektur, atau untuk biner yang dapat dikonfigurasi fitur yang dapat disesuaikan pada waktu {i>build<i}.

Contoh

# 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",
    },
)

Tindakan ini mendeklarasikan cc_binary yang "memilih" dependensinya berdasarkan flag pada command line. Khususnya, deps menjadi:

Perintah dependensi =
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() berfungsi sebagai placeholder untuk nilai yang akan dipilih berdasarkan kondisi konfigurasi, yang merupakan label yang merujuk pada config_setting target. Dengan menggunakan select() dalam atribut yang dapat dikonfigurasi, atribut tersebut secara efektif mengadopsi nilai yang berbeda ketika kondisi yang berbeda berlaku.

Kecocokan harus jelas: jika beberapa kondisi cocok, * Semuanya diselesaikan dengan nilai yang sama. Misalnya, saat berjalan di Linux x86, ini jelas {"@platforms//os:linux": "Hello", "@platforms//cpu:x86_64": "Hello"} karena kedua cabang ditetapkan ke "hello". * values seseorang adalah superset ketat dari semua yang lainnya. Misalnya, values = {"cpu": "x86", "compilation_mode": "dbg"} adalah spesialisasi yang tidak ambigu dari values = {"cpu": "x86"}.

Kondisi bawaan //conditions:default akan otomatis dicocokkan saat tidak dilakukan apa pun.

Meskipun contoh ini menggunakan deps, select() juga berfungsi juga di srcs, resources, cmd, dan sebagian besar atribut lainnya. Hanya sedikit atribut tidak dapat dikonfigurasi, dan dianotasikan dengan jelas. Misalnya, config_setting milik sendiri Atribut values tidak dapat dikonfigurasi.

select() dan dependensi

Atribut tertentu mengubah parameter build untuk semua dependensi transitif di bawah target. Misalnya, tools genrule mengubah --cpu menjadi CPU komputer yang menjalankan Bazel (yang, berkat kompilasi silang, mungkin berbeda daripada CPU yang menjadi target build). Hal ini dikenal sebagai transisi konfigurasi.

Diberikan

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

berlari

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

di mesin developer x86 akan mengikat build ke g_arm.src, tool1, dan x86tool.cc. Kedua select yang dilampirkan ke my_genrule menggunakan my_genrule parameter build, yang mencakup --cpu=arm. Atribut tools berubah --cpu hingga x86 untuk tool1 dan dependensi transitifnya. select di tool1 menggunakan parameter build tool1, yang menyertakan --cpu=x86.

Kondisi konfigurasi

Setiap kunci dalam atribut yang dapat dikonfigurasi adalah referensi label ke config_setting atau constraint_value.

config_setting hanyalah kumpulan dari pengaturan penanda baris perintah yang diharapkan. Dengan mengenkapsulasinya dalam target, hal itu mudah untuk mempertahankan "standar" kondisi yang dapat dirujuk dari beberapa tempat.

constraint_value memberikan dukungan untuk perilaku multi-platform.

Tanda bawaan

Tanda seperti --cpu sudah disertakan dalam Bazel: alat build memahami secara native pada semua build di semua project. Hal ini ditentukan dengan config_setting Atribut values:

config_setting(
    name = "meaningful_condition_name",
    values = {
        "flag1": "value1",
        "flag2": "value2",
        ...
    },
)

flagN adalah nama tanda (tanpa --, jadi "cpu", bukan "--cpu"). valueN adalah nilai yang diharapkan untuk tanda tersebut. :meaningful_condition_name cocok jika setiap entri dalam values cocok. Pesanan tidak relevan.

valueN diuraikan seolah-olah ditetapkan pada command line. Artinya:

  • values = { "compilation_mode": "opt" } cocok dengan bazel build -c opt
  • values = { "force_pic": "true" } cocok dengan bazel build --force_pic=1
  • values = { "force_pic": "0" } cocok dengan bazel build --noforce_pic

config_setting hanya mendukung tanda yang memengaruhi perilaku target. Misalnya, --show_progress tidak diizinkan karena hal ini hanya mempengaruhi cara Bazel melaporkan kemajuan kepada pengguna. Target tidak bisa menggunakannya penanda untuk menyusun hasilnya. Set tanda yang didukung tidak terdokumentasi. Dalam praktiknya, sebagian besar penanda yang "masuk akal" Anda.

Tanda kustom

Anda dapat membuat model flag spesifik per project Anda sendiri dengan Setelan build Starlark. Tidak seperti penanda bawaan, ini adalah ditetapkan sebagai target build, jadi Bazel mereferensikannya dengan label target.

Peristiwa ini dipicu dengan config_setting flag_values :

config_setting(
    name = "meaningful_condition_name",
    flag_values = {
        "//myflags:flag1": "value1",
        "//myflags:flag2": "value2",
        ...
    },
)

Perilakunya sama seperti tanda bawaan. Lihat di sini untuk sebuah contoh kerja.

--define adalah sintaksis lama alternatif untuk tanda kustom (misalnya --define foo=bar). Hal ini dapat dinyatakan dalam Atribut nilai (values = {"define": "foo=bar"}) atau Atribut define_values (define_values = {"foo": "bar"}). --define hanya didukung untuk versi mundur kompatibilitas mundur. Pilih setelan build Starlark jika memungkinkan.

values, flag_values, dan define_values mengevaluasi secara terpisah. Tujuan config_setting cocok jika semua nilai di antaranya cocok.

Kondisi default

Kondisi bawaan //conditions:default cocok saat tidak ada kondisi lain yang cocok.

Karena "persis satu kecocokan" aturan, atribut yang dapat dikonfigurasi tanpa kecocokan dan tidak ada kondisi default yang memunculkan error "no matching conditions". Hal ini dapat melindungi dari kegagalan senyap dari setelan yang tidak terduga:

# 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

Untuk kesalahan yang lebih jelas lagi, Anda dapat menyetel pesan khusus dengan select() Atribut no_match_error.

Platform

Sementara kemampuan untuk menentukan beberapa tanda pada baris perintah menyediakan fleksibilitas, mungkin juga merepotkan untuk menetapkan masing-masing setiap saat Anda ingin membangun target. Platform memungkinkan Anda menggabungkannya menjadi beberapa paket sederhana.

# 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 dapat ditentukan di command line. Hal ini mengaktifkan config_setting yang berisi subset constraint_values platform, sehingga config_setting tersebut dapat dicocokkan dalam ekspresi select().

Misalnya, untuk menetapkan atribut srcs dari my_rocks ke calcite.sh, Anda cukup menjalankan

bazel build //my_app:my_rocks --platforms=//myapp:marble_platform

Tanpa platform, ini mungkin terlihat seperti

bazel build //my_app:my_rocks --define color=white --define texture=smooth --define type=metamorphic

select() juga dapat langsung membaca constraint_value:

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

Dengan cara ini, Anda tidak memerlukan config_setting boilerplate saat Anda hanya perlu diperiksa dengan satu nilai.

Platform masih dalam pengembangan. Lihat dokumentasi untuk mengetahui detailnya.

Menggabungkan select()

select dapat muncul beberapa kali dalam atribut yang sama:

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 tidak dapat muncul di dalam select yang lain. Jika Anda perlu membuat selects bertingkat dan atribut Anda menggunakan target lain sebagai nilai, gunakan target perantara:

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

Jika Anda memerlukan select untuk dicocokkan saat beberapa kondisi cocok, pertimbangkan DAN perantaian.

Perantaian ATAU

Pertimbangkan hal berikut:

sh_binary(
    name = "my_target",
    srcs = ["always_include.sh"],
    deps = select({
        ":config1": [":standard_lib"],
        ":config2": [":standard_lib"],
        ":config3": [":standard_lib"],
        ":config4": [":special_lib"],
    }),
)

Sebagian besar kondisi dievaluasi dengan dependensi yang sama. Tetapi {i>syntax<i} ini sulit dibaca dan pertahankan. Sebaiknya Anda tidak mengulangi [":standard_lib"] kali.

Salah satu opsinya adalah menetapkan nilainya di awal sebagai variabel BUILD:

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

Hal ini memudahkan pengelolaan dependensi. Tapi itu masih menyebabkan duplikasi.

Untuk dukungan langsung lainnya, gunakan salah satu opsi berikut:

selects.with_or

Tujuan with_or makro di kolom Skylib selects modul mendukung kondisi ORing langsung di dalam select:

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

Tujuan config_setting_group makro di kolom Skylib selects modul mendukung ORbeberapa config_setting:

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

Tidak seperti selects.with_or, target yang berbeda dapat berbagi :config1_or_2 dengan atribut yang berbeda.

Ini merupakan error untuk beberapa kondisi yang cocok, kecuali jika salah satunya tidak ambigu "spesialisasi" dari yang lain atau mereka semua selesai pada nilai yang sama. Lihat di sini untuk mengetahui detailnya.

Perantaian AND

Jika Anda memerlukan cabang select untuk dicocokkan saat beberapa kondisi cocok, gunakan Makro Skylib 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"],
    }),
)

Tidak seperti OR perantaian, config_setting yang ada tidak dapat AND secara langsung di dalam select. Anda harus menggabungkannya secara eksplisit dalam config_setting_group.

Pesan error khusus

Secara default, jika tidak ada kondisi yang cocok, target yang ditambahkan select() gagal dengan error:

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

Hal ini dapat disesuaikan dengan no_match_error :

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

Kompatibilitas aturan

Penerapan aturan menerima nilai yang telah diselesaikan dari variabel yang dapat dikonfigurasi . Misalnya, jika:

# myapp/BUILD

some_rule(
    name = "my_target",
    some_attr = select({
        ":foo_mode": [":foo"],
        ":bar_mode": [":bar"],
    }),
)
$ bazel build //myapp/my_target --define mode=foo

Kode penerapan aturan melihat ctx.attr.some_attr sebagai [":foo"].

Makro dapat menerima klausa select() dan meneruskannya ke native aturan. Namun, pengguna tidak dapat langsung memanipulasinya. Misalnya, tidak mungkin makro untuk mengonversi

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

hingga

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

Ini karena dua alasan.

Pertama, makro yang perlu mengetahui jalur mana yang akan dipilih select tidak dapat berfungsi karena makro dievaluasi pada fase pemuatan Bazel, yang terjadi sebelum nilai penanda diketahui. Ini adalah batasan desain Bazel inti yang tidak mungkin berubah dalam waktu dekat.

Kedua, makro yang hanya perlu melakukan iterasi pada semua jalur select, sambil layak secara teknis, tidak memiliki UI yang koheren. Desain lebih lanjut diperlukan untuk mengubah ini.

Kueri dan kueri Bazel

Bazel query beroperasi melalui fase pemuatan. Ini berarti ia tidak tahu apa yang penanda baris perintah digunakan target karena mereka tidak dievaluasi hingga nanti dalam build (dalam fase analisis). Sehingga tidak dapat menentukan cabang select() mana yang dipilih.

Bazel cquery beroperasi setelah fase analisis Bazel, sehingga memiliki semua informasi ini dan dapat me-resolve select() secara akurat.

Pertimbangkan:

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 melebihi perkiraan dependensi :my_lib:

$ bazel query 'deps(//myapp:my_lib)'
//myapp:my_lib
//myapp:foo_dep
//myapp:bar_dep

sementara cquery menunjukkan dependensi persisnya:

$ bazel cquery 'deps(//myapp:my_lib)' --//myapp:dog_type=pug
//myapp:my_lib
//myapp:bar_dep

FAQ

Mengapa select() tidak berfungsi dalam makro?

select() berfungsi sesuai aturan! Lihat Kompatibilitas Aturan untuk spesifikasi pendukung.

Masalah utama yang biasanya dimaksud dengan pertanyaan ini adalah bahwa select() tidak berfungsi di makro. Aturan ini berbeda dengan aturan. Lihat dokumentasi tentang aturan dan makro untuk memahami perbedaannya. Berikut adalah contoh menyeluruh:

Tentukan aturan dan makro:

# 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)

Buat instance aturan dan makro:

# 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",
        "//tools/target_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",
        "//tools/target_cpu:ppc": "other string",
    }),
)

Pembuatan gagal karena sad_macro tidak dapat memproses select():

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

Pembangunan berhasil jika Anda mengomentari sad_macro:

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

Hal ini tidak mungkin diubah karena makro berdasarkan definisi dievaluasi sebelum Bazel membaca flag command line build. Itu berarti tidak cukup informasi untuk mengevaluasi select().

Namun, makro dapat meneruskan select() sebagai blob buram ke aturan:

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

Mengapa select() selalu menampilkan true?

Karena makro (tetapi bukan aturan) menurut definisi tidak dapat mengevaluasi select(), upaya apa pun untuk melakukannya biasanya menghasilkan kesalahan:

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().

Boolean adalah kasus khusus yang gagal secara diam-diam, jadi Anda harus waspada dengan mereka:

$ 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,
        "//tools/target_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.

Hal ini terjadi karena makro tidak memahami konten select(). Jadi, yang sebenarnya mereka evaluasi adalah objek select() itu sendiri. Menurut Desain Pythonic standar, semua objek selain sebagian kecil pengecualian secara otomatis mengembalikan nilai benar (true).

Bisakah saya membaca select() seperti dict?

Makro tidak dapat mengevaluasi pilihan karena makro dievaluasi sebelum Bazel mengetahui parameter baris perintah build. Bisakah mereka setidaknya membaca kamus select(), misalnya, untuk menambahkan akhiran ke setiap nilai?

Secara konseptual, hal ini mungkin dilakukan, tetapi belum merupakan fitur Bazel. Yang dapat Anda lakukan sekarang adalah menyiapkan kamus langsung, lalu memasukkannya ke dalam select():

$ 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

Jika ingin mendukung select() dan jenis native, Anda dapat melakukannya:

$ 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 + "> $@",
    )

Mengapa select() tidak berfungsi dengan bind()?

Karena bind() adalah aturan WORKSPACE, bukan aturan BUILD.

Aturan Workspace tidak memiliki konfigurasi khusus, dan tidak dievaluasi di dengan cara yang sama seperti aturan BUILD. Oleh karena itu, select() di bind() tidak dapat benar-benar dievaluasi ke cabang tertentu.

Sebagai gantinya, Anda harus menggunakan alias(), dengan select() di atribut actual untuk melakukan jenis penentuan waktu proses ini. Ini berfungsi dengan benar, karena alias() adalah aturan BUILD, dan dievaluasi dengan konfigurasi spesifik.

Anda bahkan dapat memiliki titik target bind() ke alias(), jika diperlukan.

$ 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",
    }),
)

Dengan penyiapan ini, Anda dapat meneruskan --define ssl_library=alternative, dan semua target yang bergantung pada //:ssl atau //external:ssl akan melihat opsi yang terletak di @alternative//:ssl.

Mengapa select() saya tidak memilih apa yang saya harapkan?

Jika //myapp:foo memiliki select() yang tidak memilih kondisi yang Anda harapkan, gunakan cquery dan bazel config untuk men-debug:

Jika //myapp:foo adalah target level teratas yang Anda buat, jalankan:

$ bazel cquery //myapp:foo <desired build flags>
//myapp:foo (12e23b9a2b534a)

Jika Anda membuat beberapa target //bar lain yang bergantung pada //myapp:foo di subgrafiknya, jalankan:

$ bazel cquery 'somepath(//bar, //myapp:foo)' <desired build flags>
//bar:bar   (3ag3193fee94a2)
//bar:intermediate_dep (12e23b9a2b534a)
//myapp:foo (12e23b9a2b534a)

(12e23b9a2b534a) di samping //myapp:foo adalah hash dari konfigurasi yang me-resolve select() //myapp:foo. Anda dapat memeriksa nilai dengan bazel config:

$ 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]
  ...
}
...

Kemudian, bandingkan output ini dengan setelan yang diharapkan oleh setiap config_setting.

//myapp:foo mungkin berada di konfigurasi yang berbeda dalam build yang sama. Lihat dokumen kueri untuk panduan penggunaan somepath guna mendapatkan jawaban yang benar satu.

Mengapa select() tidak berfungsi dengan platform?

Bazel tidak mendukung atribut yang dapat dikonfigurasi yang memeriksa apakah platform tertentu adalah platform target karena semantiknya tidak jelas.

Contoh:

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

Dalam file BUILD ini, select() yang harus digunakan jika platform target memiliki @platforms//cpu:x86 dan @platforms//os:linux, tetapi bukan :x86_linux_platform ditentukan di sini? Penulis file BUILD dan pengguna yang mendefinisikan platform terpisah mungkin memiliki gagasan yang berbeda.

Apa yang harus saya lakukan?

Sebagai gantinya, tentukan config_setting yang cocok dengan platform apa pun dengan batasan-batasan ini:

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

Proses ini mendefinisikan semantik tertentu, sehingga lebih jelas bagi pengguna apa memenuhi kondisi yang diinginkan.

Bagaimana jika saya benar-benar ingin select ada di platform ini?

Jika persyaratan build Anda secara khusus mengharuskan pemeriksaan platform, dapat membalik nilai flag --platforms dalam config_setting:

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

Tim Bazel tidak mendukung tindakan ini; hal itu terlalu membatasi build Anda dan akan membingungkan pengguna jika kondisi yang diharapkan tidak cocok.