依附元件

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

如果 A 在建構或執行期間需要 B,目標 A 就會依附目標 Bdepends upon 關係會在目標上誘導有向非循環圖 (DAG),稱為依附元件圖

目標的直接依附元件是指在依附元件圖中,可透過長度為 1 的路徑存取的其他目標。目標的遞移依附元件,是指透過圖表中任意長度的路徑,依附於目標的目標。

事實上,在建構的內容中,有兩種依附元件圖表:實際依附元件的圖表和宣告的依附元件圖表。在大多數情況下,這兩張圖表都非常相似,因此不需要做出區分,但這點對於後續的討論相當實用。

實際和已宣告的依附元件

如果 Y 必須存在、建構且更新至最新版本,才能正確建構 X,則目標 X 實際上依賴目標 Y「已建構」可以是產生、處理、編譯、連結、封存、壓縮、執行,或任何其他在建構期間經常發生的其他類型的工作。

如果 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。這與宣告的依附元件圖表不符
實際依附元件圖表

宣告的依附元件不再過度近似實際依附元件。這可能會建構正常,因為兩個圖表的傳遞閉包相等,但會掩蓋一個問題:ac 有實際但未宣告的依附元件。

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,因此 a 與 c 的連線中斷
已宣告的依附元件圖表
實際依附元件圖表,顯示 a 連結至 b 和 c,但 b 不再連結至 c
實際依附元件圖表

即使轉換關閉,已宣告的依附元件圖表仍會低估實際依附元件,因此建構作業可能會失敗。

您可以透過確保在步驟 2 中引入的 ac 的實際依附元件已在 BUILD 檔案中正確宣告,來避免這個問題。

依附元件類型

大多數的建構規則都有三個屬性,可用來指定不同類型的一般依附元件:srcsdepsdata。以下說明這些選項。詳情請參閱「所有規則的共同屬性」。

許多規則也有其他屬性,用於規則專屬的依附元件類型,例如 compilerresources。詳情請參閱Build Encyclopedia

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 做為資料依附元件。

建立檔案 能見度