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 terakhir membuat repo dengan memanggil aturan repo. Ekstensi ini memiliki kemampuan yang mirip dengan aturan repo, yang memungkinkannya menjalankan I/O file, mengirim permintaan jaringan, dan sebagainya. Di antara fungsi lainnya, Bazel dapat berinteraksi dengan sistem pengelolaan paket lainnya sekaligus mematuhi grafik dependensi yang dibuat dari modul Bazel.
Anda dapat menentukan ekstensi modul dalam file .bzl
, seperti aturan repo. Tag tersebut tidak dipanggil secara langsung; sebagai gantinya, setiap modul menentukan bagian data yang disebut tag untuk dibaca ekstensi. Bazel menjalankan resolusi modul sebelum mengevaluasi
ekstensi apa pun. Ekstensi membaca semua tag yang dimilikinya di seluruh grafik dependensi.
Penggunaan ekstensi
Ekstensi dihosting dalam 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")
Ini mengikat nilai yang ditampilkan use_extension
ke variabel, yang memungkinkan pengguna menggunakan titik-sintaksis untuk menentukan tag untuk ekstensi. Tag harus mengikuti skema yang ditentukan oleh class tag terkait 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 perintah use_repo
untuk memindahkan repositori
yang dihasilkan oleh ekstensi ke dalam cakupan modul saat ini.
use_repo(maven, "maven")
Repo yang dibuat 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 me-resolve label dengan benar, seperti @maven//:org_junit_junit
untuk 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
, yang masing-masing memiliki sejumlah atribut. Class tag menentukan skema untuk tag yang digunakan oleh ekstensi ini. Misalnya, ekstensi "maven" di atas mungkin ditentukan sebagai berikut:
# @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 aturan repo, kecuali bahwa fungsi tersebut mendapatkan objek module_ctx
, yang memberikan akses ke semua modul menggunakan ekstensi tersebut dan semua tag terkait.
Fungsi implementasi kemudian memanggil aturan repo untuk menghasilkan 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
. Pada 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. Jika kedua versi ekstensi tersebut 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
. Untuk ekstensi yang dihosting di
modul root, bagian module_repo_canonical_name
diganti dengan string _main
. Perlu diperhatikan bahwa format nama kanonis bukanlah API yang dapat Anda gunakan. Format nama kanonis ini dapat berubah kapan saja.
Kebijakan penamaan ini berarti setiap ekstensi memiliki "namespace repositori" sendiri; dua ekstensi berbeda yang masing-masing dapat menentukan repo dengan nama yang sama tanpa menimbulkan risiko bentrok. 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 repo yang dihasilkan oleh ekstensi modul, ada beberapa aturan visibilitas repo:
- Repositori modul Bazel dapat melihat semua repo yang diperkenalkan dalam file
MODULE.bazel
melaluibazel_dep
danuse_repo
. - Repositori yang dihasilkan oleh ekstensi modul dapat melihat semua repo yang terlihat oleh modul yang menghosting ekstensi tersebut, ditambah semua repo lain yang dihasilkan oleh ekstensi modul yang sama (menggunakan nama yang ditentukan dalam panggilan aturan repo sebagai nama yang jelas).
- Hal ini dapat mengakibatkan konflik. Jika repo modul dapat melihat repo dengan nama jelas
foo
, dan ekstensi menghasilkan repo dengan nama yang ditentukanfoo
, maka untuk semua repo yang dihasilkan oleh ekstensi tersebut,foo
akan merujuk pada repo yang pertama.
- Hal ini dapat mengakibatkan konflik. Jika repo modul dapat melihat repo dengan nama jelas
Praktik terbaik
Bagian ini menjelaskan praktik terbaik saat menulis ekstensi agar mudah digunakan, mudah dikelola, dan beradaptasi dengan baik terhadap perubahan dari waktu ke waktu.
Masukkan setiap ekstensi dalam file terpisah
Jika ekstensi berada dalam file yang berbeda, ekstensi ini akan memungkinkan satu ekstensi untuk memuat repositori yang dihasilkan oleh ekstensi lain. Meskipun Anda tidak menggunakan fungsi ini, sebaiknya tempatkan dalam file terpisah jika Anda membutuhkannya nanti. Ini karena identifikasi ekstensi didasarkan pada filenya. Jadi, memindahkan ekstensi ke file lain nanti akan mengubah API publik Anda dan merupakan perubahan yang tidak kompatibel dengan versi sebelumnya bagi pengguna Anda.
Menentukan sistem operasi dan arsitektur
Jika ekstensi Anda mengandalkan sistem operasi atau jenis arsitekturnya, pastikan untuk menunjukkan hal ini dalam definisi ekstensi menggunakan atribut boolean os_dependent
dan arch_dependent
. Hal ini memastikan bahwa Bazel menyadari
kebutuhan evaluasi ulang jika ada perubahan pada salah satunya.
Hanya modul root yang harus langsung memengaruhi nama repositori
Perlu diingat bahwa saat membuat repositori, ekstensi akan dibuat dalam namespace ekstensi tersebut. Ini berarti konflik dapat terjadi jika modul yang berbeda
menggunakan ekstensi yang sama dan akhirnya membuat repositori dengan nama
yang sama. Hal ini sering kali ditunjukkan 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 menetapkan nama repositori secara langsung, atau hanya izinkan modul root untuk melakukannya. Anda dapat mengizinkan modul root kemampuan ini karena tidak ada yang akan bergantung padanya, sehingga tidak perlu khawatir tentang modul lain yang membuat nama yang bertentangan.