ターゲット A は、ビルド時または実行時に A で B が必要な場合、ターゲット B に依存します。 依存関係は、ターゲットに対して
有向非巡回グラフ
(DAG)を生成します。これは依存関係グラフと呼ばれます。
ターゲットの直接依存関係とは、依存関係グラフ内の長さ 1 のパスで到達できる他のターゲットのことです。 ターゲットの推移的依存関係とは、グラフ内の任意の長さのパスを介して依存するターゲットのことです。
実際、ビルドのコンテキストでは、 実際の依存関係のグラフと宣言された依存関係のグラフの 2 つの依存関係グラフがあります。ほとんどの場合、2 つのグラフは非常に似ているため、この区別は必要ありませんが、以下の説明では役立ちます。
実際の依存関係と宣言された依存関係
ターゲット X が正しくビルドされるためには、ターゲット Y が存在し、ビルドされ、最新の状態である必要がある場合、ターゲット X はターゲット Y に実際に依存します。 ビルド済みとは、生成、処理、コンパイル、リンク、アーカイブ、圧縮、実行、またはビルド中に日常的に発生するその他の種類のタスクを意味します。
ターゲット X のパッケージに X から Y への依存関係エッジがある場合、ターゲット X はターゲット Y に宣言された依存関係を持ちます。
ビルドを正しく行うには、実際の依存関係のグラフ A は、
宣言された依存関係のグラフ D のサブグラフである必要があります。つまり、
直接接続されたノードx --> yのペアAは、
Dでも直接接続されている必要があります。D は A の過大評価であると言えます。
BUILD ファイルの作成者は、すべてのルールの実際の直接依存関係をビルドシステムに明示的に宣言する必要があります。
この原則を守らないと、未定義の動作が発生します。ビルドが失敗する可能性があります。さらに悪いことに、ビルドが以前のオペレーションや、ターゲットが持つ推移的な宣言された依存関係に依存する可能性があります。Bazel は欠落している依存関係をチェックしてエラーを報告しますが、このチェックがすべてのケースで完了するとは限りません。
間接的にインポートされたものをすべてリストしようとする必要はありません(また、そうすべきではありません)。たとえ実行時に A で必要であってもです。
ターゲット X のビルド中、ビルドツールは X の依存関係の推移的閉包全体を検査して、これらのターゲットの変更が最終結果に反映されるようにし、必要に応じて中間ファイルを再ビルドします。
依存関係の推移的な性質により、よくある間違いが発生します。1 つのファイルのコードが、間接的な依存関係(宣言された依存関係グラフ内の推移的だが直接的ではないエッジ)によって提供されるコードを使用することがあります。 間接的な依存関係は BUILD ファイルに表示されません。ルールはプロバイダに直接依存しないため、次の例のタイムラインに示すように、変更を追跡する方法はありません。
1. 宣言された依存関係が実際の依存関係と一致する
最初はすべて正常に動作します。パッケージ a のコードはパッケージ b のコードを使用します。
パッケージ b のコードはパッケージ c のコードを使用するため、a は c に推移的に
依存します。
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();
}
|
|
|
|
宣言された依存関係は、実際の依存関係を過大評価しています。すべてが順調です。
2. 宣言されていない依存関係を追加する
誰かが a にコードを追加して
直接 実際の 依存関係を作成し、ビルドファイル
a/BUILD で宣言するのを忘れると、潜在的な危険が生じます。c
a / a.in |
|
|---|---|
import b;
import c;
b.foo();
c.garply();
|
|
|
|
|
宣言された依存関係は、実際の依存関係を過大評価しなくなりました。
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();
}
|
|
|
|
|
宣言された依存関係グラフは、推移的に閉じられていても、実際の依存関係の過小評価になっています。ビルドが失敗する可能性があります。
ステップ 2 で導入された
a から c への実際の依存関係が BUILD ファイルで正しく宣言されていれば、この問題を回避できた可能性があります。
依存関係の種類
ほとんどのビルドルールには、さまざまな種類の汎用依存関係を指定するための 3 つの属性(srcs、deps、data)があります。これらについては後ほど説明します。詳細については、
すべてのルールに共通する属性をご覧ください。
多くのルールには、ルール固有の種類の依存関係(compiler や resources など)のための追加属性もあります。これらについては、
ビルド百科事典で詳しく説明しています。
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 ファイル | 可視性 |