Bazel 可在各種硬體、作業系統和系統設定上建構及測試程式碼,並使用許多不同版本的建構工具,例如連結器和編譯器。為協助管理這項複雜度,Bazel 導入了限制和平台的概念。限制是建構或生產環境可能不同的維度,例如 CPU 架構、GPU 的存在與否,或是系統安裝的編譯器版本。平台是這些限制的命名選項集合,代表某些環境中可用的特定資源。
將環境建模為平台,有助於 Bazel 自動為建構動作選取適當的工具鍊。平台也可以與 config_setting 規則搭配使用,編寫可設定的屬性。
Bazel 可識別平台可能扮演的三種角色:
- 主機:Bazel 本身執行的平台。
- 執行 - 建構工具在平台上執行建構動作,以產生中繼和最終輸出內容。
- 目標:最終輸出內容所在及執行的平台。
Bazel 支援下列平台相關的建構情境:
單一平台建構 (預設) - 主機、執行和目標平台相同。舉例來說,在 Intel x64 CPU 上執行的 Ubuntu 中建構 Linux 可執行檔。
跨平台程式碼編譯建構作業 - 主機和執行平台相同,但目標平台不同。舉例來說,在 MacBook Pro 上執行的 macOS 上建構 iOS 應用程式。
多平台建構作業 - 主機、執行和目標平台都不同。
定義限制和平台
平台可能選項的空間是使用 BUILD
檔案中的 constraint_setting
和 constraint_value
規則定義。constraint_setting
會建立新維度,而 constraint_value
則會為指定維度建立新值;兩者共同定義列舉及其可能的值。舉例來說,以下 BUILD
檔案程式碼片段會針對系統的 glibc 版本導入限制條件,並提供兩個可能的值。
constraint_setting(name = "glibc_version")
constraint_value(
name = "glibc_2_25",
constraint_setting = ":glibc_version",
)
constraint_value(
name = "glibc_2_26",
constraint_setting = ":glibc_version",
)
工作區中不同套件的限制和值可能有所不同。這些檔案會依標籤參照,並受一般顯示設定控管。如果可見度允許,您可以為現有的限制設定定義自己的值,藉此擴充設定。
platform
規則會導入新平台,並提供特定限制值選項。以下範例會建立名為 linux_x86
的平台,並說明該平台適用於任何在 x86_64 架構上執行 Linux 作業系統,且 glibc 版本為 2.25 的環境。(如要進一步瞭解 Bazel 的內建限制,請參閱下文)。
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
":glibc_2_25",
],
)
一般實用限制和平台
為確保生態系統一致性,Bazel 團隊會維護存放區,其中包含最常見的 CPU 架構和作業系統的限制定義。這些檔案都位於 https://github.com/bazelbuild/platforms。
Bazel 隨附下列特殊平台定義:@local_config_platform//:host
。這是自動偵測到的主機平台值,代表系統 Bazel 執行的自動偵測平台。
指定建構作業的平台
您可以使用下列指令列旗標,指定建構作業的主機和目標平台:
--host_platform
- 預設為@bazel_tools//platforms:host_platform
--platforms
- 預設為@bazel_tools//platforms:target_platform
略過不相容的目標
為特定目標平台建構時,通常會略過永遠無法在該平台上運作的目標。舉例來說,在 Linux 電腦上使用 //...
建構時,Windows 裝置驅動程式可能會產生許多編譯器錯誤。使用 target_compatible_with
屬性,告知 Bazel 程式碼有哪些目標平台限制。
這個屬性最簡單的用途是將目標限制為單一平台。如果平台不符合所有限制,系統就不會為該平台建構目標。以下範例會將 win_driver_lib.cc
限制為 64 位元 Windows。
cc_library(
name = "win_driver_lib",
srcs = ["win_driver_lib.cc"],
target_compatible_with = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
],
)
:win_driver_lib
「僅」適用於使用 64 位元 Windows 建構,不適用於其他所有情況。不相容性是遞移的。凡是遞移依附於不相容目標的目標,本身也會視為不相容。
系統何時會略過目標?
如果目標不相容,且包含在建構作業中,做為目標模式擴展的一部分,系統就會略過這些目標。舉例來說,下列兩個叫用會略過在目標模式擴展中找到的任何不相容目標。
$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all
如果透過指令列指定 test_suite
,並使用 --expand_test_suites
,系統也會略過 test_suite
中的不相容測試。換句話說,指令列上的 test_suite
目標行為與 :all
和 ...
類似。使用 --noexpand_test_suites
會導致擴展作業無法進行,並導致不相容的測試也無法使用 test_suite
目標。
在命令列上明確指定不相容的目標會導致錯誤訊息,且建構作業會失敗。
$ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform
...
ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested.
...
FAILED: Build did NOT complete successfully
更具表現力的限制
如要更彈性地表示限制,請使用@platforms//:incompatible
constraint_value
,這類限制沒有任何平台符合。
搭配使用 select()
和 @platforms//:incompatible
,即可表示更複雜的限制。舉例來說,您可以使用這項功能實作基本的 OR 邏輯。以下標記的程式庫與 macOS 和 Linux 相容,但不支援其他平台。
cc_library(
name = "unixish_lib",
srcs = ["unixish_lib.cc"],
target_compatible_with = select({
"@platforms//os:osx": [],
"@platforms//os:linux": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)
上述內容可解讀如下:
- 指定 macOS 時,目標沒有限制。
- 指定 Linux 時,目標沒有限制。
- 否則,目標會受到
@platforms//:incompatible
限制。由於@platforms//:incompatible
不屬於任何平台,因此系統會將目標視為不相容。
如要讓限制更容易閱讀,請使用 skylib 的 selects.with_or()
。
您也可以用類似的方式表示反向相容性。以下範例說明與 ARM 以外的所有項目相容的程式庫。
cc_library(
name = "non_arm_lib",
srcs = ["non_arm_lib.cc"],
target_compatible_with = select({
"@platforms//cpu:arm": ["@platforms//:incompatible"],
"//conditions:default": [],
],
)
使用 bazel cquery
偵測不相容的目標
您可以使用 IncompatiblePlatformProvider
bazel cquery
的 Starlark 輸出格式,區分不相容和相容的目標。
這可用於篩除不相容的目標。以下範例只會列印相容目標的標籤。不相容的目標不會列印。
$ cat example.cquery
def format(target):
if "IncompatiblePlatformProvider" not in providers(target):
return target.label
return ""
$ bazel cquery //... --output=starlark --starlark:file=example.cquery
已知問題
不相容的目標會忽略顯示限制。