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
という名前のプラットフォームを作成し、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 には、@local_config_platform//:host
の特別なプラットフォーム定義が付属しています。これは、自動検出されたホスト プラットフォームの値で、Bazel が実行されているシステムで自動検出されたプラットフォームを表します。
ビルドのプラットフォームを指定する
次のコマンドライン フラグを使用して、ビルドのホスト プラットフォームとターゲット プラットフォームを指定できます。
--host_platform
- デフォルトは@bazel_tools//platforms:host_platform
です。--platforms
- デフォルトは@bazel_tools//platforms:target_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
制約の表現の強化
制約をより柔軟に表現するには、どのプラットフォームも満たしていない @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"],
}),
)
これは次のように解釈できます。
- macOS をターゲットとする場合、ターゲットに制約はありません。
- Linux をターゲットとする場合、ターゲットに制約はありません。
- それ以外の場合、ターゲットには
@platforms//:incompatible
制約が適用されます。@platforms//:incompatible
はどのプラットフォームにも含まれていないため、ターゲットは互換性がないと見なされます。
制約を読みやすくするには、skylib の 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 cquery
の Starlark 出力形式で 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
既知の問題
互換性のないターゲットは、公開設定の制限を無視します。