Bazel モジュールは、複数のバージョンを持つことができる Bazel プロジェクトです。各バージョンは、依存する他のモジュールに関するメタデータを公開します。これは、Maven のアーティファクト、npm のパッケージ、Go のモジュール、Cargo のcrate など、他の依存関係管理システムの一般的なコンセプトに似ています。
モジュールには、リポジトリのルート(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 バージョン a
と b
が a < 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 を使用すると、忠実度が高く再現可能なバージョン選択プロセスが作成されます。
ヤンク バージョン
回避する必要がある場合は、レジストリで特定のバージョンを「ヤンク済み」として宣言できます(セキュリティの脆弱性など)。ヤンクされたバージョンのモジュールを選択すると、Bazel はエラーをスローします。このエラーを修正するには、ヤンクされていない新しいバージョンにアップグレードするか、--allow_yanked_versions
フラグを使用してヤンクされたバージョンを明示的に許可します。
互換性レベル
Go では、MVS の下位互換性に関する想定は機能します。これは、モジュールの下位互換性のないバージョンを別のモジュールとして扱うためです。SemVer では、A 1.x
と A 2.x
は異なるモジュールと見なされ、解決された依存関係グラフに共存できます。これは、Go のパッケージパスでメジャー バージョンをエンコードすることで可能になるため、コンパイル時またはリンク時の競合は発生しません。
ただし、Bazel ではこのような保証を提供できないため、下位互換性のないバージョンを検出するには「メジャー バージョン」番号が必要です。この数値は互換性レベルと呼ばれ、各モジュール バージョンの module()
ディレクティブで指定されます。この情報により、Bazel は、解決された依存関係グラフに互換性レベルが異なる同じモジュールのバージョンが存在することを検出すると、エラーをスローできます。
オーバーライド
MODULE.bazel
ファイルでオーバーライドを指定して、Bazel モジュール解決の動作を変更します。ルート モジュールのオーバーライドのみが有効になります。モジュールが依存関係として使用されている場合、そのオーバーライドは無視されます。
各オーバーライドは特定のモジュール名に対して指定され、依存関係グラフ内のすべてのバージョンに影響します。ルート モジュールのオーバーライドのみが有効になりますが、ルート モジュールが直接依存していない推移的な依存関係に対しても適用できます。
単一バージョンのオーバーライド
single_version_override
は複数の目的で使用されます。
version
属性を使用すると、依存関係グラフでリクエストされた依存関係のバージョンに関係なく、依存関係を特定のバージョンに固定できます。registry
属性を使用すると、通常のレジストリ選択プロセスに従う代わりに、この依存関係を特定のレジストリから取得させることができます。patch*
属性を使用すると、ダウンロードしたモジュールに適用するパッチのセットを指定できます。
これらの属性はすべて省略可能で、組み合わせて使用できます。
複数バージョンのオーバーライド
multiple_version_override
を指定すると、解決された依存関係グラフ内に同じモジュールの複数のバージョンが共存できます。
モジュールに対して許可されるバージョンの明示的なリストを指定できます。このリストはすべて、解決前に依存関係グラフ内に存在する必要があります。許可される各バージョンに応じて、なんらかの推移的依存関係が存在する必要があります。解決後、許可されたバージョンのモジュールのみが残り、他のバージョンのモジュールは、同じ互換性レベルで最も近い上位の許可されたバージョンにアップグレードされます。同じ互換性レベルで許可されているバージョンより上位のバージョンが存在しない場合、Bazel はエラーをスローします。
たとえば、解決前に依存関係グラフにバージョン 1.1
、1.3
、1.5
、1.7
、2.0
が存在し、メジャー バージョンが互換性レベルの場合:
- 複数バージョンのオーバーライドで
1.3
、1.7
、2.0
を許可すると、1.1
は1.3
にアップグレードされ、1.5
は1.7
にアップグレードされ、他のバージョンは変わりません。 1.7
にはアップグレード先の互換性レベルの上位のバージョンがないため、複数バージョンのオーバーライドで1.5
と2.0
を許可するとエラーが発生します。- 解決前に
1.9
が依存関係グラフに存在しないため、複数バージョンのオーバーライドで1.9
と2.0
を許可するとエラーが発生します。
さらに、ユーザーは単一バージョンのオーバーライドと同様に、registry
属性を使用してレジストリをオーバーライドできます。
レジストリ以外のオーバーライド
レジストリ以外のオーバーライドでは、バージョン解決からモジュールが完全に削除されます。Bazel は、これらの MODULE.bazel
ファイルをレジストリではなく、リポジトリ自体からリクエストします。
Bazel は、次のレジストリ以外のオーバーライドをサポートしています。
リポジトリ名と厳格な依存関係
モジュールをバッキングするリポジトリの正規名は module_name~version
です(例: bazel_skylib~1.0.3
)。レジストリ以外のオーバーライドがあるモジュールの場合は、version
の部分を文字列 override
に置き換えます。正規名の形式は、依存すべき API ではなく、随時変更される可能性があります。
モジュールを直接依存するリポジトリの見かけ上の名前は、bazel_dep
ディレクティブの repo_name
属性に特に指定されていない限り、デフォルトでそのモジュール名になります。つまり、モジュールは直接依存関係のみを見つけることができます。これにより、推移的な依存関係の変更による偶発的な破損を防ぐことができます。
モジュール拡張機能を使用すると、モジュールの公開スコープに追加のリポジトリを導入することもできます。