Bazel 可以使用許多不同版本的建構工具 (例如連結器和編譯器),在各種硬體、作業系統和系統設定中建構及測試程式碼。為協助管理這種複雜性,Bazel 有「限制」和「平台」的概念。限制是指建構或實際環境可能不同的維度,例如 CPU 架構、是否有 GPU,或是系統安裝的編譯器版本。平台是這些限制的一組已命名選項,代表可在某些環境中提供的特定資源。
將環境建立為平台模型可協助 Bazel 自動為建構動作選取適當的工具鍊。您也可以將平台與 config_setting 規則搭配使用,編寫可設定屬性。
Bazel 會辨識平台可能提供的三種角色:
- 主機:Bazel 本身執行的平台。
- Execution:在這個平台,建構工具會執行建構動作,產生中繼和最終輸出內容。
- 目標:最終輸出內容所在的平台,也是執行平台。
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
- 預設為@local_config_platform//:host
@local_config_platform
是用於偵測主機作業系統和 CPU 的存放區規則,並寫入平台目標。- 它也會建立
@local_config_platform//:constraintz.bzl
,該檔案會公開名為HOST_CONSTRAINTS
的陣列,可用於其他 BUILD 和 Starlark 檔案。
--platforms
:預設為代管平台- 也就是說,如果未設定其他標記,
@local_config_platform//:host
就是目標平台。 - 如果設定
--host_platform
而非--platforms
,--host_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
如果您在指令列中使用 --expand_test_suites
指定 test_suite
,系統也會同樣略過 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
啟用 --skip_incompatible_explicit_targets
時,系統會略過不相容的明確目標,不顯示任何通知。
較能表達的限制
如要更靈活地表達限制條件,請使用 @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
偵測不相容的目標
您可以使用 bazel cquery
的 Starlark 輸出格式中的 IncompatiblePlatformProvider
,區分相容和不相容的目標。
可用於篩除不相容的目標。以下範例只會列印相容目標的標籤。系統不會列印不相容的目標。
$ cat example.cquery
def format(target):
if "IncompatiblePlatformProvider" not in providers(target):
return target.label
return ""
$ bazel cquery //... --output=starlark --starlark:file=example.cquery
已知問題
不相容的目標會忽略瀏覽權限限制。