依存関係

問題を報告 ソースを表示 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

ターゲット A は、ビルド時または実行時に AB が必要な場合、ターゲット 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 の依存関係の推移的閉包全体を検査し、それらのターゲットの変更が最終結果に反映されるようにします。必要に応じて中間を再ビルドします。

依存関係の推移性により、よくある間違いが発生します。1 つのファイル内のコードが、間接的な依存関係(宣言された依存関係グラフ内の推移的だが直接的ではないエッジ)によって提供されるコードを使用することがあります。間接的な依存関係は 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. 未宣言の依存関係を追加する

潜在的なハザードは、ac への直接の実際の依存関係を作成するコードを追加したものの、ビルドファイル 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. 宣言された依存関係グラフと実際の依存関係グラフの相違

この危険性は、bc に依存しなくなるようにリファクタリングされたときに明らかになります。このとき、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 への接続が切断される
宣言された依存関係グラフ
a が b と c に接続しているが、b が c に接続していないことを示す実際の依存関係グラフ
実際の依存関係グラフ

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

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

依存関係の種類

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

多くのルールには、ルール固有の依存関係の種類(compilerresources など)の追加属性もあります。詳しくは、ビルド百科事典をご覧ください。

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 ファイル 公開設定