BazelCon 2022 akan hadir pada 16-17 November ke New York dan online.
Daftar sekarang.

Atribut Build yang Dapat Dikonfigurasi

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Atribut yang dapat dikonfigurasi, biasanya disebut select(), adalah fitur Bazel yang memungkinkan pengguna mengganti 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 build.

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

Baris ini mendeklarasikan cc_binary yang "memilih" dependensinya berdasarkan flag pada command line. Secara khusus, 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 ke target config_setting. Dengan menggunakan select() dalam atribut yang dapat dikonfigurasi, atribut tersebut akan secara efektif mengadopsi nilai yang berbeda saat kondisi yang berbeda berlaku.

Pencocokan harus jelas: salah satu kondisi harus cocok atau, jika beberapa kondisi cocok, values salah satu harus merupakan superset yang ketat dari semua kondisi lainnya. Misalnya, values = {"cpu": "x86", "compilation_mode": "dbg"} adalah spesialisasi values = {"cpu": "x86"} yang tidak ambigu. Kondisi bawaan //conditions:default otomatis cocok jika tidak ada yang cocok.

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

select() dan dependensi

Atribut tertentu mengubah parameter build untuk semua dependensi transitif berdasarkan target. Misalnya, tools genrule mengubah --cpu menjadi CPU mesin yang menjalankan Bazel (yang, berkat kompilasi silang, mungkin berbeda dengan CPU yang digunakan untuk membuat target). 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"],
    }),
)

sedang berjalan

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

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

Kondisi konfigurasi

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

config_setting hanyalah kumpulan setelan tanda command line yang diharapkan. Dengan mengenkapsulasi ini dalam target, mudah untuk mempertahankan kondisi "standar" kondisi yang dapat dirujuk pengguna dari beberapa tempat.

constraint_value memberikan dukungan untuk perilaku multi-platform.

Flag bawaan

Flag seperti --cpu di-build ke Bazel: alat build secara native memahaminya untuk semua build di semua project. Hal ini ditentukan dengan atribut valuesconfig_setting:

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

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

valueN diurai seolah-olah disetel di command line. Ini 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 flag yang memengaruhi perilaku target. Misalnya, --show_progress tidak diizinkan karena hanya memengaruhi cara Bazel melaporkan progres kepada pengguna. Target tidak dapat menggunakan flag tersebut untuk membuat hasil. Kumpulan tanda yang didukung secara tepat tidak didokumentasikan. Dalam praktiknya, kebanyakan bendera yang "masuk akal" berfungsi.

Tanda khusus

Anda dapat membuat model flag khusus project dengan setelan build Starlark. Tidak seperti flag bawaan, ini didefinisikan sebagai target build, sehingga Bazel mereferensikannya dengan label target.

Ini dipicu dengan atribut flag_valuesconfig_setting:

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

Perilakunya sama seperti untuk flag bawaan. Lihat di sini untuk mengetahui contoh yang berfungsi.

--define adalah sintaksis lama alternatif untuk flag kustom (misalnya --define foo=bar). Ini dapat dinyatakan dalam atribut values (values = {"define": "foo=bar"}) atau atribut define_values (define_values = {"foo": "bar"}). --define hanya didukung untuk kompatibilitas mundur. Memilih setelan build Starlark jika memungkinkan.

values, flag_values, dan define_values mengevaluasi secara independen. config_setting cocok jika semua nilai di semuanya cocok.

Kondisi default

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

Karena aturan "tepat satu kecocokan" atribut, atribut yang dapat dikonfigurasi tanpa kecocokan dan tidak ada kondisi default yang mengeluarkan error "no matching conditions". Tindakan 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 error yang lebih jelas, Anda dapat menetapkan pesan kustom dengan atribut no_match_error select().

Platform

Meskipun kemampuan untuk menentukan beberapa flag pada command line memberikan fleksibel, kemampuan ini juga dapat menyulitkan untuk menetapkan setiap flag satu per satu setiap kali Anda ingin membuat target. Platform memungkinkan Anda menggabungkannya ke dalam 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 pada command line. Ini mengaktifkan config_setting yang berisi subset constraint_values platform, yang memungkinkan config_setting tersebut cocok dalam ekspresi select().

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

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

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

Tindakan ini menghemat kebutuhan config_setting boilerplate saat Anda hanya perlu memeriksa nilai tunggal.

Platform masih dalam pengembangan. Lihat dokumentasi untuk mengetahui detailnya.

Menggabungkan select()

select dapat muncul beberapa kali di 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 lainnya. Jika Anda perlu membuat selects bertingkat dan atribut Anda mengambil target lain sebagai nilai, gunakan target menengah:

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 perlu mencocokkan select saat beberapa kondisi cocok, pertimbangkan rantai DAN.

ATAU penautan

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 mengevaluasi dependensi yang sama. Namun, sintaksis ini sulit dibaca dan dikelola. Sebaiknya Anda tidak mengulang [":standard_lib"] beberapa kali.

Salah satu opsi adalah menentukan nilai 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. Namun, masalah ini tetap menyebabkan duplikasi yang tidak diperlukan.

Untuk dukungan langsung lainnya, gunakan salah satu dari berikut:

selects.with_or

Makro with_or dalam modul Skylib&39 mendukung OR kondisi OR 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

Makro config_setting_group di modul selects Skylib&s mendukung 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 di atribut yang berbeda.

Error untuk beberapa kondisi akan dicocokkan kecuali jika salah satunya tidak jelas ambigu dari spesialisasi lainnya. Lihat di sini untuk mengetahui detailnya.

DAN penautan

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 perantaian yang dirantai, config_settingyang ada tidak dapat langsung ANDdi lihat dalam select. Anda harus menggabungkannya secara eksplisit dalam config_setting_group.

Pesan error khusus

Secara default, jika tidak ada kondisi yang cocok, target select() akan terpasang ke 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

Ini dapat disesuaikan dengan atribut 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 diselesaikan dari atribut 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 aturan native. Namun, mereka tidak dapat memanipulasinya secara langsung. Misalnya, makro tidak dapat dikonversi

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

to

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

Hal ini disebabkan oleh dua alasan.

Pertama, makro yang perlu mengetahui jalur yang akan dipilih oleh select tidak dapat berfungsi karena makro dievaluasi dalam fase pemuatan Bazel, yang terjadi sebelum nilai flag diketahui. Ini adalah pembatasan desain Bazel inti yang tidak akan berubah dalam waktu dekat.

Kedua, makro yang hanya perlu melakukan iterasi pada semua jalur select, meskipun secara teknis memungkinkan, tidak memiliki UI yang koheren. Desain lebih lanjut diperlukan untuk mengubahnya.

Kueri Bazel dan cquery

Bazel query beroperasi melalui fase pemuatan Bazel. Ini berarti tidak mengetahui command line apa yang menandai target karena target tersebut tidak dievaluasi sampai nanti dalam build (dalam fase analisis). Jadi, cabang select() tidak dapat ditentukan.

Bazel cquery beroperasi setelah fase analisis Bazel, sehingga memiliki semua informasi ini dan dapat menyelesaikan 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 menampilkan dependensi persisnya:

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

FAQ

Mengapa Anda tidak memilih select() dalam makro?

select() memang berfungsi dalam aturan! Lihat Kompatibilitas aturan untuk mengetahui detailnya.

Masalah utama yang biasanya ditimbulkan oleh pertanyaan ini adalah select() tidak berfungsi dalam makro. Aturan ini berbeda dengan aturan. Lihat dokumentasi tentang aturan dan makro untuk memahami perbedaannya. Berikut adalah contoh end-to-end:

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.

Gedung berhasil jika Anda memberi komentar pada 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. Artinya, tidak ada 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 benar?

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

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 sangat berhati-hati dengannya:

$ 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 dari select(). Jadi, objek yang benar-benar dievaluasi adalah objek select() itu sendiri. Menurut standar desain Pythonic, semua objek selain sejumlah kecil pengecualian otomatis menampilkan true.

Bisakah saya membaca select() seperti dikte?

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

Secara konseptual, hal ini mungkin dilakukan, tetapi belum menjadi fitur Bazel. Yang dapat Anda lakukan sekarang adalah menyiapkan kamus langsung, lalu memasukkannya ke 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 jenis native dan select(), Anda dapat melakukan ini:

$ 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 tidak memilih select() yang berfungsi dengan binding()?

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

Aturan Workspace tidak memiliki konfigurasi tertentu, dan tidak dievaluasi dengan cara yang sama seperti aturan BUILD. Oleh karena itu, select() dalam bind() sebenarnya tidak dapat mengevaluasi cabang tertentu.

Sebagai gantinya, Anda harus menggunakan alias(), dengan select() dalam atribut actual, untuk melakukan penentuan waktu proses jenis ini. Fungsi ini bekerja dengan benar, karena alias() adalah aturan BUILD, dan dievaluasi dengan konfigurasi tertentu.

Anda bahkan dapat memiliki bind() target poin 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 target apa pun yang bergantung pada //:ssl atau //external:ssl akan melihat alternatif 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 melakukan debug:

Jika //myapp:foo adalah target tingkat atas yang Anda buat, jalankan:

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

Jika Anda membuat beberapa target //bar lainnya yang bergantung pada //myapp:foo di suatu tempat dalam 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 konfigurasi yang menyelesaikan //myapp:foo's select(). Anda dapat memeriksa nilainya 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]
  ...
}
...

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

//myapp:foo mungkin ada dalam konfigurasi yang berbeda di build yang sama. Lihat dokumen cquery untuk mendapatkan panduan penggunaan somepath guna mendapatkan halaman yang tepat.

Mengapa select() tidak berfungsi dengan platform?

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

Misalnya:

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() mana yang harus digunakan jika platform target memiliki batasan @platforms//cpu:x86 dan @platforms//os:linux, tetapi bukan :x86_linux_platform yang ditentukan di sini? Penulis file BUILD dan pengguna yang menentukan platform terpisah mungkin memiliki ide yang berbeda.

Apa yang harus saya lakukan?

Sebagai gantinya, tentukan config_setting yang sesuai dengan platform mana pun dengan batasan berikut:

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 menentukan semantik tertentu, sehingga memperjelas kepada pengguna platform apa yang memenuhi kondisi yang diinginkan.

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

Jika persyaratan build Anda secara khusus mewajibkan pemeriksaan platform, Anda 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 untuk melakukan ini; tim terlalu membatasi build Anda dan membingungkan pengguna jika kondisi yang diharapkan tidak cocok.