Halaman ini membahas manfaat dan penggunaan dasar konfigurasi Starlark, API Bazel untuk menyesuaikan cara project Anda di-build. Halaman ini mencakup cara menentukan setelan build dan memberikan contoh.
Hal ini memungkinkan Anda untuk:
- menentukan flag kustom untuk project Anda, sehingga tidak perlu menggunakan
--define - menulis
transisi untuk mengonfigurasi dependensi dalam
konfigurasi yang berbeda dari induknya
(seperti
--compilation_mode=optatau--cpu=arm) - membuat default yang lebih baik ke dalam aturan (seperti otomatis membuat
//my:android_appdengan SDK yang ditentukan)
dan lainnya, semuanya sepenuhnya dari file .bzl (tidak diperlukan rilis Bazel). Lihat repo
bazelbuild/examples untuk
contoh.
Setelan build yang ditentukan pengguna
Setelan build adalah satu bagian
konfigurasi
informasi. Anggap konfigurasi sebagai peta nilai kunci. Menetapkan --cpu=ppc
dan --copt="-DFoo" akan menghasilkan konfigurasi yang terlihat seperti
{cpu: ppc, copt: "-DFoo"}. Setiap entri adalah setelan build.
Flag tradisional seperti cpu dan copt adalah setelan native — kuncinya ditentukan dan nilainya ditetapkan di dalam kode java bazel native.
Pengguna Bazel hanya dapat membaca dan menulisnya melalui command line dan API lain yang dikelola secara native. Mengubah flag native, dan API yang mengeksposnya, memerlukan rilis bazel. Setelan build yang ditentukan pengguna ditentukan dalam file .bzl (dan dengan demikian, tidak memerlukan rilis bazel untuk mendaftarkan perubahan). Setelan ini juga dapat ditetapkan melalui command line
(jika ditetapkan sebagai flags, lihat lebih lanjut di bawah), tetapi juga dapat ditetapkan melalui
set via transisi yang ditentukan pengguna.
Menentukan setelan build
Parameter build_setting rule()
Setelan build adalah aturan seperti aturan lainnya dan dibedakan menggunakan
atribut fungsi rule()Starlark build_setting.
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
Atribut build_setting menggunakan fungsi yang menetapkan jenis setelan build. Jenis ini terbatas pada kumpulan jenis Starlark dasar seperti bool dan string. Lihat dokumentasi modul config
untuk mengetahui detailnya. Pengetikan yang lebih rumit dapat dilakukan dalam fungsi implementasi aturan. Selengkapnya tentang hal ini di bawah.
Fungsi modul config menggunakan parameter boolean opsional, flag,
yang ditetapkan ke false secara default. Jika flag ditetapkan ke true, setelan build
dapat ditetapkan di command line oleh pengguna serta secara internal oleh penulis aturan
melalui nilai default dan transisi.
Tidak semua setelan harus dapat ditetapkan oleh pengguna. Misalnya, jika Anda sebagai penulis aturan memiliki beberapa mode debug yang ingin diaktifkan di dalam aturan pengujian, Anda tidak ingin memberikan kemampuan kepada pengguna untuk mengaktifkan fitur tersebut secara sembarangan di dalam aturan non-pengujian lainnya.
Menggunakan ctx.build_setting_value
Seperti semua aturan, aturan setelan build memiliki fungsi implementasi.
Nilai jenis Starlark dasar dari setelan build dapat diakses melalui metode ctx.build_setting_value. Metode ini hanya tersedia untuk
ctx objek aturan setelan build. Metode implementasi ini dapat meneruskan nilai setelan build secara langsung atau melakukan pekerjaan tambahan, seperti pemeriksaan jenis atau pembuatan struct yang lebih kompleks. Berikut cara menerapkan setelan build berjenis enum:
# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])
temperatures = ["HOT", "LUKEWARM", "ICED"]
def _impl(ctx):
raw_temperature = ctx.build_setting_value
if raw_temperature not in temperatures:
fail(str(ctx.label) + " build setting allowed to take values {"
+ ", ".join(temperatures) + "} but was set to unallowed value "
+ raw_temperature)
return TemperatureProvider(type = raw_temperature)
temperature = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
Menentukan flag string multi-set
Setelan string memiliki parameter allow_multiple tambahan yang memungkinkan flag ditetapkan beberapa kali di command line atau di bazelrc. Nilai default-nya masih ditetapkan dengan atribut berjenis string:
# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
name = "roasts",
build_setting_default = "medium"
)
Setiap setelan flag diperlakukan sebagai satu nilai:
$ bazel build //my/target --//example:roasts=blonde \
--//example:roasts=medium,dark
Di atas diuraikan ke {"//example:roasts": ["blonde", "medium,dark"]} dan
ctx.build_setting_value menampilkan daftar ["blonde", "medium,dark"].
Membuat instance setelan build
Aturan yang ditentukan dengan parameter build_setting memiliki atribut wajib implisit
build_setting_default. Atribut ini memiliki jenis yang sama seperti yang dideklarasikan oleh parameter build_setting.
# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])
def _impl(ctx):
return FlavorProvider(type = ctx.build_setting_value)
flavor = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
Setelan yang telah ditentukan sebelumnya
Library Skylib menyertakan kumpulan setelan yang telah ditentukan sebelumnya yang dapat Anda buat instance-nya tanpa harus menulis Starlark kustom.
Misalnya, untuk menentukan setelan yang menerima kumpulan nilai string terbatas:
# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
name = "myflag",
values = ["a", "b", "c"],
build_setting_default = "a",
)
Untuk daftar lengkap, lihat Aturan setelan build umum.
Menggunakan setelan build
Bergantung pada setelan build
Jika target ingin membaca informasi konfigurasi, target tersebut dapat langsung bergantung pada setelan build melalui dependensi atribut reguler.
# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
...
drink_rule = rule(
implementation = _rule_impl,
attrs = {
"flavor": attr.label()
}
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
drink_rule(
name = "my_drink",
flavor = ":favorite_flavor",
)
Bahasa mungkin ingin membuat kumpulan setelan build kanonis yang bergantung pada semua aturan untuk bahasa tersebut. Meskipun konsep native fragments tidak lagi ada sebagai objek hardcode di dunia konfigurasi Starlark, salah satu cara untuk menerjemahkan konsep ini adalah dengan menggunakan kumpulan atribut implisit umum. Contoh:
# kotlin/rules.bzl
_KOTLIN_CONFIG = {
"_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
"_mode": attr.label(default = "//kotlin/config:mode-flag"),
...
}
...
kotlin_library = rule(
implementation = _rule_impl,
attrs = dicts.add({
"library-attr": attr.string()
}, _KOTLIN_CONFIG)
)
kotlin_binary = rule(
implementation = _binary_impl,
attrs = dicts.add({
"binary-attr": attr.label()
}, _KOTLIN_CONFIG)
Menggunakan setelan build di command line
Mirip dengan sebagian besar flag native, Anda dapat menggunakan command line untuk menetapkan setelan build
yang ditandai sebagai flag. Nama setelan build adalah jalur target lengkapnya menggunakan sintaksis name=value:
$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed
Sintaksis boolean khusus didukung:
$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag
Menggunakan alias setelan build
Anda dapat menetapkan alias untuk jalur target setelan build agar lebih mudah dibaca di command line. Fungsi alias mirip dengan flag native dan juga menggunakan sintaksis opsi tanda hubung ganda.
Tetapkan alias dengan menambahkan --flag_alias=ALIAS_NAME=TARGET_PATH ke .bazelrc Anda . Misalnya, untuk menetapkan alias ke coffee:
# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp
Praktik Terbaik: Menetapkan alias beberapa kali akan menyebabkan alias terbaru yang diutamakan. Gunakan nama alias yang unik untuk menghindari hasil penguraian yang tidak diinginkan.
Untuk menggunakan alias, ketik alias tersebut di tempat jalur target setelan build.
Dengan contoh coffee di atas yang ditetapkan di .bazelrc pengguna:
$ bazel build //my/target --coffee=ICED
bukan
$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED
Praktik Terbaik: Meskipun alias dapat ditetapkan di command line, membiarkannya di .bazelrc akan mengurangi kekacauan command line.
Setelan build berjenis label
Tidak seperti setelan build lainnya, setelan berjenis label tidak dapat ditentukan menggunakan parameter aturan build_setting. Sebagai gantinya, bazel memiliki dua aturan bawaan: label_flag dan label_setting. Aturan ini meneruskan penyedia target sebenarnya tempat setelan build ditetapkan. label_flag dan label_setting dapat dibaca/ditulis oleh transisi dan label_flag dapat ditetapkan oleh pengguna seperti aturan build_setting lainnya. Satu-satunya perbedaan adalah bahwa setelan ini tidak dapat ditentukan secara kustom.
Setelan berjenis label pada akhirnya akan menggantikan fungsi default yang terikat terlambat. Atribut default yang terikat terlambat adalah atribut berjenis Label yang nilai akhirnya dapat dipengaruhi oleh konfigurasi. Di Starlark, hal ini akan menggantikan
configuration_field
API.
# example/rules.bzl
MyProvider = provider(fields = ["my_field"])
def _dep_impl(ctx):
return MyProvider(my_field = "yeehaw")
dep_rule = rule(
implementation = _dep_impl
)
def _parent_impl(ctx):
if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
...
parent_rule = rule(
implementation = _parent_impl,
attrs = { "my_field_provider": attr.label() }
)
# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")
dep_rule(name = "dep")
parent_rule(name = "parent", my_field_provider = ":my_field_provider")
label_flag(
name = "my_field_provider",
build_setting_default = ":dep"
)
Setelan build dan select()
Pengguna dapat mengonfigurasi atribut pada setelan build menggunakan
select(). Target setelan build dapat diteruskan ke atribut flag_values dari config_setting. Nilai yang akan dicocokkan dengan konfigurasi diteruskan sebagai String, lalu diuraikan ke jenis setelan build untuk pencocokan.
config_setting(
name = "my_config",
flag_values = {
"//example:favorite_flavor": "MANGO"
}
)
Transisi yang ditentukan pengguna
Transisi konfigurasi memetakan transformasi dari satu target yang dikonfigurasi ke target lain dalam grafik build.
Menentukan
Transisi menentukan perubahan konfigurasi antar-aturan. Misalnya, permintaan seperti "kompilasi dependensi saya untuk CPU yang berbeda dari induknya" ditangani oleh transisi.
Secara formal, transisi adalah fungsi dari konfigurasi input ke satu atau beberapa konfigurasi output. Sebagian besar transisi adalah 1:1 seperti "ganti konfigurasi input dengan --cpu=ppc". Transisi 1:2+ juga dapat ada, tetapi memiliki batasan khusus.
Di Starlark, transisi ditentukan seperti aturan, dengan fungsi
transition()
yang menentukan
dan fungsi implementasi.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//example:favorite_flavor" : "MINT"}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
Fungsi transition() mengambil fungsi implementasi, kumpulan setelan build untuk dibaca(inputs), dan kumpulan setelan build untuk ditulis (outputs). Fungsi implementasi memiliki dua parameter, settings dan attr. settings adalah kamus {String:Object} dari semua setelan yang dideklarasikan dalam parameter inputs ke transition().
attr adalah kamus atribut dan nilai aturan tempat transisi dilampirkan. Saat dilampirkan sebagai transisi edge keluar, nilai semua atribut ini dikonfigurasi pasca-resolusi select(). Saat dilampirkan sebagai
transisi edge masuk, attr tidak
menyertakan atribut apa pun yang menggunakan pemilih untuk me-resolve nilainya. Jika transisi edge masuk di --foo membaca atribut bar, lalu juga memilih --foo untuk menetapkan atribut bar, ada kemungkinan transisi edge masuk membaca nilai bar yang salah dalam transisi.
Fungsi implementasi harus menampilkan kamus (atau daftar kamus, dalam kasus transisi dengan beberapa konfigurasi output) dari nilai setelan build baru yang akan diterapkan. Kumpulan kunci kamus yang ditampilkan harus berisi kumpulan setelan build yang diteruskan ke parameter outputs fungsi transisi. Hal ini berlaku meskipun setelan build tidak benar-benar diubah selama transisi - nilai aslinya harus diteruskan secara eksplisit dalam kamus yang ditampilkan.
Menentukan transisi 1:2+
Transisi edge keluar dapat memetakan satu konfigurasi input ke dua atau beberapa konfigurasi output. Hal ini berguna untuk menentukan aturan yang menggabungkan kode multi-arsitektur.
Transisi 1:2+ ditentukan dengan menampilkan daftar kamus dalam fungsi implementasi transisi.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return [
{"//example:favorite_flavor" : "LATTE"},
{"//example:favorite_flavor" : "MOCHA"},
]
coffee_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
Transisi ini juga dapat menetapkan kunci kustom yang dapat digunakan oleh fungsi implementasi aturan untuk membaca dependensi individual:
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
Melampirkan transisi
Transisi dapat dilampirkan di dua tempat: edge masuk dan edge keluar. Secara efektif, hal ini berarti aturan dapat melakukan transisi konfigurasi sendiri (transisi edge masuk) dan melakukan transisi konfigurasi dependensinya (transisi edge keluar).
CATATAN: Saat ini tidak ada cara untuk melampirkan transisi Starlark ke aturan native. Jika Anda perlu melakukannya, hubungi bazel-discuss@googlegroups.com untuk mendapatkan bantuan dalam mencari solusinya.
Transisi edge masuk
Transisi edge masuk diaktifkan dengan melampirkan objek transition
(dibuat oleh transition()) ke rule()'s cfg parameter:
# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
implementation = _impl,
cfg = hot_chocolate_transition,
...
Transisi edge masuk harus berupa transisi 1:1.
Transisi edge keluar
Transisi edge keluar diaktifkan dengan melampirkan objek transition
(dibuat oleh transition()) ke parameter cfg atribut:
# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
implementation = _impl,
attrs = { "dep": attr.label(cfg = coffee_transition)}
...
Transisi edge keluar dapat berupa 1:1 atau 1:2+.
Lihat Mengakses atribut dengan transisi untuk mengetahui cara membaca kunci ini.
Transisi pada opsi native
Transisi Starlark juga dapat mendeklarasikan pembacaan dan penulisan pada opsi konfigurasi build native melalui awalan khusus ke nama opsi.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//command_line_option:cpu": "k8"}
cpu_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
Opsi native yang tidak didukung
Bazel tidak mendukung transisi di --define dengan
"//command_line_option:define". Sebagai gantinya, gunakan setelan build kustom
. Secara umum, penggunaan --define baru tidak dianjurkan dan sebaiknya gunakan setelan build.
Bazel tidak mendukung transisi di --config. Hal ini karena --config adalah flag "ekspansi" yang diperluas ke flag lain.
Yang penting, --config dapat menyertakan flag yang tidak memengaruhi konfigurasi build,
seperti
--spawn_strategy
. Bazel, berdasarkan desainnya, tidak dapat mengikat flag tersebut ke target individual. Artinya, tidak ada cara yang koheren untuk menerapkannya dalam transisi.
Sebagai solusinya, Anda dapat mencantumkan secara eksplisit flag yang merupakan bagian dari konfigurasi dalam transisi Anda. Hal ini mengharuskan Anda mempertahankan ekspansi --config di dua tempat, yang merupakan kekurangan UI yang diketahui.
Transisi pada setelan build yang mengizinkan beberapa
Saat menetapkan setelan build yang mengizinkan beberapa nilai, nilai setelan harus ditetapkan dengan daftar.
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
# Using a value of just "dark" here will throw an error
return {"//example:roasts" : ["dark"]},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:roasts"]
)
Transisi tanpa pengoperasian
Jika transisi menampilkan {}, [], atau None, ini adalah singkatan untuk mempertahankan semua setelan pada nilai aslinya. Hal ini dapat lebih mudah daripada menetapkan setiap output secara eksplisit ke dirinya sendiri.
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (attr)
if settings["//example:already_chosen"] is True:
return {}
return {
"//example:favorite_flavor": "dark chocolate",
"//example:include_marshmallows": "yes",
"//example:desired_temperature": "38C",
}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = ["//example:already_chosen"],
outputs = [
"//example:favorite_flavor",
"//example:include_marshmallows",
"//example:desired_temperature",
]
)
Mengakses atribut dengan transisi
Saat melampirkan transisi ke edge keluar
(terlepas dari apakah transisi adalah transisi 1:1 atau 1:2+), ctx.attr akan dipaksa menjadi daftar
jika belum. Urutan elemen dalam daftar ini tidak ditentukan.
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
return {"//example:favorite_flavor" : "LATTE"},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
def _rule_impl(ctx):
# Note: List access even though "dep" is not declared as list
transitioned_dep = ctx.attr.dep[0]
# Note: Access doesn't change, other_deps was already a list
for other_dep in ctx.attr.other_deps:
# ...
coffee_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = coffee_transition)
"other_deps": attr.label_list(cfg = coffee_transition)
})
Jika transisi adalah 1:2+ dan menetapkan kunci kustom, ctx.split_attr dapat digunakan untuk membaca dependensi individual untuk setiap kunci:
# example/transitions/rules.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
def _rule_impl(ctx):
apple_dep = ctx.split_attr.dep["Apple deps"]
linux_dep = ctx.split_attr.dep["Linux deps"]
# ctx.attr has a list of all deps for all keys. Order is not guaranteed.
all_deps = ctx.attr.dep
multi_arch_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = multi_arch_transition)
})
Lihat contoh lengkapnya di sini.
Integrasi dengan platform dan toolchain
Banyak flag native saat ini, seperti --cpu dan --crosstool_top, terkait dengan resolusi toolchain. Di masa mendatang, transisi eksplisit pada jenis flag ini kemungkinan akan diganti dengan transisi pada
platform target.
Pertimbangan memori dan performa
Menambahkan transisi, dan oleh karena itu konfigurasi baru, ke build Anda akan menimbulkan biaya: grafik build yang lebih besar, grafik build yang kurang dapat dipahami, dan build yang lebih lambat. Sebaiknya pertimbangkan biaya ini saat mempertimbangkan penggunaan transisi dalam aturan build Anda. Di bawah ini adalah contoh bagaimana transisi dapat membuat pertumbuhan eksponensial grafik build Anda.
Build yang berperilaku buruk: studi kasus

Gambar 1. Grafik skalabilitas yang menampilkan target tingkat atas dan dependensinya.
Grafik ini menampilkan target tingkat atas, //pkg:app, yang bergantung pada dua target, //pkg:1_0 dan //pkg:1_1. Kedua target ini bergantung pada dua target, //pkg:2_0 dan //pkg:2_1. Kedua target ini bergantung pada dua target, //pkg:3_0 dan //pkg:3_1.
Hal ini terus berlanjut hingga //pkg:n_0 dan //pkg:n_1, yang keduanya bergantung pada satu target, //pkg:dep.
Membuat //pkg:app memerlukan \(2n+2\) target:
//pkg:app//pkg:dep//pkg:i_0dan//pkg:i_1untuk \(i\) in \([1..n]\)
Bayangkan Anda menerapkan flag
--//foo:owner=<STRING> dan //pkg:i_b berlaku
depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"
Dengan kata lain, //pkg:i_b menambahkan b ke nilai lama --owner untuk semua
dependensinya.
Hal ini menghasilkan target yang dikonfigurasi berikut:
//pkg:app //foo:owner=""
//pkg:1_0 //foo:owner=""
//pkg:1_1 //foo:owner=""
//pkg:2_0 (via //pkg:1_0) //foo:owner="0"
//pkg:2_0 (via //pkg:1_1) //foo:owner="1"
//pkg:2_1 (via //pkg:1_0) //foo:owner="0"
//pkg:2_1 (via //pkg:1_1) //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0) //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1) //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0) //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1) //foo:owner="11"
...
//pkg:dep menghasilkan \(2^n\) target yang dikonfigurasi: config.owner=
"\(b_0b_1...b_n\)" untuk semua \(b_i\) in \(\{0,1\}\).
Hal ini membuat grafik build menjadi lebih besar secara eksponensial daripada grafik target, dengan konsekuensi memori dan performa yang sesuai.
TODO: Tambahkan strategi untuk pengukuran dan mitigasi masalah ini.
Bacaan lebih lanjut
Untuk mengetahui detail selengkapnya tentang mengubah konfigurasi build, lihat:
- Konfigurasi Build Starlark
- Roadmap Konfigurasi Bazel
- Kumpulan lengkap set contoh end-to-end