外部依附元件總覽

回報問題 查看來源

Bazel 支援外部依附元件,也就是建構中所用來源檔案 (文字和二進位檔),並非來自工作區。例如,資料夾可能是在 GitHub 存放區中代管的規則集、Maven 成果,或是本機機器在目前工作區以外的目錄。

自 Bazel 6.0 起,您可以透過 Bazel 管理外部依附元件,方法有兩種方式:傳統、以存放區為中心的 WORKSPACE 系統,以及著重於模組的較新 MODULE.bazel 系統 (代碼稱為 Bzlmod,並以標記 --enable_bzlmod 啟用)。這兩個系統可以一起使用,但 Bzlmod 想在日後遷移 Bazel Bz 版本中替換 WORKSPACE 系統。請參閱相關指南。

本文件將說明 Bazel 中外部依附元件管理的相關概念,再依序深入說明這兩個系統。

概念

存放區

目錄樹狀結構,根部有邊界標記檔案,內含可在 Bazel 建構作業中使用的來源檔案。通常會縮短為只有 repo

存放區邊界標記檔案可以有 MODULE.bazel (表示這個存放區代表 Bazel 模組)、REPO.bazel (請見下方),或是在舊版結構定義中為 WORKSPACEWORKSPACE.bazel。任何存放區邊界標記檔案都代表存放區的邊界;您可以在目錄中同時存在多個這類檔案。

主要存放區

正在執行目前 Bazel 指令的存放區。

Workspace

所有 Bazel 指令共用的環境都會在同一個主要存放區中執行。

請注意,過去「存放區」和「工作區」的概念已經過合併,經常用來指稱主要存放區,有時甚至用來做為「存放區」的同義詞。

標準存放區名稱

存放區可定址的正規名稱,在工作區中,每個存放區都有一個標準名稱。存放區中的標準名稱為 canonical_name 的目標,可透過標籤 @@canonical_name//pac/kage:target 解決 (注意雙雙 @)。

主要存放區一律以空字串做為標準名稱。

顯示的存放區名稱

存放區名稱可透過其他特定存放區來定址。這可以視為存放區的「nickname」:含有標準名稱 michael 的存放區可能在存放區 alice 中具有明顯名稱 mike,但存放區 bob 中的顯示名稱可能是 mickey。在這種情況下,michael 內的目標可透過 alice 中的 @mike//pac/kage:target 標籤來解決 (請注意單一 @)。

反之,這可以理解為「存放區對應」:每個存放區都會維護從「apparent repo name」對應到「標準存放區名稱」。

存放區規則

存放區定義的結構定義,用於指示 Bazel 如何具體化存放區。舉例來說,可執行的操作包括「從特定網址下載 ZIP 封存檔並解壓縮」、「擷取特定 Maven 構件並將其提供給 java_import 目標使用」,或直接「符號連結本機目錄」。每個存放區都是藉由呼叫含有適當引數數量的存放區規則來定義

如要進一步瞭解如何編寫自己的存放區規則,請參閱存放區規則

目前為止,最常見的存放區規則是 http_archive,用於從網址下載封存檔案並加以擷取,而 local_repository (後者會符號連結已位於 Bazel 存放區的本機目錄)。

擷取存放區

透過執行相關聯的存放區規則,允許本機磁碟使用存放區。系統在擷取工作區中定義的存放區,就不適用於本機磁碟。

一般來說,Bazel 只會在需要存放區中的內容,且尚未擷取該存放區時,才擷取存放區。如果先前已擷取該存放區,Bazel 只會在定義有所變更時重新擷取。

fetch 指令可用來啟動預先擷取存放區、目標或執行任何建構作業的所有必要存放區。這項功能使用 --nofetch 選項啟用離線建構作業。

--fetch 選項可用於管理網路存取權。預設值為 true。然而,如果設為 false (--nofetch),指令會使用任何依附元件的快取版本,如果不存在,指令將導致失敗。

如要進一步瞭解如何控制擷取作業,請參閱擷取選項

目錄版面配置

擷取完成後,就在輸出基準的子目錄 external 中,這個存放區的標準名稱下方可以找到。

您可以執行下列指令,查看包含正規名稱 canonical_name 的存放區內容:

ls $(bazel info output_base)/external/ canonical_name 

REPO.bazel 檔案

REPO.bazel 檔案是用來標記構成存放區的目錄樹狀結構最頂層邊界。您不需要包含任何內容就能做為存放區邊界檔案,但也可以用來指定存放區中所有建構目標的常見屬性。

REPO.bazel 檔案的語法與 BUILD 檔案類似,差別在於前者不支援 load 陳述式,且只能使用單一函式 repo()repo() 使用的引數與 BUILD 檔案中的 package() 函式相同;而 package() 會指定套件中所有建構目標的通用屬性,repo() 則是針對存放區中的所有建構目標指定。

例如,您可以使用以下 REPO.bazel 檔案,為存放區中的所有目標指定通用授權:

repo(
    default_package_metadata = ["//:my_license"],
)

使用 Bzlmod 管理外部依附元件

新的外部依附元件子系統 Bzlmod 無法直接搭配存放區定義使用。而是會從模組建構依附元件圖表,並在圖表上方執行擴充功能,並據此定義存放區。

Bazel 模組是可能包含多個版本的 Bazel 專案,每個版本都會發布其所依附的其他模組相關中繼資料。模組的存放區根目錄必須含有 WORKSPACE 檔案旁的 MODULE.bazel 檔案。這個檔案是模組的資訊清單,用於宣告其名稱、版本、依附元件清單及其他資訊。基本範例如下:

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

模組只能列出其直接依附元件;Bzlmod 會在 Bazel 註冊資料庫中查詢其直接依附元件,而後者預設是 Bazel Central Registry。註冊資料庫會提供依附元件的 MODULE.bazel 檔案,讓 Bazel 在執行版本解析前,先探索完整的遞移依附元件圖表。

版本解析後,每個模組都會選取一個版本後,Bazel 會再次查詢註冊資料庫,瞭解如何為每個模組定義存放區 (在多數情況下會使用 http_archive)。

模組也可以指定稱為「標記」的自訂資料部分,模組解析後會由模組擴充功能使用,以便定義其他存放區。這些擴充功能的功能與存放區規則類似,可執行檔案 I/O 及傳送網路要求等動作。除此之外,這些物件可讓 Bazel 與其他套件管理系統互動,同時遵循透過 Bazel 模組建構的依附元件圖表。

使用 WORKSPACE 定義存放區

以往,您可以在 WORKSPACE (或 WORKSPACE.bazel) 檔案中定義存放區,藉此管理外部依附元件。這個檔案的語法與 BUILD 檔案類似,並採用存放區規則,而非建構規則。

以下程式碼片段範例是在 WORKSPACE 檔案中使用 http_archive 存放區規則:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "foo",
    urls = ["https://example.com/foo.zip"],
    sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)

這個程式碼片段會定義標準名稱為 foo 的存放區。在 WORKSPACE 系統中,根據預設,存放區的正規名稱也是所有其他存放區的顯示名稱。

查看 WORKSPACE 檔案中可用函式的完整清單

WORKSPACE 系統的缺點

WORKSPACE 系統導入以來,使用者回報了許多痛點,包括:

  • Bazel 不會評估任何依附元件的 WORKSPACE 檔案,因此除了直接依附元件之外,您還必須在主要存放區的 WORKSPACE 檔案中定義所有遞移依附元件。
  • 為解決這個問題,專案採用「deps.bzl」模式,也就是定義巨集,而巨集會定義多個存放區,並要求使用者在 WORKSPACE 檔案中呼叫這個巨集。
    • 這有其相關問題:巨集無法load其他 .bzl 檔案,因此這些專案必須在這個「deps」巨集中定義轉換依附元件,或者請使用者呼叫多個分層「deps」巨集來解決這個問題。
    • Bazel 會依序評估 WORKSPACE 檔案。此外,系統會使用含網址的 http_archive 指定依附元件,不含任何版本資訊。這表示若是鑽石依附元件,執行版本解析並沒有可靠的方法 (A 依附 BCBC 都依附於不同版本的 D)。

由於 WORKSPACE 的缺點,Bzlmod 會在未來的 Bazel 版本中取代舊版 WORKSPACE 系統。請參閱 Bzlmod 遷移指南,瞭解如何遷移至 Bzlmod。