このページでは、Bazel の外部依存関係に関するよくある質問とその回答を掲載しています。
MODULE.bazel
Bazel モジュールにバージョンを付けるにはどうすればよいですか?
ソース アーカイブ
MODULE.bazel の module ディレクティブで version を設定すると、
慎重に管理しないと、いくつかの欠点や意図しない副作用が生じる可能性があります。
重複: モジュールの新しいバージョンをリリースするには、通常、
MODULE.bazelのバージョンをインクリメントしてリリースにタグを付けるという 2 つの別々の手順が必要で、同期が取れなくなる可能性があります。自動化によってこのリスクを軽減できますが、完全に回避する方が簡単で安全です。不整合: レジストリ以外のオーバーライドを使用して特定の commit でモジュールをオーバーライドすると、誤ったバージョンが表示されます。たとえば、ソース アーカイブの
MODULE.bazelでversion = "0.3.0"が設定されていても、 そのリリース以降に追加の commit が行われている場合、ユーザーが それらの commit のいずれかでオーバーライドしても、0.3.0が表示されます。実際には、バージョンはリリースよりも先行していることを反映する必要があります(例:0.3.1-rc1)。レジストリ以外のオーバーライドの問題: プレースホルダ値を使用すると、ユーザーがレジストリ以外のオーバーライドでモジュールをオーバーライドするときに問題が発生する可能性があります。たとえば、
0.0.0は最も高いバージョンとして並べ替えられません。これは通常、ユーザーがレジストリ以外のオーバーライドを行うときに期待する動作です。
したがって、ソース アーカイブ MODULE.bazel でバージョンを設定しないことをおすすめします。代わりに、レジストリ
(例: Bazel Central Registry)に保存されている MODULE.bazel で設定します。これは、Bazel の外部依存関係の解決時のモジュール バージョンの実際の信頼できる情報源です(Bazel
レジストリをご覧ください)。
通常、これは自動化されます。たとえば、rules-template サンプルルール
リポジトリでは、bazel-contrib/publish-to-bcr publish.yaml GitHub アクションを使用して、
リリースを BCR に公開します。このアクションは、リリース バージョンを含むソース
アーカイブ MODULE.bazel のパッチを生成します。このパッチはレジストリに保存され、Bazel の外部依存関係の解決時にモジュールがフェッチされるときに適用されます。
このようにすると、レジストリのリリース バージョンが
リリース バージョンに正しく設定されるため、bazel_dep、single_version_override、
multiple_version_override が想定どおりに動作します。また、ソース
アーカイブのバージョンがデフォルト値('')になるため、レジストリ以外のオーバーライドを行う際に発生する可能性のある
問題を回避できます。この値は常に正しく処理され(デフォルトのバージョン値であるため)、並べ替え時に
想定どおりに動作します(空の文字列は最も高いバージョンとして扱われます)。
互換性レベルとは何ですか?
compatibility_level の使用は中止してください。
compatibility_level を大きくすると、エンドユーザーが解決するのが難しいバージョンの競合が発生します。そのため、Bazel 8.6.0 と 9.1.0 以降では、
compatibility_level と max_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 範囲を指定できますか?
いいえ。npm や Cargo などの他のパッケージ管理システムは、バージョン範囲をサポートしています(暗黙的または明示的)。これには多くの場合、制約ソルバーが必要となり(ユーザーにとって出力の予測が難しくなります)、ロックファイルがないとバージョン解決を再現できなくなります。
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 です。
次に、コンテキスト リポジトリに応じて、移行ガイドの"リポジトリ の可視性"表を使用して、見かけの名前が実際に解決されるリポジトリを確認できます。
- コンテキスト リポジトリがメイン リポジトリ(
@@)の場合:barがルートモジュールの MODULE.bazel ファイル(bazel_dep、use_repo、module、use_repo_ruleのいずれか)によって導入された見かけのリポジトリ名である場合、@barはその MODULE.bazel ファイルが主張する内容に解決されます。- それ以外の場合、
barが WORKSPACE で定義されたリポジトリ(つまり、正規名が@@bar)である場合、@barは@@barに解決されます。 - それ以外の場合、
@barは@@[unknown repo 'bar' requested from @@]のようなものに解決され、最終的に エラーが発生します。
- コンテキスト リポジトリが Bzlmod ワールド リポジトリ(つまり、ルート以外の Bazel モジュールに対応するか、モジュール拡張機能によって生成される)の場合、他の Bzlmod ワールド リポジトリのみが表示され、WORKSPACE ワールド リポジトリは表示されません。
- 特に、これには、ルートモジュールの
non_module_depsのようなモジュール拡張機能で導入されたリポジトリや、ルートモジュールのuse_repo_ruleのインスタンス化が含まれます。
- 特に、これには、ルートモジュールの
- コンテキスト リポジトリが WORKSPACE で定義されている場合:
- まず、コンテキスト リポジトリ定義に特別な
repo_mapping属性があるかどうかを確認します。ある場合は、最初にマッピングを確認します(repo_mapping = {"@bar": "@baz"}で定義されたリポジトリの場合、以下で@bazを確認します)。 barがルートモジュールの MODULE.bazel ファイルによって導入された見かけのリポジトリ名である場合、@barはその MODULE.bazel ファイル が主張する内容に解決されます。(これは、メイン リポジトリの場合の項目 1 と同じです)。- それ以外の場合、
@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 システム
プロパティを使用します。
詳細:
--host_jvm_args=-Djava.net.preferIPv6Addresses=true起動 オプションを使用します。たとえば、 次の行を.bazelrcファイルに追加します。startup --host_jvm_args=-Djava.net.preferIPv6Addresses=trueインターネットに接続する必要がある Java ビルド ターゲット(統合テストなど)を実行する場合は、
--jvmopt=-Djava.net.preferIPv6Addresses=trueツール フラグを使用します。たとえば、.bazelrcファイルに次のように記述します。build --jvmopt=-Djava.net.preferIPv6Addresses依存関係のバージョン解決に
rules_jvm_externalを使用している場合は、Coursierの JVM オプションを指定するために、-Djava.net.preferIPv6Addresses=trueをCOURSIER_OPTS環境 変数に追加します。
リポジトリ ルールは、リモート実行を使用してリモートで実行できますか?
いいえ。少なくとも、まだできません。リモート実行サービスを使用してビルドを高速化しているユーザーは、リポジトリ ルールがローカルで実行されていることに気づくかもしれません。たとえば、http_archive は最初にローカル マシンにダウンロードされ(該当する場合はローカル ダウンロード キャッシュを使用)、抽出されます。その後、各ソースファイルが入力ファイルとしてリモート実行サービスにアップロードされます。リモート実行サービスがそのアーカイブをダウンロードして抽出するだけで、無駄なラウンドトリップを回避できないのはなぜでしょうか。
理由の一つは、リポジトリ ルール(およびモジュール拡張機能)が Bazel 自体によって実行される「スクリプト」に似ていることです。リモート エグゼキュータに Bazel がインストールされているとは限りません。
もう一つの理由は、Bazel がダウンロードして抽出したアーカイブ内の BUILD ファイルを読み込みと分析に使用する必要があるためです。これらはローカルで実行されます。
リポジトリ ルールをビルドルールとして再考することでこの問題を解決する予備的なアイデアがあります。これにより、リモートで実行できるようになりますが、逆に新しいアーキテクチャ上の懸念が生じます(たとえば、query コマンドはアクションを実行する必要があるため、設計が複雑になります)。
このトピックに関する以前のディスカッションについては、フェッチに Bazel が必要なリポジトリをサポートする方法 をご覧ください。