Bazel モジュール

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")

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

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

バージョンの形式

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

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

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

有効な SemVer バージョンはすべて有効な Bazel モジュール バージョンです。また、2 つの SemVer バージョン ab は、Bazel モジュール バージョンとして比較した場合と同じ場合にのみ a < b と比較されます。

バージョンの選択

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

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

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

ヤンクされたバージョン

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

互換性レベル

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

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

オーバーライド

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

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

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

The single_version_override には複数の目的があります。

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

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

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

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

モジュールで許可されるバージョンの明示的なリストを指定できます。これらのバージョンはすべて、解決前に依存関係グラフに存在する必要があります。つまり、許可された各バージョンに依存する推移的な依存関係が存在する必要があります。解決後、モジュールの許可されたバージョンのみが残ります。Bazel は、同じ 互換性レベルで、モジュールの他のバージョンを最も近い上位の許可されたバージョンにアップグレードします。同じ互換性 レベルで上位の許可されたバージョンが存在しない場合、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 は、次の非レジストリのオーバーライドをサポートしています。

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

モジュールをサポートするリポジトリの正規名module_name~version(例: bazel_skylib~1.0.3)です。非レジストリのオーバーライドを使用するモジュールの場合、versionの部分を文字列overrideに置き換えます。正規名の形式は、依存すべき API ではなく、随時変更される可能性があることに注意してください。

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

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