Bzlmod adalah nama kode sistem dependensi eksternal yang baru yang diperkenalkan di Bazel 5.0. Bagian ini diperkenalkan untuk mengatasi beberapa titik permasalahan pada sistem lama yang tidak dapat diperbaiki secara wajar secara bertahap; lihat Bagian Pernyataan Masalah di dokumen desain asli untuk mengetahui detail selengkapnya.
Di Bazel 5.0, Bzlmod tidak diaktifkan secara default; bendera
--experimental_enable_bzlmod
harus ditentukan agar hal berikut dapat diambil
pengaruh tersebut. Seperti yang disarankan oleh nama tanda, fitur ini saat ini bersifat eksperimental;
API dan perilaku dapat berubah hingga fitur ini diluncurkan secara resmi.
Untuk memigrasikan project Anda ke Bzlmod, ikuti Panduan Migrasi Bzlmod. Anda juga dapat menemukan contoh penggunaan Bzlmod di repositori contoh.
Modul Bazel
Sistem dependensi eksternal berbasis WORKSPACE
lama berpusat pada
repositori (atau repo), yang dibuat melalui aturan repositori (atau aturan repo).
Meskipun repositori masih merupakan konsep penting dalam sistem baru, modul adalah
unit inti dependensi.
Modul pada dasarnya adalah project Bazel yang dapat memiliki beberapa versi, masing-masing yang memublikasikan metadata tentang modul lain yang menjadi dependensinya. Ini adalah analog dengan konsep yang sudah dikenal dalam sistem manajemen dependensi lainnya: Maven artefak, paket npm, peti Cargo, modul Go, dll.
Modul hanya menentukan dependensinya menggunakan pasangan name
dan version
,
bukan URL tertentu di WORKSPACE
. Dependensi tersebut kemudian
dicari pada
registry Bazel; secara {i>default<i},
Bazel Central Registry. Di ruang kerja Anda, masing-masing
kemudian berubah
menjadi repositori.
MODULE.bazel
Setiap versi dari setiap modul memiliki file MODULE.bazel
yang mendeklarasikan
dependensi dan {i>metadata<i} lainnya. Berikut adalah contoh dasarnya:
module(
name = "my-module",
version = "1.0",
)
bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")
File MODULE.bazel
harus berada di root direktori Workspace
(di samping file WORKSPACE
). Berbeda dengan file WORKSPACE
, Anda tidak perlu
untuk menentukan dependensi transitif Anda; sebagai gantinya, Anda sebaiknya hanya menentukan
dependensi direct, dan file MODULE.bazel
dependensi
diproses untuk menemukan dependensi
transitif secara otomatis.
File MODULE.bazel
mirip dengan file BUILD
karena tidak mendukung
bentuk alur kontrol; kebijakan ini juga melarang pernyataan load
. Perintah
File MODULE.bazel
yang didukung adalah:
module
, untuk menentukan metadata tentang modul saat ini, termasuk nama, versi, dan sebagainya;bazel_dep
, untuk menentukan dependensi pada modul Bazel lainnya;- Pengganti, yang hanya dapat digunakan oleh modul root (bukan oleh modul modul yang digunakan sebagai dependensi) untuk menyesuaikan perilaku dependensi langsung atau transitif tertentu:
- Perintah yang terkait dengan ekstensi modul:
Format versi
Bazel memiliki ekosistem yang beragam dan proyek menggunakan berbagai skema pembuatan versi. Tujuan
yang paling populer sejauh ini adalah SemVer, tetapi ada
juga proyek terkemuka menggunakan berbagai skema seperti
Abseil, yang
versi berbasis tanggal, misalnya 20210324.2
).
Karena alasan ini, Bzlmod mengadopsi versi spesifikasi SemVer yang lebih longgar. Tujuan perbedaan tersebut meliputi:
- SemVer menentukan bahwa "rilis" bagian dari versi harus terdiri dari 3
segmen:
MAJOR.MINOR.PATCH
. Pada Bazel, persyaratan ini dilonggarkan sehingga jumlah segmen yang diizinkan. - Di SemVer, setiap segmen dalam "rilis" bagian harus dalam angka saja. Pada Bazel, ini dilonggarkan untuk memungkinkan huruf, dan perbandingan semantik cocok dengan "ID" dalam "prarilis" bagian.
- Selain itu, semantik peningkatan versi besar, minor, dan patch tidak diterapkan. (Namun, lihat tingkat kompatibilitas untuk 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 memiliki kesamaan yang berlaku saat
dibandingkan dengan versi modul Bazel.
Resolusi versi
Masalah dependensi {i>diamond<i} adalah masalah utama dalam dependensi berversi ruang manajemen proyek. Misalkan Anda memiliki grafik dependensi berikut:
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
Versi D mana yang harus digunakan? Untuk menjawab pertanyaan ini, Bzlmod menggunakan Pilihan Versi Minimal (MVS) yang diperkenalkan dalam sistem modul Go. MVS mengasumsikan bahwa semua versi modulnya kompatibel dengan versi sebelumnya, sehingga hanya memilih jenis yang ditentukan oleh dependen apa pun (D 1.1 dalam contoh kita). Namanya "minimal" karena D 1.1 di sini adalah versi minimal yang dapat memenuhi persyaratan; meskipun ada D 1.2 atau yang lebih baru, kita tidak memilihnya. Cara ini memiliki manfaat tambahan bahwa pemilihan versi berakurasi tinggi dan dapat direproduksi.
Resolusi versi dilakukan secara lokal di komputer Anda, bukan oleh registry.
Tingkat kompatibilitas
Perhatikan bahwa asumsi MVS tentang kompatibilitas mundur dapat diterima karena hanya memperlakukan versi modul yang tidak kompatibel dengan versi sebelumnya sebagai modul terpisah. Dalam hal SemVer, itu berarti A 1.x dan A 2.x dianggap sebagai modul yang berbeda, dan dapat berdampingan dalam grafik dependensi yang di-resolve. Hal ini kemudian membuat dimungkinkan oleh fakta bahwa versi utama dienkode dalam jalur paket di Lanjutkan, sehingga tidak ada konflik waktu kompilasi atau waktu penautan.
Untuk Bazel, kami tidak memiliki jaminan seperti itu. Dengan demikian, kita butuh cara untuk
menunjukkan "utama
versi" untuk mendeteksi versi yang tidak kompatibel dengan versi sebelumnya. Nomor ini
disebut tingkat kompatibilitas, dan ditentukan oleh setiap versi modul di
direktif module()
-nya. Dengan informasi ini, kita bisa menampilkan {i>error<i}
ketika kami mendeteksi bahwa versi modul yang sama dengan kompatibilitas berbeda
ada di grafik dependensi yang di-resolve.
Nama repositori
Di Bazel, setiap dependensi eksternal
memiliki nama repositori. Terkadang, hal yang sama
dependensi mungkin digunakan melalui nama repositori yang berbeda (misalnya,
Rata-rata @io_bazel_skylib
dan @bazel_skylib
Bazel skylib), atau model
nama repositori dapat digunakan untuk dependensi yang berbeda dalam project yang berbeda.
Di Bzlmod, repositori dapat dihasilkan oleh modul Bazel dan ekstensi modul. Untuk menyelesaikan konflik nama repositori, kami mulai menangani pemetaan repositori mekanisme atensi dalam sistem baru. Berikut adalah dua konsep penting:
Nama repositori kanonis: Nama repositori yang unik secara global untuk setiap repositori resource. Nama ini akan menjadi nama direktori tempat repositori berada.
Disusun sebagai berikut (Peringatan: format nama kanonis: bukan API yang harus Anda andalkan, API ini dapat berubah sewaktu-waktu):- Untuk repositori modul Bazel:
module_name~version
(Contoh.@bazel_skylib~1.0.3
) - Untuk repositori ekstensi modul:
module_name~version~extension_name~repo_name
(Contoh.@rules_cc~0.0.1~cc_configure~local_config_cc
)
- Untuk repositori modul Bazel:
Nama repositori yang jelas: Nama repositori yang akan digunakan di
BUILD
dan.bzl
dalam repo. Dependensi yang sama dapat menimbulkan di berbagai repositori.
Ditentukan sebagai berikut:
Setiap repositori memiliki kamus pemetaan
repositori untuk dependensi langsungnya,
yang merupakan peta dari nama repositori yang
jelas ke nama repositori kanonis.
Kita menggunakan pemetaan repositori untuk me-resolve nama repositori saat membangun
label. Perlu diperhatikan bahwa tidak ada konflik nama repositori kanonis, dan
penggunaan nama repositori yang jelas dapat ditemukan dengan menguraikan MODULE.bazel
sehingga konflik dapat dengan mudah ditemukan dan diselesaikan tanpa memengaruhi
dependensi lainnya.
Dependensi ketat
Format spesifikasi dependensi baru memungkinkan kita melakukan pemeriksaan yang lebih ketat. Di beberapa kita sekarang menegakkan bahwa modul hanya dapat menggunakan repo yang dibuat dari dependensi langsung. Hal ini membantu mencegah kerusakan yang tidak disengaja dan sulit di-debug ketika sesuatu dalam grafik dependensi transitif berubah.
Dependensi ketat diimplementasikan berdasarkan pemetaan repositori. Pada dasarnya, fungsi pemetaan repositori untuk setiap repo berisi semua dependensi langsung, repositori lain tidak terlihat. Dependensi yang terlihat untuk setiap repositori ditentukan sebagai berikut:
- Repositori modul Bazel dapat melihat semua repositori yang diperkenalkan dalam file
MODULE.bazel
melaluibazel_dep
danuse_repo
. - Repo ekstensi modul bisa melihat semua dependensi modul yang terlihat menyediakan ekstensi, serta semua repositori lain yang dihasilkan oleh modul yang sama .
Registry
Bzlmod menemukan ketergantungan dengan meminta informasi dari Bazel registribusi. {i>Registry<i} Bazel hanyalah sebuah {i>database<i} dari modul Bazel. Satu-satunya bentuk registry yang didukung adalah registry indeks, yang direktori lokal atau server HTTP statis yang mengikuti format tertentu. Di kolom di masa mendatang, kami berencana untuk menambahkan dukungan untuk registry modul tunggal, yang hanya git repo yang berisi sumber dan histori project.
Registry indeks
{i>Registry indeks<i} adalah direktori lokal
atau server HTTP statis yang berisi
informasi tentang daftar modul, termasuk beranda, pengelola,
MODULE.bazel
file dari setiap versi, dan cara mengambil sumber setiap versi
. Secara khusus, server ini tidak perlu menayangkan arsip sumber itu sendiri.
Registry indeks harus mengikuti format berikut:
/bazel_registry.json
: File JSON yang berisi metadata untuk registry seperti:mirrors
, yang menentukan daftar duplikat yang akan digunakan untuk arsip sumber.module_base_path
, yang menentukan jalur dasar untuk modul dengan Jenislocal_repository
dalam filesource.json
.
/modules
: Direktori yang berisi subdirektori untuk setiap modul dalam ini {i>registry<i}./modules/$MODULE
: Direktori yang berisi subdirektori untuk setiap versi di modul ini, serta file berikut:metadata.json
: File JSON yang berisi informasi tentang modul, dengan kolom berikut:homepage
: URL halaman beranda project.maintainers
: Daftar objek JSON, yang masing-masing sesuai dengan informasi pengelola modul di registry. Perhatikan bahwa ini belum tentu sama dengan penulis proyek.versions
: Daftar semua versi modul ini yang dapat ditemukan di {i>registry<i} ini.yanked_versions
: Daftar versi yang ditarik dari modul ini. Ini saat ini tanpa pengoperasian, tetapi di masa mendatang, versi yang ditarik akan dilewati atau menghasilkan error.
/modules/$MODULE/$VERSION
: Direktori yang berisi file berikut:MODULE.bazel
: FileMODULE.bazel
versi modul ini.source.json
: File JSON yang berisi informasi tentang cara mengambil versi modul ini.- Jenis defaultnya adalah "arsip" dengan kolom berikut:
url
: URL arsip sumber.integrity
: Integritas Subresource {i>checksum<i} arsip.strip_prefix
: Awalan direktori yang akan dihapus saat mengekstrak arsip sumber.patches
: Daftar string, yang masing-masing menamai file patch untuk diterapkan ke arsip yang diekstrak. File {i>patch<i} terletak di direktori/modules/$MODULE/$VERSION/patches
.patch_strip
: Sama dengan argumen--strip
pada patch Unix.
- Jenis ini dapat diubah untuk menggunakan jalur lokal dengan kolom berikut:
type
:local_path
path
: Jalur lokal ke repo, yang dihitung sebagai berikut:- Jika jalur adalah jalur absolut, jalur tersebut akan digunakan sebagaimana adanya.
- Jika jalur adalah jalur relatif dan
module_base_path
adalah jalur absolut, jalur diselesaikan menjadi<module_base_path>/<path>
- Jika jalur dan
module_base_path
merupakan jalur relatif, jalur adalah diselesaikan menjadi<registry_path>/<module_base_path>/<path>
. Registry harus dihosting secara lokal dan digunakan oleh--registry=file://<registry_path>
. Jika tidak, Bazel akan menampilkan pesan error.
- Jenis defaultnya adalah "arsip" dengan kolom berikut:
patches/
: Direktori opsional yang berisi file patch, hanya digunakan jikasource.json
memiliki "arsip" .
Bazel Central Registry
Bazel Central Registry (BCR) adalah {i>registry<i} indeks yang terletak di
bcr.bazel.build. Isinya
didukung oleh repositori GitHub
bazelbuild/bazel-central-registry
BCR dikelola oleh komunitas Bazel; kontributor dipersilakan untuk mengirimkan permintaan pull. Lihat Kebijakan dan Prosedur Bazel Central Registry.
Selain mengikuti format {i>registry<i} indeks
normal, BCR memerlukan
file presubmit.yml
untuk setiap versi modul
(/modules/$MODULE/$VERSION/presubmit.yml
). File ini menentukan beberapa hal penting
membangun dan menguji target yang dapat
digunakan untuk memeriksa validitas
modul, dan digunakan oleh pipeline CI BCR untuk memastikan interoperabilitas
antar-modul dalam BCR.
Memilih registry
Tanda Bazel berulang --registry
dapat digunakan untuk menentukan daftar
registry untuk meminta modul, jadi Anda dapat menyiapkan project yang akan diambil
dependensi dari pihak ketiga
atau {i>registry<i} internal. Registri sebelumnya mengambil
prioritas tinggi. Untuk memudahkan, Anda dapat menempatkan daftar tanda --registry
di
.bazelrc
dari project Anda.
Ekstensi Modul
Ekstensi modul memungkinkan Anda 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. Mereka serupa
dalam fungsi ke makro WORKSPACE
saat ini, tetapi lebih cocok di dunia
modul dan dependensi transitif.
Ekstensi modul ditentukan dalam file .bzl
, seperti aturan repo atau
makro WORKSPACE
. Mereka tidak dipanggil secara langsung; setiap modul dapat
menentukan bagian data yang disebut tag untuk dibaca ekstensi. Kemudian, setelah modul
resolusi versi selesai, ekstensi modul akan dijalankan. Setiap ekstensi dijalankan
sekali setelah resolusi modul (masih sebelum build apa pun benar-benar terjadi), dan
membaca semua tag yang memilikinya di seluruh grafik dependensi.
[ A 1.1 ]
[ * maven.dep(X 2.1) ]
[ * maven.pom(...) ]
/ \
bazel_dep / \ bazel_dep
/ \
[ B 1.2 ] [ C 1.0 ]
[ * maven.dep(X 1.2) ] [ * maven.dep(X 2.1) ]
[ * maven.dep(Y 1.3) ] [ * cargo.dep(P 1.1) ]
\ /
bazel_dep \ / bazel_dep
\ /
[ D 1.4 ]
[ * maven.dep(Z 1.4) ]
[ * cargo.dep(Q 1.1) ]
Pada contoh grafik dependensi di atas, A 1.1
dan B 1.2
dll. adalah modul Bazel;
Anda dapat menganggap
masing-masing sebagai file MODULE.bazel
. Setiap modul dapat menentukan beberapa
tag untuk ekstensi modul; di sini beberapa ditentukan untuk
ekstensi "{i>maven<i}",
dan beberapa ditentukan
untuk "{i>cargo<i}". Ketika grafik dependensi ini diselesaikan (untuk
misalnya, mungkin B 1.2
benar-benar memiliki bazel_dep
di D 1.3
tetapi diupgrade menjadi
D 1.4
karena C
), ekstensi "maven" dijalankan, dan skrip itu dapat membaca semua
maven.*
, menggunakan informasi di dalamnya untuk menentukan repositori yang akan dibuat.
Demikian pula untuk "{i>cargo<i}" .
Penggunaan ekstensi
Ekstensi di-{i>host<i} di modul Bazel sendiri, jadi untuk menggunakan ekstensi
modul Anda, Anda harus menambahkan bazel_dep
terlebih dahulu pada modul tersebut, lalu memanggil
use_extension
bawaan
fungsi untuk membawanya
ke dalam ruang lingkup. Perhatikan contoh berikut, cuplikan dari
file MODULE.bazel
untuk menggunakan "maven" fiktif ekstensi yang ditentukan dalam
Modul rules_jvm_external
:
bazel_dep(name = "rules_jvm_external", version = "1.0")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Setelah memasukkan ekstensi ke dalam cakupan, Anda dapat menggunakan sintaksis titik untuk
dan menentukan tag untuk resource tersebut. Perhatikan bahwa tag harus mengikuti skema yang ditetapkan oleh
class tag yang sesuai (lihat definisi ekstensi
di bawah). Berikut adalah contoh yang menentukan beberapa tag maven.dep
dan maven.pom
.
maven.dep(coord="org.junit:junit:3.0")
maven.dep(coord="com.google.guava:guava:1.2")
maven.pom(pom_xml="//:pom.xml")
Jika ekstensi menghasilkan repositori yang ingin Anda gunakan dalam modul, gunakan
Perintah use_repo
yang akan dideklarasikan
mereka. Ini untuk memenuhi kondisi dependensi yang ketat dan menghindari nama repo lokal
konflik.
use_repo(
maven,
"org_junit_junit",
guava="com_google_guava_guava",
)
Repositori yang dihasilkan oleh ekstensi adalah bagian dari API-nya, jadi dari tag yang
ditentukan, Anda harus tahu bahwa "maven" akan menghasilkan
repositori bernama "org_junit_junit", dan satu lagi bernama "com_google_guava_guava". Dengan
use_repo
, Anda dapat mengganti namanya dalam cakupan modul, seperti
"jambu biji" di sini.
Definisi ekstensi
Ekstensi modul didefinisikan mirip dengan aturan repo, menggunakan
Fungsi module_extension
.
Keduanya memiliki fungsi implementasi; tetapi meskipun aturan repo memiliki sejumlah
ekstensi modul memiliki sejumlah
tag_class
, yang masing-masing memiliki
sejumlah atribut. Class tag menentukan skema untuk tag yang digunakan
. Melanjutkan contoh "maven" fiktif ekstensi di atas:
# @rules_jvm_external//:extensions.bzl
maven_dep = tag_class(attrs = {"coord": attr.string()})
maven_pom = tag_class(attrs = {"pom_xml": attr.label()})
maven = module_extension(
implementation=_maven_impl,
tag_classes={"dep": maven_dep, "pom": maven_pom},
)
Deklarasi ini memperjelas bahwa tag maven.dep
dan maven.pom
dapat
ditentukan, dengan menggunakan skema atribut yang didefinisikan di atas.
Fungsi implementasi ini mirip dengan makro WORKSPACE
, hanya saja
mendapatkan objek module_ctx
, yang memberikan
akses ke grafik dependensi dan semua tag yang terkait. Implementasi
fungsi ini kemudian memanggil aturan repo untuk menghasilkan repositori:
# @rules_jvm_external//:extensions.bzl
load("//:repo_rules.bzl", "maven_single_jar")
def _maven_impl(ctx):
coords = []
for mod in ctx.modules:
coords += [dep.coord for dep in mod.tags.dep]
output = ctx.execute(["coursier", "resolve", coords]) # hypothetical call
repo_attrs = process_coursier(output)
[maven_single_jar(**attrs) for attrs in repo_attrs]
Pada contoh di atas, kita membahas semua modul dalam grafik dependensi
(ctx.modules
), yang masing-masing merupakan
Objek bazel_module
dengan kolom tags
mengekspos semua tag maven.*
pada modul. Kemudian kita panggil utilitas CLI
Coursier untuk menghubungi Maven dan melakukan resolusi. Terakhir, kita gunakan model
untuk membuat sejumlah repositori, menggunakan hipotesis maven_single_jar
repo.