本頁將說明工具鍊架構,這是規則作者 將規則邏輯與平台式工具分離。是 建議您參閱規則和平台 再繼續進行。此頁面說明需要工具鍊的原因,以及如何 以及 Bazel 如何根據指標選擇適當的工具鍊 平台限制
動機
首先來看看問題工具鍊的訴求。假設您
撰寫規則支援程式設計語言你的bar_binary
規則會使用 barc
編譯器 (即本身的工具本身)*.bar
做為工作區中的另一個目標因為使用者寫入「bar_binary
」
目標也不必指定編譯器的依附元件
隱含依附性,方法是將其新增至規則定義做為私有屬性。
bar_binary = rule(
implementation = _bar_binary_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
...
"_compiler": attr.label(
default = "//bar_tools:barc_linux", # the compiler running on linux
providers = [BarcInfo],
),
},
)
//bar_tools:barc_linux
現在是每個 bar_binary
目標的依附元件,所以
這類模型會在建立 bar_binary
目標前建立只要規則的
的實作函式,就像其他屬性一樣:
BarcInfo = provider(
doc = "Information about how to invoke the barc compiler.",
# In the real world, compiler_path and system_lib might hold File objects,
# but for simplicity they are strings for this example. arch_flags is a list
# of strings.
fields = ["compiler_path", "system_lib", "arch_flags"],
)
def _bar_binary_impl(ctx):
...
info = ctx.attr._compiler[BarcInfo]
command = "%s -l %s %s" % (
info.compiler_path,
info.system_lib,
" ".join(info.arch_flags),
)
...
這裡的問題是,編譯器的標籤是以硬式編碼的方式寫入 bar_binary
視目標平台而定,不同目標可能需要不同的編譯器
以及建立在哪個平台時
目標平台和執行平台。此外
不一定知道所有可用的工具和平台
您無法在規則定義中以硬式編碼的方式加入代碼。
更好的解決方案是將負擔轉化為使用者
_compiler
屬性 (非私人)接著,個別指定目標即可
都須經過硬式編碼,才能針對單一平台或平台
bar_binary(
name = "myprog_on_linux",
srcs = ["mysrc.bar"],
compiler = "//bar_tools:barc_linux",
)
bar_binary(
name = "myprog_on_windows",
srcs = ["mysrc.bar"],
compiler = "//bar_tools:barc_windows",
)
您可以使用 select
選擇compiler
,讓這個解決方案更臻完善
不同平台:
config_setting(
name = "on_linux",
constraint_values = [
"@platforms//os:linux",
],
)
config_setting(
name = "on_windows",
constraint_values = [
"@platforms//os:windows",
],
)
bar_binary(
name = "myprog",
srcs = ["mysrc.bar"],
compiler = select({
":on_linux": "//bar_tools:barc_linux",
":on_windows": "//bar_tools:barc_windows",
}),
)
但要逐一詢問每位 bar_binary
使用者既辛苦了,
如未在整個工作區中持續使用這個樣式,
適用於單一平台,但在延伸至
多平台的情境也未解決新增支援問題
,不必修改現有規則或目標。
如要解決這個問題,工具鍊架構會增加額外的 間接基本上,您應宣告規則具有抽象依附元件 適用於部分目標系列 (工具鍊類型) 和 Bazel 的角色 會根據 適用的平台限制。規則作者和目標作者皆不包含 且需要全面瞭解可用的平台和工具鍊。
編寫使用工具鍊的規則
在工具鍊架構下,規則不應直接依賴工具 而是依附工具鍊類型。工具鍊類型是一項簡單的目標 代表的一系列工具為 平台。舉例來說,您可以宣告代表長條的類型 編譯器:
# By convention, toolchain_type targets are named "toolchain_type" and
# distinguished by their package path. So the full path for this would be
# //bar_tools:toolchain_type.
toolchain_type(name = "toolchain_type")
上一節的規則定義經過修改,因此
將編譯器視為屬性,就會宣告該函式會使用
//bar_tools:toolchain_type
工具鍊。
bar_binary = rule(
implementation = _bar_binary_impl,
attrs = {
"srcs": attr.label_list(allow_files = True),
...
# No `_compiler` attribute anymore.
},
toolchains = ["//bar_tools:toolchain_type"],
)
實作函式現在會在 ctx.toolchains
底下存取這個依附元件
而非 ctx.attr
,使用工具鍊類型做為金鑰。
def _bar_binary_impl(ctx):
...
info = ctx.toolchains["//bar_tools:toolchain_type"].barcinfo
# The rest is unchanged.
command = "%s -l %s %s" % (
info.compiler_path,
info.system_lib,
" ".join(info.arch_flags),
)
...
ctx.toolchains["//bar_tools:toolchain_type"]
會傳回
ToolchainInfo
供應商
任何目標 Bazel 都會解析工具鍊依附元件。Deployment 的欄位
ToolchainInfo
物件由基礎工具的規則設定;未來
該規則,會定義讓 barcinfo
欄位用來包裝
BarcInfo
物件。
說明 Bazel 將工具鍊解析至目標的程序
下文。實際上,只有已解析的工具鍊目標
做出了 bar_binary
目標的依附元件,而非整個候選空間
工具鍊。
必要和選用工具鍊
根據預設,當規則使用裸標籤表示工具鍊類型依附元件時 (如上所示),工具鍊類型視為「必要」。如果 Bazel 找不到相符的工具鍊 (請參閱 工具鍊解決方法) 則為錯誤且分析停止。
您可改為宣告「選用」工具鍊類型依附元件,如下所示: 如下:
bar_binary = rule(
...
toolchains = [
config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
],
)
如果無法解析選用工具鍊類型,系統會繼續進行分析,然後
ctx.toolchains["//bar_tools:toolchain_type"]
的結果是 None
。
config_common.toolchain_type
函式預設為必要
你可以使用以下表單:
- 必要工具鍊類型:
toolchains = ["//bar_tools:toolchain_type"]
toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type")]
toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = True)]
- 選用工具鍊類型:
toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False)]
bar_binary = rule(
...
toolchains = [
"//foo_tools:toolchain_type",
config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
],
)
您也可以在同一項規則中混和比對表單。不過,如果 工具鍊類型已多次列出,只有最嚴格的版本 強制要求比選填項目更嚴格
編寫使用工具鍊的切面
切面可存取與規則相同的工具鍊 API:您可以定義 工具鍊型別、透過結構定義存取工具鍊,以及使用這些工具鍊來產生新的 建立動作
bar_aspect = aspect(
implementation = _bar_aspect_impl,
attrs = {},
toolchains = ['//bar_tools:toolchain_type'],
)
def _bar_aspect_impl(target, ctx):
toolchain = ctx.toolchains['//bar_tools:toolchain_type']
# Use the toolchain provider like in a rule.
return []
定義工具鍊
如要定義特定工具鍊類型的工具鍊,您需要具備以下三件事:
代表特定工具或工具套件種類的語言專屬規則。變更者: 請使用「_工具鍊」結尾。
- 注意:
\_toolchain
規則無法建立任何建構動作。 而是從其他規則收集構件,並轉送至 建立使用工具鍊的規則該規則負責 建構動作
- 注意:
這個規則類型的多個目標,代表工具或工具的版本 分別適用於不同平台
針對每個這類目標,一般通用的
toolchain
敬上 來提供工具鍊架構使用的中繼資料。此toolchain
目標也是指與這個工具鍊相關聯的toolchain_type
。 這表示指定的_toolchain
規則可與任何toolchain_type
,而且只限於使用toolchain
執行個體的 這項_toolchain
規則與toolchain_type
相關聯。
以下是我們舉例說明的 bar_toolchain
規則定義。我們的
其中只有編譯器,但連結器等其他工具
歸入同一個群組
def _bar_toolchain_impl(ctx):
toolchain_info = platform_common.ToolchainInfo(
barcinfo = BarcInfo(
compiler_path = ctx.attr.compiler_path,
system_lib = ctx.attr.system_lib,
arch_flags = ctx.attr.arch_flags,
),
)
return [toolchain_info]
bar_toolchain = rule(
implementation = _bar_toolchain_impl,
attrs = {
"compiler_path": attr.string(),
"system_lib": attr.string(),
"arch_flags": attr.string_list(),
},
)
規則必須傳回 ToolchainInfo
供應器,這個提供者會成為
使用規則擷取時,會使用 ctx.toolchains
和
工具鍊類型。ToolchainInfo
(例如 struct
) 可存放任意欄位/值
配對。新增至 ToolchainInfo
欄位的確切規格
都應在工具鍊類型中明確記錄在本例中
傳回納入 BarcInfo
物件以重複使用上述定義的結構定義;本
樣式有助於驗證及重複使用程式碼。
您現在可以為特定 barc
編譯器定義目標。
bar_toolchain(
name = "barc_linux",
arch_flags = [
"--arch=Linux",
"--debug_everything",
],
compiler_path = "/path/to/barc/on/linux",
system_lib = "/usr/lib/libbarc.so",
)
bar_toolchain(
name = "barc_windows",
arch_flags = [
"--arch=Windows",
# Different flags, no debug support on windows.
],
compiler_path = "C:\\path\\on\\windows\\barc.exe",
system_lib = "C:\\path\\on\\windows\\barclib.dll",
)
最後,您要為兩個 bar_toolchain
目標建立 toolchain
定義。
這些定義會將語言特定目標連結至工具鍊類型
提供了限制資訊,告知 Bazel 在工具鍊
指定不同平台適用的資源
toolchain(
name = "barc_linux_toolchain",
exec_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
target_compatible_with = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
toolchain = ":barc_linux",
toolchain_type = ":toolchain_type",
)
toolchain(
name = "barc_windows_toolchain",
exec_compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
target_compatible_with = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
toolchain = ":barc_windows",
toolchain_type = ":toolchain_type",
)
使用上述相對路徑語法暗示這些定義都位於
但就沒有理由使用工具鍊類型、特定語言
工具鍊目標和 toolchain
定義目標不得各自獨立
套件
請參閱 go_toolchain
查看實際範例
工具鍊和設定
規則作者的一個重要問題是,當 bar_toolchain
目標如下
分析的畫面、可檢視的設定,以及可進行哪些轉換
是否應用於依附元件?上述範例使用字串屬性,
複雜工具鍊會發生什麼情況,並依附於其他目標
該如何設定?
以下是更複雜的 bar_toolchain
版本:
def _bar_toolchain_impl(ctx):
# The implementation is mostly the same as above, so skipping.
pass
bar_toolchain = rule(
implementation = _bar_toolchain_impl,
attrs = {
"compiler": attr.label(
executable = True,
mandatory = True,
cfg = "exec",
),
"system_lib": attr.label(
mandatory = True,
cfg = "target",
),
"arch_flags": attr.string_list(),
},
)
attr.label
的使用方式與標準規則相同。
但 cfg
參數的含意略有不同
透過工具鍊從目標 (稱為「父項」) 到工具鍊的依附元件
解析時,我們會使用稱為「工具鍊」的特殊設定轉換
轉換」。工具鍊轉換設定維持不變,但
強制讓工具鍊的執行平台與
父項 (否則,工具鍊的工具鍊解析可以選擇任何
執行平台,不一定會與父項相同)。這個
讓工具鍊的任何 exec
依附元件也都能對
父項的建構動作。任何使用 cfg =
"target"
(或未指定 cfg
,因為「target」為預設值) 的工具鍊依附元件都是
。這可讓工具鍊規則
同時提供程式庫 (上述 system_lib
屬性) 和工具 (
compiler
屬性) 傳送至需要這些規則的建構規則。系統程式庫
會連結至最終成果,因此必須針對相同項目進行建構
而編譯器則是在建構期間叫用的工具,必須
以及您要在執行平台上運行的系統
使用工具鍊註冊及建構
到目前為止,所有建構模塊都已組成,您只需在
適用於 Bazel 解析程序的工具鍊。做法是
在 WORKSPACE
檔案中
register_toolchains()
,或傳遞工具鍊指令的標籤
方法是使用 --extra_toolchains
旗標,
register_toolchains(
"//bar_tools:barc_linux_toolchain",
"//bar_tools:barc_windows_toolchain",
# Target patterns are also permitted, so you could have also written:
# "//bar_tools:all",
# or even
# "//bar_tools/...",
)
使用目標模式註冊工具鍊時, 個別工具鍊的註冊方式取決於下列規則:
- 套件子套件中定義的工具鍊會先註冊 套件本身中定義的工具鍊。
- 在套件中,工具鍊會按照 他們的名稱
現在要建立依附工具鍊類型的目標時 根據目標和執行平台選擇工具鍊。
# my_pkg/BUILD
platform(
name = "my_target_platform",
constraint_values = [
"@platforms//os:linux",
],
)
bar_binary(
name = "my_bar_binary",
...
)
bazel build //my_pkg:my_bar_binary --platforms=//my_pkg:my_target_platform
Bazel 會注意到 //my_pkg:my_bar_binary
正透過符合下列條件的平台建構:
具有 @platforms//os:linux
,因此會解析
//bar_tools:barc_linux_toolchain
的 //bar_tools:toolchain_type
參照。
這會導致建立 //bar_tools:barc_linux
,但不會
//bar_tools:barc_windows
。
工具鍊解析度
針對使用工具鍊的每個目標,Bazel 的工具鍊解析程序 決定目標的具體工具鍊依附元件。模型會接收輸入內容 目標平台、可用清單、 執行平台和可用工具鍊清單。其輸出內容是 為每種工具鍊類型和選定的執行作業選取的工具鍊 和目前的目標平台
可用的執行平台和工具鍊是從
WORKSPACE
個檔案透過以下應用程式存取:
register_execution_platforms
和
register_toolchains
。
您也可以在
透過指令列
--extra_execution_platforms
敬上
和
--extra_toolchains
。
系統會自動將主機平台納入為可用的執行平台。
可用的平台和工具鍊會以排序清單追蹤,以做出判斷、
優先採用清單中較早的項目
這組工具鍊 (按優先順序排列) 是從
--extra_toolchains
和 register_toolchains
:
- 系統會先新增使用
--extra_toolchains
註冊的工具鍊。- 在這些中,「最後一個」工具鍊的優先順序最高。
- 使用
register_toolchains
註冊的工具鍊- 在這些中,第一個提到的工具鍊是最高的優先順序。
注意: 虛擬目標,例如 :all
、:*
和
/...
會依照 Bazel 的套件排序
採用字母順序的載入機制
解決步驟如下。
target_compatible_with
或exec_compatible_with
子句「符合」 如果平台中的每個constraint_value
,平台也constraint_value
(明確或預設為預設值)。如果平台有來自
constraint_setting
的constraint_value
所參照的子句,這不會影響比對。如果建構的目標指定
exec_compatible_with
屬性 (或其規則定義指定exec_compatible_with
引數), 可用的執行平台清單經過篩選,即可移除 不符合執行限制條件的任何通知針對每個可用的執行平台,您可將每個工具鍊類型與 第一個與此執行作業相容的工具鍊 (若有) 平台和目標平台
任何執行平台找不到相容的必要工具鍊 適用的工具鍊類型排除流量。其餘平台的 第一個是目前目標的執行平台 toolchains (若有) 會成為目標的依附元件。
所選執行平台會用來執行目標的所有動作 產生的結果。
能夠以多種設定建構同一個目標 (例如 如果使用的是相同版本中的不同 CPU,則會套用解析度程序 獨立於目標版本
如果規則使用執行群組,每次執行時 每個群組會分別執行工具鍊解析,且各自會有自己的執行 平台和工具鍊
偵錯工具鍊
如果您要為現有規則新增工具鍊支援,請使用
--toolchain_resolution_debug=regex
標記。在工具鍊解析期間,標記
可針對與規則運算式變數相符的工具鍊類型或目標名稱,提供詳細輸出。個人中心
可以使用 .*
輸出所有資訊。Bazel 會輸出工具鍊的名稱
會在解決過程中檢查和略過
想瞭解哪些 cquery
依附元件來自工具鍊
解析度,請使用 cquery
的 --transitions
標記:
# Find all direct dependencies of //cc:my_cc_lib. This includes explicitly
# declared dependencies, implicit dependencies, and toolchain dependencies.
$ bazel cquery 'deps(//cc:my_cc_lib, 1)'
//cc:my_cc_lib (96d6638)
@bazel_tools//tools/cpp:toolchain (96d6638)
@bazel_tools//tools/def_parser:def_parser (HOST)
//cc:my_cc_dep (96d6638)
@local_config_platform//:host (96d6638)
@bazel_tools//tools/cpp:toolchain_type (96d6638)
//:default_host_platform (96d6638)
@local_config_cc//:cc-compiler-k8 (HOST)
//cc:my_cc_lib.cc (null)
@bazel_tools//tools/cpp:grep-includes (HOST)
# Which of these are from toolchain resolution?
$ bazel cquery 'deps(//cc:my_cc_lib, 1)' --transitions=lite | grep "toolchain dependency"
[toolchain dependency]#@local_config_cc//:cc-compiler-k8#HostTransition -> b6df211