Ekstensi modul

Ekstensi modul memungkinkan pengguna memperluas sistem modul dengan membaca data input dari modul di seluruh grafik dependensi, melakukan logika yang diperlukan untuk mengatasi dependensi, dan akhirnya membuat repositori dengan memanggil aturan repositori. Ekstensi ini memiliki kemampuan yang mirip dengan aturan repositori, yang memungkinkannya melakukan I/O file, mengirim permintaan jaringan, dan sebagainya. Selain hal lainnya, ekstensi ini memungkinkan Bazel berinteraksi dengan sistem pengelolaan paket lain sekaligus mematuhi grafik dependensi yang dibuat dari modul Bazel.

Anda dapat menentukan ekstensi modul dalam file .bzl, seperti aturan repositori. Ekstensi ini tidak dipanggil secara langsung; melainkan, setiap modul menentukan bagian data yang disebut tag untuk dibaca oleh ekstensi. 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 itu 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 rules_jvm_external modul:

bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")

Hal ini mengikat nilai yang ditampilkan use_extension ke variabel, yang memungkinkan pengguna menggunakan sintaksis titik untuk menentukan tag untuk ekstensi. Tag harus mengikuti skema yang ditentukan oleh class tag yang sesuai 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 direktif use_repo untuk memasukkan repositori yang dibuat 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, ekstensi modul "maven" berjanji untuk membuat repositori bernama maven. Dengan deklarasi di atas, ekstensi akan menyelesaikan label dengan benar seperti @maven//:org_junit_junit untuk mengarah ke repositori yang dibuat oleh ekstensi "maven".

Definisi ekstensi

Anda dapat menentukan ekstensi modul dengan cara yang sama seperti aturan repositori, menggunakan the module_extension fungsi. Namun, meskipun aturan repositori memiliki sejumlah atribut, ekstensi modul memiliki tag_classes, yang masing-masing memiliki sejumlah atribut. Class tag menentukan skema untuk tag yang digunakan oleh ekstensi ini. Misalnya, ekstensi "maven" di atas dapat 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 penerapan ekstensi modul mirip dengan fungsi aturan repositori, kecuali bahwa fungsi tersebut mendapatkan objek module_ctx, yang memberikan akses ke semua modul menggunakan ekstensi dan semua tag yang relevan. Fungsi penerapan kemudian memanggil aturan repositori 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. 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 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. Perhatikan bahwa format nama kanonis bukanlah API yang harus Anda andalkan — format ini dapat berubah kapan saja.

Kebijakan penamaan ini berarti bahwa setiap ekstensi memiliki "namespace repositori" sendiri; dua ekstensi yang berbeda dapat menentukan repositori dengan nama yang sama tanpa menimbulkan konflik. Hal ini juga berarti bahwa repository_ctx.name melaporkan nama kanonis repositori, yang tidak sama dengan nama yang ditentukan dalam panggilan aturan repositori.

Dengan mempertimbangkan repositori yang dibuat oleh ekstensi modul, ada beberapa aturan visibilitas repositori:

  • Repositori modul Bazel dapat melihat semua repositori yang diperkenalkan dalam file MODULE.bazelnya melalui bazel_dep dan use_repo.
  • Repositori yang dibuat oleh ekstensi modul dapat melihat semua repositori yang terlihat oleh modul yang menghosting ekstensi, ditambah semua repositori lain yang dibuat oleh ekstensi modul yang sama (menggunakan nama yang ditentukan dalam panggilan aturan repositori sebagai nama yang terlihat).
    • Hal ini dapat menyebabkan konflik. Jika repositori modul dapat melihat repositori dengan nama yang terlihat foo, dan ekstensi membuat repositori dengan nama yang ditentukan foo, maka untuk semua repositori yang dibuat oleh ekstensi tersebut, foo merujuk ke yang pertama.

Praktik terbaik

Bagian ini menjelaskan praktik terbaik saat menulis ekstensi sehingga mudah digunakan, dapat dipertahankan, dan beradaptasi dengan baik terhadap perubahan dari waktu ke waktu.

Menempatkan setiap ekstensi dalam file terpisah

Jika ekstensi berada dalam file yang berbeda, satu ekstensi dapat memuat repositori yang dibuat oleh ekstensi lain. Meskipun Anda tidak menggunakan fungsi ini, sebaiknya tempatkan ekstensi dalam file terpisah jika Anda memerlukannya nanti. Hal ini karena identitas ekstensi didasarkan pada filenya, sehingga memindahkan ekstensi ke file lain nanti akan mengubah API publik Anda dan merupakan perubahan yang tidak kompatibel dengan versi sebelumnya bagi pengguna Anda.