平台

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

  • 主機:Bazel 本身執行的平台。
  • 執行:建構工具執行建構動作的平台,用於產生中繼和最終輸出項目。
  • 「Target」:儲存並執行最終輸出內容的平台。

Bazel 支援下列平台建構情境:

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

  • 跨平台編譯建構作業:主機和執行平台相同,但目標平台不同。例如在 MacBook Pro 上執行的 macOS 上建構 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 的平台,並說明該平台會描述在 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 會提供以下特殊平台定義:@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 裝置驅動程式可能會產生大量編譯器錯誤。使用 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

已知問題

不相容的目標會忽略可見度限制