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 repo dengan memanggil aturan repo. Ekstensi ini memiliki kemampuan yang mirip dengan aturan repo, yang memungkinkan ekstensi melakukan I/O file, mengirim permintaan jaringan, dan sebagainya. Di antara tugas tersebut, keduanya memungkinkan Bazel berinteraksi dengan sistem pengelolaan paket lainnya sekaligus mengikuti grafik dependensi yang dibuat dari modul Bazel.
Anda dapat menentukan ekstensi modul dalam file .bzl
, seperti aturan repo. Modul tersebut tidak dipanggil secara langsung. Namun, setiap modul menentukan bagian data yang disebut tag agar ekstensi dapat dibaca. Bazel menjalankan resolusi modul sebelum mengevaluasi
ekstensi apa pun. Ekstensi membaca semua tag yang dimilikinya di seluruh grafik dependensi.
Penggunaan ekstensi
Ekstensi dihosting di modul Bazel sendiri. Untuk menggunakan ekstensi dalam
modul, pertama-tama tambahkan bazel_dep
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")
Tindakan ini akan mengikat nilai yang ditampilkan use_extension
ke variabel, yang memungkinkan pengguna menggunakan titik-sintaksis untuk menentukan tag ekstensi. Tag harus mengikuti
skema yang ditentukan oleh class tag terkait yang ditentukan dalam
definisi ekstensi. Untuk contoh cara 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 perintah use_repo
untuk memasukkan repo yang dihasilkan oleh ekstensi ke dalam cakupan modul saat ini.
use_repo(maven, "maven")
Repos yang dihasilkan oleh ekstensi adalah bagian dari API-nya. Dalam contoh ini, ekstensi modul "maven" menjanjikan untuk menghasilkan repo yang disebut maven
. Dengan
deklarasi di atas, ekstensi akan menyelesaikan label dengan benar seperti
@maven//:org_junit_junit
agar mengarah ke repo yang dihasilkan oleh ekstensi "maven".
Definisi ekstensi
Anda dapat menentukan ekstensi modul 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 mungkin 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 implementasi ekstensi modul mirip dengan fungsi aturan repo, tetapi fungsi tersebut mendapatkan objek module_ctx
, yang memberikan akses ke semua modul menggunakan ekstensi dan semua tag terkait.
Fungsi implementasi 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 menurut nama dan file .bzl
yang muncul
dalam panggilan ke use_extension
. Dalam contoh berikut, ekstensi maven
diidentifikasi oleh file .bzl
@rules_jvm_external//:extension.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 tersebut digunakan dalam grafik modul transitif, ekstensi tersebut akan dievaluasi secara terpisah dan hanya akan melihat tag yang terkait dengan identitas tersebut.
Sebagai penulis ekstensi, Anda harus memastikan bahwa pengguna hanya akan menggunakan ekstensi modul Anda dari satu file .bzl
.
Nama dan visibilitas repositori
Repos yang dibuat oleh ekstensi memiliki nama kanonis dalam bentuk module_repo_canonical_name~extension_name~repo_name
. Untuk ekstensi yang dihosting di
modul root, bagian module_repo_canonical_name
diganti
dengan string _main
. Perhatikan bahwa format nama kanonis bukan
API yang harus Anda andalkan — format ini dapat berubah sewaktu-waktu.
Kebijakan penamaan ini berarti bahwa setiap ekstensi memiliki "namespace repo"-nya sendiri; dua
ekstensi yang berbeda dapat menentukan repo dengan nama yang sama tanpa risiko
bentrok apa pun. Ini juga berarti bahwa repository_ctx.name
melaporkan nama kanonis
repo, yang tidak sama dengan nama yang ditentukan dalam panggilan aturan
repo.
Dengan mempertimbangkan repo yang dihasilkan oleh ekstensi modul, ada beberapa aturan visibilitas repo:
- Repositori modul Bazel dapat melihat semua repo yang diperkenalkan dalam file
MODULE.bazel
-nya melaluibazel_dep
danuse_repo
. - Repositori yang dihasilkan oleh ekstensi modul dapat melihat semua repo yang terlihat oleh
modul yang menghosting ekstensi, plus semua repo lain yang dihasilkan oleh
ekstensi modul yang sama (menggunakan nama yang ditentukan dalam panggilan aturan repo sebagai
nama yang terlihat).
- Hal ini dapat mengakibatkan konflik. Jika repo modul dapat melihat repo dengan
nama yang jelas
foo
, dan ekstensi menghasilkan repo dengan namafoo
yang ditentukan, maka untuk semua repo yang dihasilkan oleh ekstensi tersebutfoo
merujuk ke repositori sebelumnya.
- Hal ini dapat mengakibatkan konflik. Jika repo modul dapat melihat repo dengan
nama yang jelas
Praktik terbaik
Bagian ini menjelaskan praktik terbaik saat menulis ekstensi sehingga mudah digunakan, dikelola, dan beradaptasi dengan baik terhadap perubahan dari waktu ke waktu.
Masukkan setiap ekstensi ke dalam file terpisah
Jika berada dalam file yang berbeda, ekstensi memungkinkan satu ekstensi untuk memuat repositori yang dihasilkan oleh ekstensi lain. Meskipun Anda tidak menggunakan fungsi ini, sebaiknya tempatkan dalam file yang terpisah jika sewaktu-waktu Anda perlu. Hal ini karena identitas ekstensi didasarkan pada file-nya, sehingga memindahkan ekstensi ke file lain akan mengubah API publik Anda dan menampilkan perubahan yang tidak kompatibel dengan versi sebelumnya bagi pengguna.