Bazel 模組

回報問題 查看來源

Bazel 模組是可擁有多個版本的 Bazel 專案,每個版本都會發布其所依附的其他模組相關中繼資料。這類似於其他依附元件管理系統中的熟悉概念,例如 Maven 構件、npm 套件、Go 模組或 Cargo crate

模組的存放區根目錄 (在 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")

為了執行模組解析,Bazel 會先讀取根模組的 MODULE.bazel 檔案,然後透過 Bazel 註冊資料庫重複要求任何依附元件的 MODULE.bazel 檔案,直到發現整個依附元件圖表為止。

根據預設,Bazel 接著會選取每個模組要使用的一個版本。Bazel 會透過存放區代表每個模組,並再次諮詢該註冊資料庫,瞭解如何定義各個存放區。

版本格式

Bazel 的生態系統五花八門,而專案會使用不同的版本管理架構。目前為止最受歡迎的是 SemVer,但也有使用不同配置的知名專案,例如 Abseil,其中的版本都是以日期為基礎的版本,例如 20210324.2

因此,Bzlmod 採用較寬鬆的 SemVer 規格版本。差異包括:

  • SemVer 規定版本的「發行」部分必須包含 3 個片段:MAJOR.MINOR.PATCH。在 Bazel 中,這項要求會放寬,因此可以允許任意數量的區隔。
  • 在 SemVer 中,「release」部分中的每個片段都只能使用數字。在 Bazel 中,這樣會放寬出允許字母,而且比較語意與「prerelease」部分中的「ID」相符。
  • 此外,系統不會強制執行主要、次要和修補程式版本的語意。但是,請參閱相容性等級,進一步瞭解我們如何表示回溯相容性。

任何有效的 SemVer 版本都是有效的 Bazel 模組版本。此外,只有在 ab 這兩個 Bazel 模組版本都具有相同的保留時,才會比較 a < b

選擇版本

請考慮鑽石依附元件問題,也就是版本化依附元件管理空間中的基石。假設您有一個依附關係圖:

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

應使用哪個版本的「D」?為解決這個問題,Bzlmod 使用 Go 模組系統中導入的最小版本選取 (MVS) 演算法。MVS 會假設模組的所有新版本都具有回溯相容性,因此請選擇任何相依項目指定的最高版本 (在本例中為 D 1.1)。之所以稱為「最小化」,因為 D 1.1 是可滿足需求的最早版本,即使有 D 1.2 或以上版本,也不會選取。使用 MVS 會建立一個「高保真」且「可重現」的版本選擇程序。

彎曲版本

如果需要避免特定版本 (例如安全漏洞),註冊資料庫可以宣告這些版本為「yanked」。Bazel 會在選取模組的分支版本時擲回錯誤。如要修正這個錯誤,請升級至較新的非 Ayked 版本,或使用 --allow_yanked_versions 標記明確允許 yanked 版本。

相容性等級

在 Go 中,MVS 對回溯相容性的假設是有效的,因為這會將不相容的模組版本視為獨立模組。就 SemVer 而言,這表示 A 1.xA 2.x 被視為不同的模組,可以並存在於已解析的依附元件圖表中。反過來,在 Go 套件路徑中對主要版本進行編碼後,即可達成這個目標,因此不會發生編譯時間或連結時間衝突的情形。

然而,Bazel 無法提供這類保證,因此需要「主要版本」編號才能偵測回溯不相容的版本。該數字稱為「相容性等級」,由每個模組版本在其 module() 指令中指定。有了這些資訊,Bazel 偵測到在解析的依附元件圖表中,存在不同相容性等級的相同模組版本時,就會擲回錯誤。

覆寫

MODULE.bazel 檔案中指定覆寫值,以變更 Bazel 模組解析的行為。只有根模組的覆寫設定會生效,如果模組做為依附元件使用,系統就會忽略其覆寫。

每個覆寫值都是由特定模組名稱指定,進而影響依附元件圖中的所有版本。雖然只有根模組的覆寫會生效,但可能適用於根模組不會直接依附的遞移依附元件。

單一版本覆寫

single_version_override 有多項用途:

  • 使用 version 屬性,即可將依附元件固定在特定版本,無論依附元件圖中要求的依附元件版本為何。
  • 透過 registry 屬性,您可以強制這個依附元件來自特定登錄檔,而非遵循一般的登錄檔選取程序。
  • 您可以使用 patch* 屬性,指定要套用至下載的模組的一組修補程式。

這些屬性為選用屬性,可以混合搭配。

多重版本覆寫

您可以指定 multiple_version_override,允許同一模組的多個版本同時存在於解析的依附元件圖表中。

您可以指定模組的明確允許版本清單 (這些版本必須在解析前顯示在依附元件圖中),視每個允許版本而定,這必須具有「部分」遞移依附元件。解析後,系統只會保留允許的模組版本,而 Bazel 則會在相同相容性等級,將模組的其他版本升級至最接近的更高版本。如果該相容性等級沒有更高層級的允許版本,Bazel 會擲回錯誤。

舉例來說,如果 1.11.31.51.72.0 版本在解析度之前的依附元件圖中就有,而主要版本是相容性等級:

  • 允許 1.31.72.0 的多重版本覆寫會導致 1.1 升級至 1.31.5 升級為 1.7,其他版本則維持不變。
  • 允許 1.52.0 的多重版本覆寫會導致錯誤,因為 1.7 沒有相同相容性等級的更高版本。
  • 允許 1.92.0 的多重版本覆寫會導致錯誤,因為在解析前,依附元件圖表中未顯示 1.9

此外,使用者也可以使用 registry 屬性覆寫登錄檔,這類似於單一版本覆寫。

非登錄檔覆寫

非登錄檔覆寫會將模組從版本解析中完全移除。Bazel 不會向註冊資料庫要求這些 MODULE.bazel 檔案,而是從存放區本身要求。

Bazel 支援下列非登錄檔覆寫:

存放區名稱和嚴格依附元件

備份模組的存放區標準名稱module_name~version (例如 bazel_skylib~1.0.3)。如果模組包含非登錄檔覆寫,請將 version 部分替換為 override 字串。請注意,標準名稱格式並非您應依附的 API,且隨時可能變更。

除非 bazel_dep 指令的 repo_name 屬性另有規定,否則存放區的「apparent name」會預設為模組直接依附關係。請注意,這表示模組只能找出其直接依附元件。這有助於防止因遞移依附元件變更而導致的意外中斷。

模組擴充功能也可以將其他存放區引入模組的可見範圍。