由於 WORKSPACE 的缺點的關係,Bzlmod 將在日後的 Bazel 版本中取代舊版 WORKSPACE 系統。本指南可協助您將專案遷移至 Bzlmod,並捨棄 WORKSPACE 以擷取外部依附元件。
WORKSPACE 與 Bzlmod
Bazel 的 WORKSPACE 和 Bzlmod 的語法不同。本節說明如何從特定的 WORKSPACE 功能遷移至 Bzlmod。
定義 Bazel 工作區的根目錄
WORKSPACE 檔案會標記 Bazel 專案的來源根目錄,在 Bazel 6.3 以上版本中,這個責任會替換為 MODULE.bazel。使用 6.3 以下版本的 Bazel 版本時,工作區根目錄中仍應有 WORKSPACE
或 WORKSPACE.bazel
檔案,也許會有下列註解:
工作區
# This file marks the root of the Bazel workspace. # See MODULE.bazel for external dependencies setup.
在 azelrc 中啟用 Bzlmod
.bazelrc
可讓您設定每次執行 Bazel 時適用的標記。如要啟用 Bzlmod,請使用 --enable_bzlmod
旗標並套用至 common
指令,以便套用至所有指令:
.bazelrc
# Enable Bzlmod for every Bazel command common --enable_bzlmod
指定工作區的存放區名稱
工作區
workspace
函式是用來指定工作區的存放區名稱。這樣工作區中的目標//foo:bar
就能以@<workspace name>//foo:bar
的形式參照。如未指定,工作區的預設存放區名稱為__main__
。## WORKSPACE workspace(name = "com_foo_bar")
布茲拉夫
建議您使用
//foo:bar
語法,在同一個工作區中參照不含@<repo name>
的目標。但如需舊語法,可以使用module
函式指定的模組名稱做為存放區名稱。如果模組名稱與所需的存放區名稱不同,您可以使用module
函式的repo_name
屬性覆寫存放區名稱。## MODULE.bazel module( name = "bar", repo_name = "com_foo_bar", )
擷取外部依附元件做為 Bazel 模組
如果您的依附元件是 Bazel 專案,那麼當其也採用 Bzlmod 時,您就應該將其做為 Bazel 模組,
工作區
使用 WORKSPACE 時,通常會使用
http_archive
或git_repository
存放區規則來下載 Bazel 專案的來源。## WORKSPACE load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "bazel_skylib", urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz"], sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa", ) load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") bazel_skylib_workspace() http_archive( name = "rules_java", urls = ["https://github.com/bazelbuild/rules_java/releases/download/6.1.1/rules_java-6.1.1.tar.gz"], sha256 = "76402a50ae6859d50bd7aed8c1b8ef09dae5c1035bb3ca7d276f7f3ce659818a", ) load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_java_toolchains") rules_java_dependencies() rules_java_toolchains()
如您所見,這是使用者從依附元件巨集載入遞移依附元件的常見模式。假設
bazel_skylib
和rules_java
都依附platform
,則platform
依附元件的確切版本取決於巨集順序。布茲拉夫
使用 Bzlmod 時,只要您的依附元件可在 Bazel Central Registry 或自訂的 Bazel Registry 中取得,即可使用
bazel_dep
指令直接使用該依附元件。## MODULE.bazel bazel_dep(name = "bazel_skylib", version = "1.4.2") bazel_dep(name = "rules_java", version = "6.1.1")
Bzlmod 會使用 MVS 演算法,遞移解析 Bazel 模組依附元件。因此,系統會自動選取所需的
platform
最大版本。
以 Bazel 模組的形式覆寫依附元件
如果是根模組,您可以透過不同方式覆寫 Bazel 模組依附元件。
詳情請參閱「overrides」一節。
您可以在範例存放區中找到一些使用範例。
使用模組擴充功能擷取外部依附元件
如果依附元件不是 Bazel 專案,或尚未在任何 Bazel 登錄檔中提供,就能使用 use_repo_rule
或模組擴充功能導入依附元件。
工作區
使用
http_file
存放區規則下載檔案。## WORKSPACE load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") http_file( name = "data_file", url = "http://example.com/file", sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", )
布茲拉夫
您可以使用 Bzlmod,在 MODULE.bazel 檔案中使用
use_repo_rule
指令直接對存放區執行個體化:## MODULE.bazel http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") http_file( name = "data_file", url = "http://example.com/file", sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", )
實際上,此實作是使用模組擴充功能實作。如果需要執行更複雜的邏輯,而不只是叫用存放區規則,您也可以自行實作模組擴充功能。您需要將定義移至
.bzl
檔案,這也能讓您在遷移期間在 WORKSPACE 和 Bzlmod 之間共用定義。## repositories.bzl load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") def my_data_dependency(): http_file( name = "data_file", url = "http://example.com/file", sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", )
實作模組擴充功能以載入依附元件巨集。您可以在巨集的同一個
.bzl
檔案中定義標記,但如要與舊版 Bazel 檔案保持相容,最好在獨立的.bzl
檔案中定義。## extensions.bzl load("//:repositories.bzl", "my_data_dependency") def _non_module_dependencies_impl(_ctx): my_data_dependency() non_module_dependencies = module_extension( implementation = _non_module_dependencies_impl, )
如要讓根專案能看到存放區,應在 MODULE.bazel 檔案中宣告模組擴充功能和存放區的用法。
## MODULE.bazel non_module_dependencies = use_extension("//:extensions.bzl", "non_module_dependencies") use_repo(non_module_dependencies, "data_file")
透過模組擴充功能解決外部依附元件的衝突
專案可以提供一個巨集,根據呼叫端的輸入來導入外部存放區。但是,如果依附元件圖表中有多個呼叫端並造成衝突,該怎麼辦?
假設專案 foo
提供下列巨集,後者使用 version
做為引數。
## repositories.bzl in foo {:#repositories.bzl-foo}
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
def data_deps(version = "1.0"):
http_file(
name = "data_file",
url = "http://example.com/file-%s" % version,
# Omitting the "sha256" attribute for simplicity
)
工作區
使用 WORKSPACE 時,您可以從
@foo
載入巨集,並指定所需資料依附元件的版本。假設您還有另一個依附元件@bar
,而後者也依附於@foo
,但需要不同版本的資料依附元件。## WORKSPACE # Introduce @foo and @bar. ... load("@foo//:repositories.bzl", "data_deps") data_deps(version = "2.0") load("@bar//:repositories.bzl", "bar_deps") bar_deps() # -> which calls data_deps(version = "3.0")
在這種情況下,使用者必須謹慎調整 WORKSPACE 中的巨集順序,才能取得需要的版本。這是 WORKSPACE 檔案最艱鉅的問題之一,因為這個方式並不提供解決依附元件的合理方法。
布茲拉夫
使用 Bzlmod 時,專案
foo
的作者就能使用模組擴充功能解決衝突。舉例來說,假設一律在所有 Bazel 模組之間,一律選取資料依附元件所需的最大版本,這很有意義。## extensions.bzl in foo load("//:repositories.bzl", "data_deps") data = tag_class(attrs={"version": attr.string()}) def _data_deps_extension_impl(module_ctx): # Select the maximal required version in the dependency graph. version = "1.0" for mod in module_ctx.modules: for data in mod.tags.data: version = max(version, data.version) data_deps(version) data_deps_extension = module_extension( implementation = _data_deps_extension_impl, tag_classes = {"data": data}, )
## MODULE.bazel in bar bazel_dep(name = "foo", version = "1.0") foo_data_deps = use_extension("@foo//:extensions.bzl", "data_deps_extension") foo_data_deps.data(version = "3.0") use_repo(foo_data_deps, "data_file")
## MODULE.bazel in root module bazel_dep(name = "foo", version = "1.0") bazel_dep(name = "bar", version = "1.0") foo_data_deps = use_extension("@foo//:extensions.bzl", "data_deps_extension") foo_data_deps.data(version = "2.0") use_repo(foo_data_deps, "data_file")
在這種情況下,根模組需要資料版本
2.0
,而其依附元件bar
則需要3.0
。foo
中的模組擴充功能可以正確解決這項衝突,並自動選取3.0
版本做為資料依附元件。
整合第三方套件管理員
在上一節之後,由於模組擴充功能可讓您從依附元件圖表收集資訊、執行自訂邏輯以解析依附元件,並呼叫存放區規則來導入外部存放區,因此讓規則作者能有效強化整合特定語言套件管理員的規則集。
如要進一步瞭解如何使用模組擴充功能,請參閱「模組擴充功能」頁面。
以下是已採用 Bzlmod 從不同套件管理工具擷取依附元件的規則集清單:
您可以在範例存放區中找到整合虛擬套件管理員的最簡單範例。
偵測主機上的工具鍊
當 Bazel 建構規則需要偵測主機機器上可用的工具鍊時,就會使用存放區規則檢查主機機器,並產生工具鍊資訊做為外部存放區。
工作區
根據下列存放區規則來偵測殼層工具鍊。
## local_config_sh.bzl def _sh_config_rule_impl(repository_ctx): sh_path = get_sh_path_from_env("SH_BIN_PATH") if not sh_path: sh_path = detect_sh_from_path() if not sh_path: sh_path = "/shell/binary/not/found" repository_ctx.file("BUILD", """ load("@bazel_tools//tools/sh:sh_toolchain.bzl", "sh_toolchain") sh_toolchain( name = "local_sh", path = "{sh_path}", visibility = ["//visibility:public"], ) toolchain( name = "local_sh_toolchain", toolchain = ":local_sh", toolchain_type = "@bazel_tools//tools/sh:toolchain_type", ) """.format(sh_path = sh_path)) sh_config_rule = repository_rule( environ = ["SH_BIN_PATH"], local = True, implementation = _sh_config_rule_impl, )
您可以在 WORKSPACE 中載入存放區規則。
## WORKSPACE load("//:local_config_sh.bzl", "sh_config_rule") sh_config_rule(name = "local_config_sh")
布茲拉夫
使用 Bzlmod 時,您可以使用模組擴充功能導入相同的存放區,這與在上一節介紹
@data_file
存放區類似。## local_config_sh_extension.bzl load("//:local_config_sh.bzl", "sh_config_rule") sh_config_extension = module_extension( implementation = lambda ctx: sh_config_rule(name = "local_config_sh"), )
然後使用 MODULE.bazel 檔案中的擴充功能。
## MODULE.bazel sh_config_ext = use_extension("//:local_config_sh_extension.bzl", "sh_config_extension") use_repo(sh_config_ext, "local_config_sh")
註冊工具鍊和執行平台
在上一節中,導入存放區代管工具鍊資訊 (例如 local_config_sh
) 後,您可能會想要註冊工具鍊。
工作區
使用 WORKSPACE 時,您可以透過下列方式註冊工具鍊。
您可以在
.bzl
檔案中註冊工具鍊,並在 WORKSPACE 檔案中載入巨集。## local_config_sh.bzl def sh_configure(): sh_config_rule(name = "local_config_sh") native.register_toolchains("@local_config_sh//:local_sh_toolchain")
## WORKSPACE load("//:local_config_sh.bzl", "sh_configure") sh_configure()
或者直接在 WORKSPACE 檔案中註冊工具鍊。
## WORKSPACE load("//:local_config_sh.bzl", "sh_config_rule") sh_config_rule(name = "local_config_sh") register_toolchains("@local_config_sh//:local_sh_toolchain")
布茲拉夫
使用 Bzlmod 時,
register_toolchains
和register_execution_platforms
API 只能在 MODULE.bazel 檔案中使用。您無法在模組擴充功能中呼叫native.register_toolchains
。## MODULE.bazel sh_config_ext = use_extension("//:local_config_sh_extension.bzl", "sh_config_extension") use_repo(sh_config_ext, "local_config_sh") register_toolchains("@local_config_sh//:local_sh_toolchain")
在 WORKSPACE
、WORKSPACE.bzlmod
和每個 Bazel 模組的 MODULE.bazel
檔案中註冊的工具鍊和執行平台,均遵循以下優先順序 (從最高到最低):
- 在根模組的
MODULE.bazel
檔案中註冊的工具鍊和執行平台。 - 在
WORKSPACE
或WORKSPACE.bzlmod
檔案中註冊的工具鍊和執行平台。 - 由屬於根模組 (遞移) 依附元件的模組註冊的工具鍊和執行平台。
- 不使用
WORKSPACE.bzlmod
的情況:在WORKSPACE
後置字串中註冊的工具鍊。
導入本機存放區
當您需要本機版本的依附元件進行偵錯,或是想將目錄納入工作區做為外部存放區時,您可能需要將依附元件導入為本機存放區。
工作區
使用 WORKSPACE 時,可透過兩項原生存放區規則
local_repository
和new_local_repository
達成。## WORKSPACE local_repository( name = "rules_java", path = "/Users/bazel_user/workspace/rules_java", )
布茲拉夫
使用 Bzlmod 時,您可以使用
local_path_override
覆寫本機路徑的模組。## MODULE.bazel bazel_dep(name = "rules_java") local_path_override( module_name = "rules_java", path = "/Users/bazel_user/workspace/rules_java", )
您也可以使用模組擴充功能引入本機存放區。不過,您無法在模組擴充功能中呼叫
native.local_repository
,系統會持續為所有原生存放區規則加上星號 (如要瞭解進度,請參閱 #18285)。 然後,您可以在模組擴充功能中呼叫對應的星號local_repository
。如果這是造成封鎖問題,實作自訂版本的local_repository
存放區規則也較不容易。
繫結目標
WORKSPACE 中的 bind
規則已淘汰,Bzlmod 不支援。而是為特殊 //external
套件的目標別名提供。所有根據這項設定的使用者都應停止遷移。
舉例來說
## WORKSPACE
bind(
name = "openssl",
actual = "@my-ssl//src:openssl-lib",
)
這樣可讓其他目標依附 //external:openssl
。您可以透過下列方式遷移:
將所有
//external:openssl
的用法替換為@my-ssl//src:openssl-lib
。或使用
alias
建構規則在套件中定義下列目標 (例如
//third_party
)## third_party/BUILD alias( name = "openssl", actual = "@my-ssl//src:openssl-lib", )
將所有
//external:openssl
的用法替換為//third_party:openssl
。
擷取和同步處理
擷取和同步指令的用途是在本機下載外部存放區,並持續更新這些存放區。有時也能在擷取建構所需的所有存放區後,使用 --nofetch
標記離線建構。
工作區
同步處理作業會針對所有存放區或一組特定的設定存放區執行強制擷取,而擷取作業「只」用於擷取特定目標。
布茲拉夫
已無法使用同步指令,但擷取功能提供多種選項。您可以擷取目標、存放區、一組已設定的存放區,或是與依附元件解決方案和模組擴充功能相關的所有存放區。系統會快取擷取結果,並強制擷取作業。您必須在擷取程序中加入
--force
選項。
遷移
本節提供 Bzlmod 遷移程序的實用資訊和指引。
瞭解 WORKSPACE 中的依附元件
遷移作業的第一步是瞭解您擁有哪些依附元件。這可能很難釐清 WORKSPACE 檔案中導入的確切依附元件,因為轉換依附元件通常會透過 *_deps
巨集載入。
使用工作區解析的檔案檢查外部依附元件
幸好,旗標 --experimental_repository_resolved_file
可以派上用場。這個標記基本上,在您最後的 Bazel 指令中,所有已擷取的外部依附元件會產生「鎖定檔案」。詳情請參閱這篇網誌文章。
使用方法有兩種:
擷取建立特定目標所需的外部依附元件資訊。
bazel clean --expunge bazel build --nobuild --experimental_repository_resolved_file=resolved.bzl //foo:bar
擷取 WORKSPACE 檔案中定義的所有外部依附元件資訊。
bazel clean --expunge bazel sync --experimental_repository_resolved_file=resolved.bzl
您可以使用
bazel sync
指令,擷取 WORKSPACE 檔案中定義的所有依附元件,包括:bind
使用情形register_toolchains
和register_execution_platforms
用量
不過,如果專案是跨平台,Bazel 同步處理作業可能會在特定平台上中斷,因為部分存放區規則只能在支援的平台上正確執行。
執行指令之後,您應可在 resolved.bzl
檔案中取得外部依附元件的資訊。
使用 bazel query
檢查外部依附元件
您也會知道 bazel query
可用於檢查存放區規則,
bazel query --output=build //external:<repo name>
雖然這個方法較為便利和速度更快,但 bazel 查詢可能會遇到外部依附元件版本,因此使用時請小心!使用 Bzlmod 查詢及檢查外部依附元件時,需透過新的子指令完成。
內建預設依附元件
如果檢查 --experimental_repository_resolved_file
產生的檔案,您會發現許多未在 WORKSPACE 中定義的依附元件。這是因為 Bazel 實際上會在使用者的 WORKSPACE 檔案內容中新增前置字串和後置字串,以插入原生規則通常需要的預設依附元件 (例如 @bazel_tools
、@platforms
和 @remote_java_tools
)。使用 Bzlmod 時,這些依附元件是透過內建的模組 bazel_tools
導入,這是所有其他 Bazel 模組的預設依附元件。
用於逐步遷移的混合模式
Bzlmod 和 WORKSPACE 可並排運作,這允許將依附元件從 WORKSPACE 檔案遷移至 Bzlmod,成為循序漸進的過程。
WORKSPACE.bzlmod
在遷移期間,不論是否啟用 Bzlmod,Bazel 使用者可能需要在建構作業之間切換。已實作 WORKSPACE.bzlmod 支援,讓程序更加順暢。
WORKSPACE.bzlmod 的語法與 WORKSPACE 相同。啟用 Bzlmod 時,如果 WORKSPACE.bzlmod 檔案也存在於工作區根目錄中:
WORKSPACE.bzlmod
生效,並忽略WORKSPACE
的內容。- 系統不會在 WORKSPACE.bzlmod 檔案中加入任何前置字串或後置字串。
使用 WORKSPACE.bzlmod 檔案能簡化遷移作業,原因如下:
- Bzlmod 停用後,您將改回從原始 WORKSPACE 檔案擷取依附元件。
- 啟用 Bzlmod 後,即可透過 WORKSPACE.bzlmod 追蹤還剩下哪些要遷移的依附元件。
存放區瀏覽權限
Bzlmod 可以控制特定存放區可以看到哪些其他存放區,詳情請參閱存放區名稱和嚴格依附元件一節。
以下摘要說明在一併考量 WORKSPACE 時,來自不同類型存放區的存放區瀏覽權限。
從主要存放區 | 從 Bazel 模組存放區 | 透過模組擴充功能存放區 | 來自 WORKSPACE 存放區 | |
---|---|---|---|---|
主要存放區 | 可見 | 如果根模組是直接依附元件 | 如果根模組是代管模組擴充功能的直接依附元件 | 可見 |
Bazel 模組存放區 | 直接依附元件 | 直接依附元件 | 直接依附於代管模組擴充功能的模組 | 直接的根模組依附元件 |
模組擴充功能存放區 | 直接依附元件 | 直接依附元件 | 直接卸除代管模組擴充功能的模組 + 同一個模組擴充功能產生的所有存放區 | 直接的根模組依附元件 |
WorkSPACE 存放區 | 全部顯示 | 無法查看 | 無法查看 | 全部顯示 |
@bar
遷移流程
典型的 Bzlmod 遷移程序如下所示:
- 瞭解 WORKSPACE 中的依附元件。
- 在專案根目錄中新增空白的 MODULE.bazel 檔案。
- 新增空白的 WORKSPACE.bzlmod 檔案來覆寫 WORKSPACE 檔案內容。
- 在啟用 Bzlmod 的情況下建立目標,並檢查缺少的存放區。
- 請在已解析的依附元件檔案中,檢查缺少存放區的定義。
- 透過模組擴充功能加入缺少的依附元件做為 Bazel 模組,或是將其保留在 WORKSPACE.bzlmod 中,以便稍後進行遷移。
- 返回 4 並重複操作,直到所有依附元件都可供使用。
遷移工具
提供互動式 Bzlmod 遷移輔助指令碼,可協助您快速上手。
指令碼會執行以下作業:
- 產生並剖析 WORKSPACE 檔案。
- 以使用者可理解的方式列印已解決檔案的存放區資訊。
- 執行 bazel 建構指令、偵測已辨識的錯誤訊息,並建議遷移方式。
- 檢查 BCR 中是否已有依附元件。
- 新增依附元件至 MODULE.bazel 檔案。
- 透過模組擴充功能新增依附元件。
- 新增依附元件至 WORKSPACE.bzlmod 檔案。
如要使用,請確認您已安裝最新版本的 Bazel 版本,然後執行下列指令:
git clone https://github.com/bazelbuild/bazel-central-registry.git
cd <your workspace root>
<BCR repo root>/tools/migrate_to_bzlmod.py -t <your build targets>
發布 Bazel 模組
如果您的 Bazel 專案是其他專案的依附元件,您可以在 Bazel Central Registry 中發布專案。
您需要專案的來源封存網址,才能在 BCR 中查看專案。建立來源封存檔時,請注意下列幾點:
確認封存檔指向特定版本。
BCR 只接受版本化來源封存,因為 Bzlmod 在依附元件解析期間需要比較版本。
請確保封存網址是固定網址。
Bazel 會透過雜湊值驗證封存的內容,所以您應確保下載檔案的總和檢查碼永遠不會變更。如果該網址來自 GitHub,請在發布頁面建立並上傳版本封存檔。GitHub 不保證能依需求產生來源封存檔的總和檢查碼。簡單來說,系統會將
https://github.com/<org>/<repo>/releases/download/...
形式的網址視為穩定,但https://github.com/<org>/<repo>/archive/...
不行。詳情請參閱 GitHub 封存總和檢查碼服務中斷頁面。確保原始碼樹狀結構遵循原始存放區的版面配置。
如果存放區較大,且您想透過去除不必要的來源來縮減大小的發布封存檔,請確定已移除的來源樹狀結構是原始來源樹狀結構的子集。這可讓使用者透過
archive_override
和git_override
,輕鬆將模組覆寫為非發布版本。在用於測試最常用的 API 的子目錄中加入測試模組。
測試模組是一種 Bazel 專案,其 WORKSPACE 和 MODULE.bazel 檔案位於原始碼封存的子目錄中,後者視要發布的模組而定。其中應包含範例或一些整合測試,涵蓋您最常用的 API。如要瞭解設定方式,請參閱測試模組。
準備好來源封存網址後,請按照 BCR 貢獻指南將模組透過 GitHub 提取要求提交至 BCR。
強烈建議您為存放區設定「發布至 BCR」 GitHub 應用程式,以便自動化將模組提交到 BCR 的程序。
最佳做法
本節記錄幾項您應遵循的最佳做法,以便更妥善地管理外部依附元件。
將目標分割成不同的套件,以免擷取不必要的依附元件。
檢查 #12835,在建構不需要測試的目標時,系統必須在沒有必要的情況下擷取測試開發依附元件。實際上,這並非 Bzlmod 的具體情況,但只要遵循這個做法,就能更輕鬆正確指定開發依附元件。
指定開發依附元件
您可以將 bazel_dep
和 use_extension
指令的 dev_dependency
屬性設為 true,這樣就不會套用至相依專案。如果是根模組,您可以使用 --ignore_dev_dependency
旗標驗證目標是否在沒有開發依附元件的情況下持續建構。
社群遷移進度
您可以前往 Bazel Central Registry,確認依附元件是否已可用。或者,您也可以加入這個 GitHub 討論,認同或發布會阻礙遷移作業的依附元件。
回報問題
請參閱 Bazel GitHub 問題清單,瞭解已知的 Bzlmod 問題。歡迎您提出新的問題或功能要求,協助解除封鎖遷移作業!