Bazel モジュール

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

モジュールには、リポジトリのルートに 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 ファイルを読み取り、依存関係グラフ全体が検出されるまで、依存関係の MODULE.bazel ファイルを Bazel レジストリ に繰り返しリクエストします。

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

バージョン形式

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

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

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

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

最後に、モジュールのバージョニングの詳細については、MODULE.bazel よくある質問をご覧ください。

バージョンの選択

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

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

どのバージョンの D を使用する必要がありますか?この問題を解決するために、Bazel は 最小バージョン選択 (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 は SemVer の緩いバージョンに従っているため、このような 保証を提供できません。

したがって、Bazel は SemVer のメジャー バージョン番号に相当するものを必要として、下位互換性のない(破壊的な)バージョンを検出します。この番号は 互換性レベルと呼ばれ、 module() ディレクティブで各モジュール バージョンによって指定されます。この情報により、 Bazel は、解決された依存関係グラフに互換性レベルが異なる 同じモジュールのバージョンが存在する場合にエラーをスローできます。

最後に、互換性レベルを上げると、ユーザーに影響が生じる可能性があります。 互換性レベルを上げるタイミングと方法の詳細については、MODULE.bazel FAQをご覧ください。

オーバーライド

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

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

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

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

  • version 属性を使用すると、依存関係グラフでどのバージョンの依存関係がリクエストされているかに関係なく、依存関係を特定のバージョンに固定できます。
  • registry 属性を使用すると、通常の registry selection プロセスに従うのではなく、この依存関係を a specific registry から取得するように強制できます。
  • 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.bazel にバージョン値を設定すると、デメリットが生じる可能性があります。詳細については、 よくある質問をご覧ください。MODULE.bazel

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

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

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

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

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

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

モジュールをサポートするリポジトリの正規名は、依存関係グラフ全体にモジュールの複数のバージョンがあるかどうかに応じて、module_name+versionbazel_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 コマンドを使用して、特定のリポジトリの表示名から正規名へのマッピングを取得します。

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