Ekstensi modul memungkinkan pengguna memperluas sistem modul dengan membaca data input dari modul di seluruh grafik dependensi, menjalankan logika yang diperlukan untuk menyelesaikan dependensi, dan akhirnya membuat repositori dengan memanggil repo rules. Ekstensi ini memiliki kemampuan yang mirip dengan aturan repo, yang memungkinkannya melakukan I/O file, mengirim permintaan jaringan, dan sebagainya. Selain hal lainnya, file ini memungkinkan Bazel berinteraksi dengan sistem pengelolaan paket lain sekaligus mematuhi grafik dependensi yang dibuat dari modul Bazel.
Anda dapat menentukan ekstensi modul dalam file .bzl
, seperti aturan repo. Modul ini tidak dipanggil secara langsung, melainkan setiap modul menentukan bagian data yang disebut tag untuk dibaca oleh ekstensi. Bazel menjalankan resolusi modul sebelum mengevaluasi ekstensi apa pun. Ekstensi membaca semua tag yang menjadi miliknya di seluruh
grafik dependensi.
Penggunaan ekstensi
Ekstensi dihosting dalam modul Bazel itu sendiri. Untuk menggunakan ekstensi dalam
modul, tambahkan bazel_dep
terlebih dahulu pada modul yang menghosting ekstensi, lalu
panggil fungsi bawaan use_extension
untuk memasukkannya ke dalam cakupan. Pertimbangkan contoh berikut — cuplikan dari file
MODULE.bazel
untuk menggunakan ekstensi "maven" yang ditentukan dalam
modul rules_jvm_external
:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Hal ini mengikat nilai yang ditampilkan use_extension
ke variabel, yang memungkinkan pengguna menggunakan sintaksis titik untuk menentukan tag untuk ekstensi. Tag harus mengikuti
skema yang ditentukan oleh class tag yang sesuai yang ditentukan dalam
definisi ekstensi. Untuk contoh yang menentukan beberapa tag
maven.install
dan maven.artifact
:
maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = ["com.google.j2objc:j2objc-annotations"])
Gunakan direktif use_repo
untuk memasukkan repos yang dibuat oleh ekstensi ke dalam cakupan modul saat ini.
use_repo(maven, "maven")
Repositori yang dibuat oleh ekstensi adalah bagian dari API-nya. Dalam contoh ini, ekstensi modul "maven" berjanji untuk menghasilkan repo bernama maven
. Dengan
deklarasi di atas, ekstensi akan menyelesaikan label dengan benar seperti
@maven//:org_junit_junit
untuk mengarah ke repo yang dibuat oleh ekstensi "maven".
Definisi ekstensi
Anda dapat menentukan ekstensi modul dengan cara yang sama seperti aturan repo,
menggunakan fungsi module_extension
. Namun, meskipun aturan repo memiliki sejumlah atribut, ekstensi
modul memiliki tag_class
es, yang masing-masing
memiliki sejumlah atribut. Class tag menentukan skema untuk tag yang digunakan oleh ekstensi ini. Misalnya, ekstensi "maven" di atas dapat ditentukan seperti ini:
# @rules_jvm_external//:extensions.bzl
_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
implementation = _maven_impl,
tag_classes = {"install": _install, "artifact": _artifact},
)
Deklarasi ini menunjukkan bahwa tag maven.install
dan maven.artifact
dapat
ditentukan menggunakan skema atribut yang ditentukan.
Fungsi penerapan ekstensi modul mirip dengan fungsi penerapan aturan repo, kecuali fungsi tersebut mendapatkan objek module_ctx
, yang memberikan akses ke semua modul menggunakan ekstensi dan semua tag yang relevan.
Fungsi penerapan kemudian memanggil aturan repo untuk membuat repo.
# @rules_jvm_external//:extensions.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") # a repo rule
def _maven_impl(ctx):
# This is a fake implementation for demonstration purposes only
# collect artifacts from across the dependency graph
artifacts = []
for mod in ctx.modules:
for install in mod.tags.install:
artifacts += install.artifacts
artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]
# call out to the coursier CLI tool to resolve dependencies
output = ctx.execute(["coursier", "resolve", artifacts])
repo_attrs = _process_coursier_output(output)
# call repo rules to generate repos
for attrs in repo_attrs:
http_file(**attrs)
_generate_hub_repo(name = "maven", repo_attrs)
Identitas ekstensi
Ekstensi modul diidentifikasi berdasarkan nama dan file .bzl
yang muncul
dalam panggilan ke use_extension
. Dalam contoh berikut, ekstensi maven
diidentifikasi oleh file @rules_jvm_external//:extension.bzl
.bzl
dan
nama maven
:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Mengekspor ulang ekstensi dari file .bzl
yang berbeda akan memberikan identitas baru
dan jika kedua versi ekstensi digunakan dalam grafik modul transitif,
keduanya akan dievaluasi secara terpisah dan hanya akan melihat tag yang terkait
dengan identitas tertentu tersebut.
Sebagai penulis ekstensi, Anda harus memastikan bahwa pengguna hanya akan menggunakan ekstensi modul dari satu file .bzl
.
Nama dan visibilitas repositori
Repositori yang dibuat oleh ekstensi memiliki nama kanonis dalam bentuk module_repo_canonical_name+extension_name+repo_name
. Perhatikan bahwa format nama kanonis
bukanlah API yang harus Anda andalkan — format ini dapat berubah kapan saja.
Kebijakan penamaan ini berarti setiap ekstensi memiliki "namespace repo" sendiri; dua
ekstensi yang berbeda dapat menentukan repo dengan nama yang sama tanpa risiko
bentrokan. Hal ini juga berarti bahwa repository_ctx.name
melaporkan nama kanonis
repo, yang tidak sama dengan nama yang ditentukan dalam panggilan
aturan repo.
Dengan mempertimbangkan repositori yang dihasilkan oleh ekstensi modul, ada beberapa aturan visibilitas repositori:
- Repo modul Bazel dapat melihat semua repo yang diperkenalkan dalam file
MODULE.bazel
-nya melaluibazel_dep
danuse_repo
. - Repositori yang dibuat oleh ekstensi modul dapat melihat semua repositori yang terlihat oleh
modul yang menghosting ekstensi, plus semua repositori lain yang dibuat oleh
ekstensi modul yang sama (menggunakan nama yang ditentukan dalam panggilan aturan repositori sebagai
nama yang terlihat).
- Hal ini dapat menyebabkan konflik. Jika repo modul dapat melihat repo dengan
nama
foo
, dan ekstensi membuat repo dengan namafoo
yang ditentukan, maka untuk semua repo yang dibuat oleh ekstensi tersebut,foo
merujuk ke repo yang pertama.
- Hal ini dapat menyebabkan konflik. Jika repo modul dapat melihat repo dengan
nama
- Demikian pula, dalam fungsi penerapan ekstensi modul, repos yang dibuat oleh ekstensi dapat merujuk satu sama lain berdasarkan nama yang terlihat dalam atribut, terlepas dari urutan pembuatannya.
- Jika terjadi konflik dengan repositori yang terlihat oleh modul, label yang diteruskan ke atribut aturan repositori dapat di-wrap dalam panggilan ke
Label
untuk memastikan bahwa label tersebut merujuk ke repositori yang terlihat oleh modul, bukan repositori yang dibuat oleh ekstensi dengan nama yang sama.
- Jika terjadi konflik dengan repositori yang terlihat oleh modul, label yang diteruskan ke atribut aturan repositori dapat di-wrap dalam panggilan ke
Mengganti dan menyuntikkan repositori ekstensi modul
Modul root dapat menggunakan
override_repo
dan
inject_repo
untuk mengganti atau menyuntikkan
repositori ekstensi modul.
Contoh: Mengganti rules_java
's java_tools
dengan salinan yang disediakan vendor
# MODULE.bazel
local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository")
local_repository(
name = "my_java_tools",
path = "vendor/java_tools",
)
bazel_dep(name = "rules_java", version = "7.11.1")
java_toolchains = use_extension("@rules_java//java:extension.bzl", "toolchains")
override_repo(java_toolchains, remote_java_tools = "my_java_tools")
Contoh: Menambal dependensi Go agar bergantung pada @zlib
, bukan zlib sistem
# MODULE.bazel
bazel_dep(name = "gazelle", version = "0.38.0")
bazel_dep(name = "zlib", version = "1.3.1.bcr.3")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
go_deps.module_override(
patches = [
"//patches:my_module_zlib.patch",
],
path = "example.com/my_module",
)
use_repo(go_deps, ...)
inject_repo(go_deps, "zlib")
# patches/my_module_zlib.patch
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,6 +1,6 @@
go_binary(
name = "my_module",
importpath = "example.com/my_module",
srcs = ["my_module.go"],
- copts = ["-lz"],
+ cdeps = ["@zlib"],
)
Praktik terbaik
Bagian ini menjelaskan praktik terbaik saat menulis ekstensi agar mudah digunakan, dapat dipertahankan, dan beradaptasi dengan baik terhadap perubahan dari waktu ke waktu.
Masukkan setiap ekstensi dalam file terpisah
Jika ekstensi berada dalam file yang berbeda, satu ekstensi dapat memuat repositori yang dibuat oleh ekstensi lain. Meskipun Anda tidak menggunakan fungsi ini, sebaiknya letakkan di file terpisah jika Anda memerlukannya nanti. Hal ini karena identitas ekstensi didasarkan pada filenya, sehingga memindahkan ekstensi ke file lain di kemudian hari akan mengubah API publik Anda dan merupakan perubahan yang tidak kompatibel mundur bagi pengguna Anda.
Menentukan kemampuan reproduksi
Jika ekstensi Anda selalu menentukan repositori yang sama dengan input yang sama
(tag ekstensi, file yang dibaca, dll.) dan khususnya tidak bergantung pada
download yang tidak dilindungi oleh
checksum, pertimbangkan untuk menampilkan
extension_metadata
dengan
reproducible = True
. Hal ini memungkinkan Bazel melewati ekstensi ini saat menulis ke
file kunci.
Tentukan sistem operasi dan arsitektur
Jika ekstensi Anda bergantung pada sistem operasi atau jenis arsitekturnya, pastikan untuk menunjukkannya dalam definisi ekstensi menggunakan atribut boolean os_dependent
dan arch_dependent
. Hal ini memastikan Bazel mengenali kebutuhan untuk evaluasi ulang jika ada perubahan pada salah satunya.
Karena jenis dependensi pada host ini mempersulit pemeliharaan entri file kunci untuk ekstensi ini, pertimbangkan untuk menandai ekstensi dapat direproduksi jika memungkinkan.
Hanya modul root yang dapat memengaruhi nama repositori secara langsung
Ingatlah bahwa saat ekstensi membuat repositori, repositori tersebut dibuat dalam
namespace ekstensi. Artinya, konflik dapat terjadi jika modul yang berbeda menggunakan ekstensi yang sama dan akhirnya membuat repositori dengan nama yang sama. Hal ini sering kali terwujud sebagai tag_class
ekstensi modul yang memiliki argumen name
yang diteruskan sebagai nilai name
aturan repositori.
Misalnya, modul root, A
, bergantung pada modul B
. Kedua modul
bergantung pada modul mylang
. Jika A
dan B
memanggil
mylang.toolchain(name="foo")
, keduanya akan mencoba membuat repositori bernama
foo
dalam modul mylang
dan error akan terjadi.
Untuk menghindarinya, hapus kemampuan untuk menyetel nama repositori secara langsung, atau hanya izinkan modul root melakukannya. Modul root boleh memiliki kemampuan ini karena tidak ada yang akan bergantung padanya, sehingga tidak perlu khawatir tentang modul lain yang membuat nama yang bertentangan.