プラットフォーム

問題を報告 ソースを表示

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

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

Bazel は、プラットフォームが提供する次の 3 つのロールを認識します。

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

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

  • シングル プラットフォーム ビルド(デフォルト)- ホスト プラットフォーム、実行プラットフォーム、ターゲット プラットフォームが同じです。たとえば、Intel x64 CPU で動作する Ubuntu で Linux 実行可能ファイルをビルドする場合などです。

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

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

制約とプラットフォームの定義

選択できるプラットフォームのスペースは、BUILD ファイル内で constraint_setting ルールと constraint_value ルールを使用して定義します。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 には、特別なプラットフォーム定義 @platforms//host@bazel_tools//tools:host_platform としてエイリアス)が付属しています。これは自動検出されたホスト プラットフォームの値で、Bazel が実行されているシステムで自動検出されたプラットフォームを表します。

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

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

  • --host_platform - デフォルトは @bazel_tools//tools:host_platform です。
    • このターゲットは @platforms//host にエイリアスされます。これは、ホストの OS と CPU を検出してプラットフォーム ターゲットを書き込むリポジトリ ルールに基づいています。
    • また、HOST_CONSTRAINTS という配列を公開する @platforms//host:constraints.bzl もあります。これは、他の BUILD ファイルと Starlark ファイルで使用できます。
  • --platforms - デフォルトはホスト プラットフォーム
    • つまり、他のフラグが設定されていない場合、@platforms//host がターゲット プラットフォームになります。
    • --host_platform が設定され、--platforms ではない場合、--host_platform の値はホストとターゲットの両方のプラットフォームになります。

互換性のないターゲットのスキップ

特定のターゲット プラットフォーム向けにビルドする場合、そのプラットフォームで動作しないターゲットはスキップすることをおすすめします。たとえば、//... を使用して Linux マシンでビルドすると、Windows デバイス ドライバで大量のコンパイラ エラーが生成される可能性があります。target_compatible_with 属性を使用して、コードにおけるターゲット プラットフォームの制約を Bazel に知らせます。

この属性の最も単純な使用法は、ターゲットを 1 つのプラットフォームに制限します。ターゲットは、すべての制約を満たさないプラットフォームにはビルドされません。次の例では、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

同様に、コマンドラインで --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

既知の問題

互換性のないターゲットは、公開設定の制限を無視します。