Modul Bazel adalah project Bazel yang dapat memiliki beberapa versi, yang masing-masing memublikasikan metadata tentang modul lain yang menjadi dependensinya. Hal ini analog dengan konsep yang sudah dikenal dalam sistem pengelolaan dependensi lainnya, seperti artefak Maven, paket npm, modul Go, atau crate Cargo.
Modul harus memiliki file MODULE.bazel
di root repo-nya. File ini adalah
manifes modul, yang mendeklarasikan nama, versi, daftar dependensi langsung, dan
informasi lainnya. Untuk contoh dasar:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")
Lihat daftar lengkap perintah yang tersedia di
file MODULE.bazel
.
Untuk melakukan resolusi modul, Bazel memulai dengan membaca file
MODULE.bazel
modul root, lalu berulang kali meminta file
MODULE.bazel
dependensi dari registry Bazel hingga
menemukan seluruh grafik dependensi.
Secara default, Bazel kemudian memilih satu versi dari setiap modul yang akan digunakan. Bazel merepresentasikan setiap modul dengan repo, dan memeriksa kembali registry untuk mempelajari cara menentukan setiap repo.
Format versi
Bazel memiliki ekosistem yang beragam dan project menggunakan berbagai skema pembuatan versi. Yang
paling populer sejauh ini adalah SemVer, tetapi ada
juga project terkenal yang menggunakan skema yang berbeda seperti
Abseil, yang
versinya berbasis tanggal, misalnya 20210324.2
).
Karena alasan ini, Bzlmod mengadopsi versi spesifikasi SemVer yang lebih longgar. Perbedaannya meliputi:
- SemVer menetapkan bahwa bagian "rilis" dari versi harus terdiri dari 3 segmen:
MAJOR.MINOR.PATCH
. Di Bazel, persyaratan ini dilonggarkan sehingga jumlah segmen apa pun diizinkan. - Di SemVer, setiap segmen di bagian "rilis" hanya boleh berupa angka. Di Bazel, hal ini dilonggarkan untuk mengizinkan huruf juga, dan semantik perbandingan cocok dengan "ID" di bagian "pra-rilis".
- Selain itu, semantik peningkatan versi utama, minor, dan patch tidak diterapkan. Namun, lihat tingkat kompatibilitas untuk mengetahui detail tentang cara kami menunjukkan kompatibilitas mundur.
Semua versi SemVer yang valid adalah versi modul Bazel yang valid. Selain itu, dua
versi SemVer a
dan b
membandingkan a < b
jika dan hanya jika hal yang sama berlaku saat
dibandingkan sebagai versi modul Bazel.
Pemilihan versi
Pertimbangkan masalah dependensi berlian, yang merupakan hal umum di ruang pengelolaan dependensi berversi. Misalkan Anda memiliki grafik dependensi:
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
Versi D
mana yang harus digunakan? Untuk menyelesaikan pertanyaan ini, Bzlmod menggunakan algoritma
Minimal Version Selection
(MVS) yang diperkenalkan dalam sistem modul Go. MVS mengasumsikan bahwa semua versi
baru modul kompatibel dengan versi lama, sehingga memilih versi tertinggi
yang ditentukan oleh dependensi apa pun (D 1.1
dalam contoh kita). Ini disebut "minimal"
karena D 1.1
adalah versi paling awal yang dapat memenuhi persyaratan kami —
meskipun D 1.2
atau yang lebih baru ada, kami tidak akan memilihnya. Menggunakan MVS akan membuat
proses pemilihan versi yang berfidelitas tinggi dan dapat direproduksi.
Versi yang ditarik
Registry dapat mendeklarasikan versi tertentu sebagai dicabut jika harus dihindari
(seperti untuk kerentanan keamanan). Bazel menampilkan error saat memilih
versi modul yang ditarik. Untuk memperbaiki error ini, upgrade ke versi yang lebih baru dan tidak ditarik, atau gunakan flag --allow_yanked_versions
untuk mengizinkan versi yang ditarik secara eksplisit.
Tingkat kompatibilitas
Di Go, asumsi MVS tentang kompatibilitas mundur berfungsi karena memperlakukan
versi modul yang tidak kompatibel dengan versi sebelumnya sebagai modul terpisah. Dalam hal
SemVer, artinya A 1.x
dan A 2.x
dianggap sebagai modul yang berbeda, dan dapat
bersama-sama ada dalam grafik dependensi yang di-resolve. Hal ini, pada gilirannya, dimungkinkan dengan
mengenkode versi utama di jalur paket di Go, sehingga tidak ada
konflik waktu kompilasi atau waktu penautan.
Namun, Bazel tidak dapat memberikan jaminan tersebut, sehingga memerlukan nomor "versi utama"
untuk mendeteksi versi yang tidak kompatibel dengan versi sebelumnya. Nomor ini disebut
tingkat kompatibilitas, dan ditentukan oleh setiap versi modul dalam
direktif module()
-nya. Dengan informasi ini, Bazel dapat menampilkan error saat
mendeteksi bahwa versi modul yang sama dengan tingkat kompatibilitas yang berbeda
ada dalam grafik dependensi yang di-resolve.
Ganti
Tentukan penggantian dalam file MODULE.bazel
untuk mengubah perilaku resolusi modul
Bazel. Hanya penggantian modul root yang akan diterapkan — jika modul
digunakan sebagai dependensi, penggantiannya akan diabaikan.
Setiap penggantian ditentukan untuk nama modul tertentu, yang memengaruhi semua versinya dalam grafik dependensi. Meskipun hanya penggantian modul root yang berlaku, penggantian tersebut dapat digunakan untuk dependensi transitif yang tidak langsung bergantung pada modul root.
Penggantian versi tunggal
single_version_override
memiliki beberapa tujuan:
- Dengan atribut
version
, Anda dapat menyematkan dependensi ke versi tertentu, terlepas dari versi dependensi yang diminta dalam grafik dependensi. - Dengan atribut
registry
, Anda dapat memaksa dependensi ini berasal dari registry tertentu, bukan mengikuti proses pemilihan registry normal. - Dengan atribut
patch*
, Anda dapat menentukan serangkaian patch yang akan diterapkan ke modul yang didownload.
Semua atribut ini bersifat opsional dan dapat dicampur dan dicocokkan satu sama lain.
Penggantian beberapa versi
multiple_version_override
dapat ditentukan untuk memungkinkan beberapa versi modul yang sama untuk berdampingan dalam
grafik dependensi yang di-resolve.
Anda dapat menentukan daftar eksplisit versi yang diizinkan untuk modul, yang semuanya harus ada dalam grafik dependensi sebelum resolusi — harus ada beberapa dependensi transitif bergantung pada setiap versi yang diizinkan. Setelah penyelesaian, hanya versi modul yang diizinkan yang akan tetap ada, sementara Bazel mengupgrade versi modul lainnya ke versi yang diizinkan lebih tinggi terdekat pada tingkat kompatibilitas yang sama. Jika tidak ada versi yang diizinkan lebih tinggi pada level kompatibilitas yang sama, Bazel akan menampilkan error.
Misalnya, jika versi 1.1
, 1.3
, 1.5
, 1.7
, dan 2.0
ada dalam
grafik dependensi sebelum resolusi dan versi utama adalah tingkat
kompatibilitas:
- Penggantian beberapa versi yang memungkinkan
1.3
,1.7
, dan2.0
akan menghasilkan1.1
yang diupgrade ke1.3
,1.5
yang diupgrade ke1.7
, dan versi lainnya tetap sama. - Penggantian beberapa versi yang memungkinkan
1.5
dan2.0
akan menghasilkan error, karena1.7
tidak memiliki versi yang lebih tinggi pada tingkat kompatibilitas yang sama untuk diupgrade. - Penggantian beberapa versi yang memungkinkan
1.9
dan2.0
akan menghasilkan error, karena1.9
tidak ada dalam grafik dependensi sebelum resolusi.
Selain itu, pengguna juga dapat mengganti registry menggunakan atribut
registry
, mirip dengan penggantian versi tunggal.
Penggantian non-registry
Penggantian non-registry akan menghapus modul sepenuhnya dari resolusi versi. Bazel
tidak meminta file MODULE.bazel
ini dari registry, tetapi dari
repo itu sendiri.
Bazel mendukung penggantian non-registry berikut:
Menentukan repo yang tidak merepresentasikan modul Bazel
Dengan bazel_dep
, Anda dapat menentukan repo yang mewakili modul Bazel lainnya.
Terkadang perlu menentukan repo yang tidak merepresentasikan modul
Bazel; misalnya, repo yang berisi file JSON biasa untuk dibaca sebagai data.
Dalam hal ini, Anda dapat menggunakan direktif
use_repo_rule
untuk menentukan repo secara langsung
dengan memanggil aturan repo. Repo ini hanya akan terlihat oleh modul tempat
modul tersebut ditentukan.
Di balik layar, hal ini diimplementasikan menggunakan mekanisme yang sama dengan ekstensi modul, yang memungkinkan Anda menentukan repo dengan fleksibilitas yang lebih besar.
Nama repositori dan dependensi ketat
Nama yang terlihat dari repo yang mendukung
modul ke dependen langsungnya ditetapkan secara default ke nama modulnya, kecuali jika
atribut repo_name
dari perintah
bazel_dep
mengatakan sebaliknya. Perhatikan bahwa hal ini berarti modul hanya dapat menemukan dependensi
langsungnya. Hal ini membantu mencegah kerusakan yang tidak disengaja karena perubahan
dependensi transitif.
Nama kanonis repo yang mendukung
modul adalah module_name~version
(misalnya, bazel_skylib~1.0.3
) atau module_name~
(misalnya, bazel_features~
), bergantung pada apakah ada
beberapa versi modul di seluruh grafik dependensi (lihat
multiple_version_override
).
Perhatikan bahwa format nama kanonis bukan API yang harus Anda andalkan dan
dapat berubah kapan saja. Daripada melakukan hardcode nama kanonis,
gunakan cara yang didukung untuk mendapatkannya langsung dari Bazel:
* Dalam file BUILD dan .bzl
, gunakan
Label.repo_name
pada instance Label
yang dibuat dari string label yang diberikan oleh nama repo yang terlihat, misalnya,
Label("@bazel_skylib").repo_name
.
* Saat mencari runfile, gunakan
$(rlocationpath ...)
atau salah satu library runfile di
@bazel_tools//tools/{bash,cpp,java}/runfiles
atau, untuk aturan rules_foo
,
di @rules_foo//foo/runfiles
.
* Saat berinteraksi dengan Bazel dari alat eksternal seperti IDE atau server
bahasa, gunakan perintah bazel mod dump_repo_mapping
untuk mendapatkan pemetaan dari
nama yang terlihat ke nama kanonis untuk kumpulan repositori tertentu.
Ekstensi modul juga dapat memasukkan repo tambahan ke dalam cakupan modul yang terlihat.