よくある質問

問題を報告する ソースを表示 ナイトリー · 8.2 · 8.1 · 8.0 · 7.6 · 7.5

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

MODULE.bazel

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

依存関係の解決中に、参照されているすべての外部依存関係の MODULE.bazel ファイルがレジストリから取得されます。この段階では、依存関係のソース アーカイブはまだ取得されていません。そのため、MODULE.bazel ファイルが別のファイルである場合、Bazel はソース アーカイブ全体を取得せずにそのファイルを実際に取得することはできません。loadMODULE.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 モジュール バージョンは SemVer のスーパーセットであるため、厳格な SemVer 環境で意味のあることが、Bazel モジュール バージョンに引き継がれない場合があります。

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

bazel_dep(name = "foo", version = "latest") を指定して最新バージョンの依存関係を自動的に取得する機能を求めるユーザーが時折います。これは SemVer 範囲に関する質問と同様であり、回答も「いいえ」です。

おすすめの解決策は、自動化によってこの作業を行うことです。たとえば、Renovate は Bazel モジュールをサポートしています。

この質問をするユーザーは、ローカル開発中に迅速に反復処理する方法を求めている場合があります。サマリーの更新には、local_path_override を使用します。

なぜ use_repo がこれほど多く使われているのですか?

MODULE.bazel ファイルでモジュール拡張を使用すると、大きな use_repo ディレクティブが使用されることがあります。たとえば、gazellego_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 です。

次に、コンテキスト リポジトリに応じて、移行ガイドの「リポジトリの公開設定」の表を使用して、実際の名前がどのリポジトリに解決されるかを確認します。

  • コンテキスト リポジトリがメイン リポジトリ(@@)の場合:
    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-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=trueCOURSIER_OPTS 環境変数に追加して、Coursier に JVM オプションを指定します。

リポジトリ ルールをリモート実行でリモートで実行できますか?

いいえ。少なくとも現時点ではそうではありません。リモート実行サービスを使用しているユーザーは、リポジトリ ルールが引き続きローカルで実行されていることに気付く場合があります。たとえば、http_archive は最初にローカルマシンにダウンロードされ(ローカル ダウンロード キャッシュがある場合は使用)、抽出されます。その後、各ソースファイルが入力ファイルとしてリモート実行サービスにアップロードされます。リモート実行サービスがそのアーカイブをダウンロードして解凍し、無駄なラウンドトリップを回避しないのはなぜか、と疑問に思うのは当然です。

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

もう 1 つの理由は、Bazel は多くの場合、ローカルで実行される読み込みと分析を行うために、ダウンロードして解凍したアーカイブ内の BUILD ファイルを必要とするためです。

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

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