如果 A
在建構或執行時間需要使用 B
,目標 A
「取決於」目標 B
。依附關係會使有向非循環圖 (DAG) 高於目標,這就是「依附元件圖」。
目標的「直接」依附元件是指在依附元件圖中透過長度為 1 的路徑可到達的其他目標。目標的「轉換」依附元件是指透過圖形中任意長度的路徑所依據的目標。
事實上,在建構的內容中,有兩種依附元件圖表:實際依附元件的圖表和宣告的依附元件圖表。在大多數情況下,這兩種圖表都十分相似,因此不必做出這項區別,不過在下文討論時相當實用。
實際和宣告的依附元件
只有在 Y
必須存在、建構及處於最新狀態,才能正確建構 X
時,目標 X
就「實際依賴」目標 Y
。「已建構」可以是產生、處理、編譯、連結、封存、壓縮、執行,或任何其他在建構期間經常發生的其他類型的工作。
如果 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(); } |
|
所宣告依附元件圖表現在是實際依附元件的近似值,即使間接關閉也一樣;建構可能會失敗。
藉由確保在 BUILD
檔案中正確宣告步驟 2 中導入的 c
到 a
之間的實際依附元件,問題或許就能解決。
依附元件類型
大多數的建構規則都有三個屬性,可用來指定不同類型的一般依附元件: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
做為資料依附元件。
建立檔案 | 瀏覽權限 |