平台

回報問題 查看原始碼

Bazel 可使用連結器和編譯器等許多不同版本的建構工具,在各種硬體、作業系統和系統設定上建構及測試程式碼。為協助化繁為簡,Bazel 有「限制」和「平台」的概念。限制條件是建構或實際工作環境可能不同的維度,例如 CPU 架構、GPU 是否存在,或系統安裝的編譯器版本。平台是這些限制的命名選項集合,代表某些環境中可用的特定資源。

將環境模擬為平台,可協助 Bazel 自動為建構動作選取適當的工具鍊。平台也可以與 config_setting 規則搭配使用,寫入可設定的屬性

Bazel 可辨識平台提供的三個角色:

  • Host - Bazel 本身的執行平台。
  • Execution - 這個平台可讓建構工具執行建構動作,產生中繼和最終輸出內容。
  • 目標 - 執行最終輸出內容的平台,

Bazel 支援下列與平台相關的建構情境:

  • 單一平台建構 (預設):主機、執行和目標平台相同。例如,在 Intel x64 CPU 上執行的 Ubuntu 建構 Linux 執行檔。

  • 跨編譯建構:主機和執行平台相同,但目標平台不同。例如,在 macOS 上建構在 MacBook Pro 上執行的 iOS 應用程式。

  • 多平台版本 - 主機、執行和目標平台各不相同。

定義限制和平台

平台可能選擇的空間是在 BUILD 檔案中使用 constraint_settingconstraint_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 的平台,並表示此平台說明在採用 glibc 2.25 版本的 x86_64 架構上執行 Linux 作業系統的任何環境。(詳情請參閱 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 會採用下列特殊平台定義:@platforms//host (別名為 @bazel_tools//tools:host_platform)。這是系統自動偵測的主機平台值,代表執行系統 Bazel 的平台。

指定建構的平台

您可以使用下列指令列標記,為建構作業指定主機和目標平台:

  • --host_platform - 預設為 @bazel_tools//tools:host_platform
    • 此目標的別名為 @platforms//host,其受到存放區規則支援,可偵測主機 OS 和 CPU,並寫入平台目標。
    • 我們還提供了 @platforms//host:constraints.bzl,會公開名為 HOST_CONSTRAINTS 的陣列,可用於其他 BUILD 和 Starlark 檔案。
  • --platforms - 預設為主機平台
    • 這表示在未設定其他標記時,@platforms//host 是目標平台。
    • 如果已設定 --host_platform 而非 --platforms--host_platform 的值就會是主機和目標平台。

略過不相容的目標

要針對特定目標平台進行建構時,通常應略過永遠無法在該平台上運作的目標。舉例來說,在含有 //... 的 Linux 電腦上建構 Windows 裝置驅動程式時,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"],
    }),
)

上述解釋可以解釋如下:

  1. 如果指定 macOS,則目標沒有限制。
  2. 如果以 Linux 為目標,目標沒有限制。
  3. 否則,目標會包含 @platforms//:incompatible 限制。由於 @platforms//:incompatible 不屬於任何平台,因此目標無法判定。

如要讓限制更容易閱讀,請使用 skylibselects.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 cqueryStarlark 輸出格式中使用 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

已知問題

不相容的目標會忽略瀏覽權限限制