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 など、異なるスキームを使用する著名なプロジェクトもあります。Abseil のバージョンは日付ベース(20210324.2
など)です。
このため、Bzlmod では SemVer 仕様のより緩いバージョンを採用しています。違いは次のとおりです。
- SemVer では、バージョンの「リリース」部分は 3 つのセグメント(
MAJOR.MINOR.PATCH
)で構成されることが規定されています。Bazel では、この要件が緩和され、任意の数のセグメントが許可されます。 - SemVer では、「リリース」部分の各セグメントは数字のみで構成されている必要があります。Bazel では、文字も許可するように緩和されており、比較セマンティクスは「プレリリース」部分の「識別子」と一致します。
- また、メジャー バージョン、マイナー バージョン、パッチ バージョンの増加のセマンティクスは適用されません。ただし、下位互換性の表記方法については、互換性レベルをご覧ください。
有効な SemVer バージョンは、有効な Bazel モジュール バージョンです。また、2 つの SemVer バージョン a
と b
は、Bazel モジュール バージョンとして比較した場合に同じになる場合にのみ a < b
を比較します。
バージョンの選択
バージョン管理された依存関係管理の分野でよくあるダイアモンド依存関係の問題について考えてみましょう。次の依存関係グラフがあるとします。
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 はエラーをスローします。このエラーを修正するには、新しい非 yanked バージョンにアップグレードするか、--allow_yanked_versions
フラグを使用して yanked バージョンを明示的に許可します。
互換性レベル
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 は他のバージョンのモジュールを同じ互換性レベルで最も近い上位の許可されたバージョンにアップグレードします。同じ互換性レベルで許可されているより高いバージョンが存在しない場合、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.5
と2.0
を許可する複数バージョンのオーバーライドは、1.7
にアップグレードできる同じ互換性レベルのより高いバージョンがないため、エラーになります。1.9
と2.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
。
* runfile を検索するときは、$(rlocationpath ...)
または @bazel_tools//tools/{bash,cpp,java}/runfiles
の runfile ライブラリのいずれかを使用します。ルールセット rules_foo
の場合は、@rules_foo//foo/runfiles
を使用します。* IDE や言語サーバーなどの外部ツールから Bazel を操作する場合は、bazel mod dump_repo_mapping
コマンドを使用して、特定のリポジトリのセットの表示名から正規名へのマッピングを取得します。
モジュール拡張機能は、モジュールの可視スコープに追加のリポジトリを導入することもできます。