依存関係

問題を報告 ソースを表示 Nightly · 7.4 .

ビルド時または実行時に AB を必要とする場合、ターゲット A はターゲット B に依存します。この関係は、ターゲットに対して有向非巡回グラフ(DAG)を誘導する関係であり、依存関係グラフと呼ばれます。

ターゲットの直接依存関係とは、依存関係グラフの長さ 1 のパスで到達可能な他のターゲットです。ターゲットの推移的依存関係とは、グラフ上の任意の長さのパスを通じてターゲットが依存するターゲットです。

実際、ビルドのコンテキストでは、実際の依存関係のグラフと宣言された依存関係のグラフの 2 つの依存関係グラフがあります。ほとんどの場合、2 つのグラフは非常に類似しているため、この区別を行う必要はありませんが、以下の説明では役に立ちます。

実際の依存関係と宣言された依存関係

X を正しくビルドするために Y が存在し、ビルドされ、最新である必要がある場合、ターゲット X は実際にはターゲット Y に依存しています。ビルドとは、生成、処理、コンパイル、リンク、アーカイブ、圧縮、実行など、ビルド中に定期的に発生するタスクを指します。

X のパッケージに X から Y への依存関係エッジがある場合、ターゲット X はターゲット Y に対する依存関係が宣言されています。

正しいビルドの場合、実際の依存関係のグラフ A は、宣言された依存関係のグラフ D のサブグラフである必要があります。つまり、A の直接接続されたノード x --> y のペアはすべて、D でも直接接続されている必要があります。DA過剰近似であると言えます。

BUILD ファイル ライターは、ビルドシステムに対する各ルールの実際の直接的な依存関係をすべて明示的に宣言する必要があります。

この原則に従わないと、未定義の動作が発生します。ビルドが失敗する可能性がありますが、さらに悪いことに、ビルドが以前のオペレーションに依存する可能性があります。また、ターゲットが偶然持っている伝播宣言された依存関係に依存する可能性があります。Bazel は、欠落している依存関係をチェックし、エラーを報告しますが、このチェックをすべて完了できるとは限りません。

実行時に A で必要であっても、間接的にインポートされたすべてを一覧表示しようとしないでください。また、そうしないでください。

ターゲット X のビルド中に、ビルドツールは X の依存関係の推移的クロージャ全体を検査し、それらのターゲットの変更が最終結果に反映されていることを確認します。また、必要に応じて中間を再ビルドします。

依存関係の推移的な性質は、よくある間違いにつながります。あるファイル内のコードでは、間接的な依存関係によって提供されるコードを使用することがあります。これは、宣言された依存関係グラフにおける推移的エッジであり、直接的なエッジではありません。間接的な依存関係は BUILD ファイルに表示されません。ルールはプロバイダに直接依存していないため、次のタイムライン例に示すように、変更を追跡する方法はありません。

1. 宣言された依存関係が実際の依存関係と一致している

最初はすべて正常に動作します。パッケージ a のコードは、パッケージ b のコードを使用しています。パッケージ b のコードはパッケージ c のコードを使用しているため、ac に間接的に依存しています。

a/BUILD b/BUILD
rule(
    name = "a",
    srcs = "a.in",
    deps = "//b:b",
)
      
rule(
    name = "b",
    srcs = "b.in",
    deps = "//c:c",
)
      
a / a.in b / b.in
import b;
b.foo();
    
import c;
function foo() {
  c.bar();
}
      
a、b、c を結ぶ矢印が付いた宣言された依存関係グラフ
宣言された依存関係グラフ
宣言された依存関係グラフと一致する実際の依存関係グラフ。a、b、c を接続する矢印があります。
実際の依存関係グラフ

宣言された依存関係が実際の依存関係を過剰に近似している。順調です。

2. 宣言されていない依存関係を追加する

c に対する直接の実際の依存関係を作成するコードを a に追加し、ビルドファイル a/BUILD で宣言し忘れると、潜在的なハザードが導入されます。

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
a、b、c を結ぶ矢印が付いた宣言された依存関係グラフ
宣言された依存関係グラフ
a、b、c を矢印で結んだ実際の依存関係グラフ。今度は矢印が A と C を接続しています。これは宣言された依存関係グラフと一致しません
実際の依存関係グラフ

宣言された依存関係が実際の依存関係を過剰に近似しなくなりました。2 つのグラフの推移的クロージャは等しいため、問題を回避できます。しかし、a には c に対する実際の依存関係があり、宣言されていない依存関係があります。

3. 宣言された依存関係グラフと実際の依存関係グラフの差異

この危険性は、誰かが b をリファクタリングして c に依存しなくなり、誤って自身の過失が原因で a が壊れたときに明らかになります。

  b/BUILD
 
rule(
    name = "b",
    srcs = "b.in",
    deps = "//d:d",
)
      
  b / b.in
 
      import d;
      function foo() {
        d.baz();
      }
      
a と b を接続する矢印で宣言された依存関係グラフ。b は c に接続されなくなり、a と c の接続が切断されます。
宣言された依存関係グラフ
b と c に接続しているものの、b は c に接続していない実際の依存関係グラフ
実際の依存関係グラフ

宣言された依存関係グラフは、推移的に閉じられている場合でも、実際の依存関係の下限近似になります。ビルドは失敗する可能性があります。

この問題は、ステップ 2 で導入した a から c への実際の依存関係が BUILD ファイルで適切に宣言されていれば回避できたでしょう。

依存関係のタイプ

ほとんどのビルドルールには、さまざまな種類の汎用依存関係を指定する 3 つの属性(srcsdepsdata)があります。これらについて説明します。詳細については、すべてのルールに共通する属性をご覧ください。

多くのルールには、ルール固有の依存関係(compilerresources など)の追加属性もあります。詳細については、Build Encyclopedia をご覧ください。

srcs の依存関係

ソースファイルを出力するルールによって直接使用されるファイル。

deps 依存関係

ヘッダー ファイル、シンボル、ライブラリ、データなどを提供する、個別にコンパイルされたモジュールを指すルール。

data の依存関係

ビルド ターゲットを正常に実行するには、いくつかのデータファイルが必要になる場合があります。これらのデータファイルはソースコードではなく、ターゲットのビルド方法には影響しません。たとえば、単体テストで関数の出力とファイルの内容を比較する場合があります。単体テストをビルドするときにはこのファイルは必要ありませんが、テストの実行時に必要です。実行中に起動するツールについても同様です。

ビルドシステムは、data としてリストされているファイルのみが使用可能な分離されたディレクトリでテストを実行します。したがって、バイナリ、ライブラリ、テストを実行するためにファイルが必要な場合は、data でそれらのファイル(またはそれらを含むビルドルール)を指定します。例:

# I need a config file from a directory named env:
java_binary(
    name = "setenv",
    ...
    data = [":env/default_env.txt"],
)

# I need test data from another directory
sh_test(
    name = "regtest",
    srcs = ["regtest.sh"],
    data = [
        "//data:file1.txt",
        "//data:file2.txt",
        ...
    ],
)

これらのファイルは、相対パス path/to/data/file で参照できます。テストでは、テストのソース ディレクトリのパスとワークスペース相対パスを結合して、これらのファイルを参照できます(例: ${TEST_SRCDIR}/workspace/path/to/data/file)。

ラベルを使用してディレクトリを参照する

BUILD ファイルを調べると、一部の data ラベルがディレクトリを参照していることがわかります。これらのラベルは、次の例のように /. または / で終わるため、使用できません。

非推奨 - data = ["//data/regression:unittest/."]

非推奨 - data = ["testdata/."]

非推奨 - data = ["testdata/"]

これは、ディレクトリ内のすべてのデータファイルをテストで使用できるため、特にテストには便利であるように思えます。

ただし、このようなことは行わないでください。変更後に適切な増分再ビルド(およびテストの再実行)を確実に行うには、ビルドシステムが、ビルド(またはテスト)への入力であるファイルの完全なセットを認識している必要があります。ディレクトリを指定すると、ビルドシステムはディレクトリ自体が変更された場合(ファイルの追加または削除による)にのみ再ビルドを行います。ただし、個々のファイルの変更は包含ディレクトリに影響しないため、検出できません。ビルドシステムへの入力としてディレクトリを指定するのではなく、明示的に、または glob() 関数を使用して、ディレクトリ内のファイルのセットを列挙する必要があります。(** を使用して glob() を再帰的に強制します)。

推奨 - data = glob(["testdata/**"])

ただし、ディレクトリラベルを使用しなければならない場合もあります。たとえば、testdata ディレクトリ内に、名前がラベル構文に準拠していないファイルが含まれている場合、ファイルを明示的に列挙するか、glob() 関数を使用すると、無効なラベルエラーが発生します。この場合はディレクトリラベルを使用する必要がありますが、上記の不正な再ビルドのリスクに注意してください。

ディレクトリラベルを使用する必要がある場合は、相対 ../ パスで親パッケージを参照することはできません。代わりに、//data/regression:unittest/. などの絶対パスを使用します。

複数のファイルを使用する必要があるテストなどの外部ルールは、それらすべてへの依存関係を明示的に宣言する必要があります。filegroup() を使用すると、BUILD ファイル内の複数のファイルをグループ化できます。

filegroup(
        name = 'my_data',
        srcs = glob(['my_unittest_data/*'])
)

その後、テストでデータ依存関係としてラベル my_data を参照できます。

BUILD ファイル 公開設定