外部依附元件總覽

回報問題 查看原始碼

Bazel 支援外部依附元件,也就是建構作業中使用的來源檔案 (文字和二進位檔),但並非來自工作區。舉例來說,這些變數可能是在 GitHub 存放區中託管的規則集、Maven 構件,或位於目前工作區外本機機器上的目錄。

從 Bazel 6.0 開始,您可以透過兩種方式使用 Bazel 管理外部依附元件:以存放區為中心的傳統 WORKSPACE 系統,以及以模組為主的 MODULE.bazel 系統 (代號為 Bzlmod,且加上 --enable_bzlmod 旗標)。這兩個系統搭配使用,但 Bzlmod 將於日後的 Bazel 推出遷移作業中取代 WORKSPACE Bzlmod 。{/1

本文件說明在 Bazel 中管理外部依附元件管理的概念,然後依序介紹兩種系統。

概念

存放區

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

存放區邊界標記檔案可以是 MODULE.bazel (表示此存放區代表 Bazel 模組)、REPO.bazel (請參閱下文),或者在舊版環境中為 WORKSPACEWORKSPACE.bazel。任何存放區邊界標記檔案都會表示存放區的界線;多個這類檔案可以同時存在一個目錄中。

主要存放區

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

主要存放區的根目錄也稱為工作區根目錄

工作區

所有 Bazel 指令共用的環境都會在同一個主要存放區中執行。其中包含主要存放區和所有已定義的外部存放區組合。

請注意,過去「repository」和「workspace」的概念已有所衝突;「workspace」一詞通常是指主要存放區,有時甚至用來做為「存放區」的同義詞。

標準存放區名稱

正規名稱可供存放區定址。在工作區的內容中,每個存放區都有一個正規名稱。存放區中標準名稱為 canonical_name 的目標可由標籤 @@canonical_name//pac/kage:target 處理 (請注意雙精度為 @)。

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

已知存放區名稱

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

反之,您可以解讀為「存放區對應」:每個存放區都有一個從「apparent 存放區名稱」與「標準存放區名稱」的對應關係。

存放區規則

存放區定義的結構定義,可指示 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 專案,可包含多個版本,每個版本會發布與其依附的其他模組相關的中繼資料。模組的存放區根目錄中必須包含 MODULE.bazel 檔案,位於 WORKSPACE 檔案旁。這個檔案是模組的資訊清單,宣告其名稱、版本、依附元件清單及其他資訊。以下為基本範例:

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。