このページでは、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 が想定どおりに機能します。また、ソース
アーカイブのバージョンがデフォルト値('')になるため、レジストリ以外のオーバーライドを行う際に発生する可能性のある
問題を回避できます。この値は常に正しく処理され(デフォルトのバージョン値であるため)、並べ替え時に
想定どおりに動作します(空の文字列は最も高いバージョンとして扱われます)。
互換性レベルをインクリメントするタイミング
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 モジュールのバージョンは 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...
)
この情報は参照先の go.mod ファイルにすでに含まれているため、長い use_repo ディレクティブは冗長に見えるかもしれません。
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 のリポジトリ マッピングは何ですか?
見かけのリポジトリ名(先頭に 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を使用している場合は、-Djava.net.preferIPv6Addresses=trueをCOURSIER_OPTS環境 変数に追加して、Coursier の JVM オプションを指定します。
リポジトリルールはリモート実行でリモートで実行できますか?
いいえ。少なくとも現時点ではできません。リモート実行サービスを使用してビルドを高速化しているユーザーは、リポジトリルールがローカルで実行されていることに気づくかもしれません。たとえば、http_archive は最初にローカルマシンにダウンロードされ(該当する場合はローカル ダウンロード キャッシュを使用)、抽出されます。その後、各ソースファイルが入力ファイルとしてリモート実行サービスにアップロードされます。リモート実行サービスがそのアーカイブをダウンロードして抽出するだけで、無駄なラウンドトリップを回避できないのはなぜでしょうか。
理由の一つは、リポジトリルール(およびモジュール拡張機能)が Bazel 自体によって実行される「スクリプト」に似ているためです。リモート エグゼキュータに Bazel がインストールされているとは限りません。
もう一つの理由は、Bazel がダウンロードして抽出したアーカイブ内の BUILD ファイルを、ロードと分析を行うために必要とすることがよくあるためです。これらはローカルで実行されます。
この問題を解決するために、リポジトリルールをビルドルールとして再考するという予備的なアイデアがあります。これにより、リモートで実行できるようになりますが、逆に新しいアーキテクチャ上の懸念が生じます(たとえば、query
コマンドでアクションを実行する必要が生じ、設計が複雑になる可能性があります)。
このトピックに関する以前のディスカッションについては、フェッチに Bazel が必要なリポジトリをサポートする方法 をご覧ください。