日程の確定: BazelCon 2023 は、Google ミュンヘンで 10 月 24 ~ 25 日に開催されます。詳細

プラットフォーム

問題を報告する ソースを表示

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

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

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

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

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

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

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

  • --host_platform - デフォルトは @bazel_tools//platforms:host_platform です。
  • --platforms - デフォルトは @bazel_tools//platforms:target_platform です。

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

特定のターゲット プラットフォームをビルドする場合は、そのプラットフォームで機能しないターゲットをスキップすることが望ましいです。たとえば、Windows デバイス ドライバは、//... を使用して Linux マシンでビルドするときに多くのコンパイラ エラーを生成する可能性があります。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

コマンドラインで --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

既知の問題

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