よくある質問

このページでは、Bazel の外部依存関係に関するよくある質問とその回答を掲載しています。

MODULE.bazel

Bazel モジュールにバージョンを付けるにはどうすればよいですか?

ソース アーカイブ MODULE.bazelmodule ディレクティブで version を設定すると、 慎重に管理しないと、いくつかの欠点や意図しない副作用が生じる可能性があります。

したがって、ソース アーカイブ MODULE.bazel でバージョンを設定しないことをおすすめします。代わりに、レジストリ (例: Bazel Central Registry)に保存されている MODULE.bazel で設定します。これは、Bazel の外部依存関係の解決時のモジュール バージョンの実際の信頼できる情報源です(Bazel レジストリをご覧ください)。

通常、これは自動化されます。たとえば、rules-template サンプルルール リポジトリでは、bazel-contrib/publish-to-bcr publish.yaml GitHub アクションを使用して、 リリースを BCR に公開します。このアクションは、リリース バージョンを含むソース アーカイブ MODULE.bazel のパッチを生成します。このパッチはレジストリに保存され、Bazel の外部依存関係の解決時にモジュールがフェッチされるときに適用されます。

このようにすると、レジストリのリリース バージョンが リリース バージョンに正しく設定されるため、bazel_depsingle_version_overridemultiple_version_override が想定どおりに動作します。また、ソース アーカイブのバージョンがデフォルト値('')になるため、レジストリ以外のオーバーライドを行う際に発生する可能性のある 問題を回避できます。この値は常に正しく処理され(デフォルトのバージョン値であるため)、並べ替え時に 想定どおりに動作します(空の文字列は最も高いバージョンとして扱われます)。

互換性レベルとは何ですか?

compatibility_level の使用は中止してください。

compatibility_level を大きくすると、エンドユーザーが解決するのが難しいバージョンの競合が発生します。そのため、Bazel 8.6.0 と 9.1.0 以降では、 compatibility_levelmax_compatibility_level はどちらも no-op です。

モジュールのメンテナーが大きな破壊的変更を導入する場合は、ビルドの失敗時に明確なエラー メッセージと実行可能な移行パスを提供する必要があります。

以前のドキュメント:

Bazel モジュールの compatibility_level は、後方互換性のない(破壊的な)変更を導入する commit と同じ commit でインクリメントする必要があります。

ただし、解決された依存関係 グラフに、互換性レベルが異なる同じモジュールのバージョンが存在する場合、Bazel はエラーをスローする可能性があります。これは、たとえば、2 つのモジュールが、互換性レベルが異なる 3 つ目のモジュールのバージョンに依存している場合に発生する可能性があります。

したがって、compatibility_level を頻繁にインクリメントすると、非常に中断される可能性があるため、おすすめしません。この状況を回避するには、破壊的変更がほとんどのユースケースに影響し、移行や回避が容易でない場合にのみ、compatibility_level をインクリメントする必要があります。

MODULE.bazel が load をサポートしていないのはなぜですか?

依存関係の解決中に、参照されているすべての外部依存関係の MODULE.bazel ファイルがレジストリからフェッチされます。この段階では、依存関係のソース アーカイブはまだフェッチされていません。そのため、MODULE.bazel ファイルが別のファイルを load する場合、Bazel はソース アーカイブ全体をフェッチせずにそのファイルを実際にフェッチする方法がありません。MODULE.bazel ファイル自体は、レジストリで直接ホストされているため、特殊です。

MODULE.bazel で load をリクエストするユーザーが一般に関心を持っているユースケースがいくつかあり、load を使用せずに解決できます。

  • MODULE.bazel に記載されているバージョンが、他の場所に保存されているビルド メタデータ(.bzl ファイルなど)と一致していることを確認する: これは、BUILD ファイルからロードされた .bzl ファイルで native.module_version メソッド を使用することで実現できます。
  • 非常に大きな MODULE.bazel ファイルを管理しやすいセクションに分割する( 特にモノレポの場合): ルートモジュールは include ディレクティブを使用して、その MODULE.bazel ファイルを複数のセグメントに分割できます。MODULE.bazel ファイルで load を許可しないのと同じ理由で、ルート以外のモジュールでは include を使用できません。
  • 古い WORKSPACE システムのユーザーは、リポジトリを宣言し、そのリポジトリからすぐに load して複雑なロジックを実行することを覚えているかもしれません。この 機能はモジュール拡張機能に置き換えられました。

bazel_dep に SemVer 範囲を指定できますか?

いいえ。npmCargo などの他のパッケージ管理システムは、バージョン範囲をサポートしています(暗黙的または明示的)。これには多くの場合、制約ソルバーが必要となり(ユーザーにとって出力の予測が難しくなります)、ロックファイルがないとバージョン解決を再現できなくなります。

Bazel は代わりに 最小バージョン選択 を Go のように使用します。これにより、出力の予測が容易になり、 再現性が保証されます。これは、Bazel の設計目標に合致するトレードオフです。

さらに、Bazel モジュール バージョンは a superset of SemVer であるため、厳密な SemVer 環境で意味のあるものが、Bazel モジュール バージョンに常に引き継がれるとは限りません。

bazel_dep の最新バージョンを自動的に取得できますか?

一部のユーザーは、bazel_dep(name = "foo", version = "latest") を指定して、dep の最新バージョンを自動的に取得する機能をリクエストすることがあります。これは SemVer 範囲に関する質問と似ていますが、答えは 「いいえ」です。

この場合、自動化で処理することをおすすめします。たとえば、Renovate は Bazel モジュールをサポートしています。

この質問をするユーザーは、ローカル開発中に迅速に反復処理する方法を探している場合があります。これは、 local_path_override を使用することで実現できます。

これらすべての use_repo はなぜですか?

MODULE.bazel ファイルのモジュール拡張機能の使用には、大きな use_repo ディレクティブが付属している場合があります。たとえば、 go_deps 拡張機能からgazelleの一般的な使用方法は次のようになります。

go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
    go_deps,
    "com_github_gogo_protobuf",
    "com_github_golang_mock",
    "com_github_golang_protobuf",
    "org_golang_x_net",
    ...  # potentially dozens of lines...
)

この長い use_repo ディレクティブは、参照先の go.mod ファイルにすでに情報が含まれているため、冗長に見えるかもしれません。

Bazel がこの use_repo ディレクティブを必要とするのは、モジュール拡張機能を遅延実行するためです。つまり、モジュール拡張機能は、その結果が確認された場合にのみ実行されます。モジュール拡張機能の「出力」はリポジトリ定義であるため、定義するリポジトリがリクエストされた場合にのみモジュール拡張機能を実行します(たとえば、上記の例では、ターゲット @org_golang_x_net//:foo がビルドされた場合)。ただし、モジュール拡張機能が定義するリポジトリは、実行するまでわかりません。ここで use_repo ディレクティブが登場します。ユーザーは、拡張機能が生成するリポジトリを Bazel に通知できます。Bazel は、これらの特定のリポジトリが使用されている場合にのみ拡張機能を実行します。

この use_repo ディレクティブの維持を容易にするため、モジュール拡張機能は extension_metadata オブジェクトを実装関数から返すことができます。ユーザーは bazel mod tidy コマンドを実行して、これらのモジュール拡張機能の use_repo ディレクティブを更新できます。

Bzlmod の移行

MODULE.bazel と WORKSPACE のどちらが先に評価されますか?

--enable_bzlmod--enable_workspace の両方が設定されている場合、どちらのシステムが最初に参照されるか疑問に思うのは当然です。簡単な答えは、MODULE.bazel(Bzlmod)が最初に評価されるということです。

詳細な回答は、「どちらが最初に評価されるか」は適切な質問ではないということです。むしろ、 正規名 @@foo のリポジトリのコンテキストで、見かけの リポジトリ名 @bar が解決されるか、`@@base` のリポジトリ マッピングは何かを尋ねるのが適切です。または、@@base のリポジトリ マッピングは何ですか?

見かけのリポジトリ名(先頭に 1 つの @)が付いたラベルは、解決元のコンテキストに応じて異なるものを参照できます。ラベル @bar//:baz が実際に指しているものがわからない場合は、まずコンテキスト リポジトリを特定する必要があります。たとえば、ラベルがリポジトリ @@foo にある BUILD ファイルにある場合、コンテキスト リポジトリは @@foo です。

次に、コンテキスト リポジトリに応じて、移行ガイドの"リポジトリ の可視性"表を使用して、見かけの名前が実際に解決されるリポジトリを確認できます。

  • コンテキスト リポジトリがメイン リポジトリ(@@)の場合:
    1. bar がルートモジュールの MODULE.bazel ファイル( bazel_depuse_repomoduleuse_repo_rule のいずれか)によって導入された見かけのリポジトリ名である場合、@bar はその MODULE.bazel ファイルが主張する内容に解決されます。
    2. それ以外の場合、bar が WORKSPACE で定義されたリポジトリ(つまり、正規名が @@bar)である場合、@bar@@bar に解決されます。
    3. それ以外の場合、@bar@@[unknown repo 'bar' requested from @@]のようなものに解決され、最終的に エラーが発生します。
  • コンテキスト リポジトリが Bzlmod ワールド リポジトリ(つまり、ルート以外の Bazel モジュールに対応するか、モジュール拡張機能によって生成される)の場合、他の Bzlmod ワールド リポジトリのみが表示され、WORKSPACE ワールド リポジトリは表示されません。
    • 特に、これには、ルートモジュールの non_module_deps のようなモジュール拡張機能で導入されたリポジトリや、ルートモジュールの use_repo_rule のインスタンス化が含まれます。
  • コンテキスト リポジトリが WORKSPACE で定義されている場合:
    1. まず、コンテキスト リポジトリ定義に特別な repo_mapping 属性があるかどうかを確認します。ある場合は、最初にマッピングを確認します( repo_mapping = {"@bar": "@baz"} で定義されたリポジトリの場合、以下で @baz を確認します)。
    2. bar がルートモジュールの MODULE.bazel ファイルによって導入された見かけのリポジトリ名である場合、@bar はその MODULE.bazel ファイル が主張する内容に解決されます。(これは、メイン リポジトリの場合の項目 1 と同じです)。
    3. それ以外の場合、@bar@@bar に解決されます。これは、WORKSPACE で定義されたリポジトリ bar を指す可能性が最も高くなります。このようなリポジトリが定義されていない場合、Bazel はエラーをスローします。

より簡潔なバージョン:

  • Bzlmod ワールド リポジトリ(メイン リポジトリを除く)には、Bzlmod ワールド リポジトリのみが表示されます。
  • WORKSPACE ワールド リポジトリ(メイン リポジトリを含む)は、最初に Bzlmod ワールドのルートモジュールで定義されている内容を確認し、WORKSPACE ワールド リポジトリにフォールバックします。

なお、Bazel コマンドラインのラベル(Starlark フラグ、ラベル型のフラグ値、ビルド/テスト ターゲット パターンなど)は、コンテキスト リポジトリとしてメイン リポジトリを持つものとして扱われます。

その他

オフライン ビルドを準備して実行するにはどうすればよいですか?

bazel fetch コマンドを使用して、リポジトリをプリフェッチします。--repo フラグ (bazel fetch --repo @foo など)を使用すると、リポジトリ @foo(メイン リポジトリの コンテキストで解決されます。上記の質問 を参照)のみをフェッチできます。また、ターゲット パターン(bazel fetch @foo//:bar など)を使用すると、 @foo//:bar のすべての推移的依存関係をフェッチできます(これは bazel build --nobuild @foo//:bar と同じです)。

ビルド中にフェッチが行われないようにするには、--nofetch を使用します。正確には、これにより、ローカル以外のリポジトリ ルールを実行しようとすると失敗します。

リポジトリをフェッチしてローカルでテストするために変更する場合は、 bazel vendor コマンドの使用を検討してください。

HTTP プロキシを使用するにはどうすればよいですか?

Bazel は、http_proxy 環境変数と HTTPS_PROXY 環境変数を、 curl などの他のプログラムで一般的に 使用されるものとして尊重します。

デュアルスタック IPv4/IPv6 設定で Bazel が IPv6 を優先するようにするにはどうすればよいですか?

IPv6 専用マシンでは、Bazel は変更なしで依存関係をダウンロードできます。ただし、デュアルスタック IPv4/IPv6 マシンでは、Bazel は Java と同じ規則に従い、有効になっている場合は IPv4 を優先します。IPv4 ネットワークが外部アドレスを解決または到達できない場合など、状況によっては、Network unreachable 例外が発生してビルドが失敗することがあります。このような場合は、 Bazel の動作をオーバーライドして IPv6 を優先できます。これには、 java.net.preferIPv6Addresses=true システム プロパティを使用します。 詳細:

リポジトリ ルールは、リモート実行を使用してリモートで実行できますか?

いいえ。少なくとも、まだできません。リモート実行サービスを使用してビルドを高速化しているユーザーは、リポジトリ ルールがローカルで実行されていることに気づくかもしれません。たとえば、http_archive は最初にローカル マシンにダウンロードされ(該当する場合はローカル ダウンロード キャッシュを使用)、抽出されます。その後、各ソースファイルが入力ファイルとしてリモート実行サービスにアップロードされます。リモート実行サービスがそのアーカイブをダウンロードして抽出するだけで、無駄なラウンドトリップを回避できないのはなぜでしょうか。

理由の一つは、リポジトリ ルール(およびモジュール拡張機能)が Bazel 自体によって実行される「スクリプト」に似ていることです。リモート エグゼキュータに Bazel がインストールされているとは限りません。

もう一つの理由は、Bazel がダウンロードして抽出したアーカイブ内の BUILD ファイルを読み込みと分析に使用する必要があるためです。これらはローカルで実行されます。

リポジトリ ルールをビルドルールとして再考することでこの問題を解決する予備的なアイデアがあります。これにより、リモートで実行できるようになりますが、逆に新しいアーキテクチャ上の懸念が生じます(たとえば、query コマンドはアクションを実行する必要があるため、設計が複雑になります)。

このトピックに関する以前のディスカッションについては、フェッチに Bazel が必要なリポジトリをサポートする方法 をご覧ください。