Bazel モジュール

7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel モジュールは、複数のバージョンを持つことができる Bazel プロジェクトです。各バージョンは、依存する他のモジュールに関するメタデータを公開します。これは、Maven アーティファクト、npm パッケージ、Go モジュール、Cargo クレートなど、他の依存関係管理システムにおける一般的なコンセプトに似ています。

モジュールには、リポジトリのルート(WORKSPACE ファイルの次)に MODULE.bazel ファイルが必要です。このファイルはモジュールのマニフェストで、名前、バージョン、直接依存関係のリストなどの情報を宣言します。基本的な例を次に示します。

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

MODULE.bazel ファイルで使用できるディレクティブの一覧をご覧ください。

モジュール解決を実行するために、Bazel はまずルート モジュールの MODULE.bazel ファイルを読み取り、依存関係グラフ全体が検出されるまで Bazel レジストリから依存関係の MODULE.bazel ファイルを繰り返しリクエストします。

デフォルトでは、Bazel は使用する各モジュールの 1 つのバージョンを選択します。Bazel は各モジュールをリポジトリで表し、レジストリに再度問い合わせて、各リポジトリの定義方法を学習します。

バージョン形式

Bazel には多様なエコシステムがあり、プロジェクトではさまざまなバージョニング スキームが使用されています。最も人気があるのは SemVer ですが、Abseil など、日付ベースのバージョン(20210324.2 など)を使用する有名なプロジェクトもあります。

このため、Bzlmod では、SemVer 仕様のより緩和されたバージョンを採用しています。違いは次のとおりです。

  • SemVer では、バージョンの「リリース」部分は 3 つのセグメント(MAJOR.MINOR.PATCH)で構成する必要があります。Bazel では、この要件が緩和され、任意の数のセグメントが許可されます。
  • SemVer では、「リリース」部分の各セグメントは数字のみにする必要があります。Bazel では、文字も使用できるように緩和されており、比較セマンティクスは「prerelease」部分の「識別子」と一致します。
  • また、メジャー バージョン、マイナー バージョン、パッチ バージョンの増加のセマンティクスは適用されません。ただし、下位互換性を示す方法については、互換性レベルをご覧ください。

有効な SemVer バージョンは、有効な Bazel モジュール バージョンです。また、2 つの SemVer バージョン aba < b と比較されるのは、Bazel モジュール バージョンとして比較した場合に同じ結果が得られる場合に限られます。

バージョンの選択

バージョン管理された依存関係管理の分野では定番のダイアモンド依存関係の問題について考えてみましょう。次の依存関係グラフがあるとします。

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

どのバージョンの D を使用すればよいですか?この問題を解決するために、Bzlmod は Go モジュール システムで導入された最小バージョン選択(MVS)アルゴリズムを使用します。MVS は、モジュールの新しいバージョンはすべて下位互換性があると想定するため、依存関係によって指定された最高バージョン(この例では D 1.1)を選択します。D 1.1 は要件を満たす最も古いバージョンであるため、「最小」と呼ばれます。D 1.2 以降のバージョンが存在する場合でも、選択されません。MVS を使用すると、忠実度が高く再現可能なバージョン選択プロセスが作成されます。

取り消されたバージョン

回避する必要がある場合は、レジストリで特定のバージョンを「ヤンク済み」として宣言できます(セキュリティの脆弱性など)。モジュールの yanked バージョンを選択すると、Bazel でエラーがスローされます。このエラーを解決するには、ヤンクされていない新しいバージョンにアップグレードするか、--allow_yanked_versions フラグを使用してヤンクされたバージョンを明示的に許可します。

互換性レベル

Go では、下位互換性に関する MVS の想定が機能します。これは、下位互換性のないバージョンのモジュールを別のモジュールとして扱うためです。SemVer では、A 1.xA 2.x は異なるモジュールと見なされ、解決された依存関係グラフに共存できます。これは、Go のパッケージパスにメジャー バージョンをエンコードすることで可能になります。これにより、コンパイル時またはリンク時に競合が発生しません。

ただし、Bazel ではこのような保証を提供できないため、下位互換性のないバージョンを検出するには「メジャー バージョン」番号が必要です。この数値は互換性レベルと呼ばれ、各モジュール バージョンによって module() ディレクティブで指定します。この情報により、Bazel は、解決された依存関係グラフに互換性レベルが異なる同じモジュールのバージョンが存在することを検出すると、エラーをスローできます。

オーバーライド

MODULE.bazel ファイルでオーバーライドを指定して、Bazel モジュール解決の動作を変更します。ルート モジュールのオーバーライドのみが有効になります。モジュールが依存関係として使用されている場合、そのオーバーライドは無視されます。

各オーバーライドは特定のモジュール名に対して指定され、依存関係グラフ内のすべてのバージョンに影響します。ルート モジュールのオーバーライドのみが有効になりますが、ルート モジュールが直接依存していない推移的な依存関係に対しても適用できます。

単一バージョンのオーバーライド

single_version_override は複数の目的を果たします。

  • version 属性を使用すると、依存関係グラフでリクエストされる依存関係のバージョンに関係なく、依存関係を特定のバージョンに固定できます。
  • registry 属性を使用すると、通常のレジストリの選択プロセスに従うのではなく、この依存関係を特定のレジストリから強制的に取得できます。
  • patch* 属性を使用すると、ダウンロードしたモジュールに適用するパッチのセットを指定できます。

これらの属性はすべて省略可能で、組み合わせて使用できます。

複数バージョンのオーバーライド

multiple_version_override を指定すると、解決された依存関係グラフ内に同じモジュールの複数のバージョンが共存できます。

モジュールに対して許可されるバージョンの明示的なリストを指定できます。このリストはすべて、解決前に依存関係グラフ内に存在する必要があります。許可される各バージョンに応じて、なんらかの推移的依存関係が存在する必要があります。解決後、許可されたバージョンのモジュールのみが残り、他のバージョンのモジュールは、同じ互換性レベルで最も近い上位の許可されたバージョンにアップグレードされます。同じ互換性レベルで許可されているバージョンがない場合、Bazel はエラーをスローします。

たとえば、解決前に依存関係グラフにバージョン 1.11.31.51.72.0 が存在し、メジャー バージョンが互換性レベルの場合:

  • 1.31.72.0 を許可する複数バージョンのオーバーライドを使用すると、1.11.3 にアップグレードされ、1.51.7 にアップグレードされ、他のバージョンは同じままになります。
  • 1.52.0 を許可する複数バージョンのオーバーライドでは、1.7 にアップグレードする同じ互換性レベルのバージョンがないため、エラーが発生します。
  • 1.92.0 を許可する複数バージョンのオーバーライドでは、解決前に依存関係グラフに 1.9 が存在しないため、エラーが発生します。

また、単一バージョンのオーバーライドと同様に、registry 属性を使用してレジストリをオーバーライドすることもできます。

レジストリ以外のオーバーライド

レジストリ以外のオーバーライドは、バージョン解決からモジュールを完全に削除します。Bazel は、これらの MODULE.bazel ファイルをレジストリではなく、リポジトリ自体からリクエストします。

Bazel は、次のレジストリ以外のオーバーライドをサポートしています。

Bazel モジュールを表さないリポジトリを定義する

bazel_dep を使用すると、他の Bazel モジュールを表すリポジトリを定義できます。Bazel モジュールを表さないリポジトリを定義する必要がある場合があります。たとえば、データとして読み取られる単純な JSON ファイルを含むリポジトリなどです。

この場合、use_repo_rule ディレクティブを使用してリポジトリ ルールを呼び出すことで、リポジトリを直接定義できます。このリポジトリは、定義されているモジュールにのみ表示されます。

内部的には、モジュール拡張機能と同じメカニズムを使用して実装されているため、より柔軟にリポジトリを定義できます。

リポジトリ名と厳密な依存関係

モジュールを直接依存関係にバインドするリポジトリの表示名は、bazel_dep ディレクティブの repo_name 属性で指定されていない限り、デフォルトでモジュール名になります。これは、モジュールが直接依存関係のみを見つけることができることを意味します。これにより、伝播依存関係の変更による意図しない破損を防ぐことができます。

モジュールをバッキングするリポジトリの正規名は、依存関係グラフ全体にモジュールの複数のバージョンがあるかどうかに応じて、module_name~version(例: bazel_skylib~1.0.3)または module_name~(例: bazel_features~)のいずれかになります(multiple_version_override を参照)。正規名の形式は依存すべき API ではなく、随時変更される可能性があります。正規名をハードコードする代わりに、サポートされている方法で Bazel から直接取得します。 * BUILD ファイルと .bzl ファイルでは、リポジトリの表示名で指定されたラベル文字列から作成された Label インスタンスで Label.repo_name を使用します。たとえば、Label("@bazel_skylib").repo_name です。* ランファイルを検索する場合は、$(rlocationpath ...) または @bazel_tools//tools/{bash,cpp,java}/runfiles のランファイル ライブラリのいずれかを使用します。ルールセット rules_foo の場合は @rules_foo//foo/runfiles を使用します。* IDE や言語サーバーなどの外部ツールから Bazel を操作する場合は、bazel mod dump_repo_mapping コマンドを使用して、特定の Repository セットの表示名から標準名へのマッピングを取得します。

モジュール拡張機能を使用すると、モジュールの可視スコープに追加のリポジトリを導入することもできます。