Bazel モジュールは、複数のバージョンを持つことができる Bazel プロジェクトです。各バージョンでは、依存する他のモジュールに関するメタデータが公開されます。これは、Maven アーティファクト、npm パッケージ、Go モジュール、Cargo crate など、他の依存関係管理システムでおなじみの概念に似ています。
モジュールのリポジトリのルートに 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 ですが、さまざまなスキームを使用した有名なプロジェクトもあります。たとえば、20210324.2
のように日付ベースのバージョンを持つ Abseil など、さまざまなスキームを使用しています。
このため、Bzlmod では SemVer 仕様のより緩和されたバージョンを採用しています。次の点が異なります。
- SemVer では、バージョンの「release」部分が 3 つのセグメント
MAJOR.MINOR.PATCH
で構成されている必要があります。Bazel では、この要件は緩和され、任意の数のセグメントが許容されます。 - SemVer では、「release」部分の各セグメントは数字のみで構成する必要があります。 Bazel では、文字も使用できるように緩和されており、比較セマンティクスは「プレリリース」部分の「識別子」と一致しています。
- また、メジャー、マイナー、パッチ バージョンの増加のセマンティクスも適用されません。ただし、下位互換性を示す方法の詳細については、互換性レベルをご覧ください。
有効な SemVer バージョンはすべて、有効な Bazel モジュール バージョンです。また、SemVer の 2 つのバージョン 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 を使用すると、忠実度と再現性のあるバージョン選択プロセスが作成されます。
ヤンク バージョン
レジストリでは、特定のバージョンを回避する必要がある場合(セキュリティの脆弱性がある場合など)に、特定のバージョンを「ヤンク」として宣言できます。モジュールのアンクされたバージョンを選択すると、Bazel がエラーをスローします。このエラーを修正するには、ヤンクされていない新しいバージョンにアップグレードするか、--allow_yanked_versions
フラグを使用して、ヤンクされたバージョンを明示的に許可します。
互換性レベル
Go では、MVS の下位互換性に関する想定は機能します。モジュールの下位互換性のないバージョンを別のモジュールとして扱うためです。これは、SemVer の観点からは、A 1.x
と A 2.x
は別個のモジュールとみなされ、解決済み依存関係グラフ内に共存できます。これは Go のパッケージパスにメジャー バージョンをエンコードすることによって可能になるため、コンパイル時またはリンク時の競合は発生しません。
ただし、Bazel ではこのような保証はできないため、下位互換性のないバージョンを検出するには「メジャー バージョン」番号が必要です。この番号は互換性レベルと呼ばれ、module()
ディレクティブの各モジュール バージョンによって指定されます。この情報により、解決された依存関係グラフ内に、互換性レベルの異なる同じモジュールのバージョンが存在することが検出されると、Bazel がエラーをスローすることがあります。
オーバーライド
Bazel モジュール解決の動作を変更するには、MODULE.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
が存在しないため、1.9
と2.0
を許可する複数バージョンのオーバーライドはエラーになります。
また、単一バージョンのオーバーライドと同様に、ユーザーは registry
属性を使用してレジストリをオーバーライドできます。
レジストリ以外のオーバーライド
レジストリ以外のオーバーライドでは、モジュールがバージョン解決から完全に削除されます。Bazel は、これらの MODULE.bazel
ファイルをレジストリからリクエストするのではなく、リポジトリ自体にリクエストします。
Bazel では、次のレジストリ以外のオーバーライドがサポートされています。
Bazel モジュールではないリポジトリを定義する
bazel_dep
を使用すると、他の Bazel モジュールを表すリポジトリを定義できます。Bazel モジュールを表していないリポジトリの定義が必要になる場合があります。たとえば、データとして読み取れるプレーン JSON ファイルを含むリポジトリなどです。
この場合、use_repo_rule
ディレクティブを使用して、Repo ルールを呼び出すことでリポジトリを直接定義できます。このリポジトリは、それで定義されているモジュールにのみ表示されます。
内部的には、モジュール拡張機能と同じメカニズムを使用して実装されるため、リポジトリをより柔軟に定義できます。
リポジトリ名と厳格な依存関係
モジュールの直接の依存先をバックアップするリポジトリの見かけ上の名前は、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
の runfiles ライブラリのいずれか(ルールセット rules_foo
の場合は @rules_foo//foo/runfiles
)を使用します。* IDE や言語サーバーなどの外部ツールで Bazel を操作する場合は、bazel mod dump_repo_mapping
コマンドを使用して、特定のリポジトリ セットの見かけ上の名前から正規名へのマッピングを取得します。
モジュール拡張では、モジュールの可視スコープに追加のリポジトリを導入することもできます。