Atribut Build yang Dapat Dikonfigurasi

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.
Laporkan masalah Lihat sumber

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

Ini dapat digunakan, misalnya, untuk library multiplatform yang 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",
    },
)

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() pada atribut yang dapat dikonfigurasi, atribut secara efektif mengadopsi nilai yang berbeda saat kondisi yang berbeda berlaku.

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

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

select() dan dependensi

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

pada 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 berubah --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" yang dapat direferensikan pengguna dari beberapa tempat.

constraint_value memberikan dukungan untuk perilaku multi-platform.

Tanda bawaan

Tanda seperti --cpu di-build ke dalam Bazel: alat build secara native memahaminya untuk semua build di semua project. Atribut ini ditentukan dengan atribut values config_setting:

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

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

valueN diurai seolah-olah telah ditetapkan pada 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 hasilnya. Kumpulan persis flag yang didukung tidak didokumentasikan. Dalam praktiknya, sebagian besar tanda yang berfungsi "masuk akal".

Tanda kustom

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

Ini dipicu dengan atribut flag_values config_setting:

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

Perilakunya sama dengan tanda bawaan. Lihat di sini untuk mengetahui contoh yang berfungsi.

--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 kompatibilitas mundur. Pilih setelan build Starlark jika memungkinkan.

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

Kondisi default

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

Karena aturan "tepat satu pencocokan", atribut yang dapat dikonfigurasi tanpa kecocokan dan tidak ada kondisi default akan menghasilkan 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 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 fleksibilitas, terkadang menyetel setiap flag akan berbeda setiap kali Anda ingin mem-build 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 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 akan 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"],
    }),
)

Hal ini menghemat kebutuhan boilerplate config_setting ketika Anda hanya perlu memeriksa nilai tunggal.

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 lainnya. Jika Anda perlu menyarangkan selects 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 memerlukan select untuk dicocokkan saat beberapa kondisi cocok, pertimbangkan rantai.

ATAU perantaian

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

Salah satu opsinya 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, langkah ini tetap menyebabkan duplikasi yang tidak perlu.

Untuk mendapatkan dukungan langsung lainnya, gunakan salah satu opsi berikut:

selects.with_or

Makro with_or dalam modul selects Skylib mendukung kondisi OR langsung 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 dalam modul selects Skylib mendukung ORing beberapa 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 berbeda dapat berbagi :config1_or_2 di seluruh atribut yang berbeda.

Ini adalah error untuk beberapa kondisi yang cocok kecuali jika salah satu adalah "spesialisasi" yang berbeda dari yang lain. Lihat di sini untuk mengetahui detailnya.

DAN rantai

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 OR, config_setting yang ada tidak dapat langsung AND di dalam select. Anda harus menggabungkannya secara eksplisit dalam config_setting_group.

Pesan error kustom

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

Implementasi 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 dikarenakan dua alasan.

Pertama, makro yang perlu mengetahui jalur mana 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 kecil kemungkinannya akan berubah dalam waktu dekat.

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

Kueri dan kueri Bazel

Bazel query beroperasi selama fase pemuatan Bazel. Artinya, fungsi tersebut tidak mengetahui tanda command line yang digunakan target karena flag tersebut tidak dievaluasi hingga build selesai (pada fase analisis). Sehingga tidak dapat menentukan cabang select() yang dipilih.

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 memperkirakan dependensi :my_lib secara berlebihan:

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

saat cquery menunjukkan dependensi yang tepat:

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

FAQ

Mengapa select() tidak berfungsi di makro?

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

Masalah utama yang biasanya diajukan oleh pertanyaan ini adalah 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",
    }),
)

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

Pembuatan berhasil saat 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 dapat diubah karena makro menurut 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 nilai 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 otomatis, jadi Anda harus sangat waspada 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 select(). Jadi, yang benar-benar mereka evaluasi adalah objek select() itu sendiri. Menurut standar desain Pythonic, semua objek selain sejumlah kecil pengecualian otomatis menampilkan true.

Dapatkah saya membaca select() seperti dikte?

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

Secara konseptual, ini mungkin dilakukan, tetapi belum menjadi fitur Bazel. Yang dapat Anda lakukan hari ini adalah menyiapkan kamus lurus, 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 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 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() tidak dapat benar-benar mengevaluasi cabang tertentu.

Sebagai gantinya, Anda harus menggunakan alias(), dengan select() dalam atribut actual, untuk melakukan jenis penentuan runtime ini. Cara ini berfungsi dengan benar, karena alias() merupakan aturan BUILD, dan dievaluasi dengan konfigurasi tertentu.

Anda bahkan dapat memiliki bind() titik target 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 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 suatu tempat 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 konfigurasi yang me-resolve select() //myapp:foo. 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]
  ...
}
...

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

//myapp:foo mungkin ada dalam konfigurasi yang berbeda-beda dalam build yang sama. Lihat dokumen kueri guna mendapatkan panduan tentang cara menggunakan somepath untuk mendapatkan dokumentasi yang tepat.

Mengapa select() tidak berfungsi dengan platform?

Bazel tidak mendukung atribut yang dapat dikonfigurasi, yang memeriksa apakah platform tertentu merupakan 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() mana yang harus digunakan jika platform target memiliki batasan @platforms//cpu:x86 dan @platforms//os:linux, tetapi bukan yang :x86_linux_platform 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 cocok dengan platform apa pun dengan 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 menentukan semantik tertentu, sehingga memperjelas kepada pengguna platform mana yang memenuhi kondisi yang diinginkan.

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

Jika persyaratan build Anda secara khusus mengharuskan 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 hal ini; tim ini terlalu membatasi build Anda dan membingungkan pengguna saat kondisi yang diharapkan tidak cocok.