如果 A
在建構或執行期間需要 B
,則目標 A
取決於 B
。「依附關係」關係會在目標上引發有向非循環圖 (DAG),並且稱為依附元件圖表。
目標的「直接」依附元件是指可在依附元件圖表中由長度為 1 的路徑可連接的其他目標。目標的「遞移」依附元件是指透過目標,經由圖形且長度相近的路徑,所依附於它的目標。
事實上,在建構作業中,有兩個依附元件圖表:實際依附元件圖表和已宣告的依附元件圖表。大多數圖表通常都很類似,因此不需要進行區別,但對於討論的幫助很有幫助。
實際和宣告的依附元件
如果必須存在、建構和更新 Y
,X
才能確實依賴在目標 Y
上,以便正確建構 X
。已建構的意思是產生、已處理、編譯、連結、封存、壓縮、執行,或任何其他在建構期間發生的其他工作。
如果 X
套件中有從 X
到 Y
的依附元件邊緣,則目標 X
會在目標 Y
上具有宣告的依附元件。
為正確建構,實際依附元件 A 的圖表必須是宣告的依附元件 D 的圖表。也就是說,A 中每對直接連線的節點 x --> y
也必須在 D 中直接連線。可以說 D 是 A 的超估值。
BUILD
檔案寫入者必須明確宣告建構規則中每個規則的所有實際直接依附元件,且完全不需要。
如未觀察這個原則會導致未定義的行為:建構作業可能會失敗,但較不理想時,建構可能會依靠先前的某些作業或目標宣告具有的遞移宣告。Bazel 會檢查是否有缺少的依附元件並回報錯誤,但這項檢查可能無法在所有情況下都完成。
您不需要 (也應該) 嘗試列出間接匯入的所有資料,即使 A
在執行中需要亦然。
在目標 X
的建構期間,建構工具會檢查 X
的依附元件整體關閉性,以確保這些目標中的任何變更在最終結果中反映,並且視需要重建中繼。
依附元件的遞移性會造成常見錯誤。有時候,一個檔案中的程式碼可能會使用由「間接」依附元件提供的程式碼,也就是在宣告的依附元件圖表中具有轉換的直接邊緣。間接依附元件不會顯示在 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
時,會在 c
建立直接「實際」依附元件,但忘記在建構檔案 a/BUILD
中宣告。
a / a.in |
|
---|---|
import b; import c; b.foo(); c.garply(); |
|
已宣告的依附元件不會再覆寫實際的依附元件。這可能會造成問題,因為這兩張圖表的過渡性相同,但會遮蓋問題: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
檔案中正確宣告。
依附元件類型
大部分的建構規則有三個屬性,用來指定不同類型的一般依附元件: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
做為資料依附元件。
建立檔案 | 顯示設定 |