プラットフォーム

Bazel は、リンカーやコンパイラなど、さまざまなバージョンのビルドツールを使用して、さまざまなハードウェア、オペレーティング システム、システム構成でコードをビルドしてテストできます。こうした複雑さを管理しやすくするために、Bazel には 制約プラットフォームという概念があります。制約とは、ビルド環境や本番環境が異なる可能性があるディメンションです。たとえば、CPU アーキテクチャ、GPU の有無、システムにインストールされているコンパイラのバージョンなどです。プラットフォームとは、これらの制約の選択肢の名前付きコレクションであり、特定の環境で使用できる特定のリソースを表します。

環境をプラットフォームとしてモデル化することで、Bazel はビルド アクションに適した ツールチェーン を自動的に選択できます。プラットフォームは、 config_setting ルールと組み合わせて使用して、構成可能な属性を記述することもできます。

Bazel は、プラットフォームが果たす可能性のある 3 つのロールを認識します。

  • ホスト - Bazel 自体が実行されるプラットフォーム。
  • 実行 \- ビルドツールがビルド アクションを実行して 中間出力と最終出力を生成するプラットフォーム。
  • ターゲット - 最終出力が存在し、実行されるプラットフォーム。

Bazel は、プラットフォームに関する次のビルド シナリオをサポートしています。

  • 単一プラットフォーム ビルド (デフォルト) - ホスト プラットフォーム、実行プラットフォーム、ターゲット プラットフォームは同じです。たとえば、Intel x64 CPU で実行されている Ubuntu で Linux 実行可能ファイルをビルドします。

  • クロスコンパイル ビルド \- ホスト プラットフォームと実行プラットフォームは同じですが、 ターゲット プラットフォームは異なります。たとえば、MacBook Pro で実行されている macOS で iOS アプリをビルドします。

  • マルチプラットフォーム ビルド - ホスト プラットフォーム、実行プラットフォーム、ターゲット プラットフォームはすべて 異なります。

制約とプラットフォームを定義する

プラットフォームの選択肢のスペースは、 constraint_setting および constraint_value ルールを BUILD ファイル内で使用して定義します。 constraint_setting は新しいディメンションを作成し、constraint_value は指定されたディメンションの新しい値を作成します。これらを組み合わせることで、列挙型とその可能な値を効果的に定義できます。たとえば、次の BUILD ファイルのスニペットでは、システムの glibc バージョンの制約が 2 つの可能な値で導入されています。

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 には、@local_config_platform//:host という特別なプラットフォーム定義が付属しています。これは自動検出されたホスト プラットフォームの値です。Bazel が実行されているシステムの自動検出されたプラットフォームを表します。

ビルドのプラットフォームを指定する

次のコマンドライン フラグを使用して、ビルドのホスト プラットフォームとターゲット プラットフォームを指定できます。

  • --host_platform - デフォルトは @local_config_platform//:host
      です。
    • @local_config_platform は、ホスト OS と CPU を検出し、プラットフォーム ターゲットを書き込むリポジトリ ルールです。
    • また、@local_config_platform//:constraintz.bzl も作成します。これは、他の BUILD ファイルと Starlark ファイルで使用できる HOST_CONSTRAINTS という配列を公開します。
  • --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 でのビルドにのみ互換性があり、それ以外はすべて互換性がありません。 非互換性は推移的です。互換性のないターゲットに推移的に依存するターゲットは、互換性がないと見なされます。

ターゲットがスキップされるのはいつですか?

ターゲットは、互換性がないと見なされ、ターゲット パターン展開の一部としてビルドに含まれている場合にスキップされます。たとえば、次の 2 つの呼び出しでは、ターゲット パターン展開で見つかった互換性のないターゲットがスキップされます。

$ bazel build --platforms=//:myplatform //...
$ bazel build --platforms=//:myplatform //:all

test_suite で互換性のないテストは、test_suite がコマンドラインで --expand_test_suites を指定して指定されている場合も同様にスキップされます。 つまり、コマンドラインの 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 はどのプラットフォームにも含まれていないため、ターゲットは互換性がないと見なされます。

制約を読みやすくするには、 skylib's 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 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

既知の問題

互換性のないターゲットは、可視性の制限 を無視します