Bazel 模組是一種 Bazel 專案,可包含多個版本,且每個專案會發布與其依附的其他模組相關的中繼資料。這類似於熟悉其他依附元件管理系統中的概念,例如 Maven 成果、npm 套件、Go 模組或 Cargo crate。
模組的存放區根目錄中必須有 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")
請參閱完整清單,瞭解 MODULE.bazel
檔案中可用的指令。
為了執行模組解析,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 模組版本。此外,只有在與 Bazel 模組版本比較相同的訴訟保留時,a
和 b
這兩個 SemVer 版本才會比較 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 版本
如果特定版本應避免 (例如出現安全漏洞),登錄檔可將特定版本宣告為 yanked。Bazel 會在選取模組的分割版本時擲回錯誤。如要修正這個錯誤,請升級至較新的非分割版本,或使用 --allow_yanked_versions
標記明確允許 Yanked 版本。
相容性等級
在 Go 中,MVS 假設回溯相容性能夠正常運作,因為 MVS 會將不相容的模組版本視為獨立模組。就 SemVer 而言,這表示 A 1.x
和 A 2.x
被視為不同的模組,並且可以並存在已解析的依附元件圖表中。可反而又是在 Go 中將主要版本編碼成套件路徑,這樣就不會發生編譯時間或連結時間衝突。
不過,Bazel 無法提供這類保證,因此需要「主要版本」編號才能偵測回溯不相容的版本。這個數字稱為「相容性等級」,由其 module()
指令中的每個模組版本指定。透過這項資訊,Bazel 會在已解決的依附元件圖表中偵測到相同模組的不同相容性等級,就會擲回錯誤。
覆寫
在 MODULE.bazel
檔案中指定覆寫值,以變更 Bazel 模組解析的行為。只有根模組的覆寫設定生效;如果模組做為依附元件使用,系統會忽略其覆寫值。
每個覆寫值都有指定模組名稱,會影響依附元件圖表中所有版本。雖然只有根模組的覆寫會生效,但適用於根模組未直接依附的遞移依附元件。
單一版本覆寫
single_version_override
有許多用途:
- 您可以使用
version
屬性,將依附元件固定在特定版本,無論依附元件圖表要求哪個版本的依附元件。 - 透過
registry
屬性,您可以強制此依附元件來自特定註冊資料庫,而不必依照一般的註冊資料庫程序。 - 您可以使用
patch*
屬性指定要套用至下載模組的一組修補程式。
這些屬性皆為選用,可以彼此搭配使用。
多版本覆寫
您可以指定 multiple_version_override
,讓同一個模組的多個版本在已解析的依附元件圖表中同時存在。
您可以指定模組的允許版本清單,而且在解析度前必須全部列在依附元件圖中。視每個允許的版本而定,都必須存在部分遞移依附元件。解析後,系統只會保留允許的模組版本,而 Bazel 會將其他模組的其他版本升級至相同的相容性等級,使其擁有最接近的可用版本。如果沒有相同相容性等級允許的更高版本,Bazel 會擲回錯誤。
舉例來說,如果 1.1
、1.3
、1.5
、1.7
和 2.0
版本在解析度前就存在於依附元件圖表中,且主要版本是相容性等級:
- 允許
1.3
、1.7
和2.0
的多重版本覆寫會使1.1
升級至1.3
,1.5
升級至1.7
,其他版本則維持不變。 - 允許
1.5
和2.0
的多重版本覆寫會導致錯誤,因為1.7
沒有相同相容性等級的更高版本可升級。 - 允許
1.9
和2.0
的多版本覆寫會導致錯誤,因為在解析度之前的依附元件圖中沒有1.9
。
此外,使用者也可以使用 registry
屬性覆寫註冊資料庫,做法與單一版本覆寫類似。
非登錄檔覆寫
非登錄覆寫值會從版本解析中完全移除模組。Bazel 不會要求註冊資料庫中的這些 MODULE.bazel
檔案,而是來自存放區本身。
Bazel 支援下列非登錄檔覆寫值:
定義不代表 Bazel 模組的存放區
您可以使用 bazel_dep
定義代表其他 Bazel 模組的存放區。有時候,您必須定義「不」代表 Bazel 模組的存放區,例如包含純 JSON 檔案即可讀取為資料的存放區。
在這種情況下,您可以使用 use_repo_rule
指令叫用存放區規則,直接定義存放區。只有所定義的模組才能查看這個存放區。
基本上,這種做法採用與模組擴充功能相同的機制實作,可讓您更靈活地定義存放區。
存放區名稱和嚴格依附元件
將模組支援模組的存放區ap 父項名稱預設為其模組名稱,除非 bazel_dep
指令的 repo_name
屬性另有指示。請注意,這表示模組只能找到其直接依附元件。這有助於防止遞移依附元件變更導致意外中斷。
存放區支援模組的正規名稱為 module_name~version
(例如 bazel_skylib~1.0.3
) 或 module_name~
(例如 bazel_features~
),視整個依附元件圖表是否包含多個版本 (請參閱 multiple_version_override
) 而定。請注意,標準名稱格式並不是您應該依賴的 API,而且可能會隨時變更。與其將標準名稱硬式編碼改為 Bazel,不要使用硬式編碼的方式直接取得:
* 在 BUILD 和 .bzl
檔案中,在由存放區提供的已知名稱字串所構成的 Label
執行個體上使用 Label.repo_name
(例如:Label("@bazel_skylib").repo_name
。* 查詢執行檔案時,請使用 $(rlocationpath ...)
或 @bazel_tools//tools/{bash,cpp,java}/runfiles
中的其中一個執行檔程式庫,或 @rules_foo//foo/runfiles
中的規則集 rules_foo
。* 透過外部工具 (例如 IDE 或語言伺服器) 與 Bazel 互動時,請使用 bazel mod dump_repo_mapping
指令,取得特定存放區的已知名稱與標準名稱之間的對應關係。
模組擴充功能也可能會在模組的可見範圍內,引進其他存放區。