モジュール拡張機能を使用すると、依存関係グラフ全体のモジュールから入力データを読み取り、依存関係を解決するために必要なロジックを実行して、最終的にリポジトリルールを呼び出してリポジトリを作成することにより、モジュール システムを拡張できます。これらの拡張機能はリポジトリ ルールに似た機能を備えており、ファイル I/O の実行、ネットワーク リクエストの送信などを行うことができます。特に、Bazel による他のパッケージ管理システムとのやり取りが可能になる一方で、Bazel モジュールから構築された依存関係グラフも考慮されます。
リポジトリ ルールと同様に、モジュール拡張子を .bzl
ファイルで定義できます。これらは直接呼び出されるのではなく、各モジュールが、拡張機能が読み取るためのタグと呼ばれるデータを指定します。Bazel は、拡張機能を評価する前にモジュールの解決を実行します。この拡張機能は、依存関係グラフ全体でそれに属するすべてのタグを読み取ります。
拡張機能の使用状況
拡張機能は Bazel モジュール自体でホストされます。モジュール内で拡張機能を使用するには、拡張機能をホストするモジュールに bazel_dep
を追加してから、use_extension
組み込み関数を呼び出してスコープに追加します。次の例について考えてみましょう。MODULE.bazel
ファイルのスニペットでは、rules_jvm_external
モジュールで定義された「maven」拡張機能を使用します。
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
これにより、use_extension
の戻り値が変数にバインドされ、ユーザーがドット構文を使用して拡張機能のタグを指定できるようになります。タグは、拡張機能の定義で指定された対応するタグクラスで定義されたスキーマに従う必要があります。maven.install
タグと 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"])
use_repo
ディレクティブを使用して、拡張機能によって生成されたリポジトリを現在のモジュールのスコープに移動します。
use_repo(maven, "maven")
拡張機能によって生成されたリポジトリは、拡張機能の API の一部です。この例では、「maven」モジュール拡張機能は maven
というリポジトリの生成を約束しています。上記の宣言により、拡張機能は「maven」拡張機能によって生成されたリポジトリを指すように @maven//:org_junit_junit
などのラベルを適切に解決します。
拡張機能の定義
module_extension
関数を使用して、リポジトリ ルールと同じようにモジュール拡張を定義できます。ただし、リポジトリ ルールには多くの属性がありますが、モジュール拡張機能には tag_class
があり、それぞれに複数の属性があります。タグクラスは、この拡張機能で使用されるタグのスキーマを定義します。たとえば、上記の「maven」拡張機能は次のように定義できます。
# @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},
)
これらの宣言は、指定された属性スキーマを使用して maven.install
タグと maven.artifact
タグを指定できることを表します。
モジュール拡張機能の実装関数は、リポジトリ ルールの実装関数と似ていますが、module_ctx
オブジェクトを取得する点で異なります。このオブジェクトにより、拡張機能とすべての関連タグを使用するすべてのモジュールにアクセス権が付与されます。次に、実装関数がリポジトリ ルールを呼び出して、リポジトリを生成します。
# @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)
拡張機能の ID
モジュール拡張機能は、名前と use_extension
の呼び出しにある .bzl
ファイルで識別されます。次の例では、拡張子 maven
は、.bzl
ファイル @rules_jvm_external//:extension.bzl
と名前 maven
で識別されます。
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
別の .bzl
ファイルから拡張機能を再エクスポートすると、新しい ID が作成されます。両方のバージョンの拡張機能が推移的モジュール グラフで使用されている場合は、個別に評価され、その ID に関連付けられているタグのみが表示されます。
拡張機能の作成者は、ユーザーが 1 つの .bzl
ファイル内のモジュール拡張機能のみを使用するようにする必要があります。
リポジトリ名と可視性
拡張機能によって生成されたリポジトリには、module_repo_canonical_name~extension_name~repo_name
形式の正規名が付けられています。ルート モジュールでホストされている拡張機能の場合、module_repo_canonical_name
の部分は文字列 _main
に置き換えられます。正規名の形式は、依存すべき API ではありません。随時変更される可能性があります。
この命名ポリシーは、各拡張機能に独自の「リポジトリの名前空間」があることを意味します。2 つの異なる拡張機能が、クラッシュすることなく、それぞれ同じ名前のリポジトリを定義できます。また、repository_ctx.name
はリポジトリの正規名を報告しますが、これはリポジトリ ルール呼び出しで指定された名前とは異なります。
モジュール拡張によって生成されたリポジトリを考慮すると、いくつかのリポジトリ公開ルールがあります。
- Bazel モジュール リポジトリは、
bazel_dep
とuse_repo
を介して、MODULE.bazel
ファイルに導入されたすべてのリポジトリを参照できます。 - モジュール拡張機能によって生成されたリポジトリは、拡張機能をホストするモジュールに表示されるすべてのリポジトリに加えて、同じモジュール拡張機能によって生成された他のすべてのリポジトリを認識できます(リポジトリ ルールの呼び出しで指定された名前を使用して)。
- これにより競合が発生する可能性があります。モジュール リポジトリが明らかな名前
foo
のリポジトリを認識でき、拡張機能が指定された名前foo
のリポジトリを生成する場合、その拡張機能によって生成されたすべてのリポジトリでは、foo
は前者を参照します。
- これにより競合が発生する可能性があります。モジュール リポジトリが明らかな名前
ベスト プラクティス
このセクションでは、拡張機能を簡単に使用でき、保守しやすく、時間の経過に伴う変更に適切に適応できるように、拡張機能を作成する際のベスト プラクティスについて説明します。
各拡張機能を個別のファイルに格納する
拡張機能が別のファイルにある場合、ある拡張機能が別の拡張機能によって生成されたリポジトリを読み込むことができます。この機能を使用しない場合でも、後で必要になる場合に備えて、ファイルを別のファイルに保存することをおすすめします。これは、拡張機能の ID がそのファイルに基づいているため、後で拡張機能を別のファイルに移動すると公開 API が変更され、ユーザーにとっては下位互換性のない変更になるためです。
オペレーティング システムとアーキテクチャを指定する
拡張機能がオペレーティング システムやそのアーキテクチャ タイプに依存している場合は、拡張機能の定義で「os_dependent」と「arch_dependent」のブール値属性を使用します。これにより、いずれかの変更がある場合に、Bazel は再審査の必要性を認識できます。