モジュール拡張機能

モジュール拡張機能を使用すると、依存関係グラフ全体のモジュールから入力データを読み取り、依存関係を解決するために必要なロジックを実行し、最後にリポジトリルールを呼び出してリポジトリを作成することで、モジュール システムを拡張できます。これらの拡張機能はリポジトリルールと同様の機能を備えているため、ファイル I/O やネットワーク リクエストの送信などを行うことができます。これにより、Bazel モジュールから構築された依存関係グラフを尊重しながら、Bazel が他のパッケージ管理システムとやり取りできるようになります。

リポジトリルールと同様に、.bzl ファイルでモジュール拡張機能を定義できます。直接呼び出されるのではなく、各モジュールは、拡張機能が読み取るデータの一部(タグ)を指定します。 Bazel は、拡張機能を評価する前にモジュール解決を実行します。拡張機能は、依存関係グラフ全体で、その拡張機能に属するすべてのタグを読み取ります。

拡張機能の使用

拡張機能は Bazel モジュール自体でホストされます。モジュールで拡張機能を使用するには、まず拡張機能をホストするモジュールにbazel_depを追加し、次にuse_extension組み込み関数を呼び出してスコープに含めます。次の例は、 MODULE.bazel ファイルで定義された「maven」拡張機能を使用するための rules_jvm_external モジュールのスニペットです。

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//:org_junit_junit などのラベルを適切に解決して、「maven」拡張機能によって生成されたリポジトリを指すようにします。

拡張機能の定義

` module_extension` 関数を使用して、リポジトリルールと同様にモジュール拡張機能を定義できます。ただし、 リポジトリルールには多数の属性がありますが、モジュール拡張機能には tag_classesがあり、それぞれに多数の 属性があります。タグクラスは、この拡張機能で使用されるタグのスキーマを定義します。たとえば、上記の「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 と 名前 maven によって識別されます。@rules_jvm_external//:extension.bzl

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 モジュール リポジトリは、MODULE.bazel ファイル を介して、bazel_depuse_repo に導入されたすべてのリポジトリを確認できます。
  • モジュール拡張機能によって生成されたリポジトリは、拡張機能をホストするモジュールに表示されるすべてのリポジトリに加えて、同じモジュール拡張機能によって生成された他のすべてのリポジトリ(リポジトリルールの呼び出しで指定された名前を見かけ上の名前として使用)を確認できます。
    • これにより、競合が発生する可能性があります。モジュール リポジトリが見かけ上の名前 foo のリポジトリを確認でき、拡張機能が指定された名前 foo のリポジトリを生成する場合、その拡張機能によって生成されたすべてのリポジトリで foo は前者を参照します。

ベスト プラクティス

このセクションでは、拡張機能を記述する際のベスト プラクティスについて説明します。これにより、拡張機能は使いやすく、保守しやすく、時間の経過に伴う変更に適切に対応できます。

各拡張機能を個別のファイルに配置する

拡張機能が別のファイルにある場合、1 つの拡張機能で別の拡張機能によって生成されたリポジトリを読み込むことができます。この機能を使用しない場合でも、後で必要になった場合に備えて、個別のファイルに配置することをおすすめします。これは、拡張機能の ID がファイルに基づいているため、後で拡張機能を別のファイルに移動すると、公開 API が変更され、ユーザーにとって下位互換性のない変更になるためです。