Ekstensi modul memungkinkan pengguna memperluas sistem modul dengan membaca data input dari modul di seluruh grafik dependensi, melakukan logika yang diperlukan untuk dependensi, dan terakhir membuat repositori dengan memanggil aturan repo. Ekstensi ini memiliki kemampuan yang mirip dengan aturan repo, yang memungkinkan mereka melakukan I/O file, mengirim permintaan jaringan, dan sebagainya. Di antara hal lainnya, mereka mengizinkan Bazel untuk berinteraksi dengan sistem manajemen paket lainnya sambil tetap menghormati grafik dependensi yang dibangun dari modul Bazel.
Anda dapat menentukan ekstensi modul dalam file .bzl
, sama seperti aturan repo. Mereka adalah
tidak dipanggil secara langsung; tetapi, setiap modul menentukan bagian data yang disebut tag
agar ekstensi dapat dibaca. Bazel menjalankan resolusi modul sebelum mengevaluasi
ekstensi. Ekstensi membaca semua tag yang memilikinya di seluruh
grafik dependensi.
Penggunaan ekstensi
Ekstensi dihosting dalam modul Bazel sendiri. Untuk menggunakan ekstensi di
pertama, tambahkan bazel_dep
pada modul yang menghosting ekstensi, lalu
panggil fungsi bawaan use_extension
untuk masuk ke dalam
ruang lingkup proyek. Perhatikan contoh berikut — cuplikan dari
File MODULE.bazel
untuk menggunakan "maven" ekstensi yang ditentukan dalam
rules_jvm_external
:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Fungsi ini mengikat nilai return use_extension
ke variabel, yang memungkinkan metode
pengguna untuk menggunakan sintaksis titik
guna menentukan tag untuk ekstensi. Tag harus mengikuti
skema yang ditentukan oleh class tag terkait yang ditentukan dalam
definisi ekstensi. Untuk contoh 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")
Repositori yang dibuat oleh ekstensi adalah bagian dari API-nya. Dalam contoh ini,
"ahli" ekstensi modul yang dijanjikan untuk menghasilkan repo yang disebut maven
. Dengan
pernyataan di atas, ekstensi me-resolve label dengan benar seperti
@maven//:org_junit_junit
untuk mengarah ke repositori yang dibuat oleh "maven"
.
Definisi ekstensi
Anda dapat menentukan ekstensi modul mirip dengan aturan repo, menggunakan
Fungsi module_extension
. Namun,
sementara aturan repo memiliki sejumlah atribut, ekstensi modul memiliki
tag_class
, yang masing-masing memiliki sejumlah
. Class tag menentukan skema untuk tag yang digunakan oleh ekstensi ini. Sebagai
contoh, "maven" ekstensi di atas dapat didefinisikan 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 telah ditentukan.
Fungsi implementasi ekstensi modul mirip dengan fungsi repo
aturan, kecuali bahwa metode tersebut mendapatkan objek module_ctx
,
yang memberikan akses ke semua modul yang menggunakan ekstensi dan semua tag terkait.
Fungsi implementasi kemudian memanggil aturan repo untuk membuat repositori.
# @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
lain akan memberikan identitas baru
dan jika kedua versi ekstensi digunakan
dalam grafik modul transitif,
maka tag tersebut akan dievaluasi secara terpisah dan hanya akan melihat tag yang terkait
dengan identitas tertentu.
Sebagai penulis ekstensi, Anda harus memastikan bahwa pengguna hanya akan menggunakan
ekstensi modulnya 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
. Perhatikan, format nama kanonis bukan
API yang harus Anda andalkan — API ini dapat berubah kapan saja.
Kebijakan penamaan ini berarti bahwa setiap ekstensi memiliki "namespace repo" sendiri; dua
ekstensi yang berbeda masing-masing dapat menentukan repo dengan nama yang sama tanpa mempertaruhkan
bentrokan apa pun. Hal ini juga berarti bahwa repository_ctx.name
melaporkan nama kanonis
repo, yang tidak sama dengan nama yang ditentukan dalam aturan repo
panggilan telepon.
Dengan mempertimbangkan ekstensi modul, ada beberapa aturan visibilitas repo:
- Repositori modul Bazel dapat melihat semua repositori yang diperkenalkan di file
MODULE.bazel
-nya melaluibazel_dep
danuse_repo
. - Repo yang dihasilkan oleh ekstensi modul dapat melihat semua repositori yang terlihat oleh
yang menghosting ekstensi, serta semua repositori lain yang dibuat oleh
ekstensi modul yang sama (menggunakan nama
yang ditentukan dalam aturan repo sebagai
nama jelas mereka).
- Hal ini dapat mengakibatkan konflik. Jika repo modul dapat melihat repo dengan
nama yang jelas
foo
, dan ekstensi tersebut akan menghasilkan repositori dengan menentukan namafoo
, kemudian untuk semua repositori yang dibuat oleh ekstensi tersebutfoo
merujuk ke yang pertama.
- 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 agar mudah digunakan, dipelihara, dan beradaptasi dengan baik terhadap perubahan dari waktu ke waktu.
Tempatkan setiap ekstensi di file terpisah
Jika ekstensi berada di file yang berbeda, ekstensi akan diizinkan untuk dimuat repositori yang dihasilkan oleh ekstensi lain. Bahkan jika Anda tidak menggunakan fungsi yang terbaik, sebaiknya tempatkan dalam file terpisah untuk berjaga-jaga jika Anda membutuhkannya. nanti. Hal ini karena identifikasi ekstensi didasarkan pada filenya, jadi memindahkan ekstensi ke file lain kemudian mengubah API publik Anda dan merupakan perubahan yang tidak kompatibel untuk pengguna.
Menentukan sistem operasi dan arsitektur
Jika ekstensi Anda bergantung pada
sistem operasi atau jenis arsitekturnya,
pastikan untuk menunjukkannya dalam definisi ekstensi menggunakan os_dependent
dan arch_dependent
atribut boolean. Hal ini memastikan bahwa Bazel mengenali
perlu dievaluasi ulang jika
ada perubahan pada salah satunya.
Hanya modul root yang akan memengaruhi nama repositori secara langsung
Ingat bahwa ketika ekstensi membuat repositori, ekstensi tersebut dibuat di dalam
namespace ekstensi. Ini berarti tumbukan
dapat terjadi jika
modul menggunakan ekstensi yang sama dan pada akhirnya membuat repositori dengan
nama. Fungsi ini sering kali berwujud sebagai tag_class
ekstensi modul yang memiliki 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
melakukan panggilan
mylang.toolchain(name="foo")
, mereka berdua akan mencoba membuat repositori bernama
foo
dalam modul mylang
dan error akan terjadi.
Untuk menghindari hal ini, hapus kemampuan menetapkan nama repositori secara langsung, atau hanya mengizinkan modul {i>root<i} untuk melakukannya. Tidak masalah jika modul {i>root<i} ini kemampuan karena tidak ada yang akan bergantung padanya, jadi ia tidak perlu khawatir tentang modul lain yang menyebabkan nama yang berkonflik.