簡介
Bazel 可以在各種硬體、作業系統和系統設定上建構及測試程式碼。這可能涉及不同版本的建構工具,例如連結器和編譯器。為管理這項複雜度,Bazel 導入了限制和平台的概念。
限制是建構或生產機器的區別屬性。 常見的限制包括 CPU 架構、GPU 的存在與否,或是本機安裝的編譯器版本。但限制可以是任何有意義的機器區別,用於協調建構工作。
平台是一組限制,可指定完整機器。Bazel 會使用這個概念,讓開發人員選擇要建構的機器、應執行編譯和測試動作的機器,以及建構動作應使用哪些工具鍊進行編譯。
開發人員也可以使用限制,在建構規則中選取自訂屬性或依附元件。例如:「建構目標為 Arm 電腦時,請使用 src_arm.cc」。
平台類型
Bazel 可識別平台可能扮演的三個角色:
- 主機 - Bazel 本身執行的平台。
- 執行 - 執行編譯動作以產生建構輸出內容的平台。
- 目標:程式碼建構完成後執行的平台。
建構與平台之間的關係通常有三種:
單一平台建構作業 - 主機、執行和目標平台相同。舉例來說,在開發人員電腦上建構,但不使用遠端執行,然後在同一部電腦上執行建構的二進位檔。
跨平台編譯建構 - 主機和執行平台相同,但目標平台不同。舉例來說,在 Macbook Pro 上建構 iOS 應用程式,但未執行遠端執行作業。
跨平台建構作業:主機、執行和目標平台都不同。舉例來說,在 Macbook Pro 上建構 iOS 應用程式,並使用遠端 Linux 電腦編譯不需要 Xcode 的 C++ 動作。
指定平台
開發人員最常使用平台的方式,是透過 --platforms 標記指定所需的目標機器:
$ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86
由於各機構的建構機器設定不同,因此通常會維護自己的平台定義。
如未設定 --platforms,則預設為 @platforms//host。這項設定專門用於自動偵測主機的 OS 和 CPU 屬性,因此建構作業會以 Bazel 執行的同一部機器為目標。建構規則可使用 @platforms/os 和 @platforms/cpu 限制,選取這些屬性。
一般實用限制和平台
為確保生態系統一致,Bazel 團隊會維護存放區,其中包含最常見的 CPU 架構和作業系統的限制定義。這些定義都位於 https://github.com/bazelbuild/platforms。
Bazel 隨附下列特殊平台定義:@platforms//host (別名為 @bazel_tools//tools:host_platform)。這會自動偵測 Bazel 執行的機器 OS 和 CPU 屬性。
定義限制
限制是透過 constraint_setting 和 constraint_value 建構規則建立模型。
constraint_setting 宣告屬性類型。例如:
constraint_setting(name = "cpu")
constraint_value 宣告該屬性的可能值:
constraint_value(
name = "x86",
constraint_setting = ":cpu"
)
定義平台或自訂建構規則時,可以將這些項目當做標籤參照。如果在 cpus/BUILD 中定義上述範例,您可以將 x86 限制參照為 //cpus:x86。
如果可見度允許,您可以擴充現有的 constraint_setting,方法是為其定義自己的值。
定義平台
platform 建構規則會將平台定義為 constraint_value 的集合:
platform(
name = "linux_x86",
constraint_values = [
"//oses:linux",
"//cpus:x86",
],
)
這部機器必須同時符合 //oses:linux 和 //cpus:x86 限制。
平台只能為特定 constraint_setting 提供一個 constraint_value。舉例來說,除非您建立另一個 constraint_setting 型別來模擬第二個值,否則平台無法有兩個 CPU。
略過不相容的目標
為特定目標平台建構時,通常會略過永遠無法在該平台上運作的目標。舉例來說,在 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
如果啟用 --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 偵測不相容的目標
您可以使用 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
已知問題
不相容的目標會忽略顯示限制。