依附元件

如果 A 在建構或執行時需要 B,目標 A「取決於」目標 B。「依附於」關係會產生有向非循環圖 (DAG) 而非目標,這就是所謂的依附元件圖表

目標的「直接」依附元件是指可在依附元件圖表中透過長度為 1 的路徑到達的其他目標。目標的「遞移」依附元件是指透過圖表中任何長度的路徑做為目標。

事實上,建構作業中有兩個依附元件圖表,分別是「實際依附元件」圖表和「已宣告依附元件」圖表。大多數情況下,兩個圖表非常相似,不需要做出這項差異,但這對於下方的討論很有用。

實際和宣告的依附元件

假如 Y 必須存在、建構和處於最新狀態,目標 X「確實取決於」目標 Y,才能正確建構 X「已建立」是指產生、處理、已編譯、連結、封存、壓縮、執行,或是建構期間經常發生的任何種類工作。

如果 X 套件中有來自 XY 的依附元件邊緣,目標 X 就會具有目標 Y已宣告依附元件

如果是正確的建構作業,實際依附元件 A 的圖表必須是已宣告依附元件 D 圖表的子圖表。也就是說,A 中的每一對直接連結節點 x --> y 都必須在 D 中直接連線。可說是 DA 的「概略」值。

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();
}
      
已宣告的依附元件圖表,帶有連接 a、b 和 c 的箭頭
已宣告依附元件圖表
實際依附元件圖,與宣告的依附元件圖表相符,並包含連接 a、b 和 c 的箭頭
實際依附元件圖表

宣告的依附元件高於實際的依附元件。一切正常。

2. 新增未宣告的依附元件

如果使用者將程式碼新增至 a,因而在 c 上直接建立「實際」依附元件,但忘記在建構檔案 a/BUILD 中宣告,則會發生潛在的危險。

a / a.in  
        import b;
        import c;
        b.foo();
        c.garply();
      
 
已宣告的依附元件圖表,帶有連接 a、b 和 c 的箭頭
已宣告依附元件圖表
實際的依附元件圖表,帶有連接 a、b 和 c 的箭頭。現在,箭頭也將 A 連接至 C。這與宣告的依附元件圖表不符
實際依附元件圖表

宣告的依附元件不會再過於接近實際的依附元件。這可能會建構沒有問題,因為兩個圖形的遞移閉合相等,但會遮蓋問題: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 連線,進而中斷與 c 的連線
已宣告依附元件圖表
實際依附元件圖表顯示連線至 b 和 c,但 b 不再連線至 c
實際依附元件圖表

宣告的依附元件圖現在是實際依附元件的近似值,即使以遞移方式關閉也一樣;因此建構可能會失敗。

但只要確認在步驟 2 中導入的 ac 的實際依附元件,已正確宣告在 BUILD 檔案中,就可能會發生這個問題。

依附元件類型

大多數的建構規則都有三種屬性,可用於指定不同種類的一般依附元件: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 做為資料依附元件。

已建立的檔案 瀏覽權限