このページでは、Bazel の外部依存関係に関するよくある質問に回答します。
MODULE.bazel
MODULE.bazel が load
をサポートしていないのはなぜですか?
依存関係の解決中に、参照されているすべての外部依存関係の MODULE.bazel ファイルがレジストリから取得されます。この段階では、依存関係のソース アーカイブはまだ取得されていません。そのため、MODULE.bazel ファイルが別のファイルである場合、Bazel はソース アーカイブ全体を取得せずにそのファイルを実際に取得することはできません。load
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")
を指定して最新バージョンの依存関係を自動的に取得する機能を求めるユーザーが時折います。これは SemVer 範囲に関する質問と同様であり、回答も「いいえ」です。
おすすめの解決策は、自動化によってこの作業を行うことです。たとえば、Renovate は Bazel モジュールをサポートしています。
この質問をするユーザーは、ローカル開発中に迅速に反復処理する方法を求めている場合があります。サマリーの更新には、local_path_override
を使用します。
なぜ use_repo
がこれほど多く使われているのですか?
MODULE.bazel ファイルでモジュール拡張を使用すると、大きな use_repo
ディレクティブが使用されることがあります。たとえば、gazelle
の go_deps
拡張機能の一般的な使用方法は次のようになります。
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
のリポジトリ マッピングは何ですか?
リポジトリ名が明らかなラベル(先頭に @
が 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-world リポジトリ(メイン リポジトリを除く)には、Bzlmod-world リポジトリのみが表示されます。
- 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 は、curl などの他のプログラムで一般的に使用される http_proxy
環境変数と HTTPS_PROXY
環境変数を尊重します。
デュアルスタック IPv4/IPv6 設定で Bazel が IPv6 を優先するようにするにはどうすればよいですか?
IPv6 専用マシンでは、Bazel は変更なしで依存関係をダウンロードできます。ただし、デュアルスタック IPv4/IPv6 マシンでは、Bazel は Java と同じ規則に従い、有効になっている場合は IPv4 を優先します。IPv4 ネットワークが外部アドレスを解決または到達できない場合など、状況によっては、Network
unreachable
例外が発生してビルドが失敗することがあります。このような場合は、java.net.preferIPv6Addresses=true
システム プロパティを使用して、Bazel の動作をオーバーライドし、IPv6 を優先できます。詳細は以下のとおりです。
--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 がインストールされている必要はありません。
もう 1 つの理由は、Bazel は多くの場合、ローカルで実行される読み込みと分析を行うために、ダウンロードして解凍したアーカイブ内の BUILD ファイルを必要とするためです。
リポジトリ ルールをビルドルールとして再設計することで、この問題を解決する初期的なアイデアがあります。これにより、リモートで実行できるようになりますが、逆にアーキテクチャに関する新しい懸念が生じます(たとえば、query
コマンドでアクションを実行する必要があり、設計が複雑になる可能性があります)。
このトピックに関する以前のディスカッションについては、フェッチに Bazel を必要とするリポジトリをサポートする方法をご覧ください。