外部依存関係の操作

Bazel は、他のプロジェクトのターゲットに依存することがあります。このような他のプロジェクトからの依存関係は、外部依存関係と呼ばれます。

ワークスペース ディレクトリWORKSPACE ファイル(または WORKSPACE.bazel ファイル)で、他のプロジェクトのソースを取得する方法を Bazel に指示できます。これらの他のプロジェクトには、独自のターゲットを持つ 1 つ以上の BUILD ファイルを含めることができます。メイン プロジェクト内の BUILD ファイルは、WORKSPACE ファイルにある名前を使用することで、これらの外部ターゲットに依存できます。

たとえば、システムに 2 つのプロジェクトがあるとします。

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

project1/home/user/project2/BUILD で定義されているターゲット :foo に依存する必要がある場合は、project2 という名前のリポジトリが /home/user/project2 にあることを指定できます。この場合、/home/user/project1/BUILD 内のターゲットは @project2//:foo に依存することになります。

WORKSPACE ファイルを使用すると、ファイル システムの他の部分にあるターゲットやインターネットからダウンロードしたターゲットに依存できます。BUILD ファイルと同じ構文を使用しますが、リポジトリ ルールと呼ばれる別のルールセットを使用できます(ワークスペース ルールとも呼ばれます)。Bazel には、いくつかの組み込みのリポジトリ ルールと、一連の埋め込みの Starlark リポジトリ ルールが用意されています。カスタム リポジトリ ルールを作成して、より複雑な動作を行うこともできます。

サポートされている外部依存関係のタイプ

いくつかの基本的なタイプの外部依存関係を使用できます。

他の Bazel プロジェクトに依存

2 つ目の Bazel プロジェクトのターゲットを使用する場合は、local_repositorygit_repositoryhttp_archive のいずれかを使用して、ローカル ファイルシステムからシンボリック リンクするか、Git リポジトリを参照するか、ダウンロードします。

たとえば、作業しているプロジェクト my-project/ が、同僚のプロジェクト coworkers-project/ のターゲットに依存するとします。どちらのプロジェクトも Bazel を使用するため、同僚のプロジェクトを外部依存関係として追加し、同僚が自分の BUILD ファイルから定義したターゲットを使用できます。my_project/WORKSPACE に次のように追加します。

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

同僚がターゲット //foo:bar を使用している場合、プロジェクトでそれを @coworkers_project//foo:bar として参照できます。外部プロジェクト名は、有効なワークスペース名にする必要があります。

Bazel 以外のプロジェクトに依存

new_local_repository などの new_ で始まるルールを使用すると、Bazel を使用しないプロジェクトからターゲットを作成できます。

たとえば、作業しているプロジェクト my-project/ が同僚のプロジェクト coworkers-project/ に依存するとします。同僚のプロジェクトでは make を使用してビルドしていますが、生成された .so ファイルの 1 つに依存したいと考えています。そのためには、次のコードを my_project/WORKSPACE に追加します。

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file は、既存のプロジェクトにオーバーレイする BUILD ファイルを指定します。次に例を示します。

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

これで、プロジェクトの BUILD ファイルの @coworkers_project//:some-lib に依存できるようになります。

外部パッケージに依存

Maven アーティファクトとリポジトリ

ルールセット rules_jvm_external を使用して、Maven リポジトリからアーティファクトをダウンロードして、Java の依存関係として使用できるようにします。

依存関係をフェッチする

デフォルトでは、外部依存関係は bazel build 中に必要に応じて取得されます。特定のターゲット セットに必要な依存関係をプリフェッチする場合は、bazel fetch を使用します。すべての外部依存関係を無条件にフェッチするには、bazel sync を使用します。フェッチされたリポジトリは出力ベースに格納されるため、取得はワークスペースごとに行われます。

依存関係のシャドーイング

可能な限り、プロジェクトには単一のバージョン ポリシーを設定することをおすすめします。これは、コンパイルして最終的なバイナリになる依存関係に必要です。ただし、これが当てはまらない場合は、依存関係をシャドーイングできます。次のシナリオを考えてみます。

myproject/WorkSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/Workspace

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

依存関係 AB はどちらも testrunner に依存していますが、異なるバージョンの testrunner に依存しています。これらのテストランナーが myproject 内で平和に共存しない理由はありませんが、同じ名前のテストランナーは競合します。両方の依存関係を宣言するには、myproject/WORKSPACE を更新します。

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

このメカニズムは、ダイヤモンドの接合にも使用できます。たとえば、AB に同じ依存関係があり、名前が異なると、これらの依存関係を myproject/WORKSPACE で結合できます。

コマンドラインからリポジトリをオーバーライドする

コマンドラインからローカル リポジトリを使用して、宣言されたリポジトリをオーバーライドするには、--override_repository フラグを使用します。このフラグを使用すると、ソースコードを変更せずに外部リポジトリの内容を変更できます。

たとえば、@foo をローカル ディレクトリ /path/to/local/foo にオーバーライドするには、--override_repository=foo=/path/to/local/foo フラグを渡します。

ユースケースの一部を示します。

  • 問題のデバッグ。たとえば、http_archive リポジトリをローカル ディレクトリにオーバーライドすると、変更が容易になります。
  • ベンダー化。ネットワーク呼び出しできない環境では、代わりにローカル ディレクトリを指すようにネットワーク ベースのリポジトリ ルールをオーバーライドします。

プロキシの使用

Bazel は、HTTPS_PROXY 環境変数と HTTP_PROXY 環境変数からプロキシ アドレスを取得し、これを使用して HTTP/HTTPS ファイルをダウンロードします(指定されている場合)。

IPv6 のサポート

IPv6 専用マシンでは、Bazel を変更せずに依存関係をダウンロードできます。ただし、デュアルスタックの IPv4/IPv6 マシンでは、Bazel は Java と同じ規則に従います。IPv4 が有効になっている場合は、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 を使用している場合は、COURSIER_OPTS 環境変数に -Djava.net.preferIPv6Addresses=true を追加して、Coursier 用の JVM オプションを指定することもできます。

推移的依存関係

Bazel は、WORKSPACE ファイルにリストされている依存関係のみを読み取ります。プロジェクト(A)が、WORKSPACE ファイルに 3 つ目のプロジェクト(C)への依存関係を記述している別のプロジェクト(B)に依存している場合は、BC の両方をプロジェクトの WORKSPACE ファイルに追加する必要があります。この要件は WORKSPACE ファイルサイズを膨張させることができますが、あるライブラリにバージョン 1.0 の C が含まれ、別のライブラリに 2.0 の C が含まれる可能性が制限されます。

外部依存関係のキャッシュ保存

デフォルトでは、Bazel は外部依存関係の定義が変更された場合にのみ、外部依存関係を再ダウンロードします。定義内で参照されるファイル(パッチや BUILD ファイルなど)の変更も bazel で考慮されます。

強制的に再ダウンロードするには、bazel sync を使用します。

レイアウト

外部依存関係はすべて、出力ベースのサブディレクトリ external の下にあるディレクトリにダウンロードされます。ローカル リポジトリの場合は、新しいディレクトリを作成する代わりに、シンボリック リンクが作成されます。以下を実行すると、external ディレクトリを表示できます。

ls $(bazel info output_base)/external

bazel clean を実行しても、外部ディレクトリが実際に削除されるわけではありません。すべての外部アーティファクトを削除するには、bazel clean --expunge を使用します。

オフライン ビルド

ビルドをオフラインで実行するのが望ましい場合があります。飛行機での移動などの単純なユースケースでは、必要なリポジトリを bazel fetch または bazel syncプリフェッチするだけで十分です。さらに、ビルド中にオプション --nofetch を使用して、それ以上のリポジトリの取得を無効にすることもできます。

真のオフライン ビルド(必要なファイルの提供を bazel とは異なるエンティティによって行う場合)の場合、bazel はオプション --distdir をサポートします。リポジトリ ルールが bazel に ctx.download または ctx.download_and_extract を介してファイルを取得するよう要求し、必要なファイルのハッシュサムを提供すると、bazel は最初にそのオプションで指定されたディレクトリを調べ、指定された最初の URL のベース名に一致するファイルを探し、ハッシュが一致する場合はそのローカルコピーを使用します。

Bazel 自体は、この手法を使用して配布アーティファクトからオフラインをブートストラップします。これは、内部 distdir_tar必要なすべての外部依存関係を収集することで実現されます。

ただし、bazel では、リポジトリ ルールで任意のコマンドを、ネットワークを呼び出すかどうかに関係なく実行できます。そのため、bazel にはビルドを完全にオフラインにするオプションはありません。そのため、ビルドがオフラインで正しく機能するかどうかをテストするには、bazel のブートストラップ テストのように、ネットワークの外部ブロックが必要です。

ベスト プラクティス

リポジトリのルール

通常、リポジトリ ルールでは次のことを行います。

  • システム設定を検出してファイルに書き込む。
  • システム上の別の場所でリソースを検索する。
  • URL からリソースをダウンロードします。
  • BUILD ファイルを生成または外部リポジトリ ディレクトリにシンボリック リンクします。

可能であれば、repository_ctx.execute は使用しないでください。たとえば、Make を使用したビルドを含む Bazel 以外の C++ ライブラリを使用している場合は、ctx.execute(["make"]) を実行する代わりに、repository_ctx.download() を使用してビルドする BUILD ファイルを書き込むことをおすすめします。

git_repositorynew_git_repository よりも http_archive を優先します。その理由は次のとおりです。

  • Git リポジトリ ルールはシステムの git(1) に依存しますが、HTTP ダウンローダーは Bazel に組み込まれており、システムの依存関係はありません。
  • http_archiveurls のリストをミラーとしてサポートし、git_repository は単一の remote のみをサポートします。
  • http_archiveリポジトリ キャッシュでは動作しますが、git_repository は動作しません。詳しくは、#5116 をご覧ください。

bind() を使用しない。この問題とその代替方法については、バインドの削除を検討するをご覧ください。