プラットフォームを使った構築

Bazel は、プラットフォームツールチェーンのモデリングを高度にサポートしています。これを実際のプロジェクトに統合するには、コードオーナー、ルール メンテナー、コア Bazel デベロッパー間の慎重な連携が必要です。

このページでは、プラットフォームの目的をまとめ、プラットフォームを使用して構築する方法について説明します。

要約: Bazel のプラットフォームとツールチェーンの API は利用可能ですが、すべての言語ルール、select()、その他の以前の参照が更新されるまでは、どこでも機能するわけではありません。これは継続的な取り組みです。最終的には、すべてのビルドがプラットフォーム ベースになります。ビルドがどのカテゴリに該当するかについては、以下をご覧ください。

より正式なドキュメントについては、以下をご覧ください。

背景

プラットフォームツールチェーンは、ソフトウェア プロジェクトがさまざまなマシンをターゲットとし、適切な言語ツールでビルドする方法を標準化するために導入されました。

これは、Bazel に比較的最近追加されたものです。これは、言語のメンテナーがすでにアドホックで互換性のない方法でこれを行っているという観察に触発されたものです。たとえば、C++ ルールでは --cpu--crosstool_top を使用して、ビルドのターゲット CPU と C++ ツールチェーンを設定します。どちらも「プラットフォーム」を正しくモデル化していません。過去にこの方法を試みたところ、不自然で不正確なビルドになってしまいました。これらのフラグは、--java_toolchain を使用して独自の独立したインターフェースを進化させた Java コンパイルも制御しません。

Bazel は、大規模なマルチ言語、マルチプラットフォーム プロジェクトを対象としています。そのため、言語とプロジェクトの相互運用性を促進する明確な API など、これらのコンセプトをより原則に沿ってサポートする必要があります。これらの新しい API は、このために用意されています。

移行

プラットフォームとツールチェーンの API は、プロジェクトで実際に使用されている場合にのみ機能します。プロジェクトのルールロジック、ツールチェーン、依存関係、select() がそれらをサポートする必要があるため、これは簡単なことではありません。これには、すべてのプロジェクトとその依存関係が正しく機能するように、慎重な移行シーケンスが必要です。

たとえば、Bazel の C++ ルールはプラットフォームをサポートしています。ただし、Apple ルールは異なります。C++ プロジェクトでは Apple を考慮しない場合があります。ただし、他のプラットフォームでは有効になる可能性があります。そのため、すべての C++ ビルドでプラットフォームをグローバルに有効にすることはまだ安全ではありません。

このページの残りの部分では、この移行シーケンスと、プロジェクトをいつどのように適合させることができるかについて説明します。

目標

すべてのプロジェクトが次の形式でビルドされると、Bazel のプラットフォーム移行が完了します。

bazel build //:myproject --platforms=//:myplatform

これは以下を意味します。

  1. プロジェクトで使用するルールは、//:myplatform から正しいツールチェーンを推測できます。
  2. プロジェクトの依存関係で使用されるルールは、//:myplatform から正しいツールチェーンを推測できます。
  3. いずれか: 依存関係のあるプロジェクトが //:myplatform をサポートしている、プロジェクトがレガシー API(--crosstool_top など)をサポートしている。
  4. //:myplatform は、CPUOS、および自動クロス プロジェクト互換性をサポートするその他の一般的なコンセプトの [共通宣言][Common Platform Declaration]{: .external} を参照します。
  5. 関連するすべてのプロジェクトの select() は、//:myplatform によって示されるマシン プロパティを理解しています。
  6. //:myplatform は、明確で再利用可能な場所に定義されます。プラットフォームがプロジェクトに固有の場合はプロジェクトの repo に、それ以外の場合は、このプラットフォームを使用する可能性のあるすべてのプロジェクトが確認できる場所に定義されます。

この目標が達成されると、古い API はすぐに削除されます。これが、プロジェクトがプラットフォームとツールチェーンを選択する標準的な方法になります。

プラットフォームを使用すべきですか?

プロジェクトのビルドまたはクロス コンパイルのみを行う場合は、プロジェクトの公式ドキュメントに沿って操作してください。

プロジェクト、言語、ツールチェーンのメンテナーは、最終的に新しい API をサポートすることになります。グローバル移行が完了するまで待つか、早期にオプトインするかは、価値と費用のニーズによって異なります。

  • --cpu などのハードコードされたフラグではなく、関心のある正確なプロパティで select() を使用するか、ツールチェーンを選択できます。たとえば、複数の CPU が同じ命令セットをサポートできます。
  • より正確なビルド。上記の例で select()--cpu に置き換えて、同じ命令セットをサポートする新しい CPU を追加すると、select() は新しい CPU を認識できません。ただし、プラットフォームでの select() は正確です。
  • ユーザー エクスペリエンスの簡素化。すべてのプロジェクトが理解している: --platforms=//:myplatform。コマンドラインで言語固有のフラグを複数指定する必要はありません。
  • 言語設計がシンプルになります。すべての言語で、ツールチェーンの定義、ツールチェーンの使用、プラットフォームに適したツールチェーンの選択に共通の API が使用されます。
  • ターゲットがターゲット プラットフォームと互換性がない場合、ビルドフェーズとテストフェーズでスキップできます。

料金

  • プラットフォームをまだサポートしていない依存プロジェクトは、自動的に動作しない可能性があります。
  • 動作させるには、一時的なメンテナンスを追加する必要がある場合があります。
  • 新しい API と以前の API の共存には、混乱を避けるためにユーザーへのより慎重なガイダンスが必要です。
  • OSCPU などの共通プロパティの正規定義はまだ進化しており、追加の初期コントリビューションが必要になる場合があります。
  • 言語固有のツールチェーンの正規定義はまだ進化しており、追加の初期コントリビューションが必要になる場合があります。

API による審査

platform は、constraint_value ターゲットのコレクションです。

platform(
    name = "myplatform",
    constraint_values = [
        "@platforms//os:linux",
        "@platforms//cpu:arm",
    ],
)

constraint_value はマシン プロパティです。同じ「種類」の値は、共通の constraint_setting にグループ化されます。

constraint_setting(name = "os")
constraint_value(
    name = "linux",
    constraint_setting = ":os",
)
constraint_value(
    name = "mac",
    constraint_setting = ":os",
)

toolchainStarlark ルールです。その属性は言語のツール(compiler = "//mytoolchain:custom_gcc" など)を宣言します。そのプロバイダは、これらのツールでビルドする必要があるルールにこの情報を渡します。

ツールチェーンは、ターゲットにできるマシン(target_compatible_with = ["@platforms//os:linux"])と、ツールを実行できるマシン(exec_compatible_with = ["@platforms//os:mac"])の constraint_value を宣言します。

$ bazel build //:myproject --platforms=//:myplatform をビルドするとき、Bazel はビルドマシンで実行でき、//:myplatform のバイナリをビルドできるツールチェーンを自動的に選択します。これはツールチェーン解決と呼ばれます。

使用可能なツールチェーンのセットは、WORKSPACEregister_toolchains を使用して登録するか、コマンドラインで --extra_toolchains を使用して登録できます。

詳しくは、こちらをご覧ください。

ステータス

現在のプラットフォームのサポートは言語によって異なります。Bazel の主要なルールのすべてがプラットフォームに移行しています。ただし、この処理には時間がかかります。主な理由は次の 3 つです。

  1. ルールロジックを更新して、新しいツールチェーン APIctx.toolchains)からツール情報を取得し、--cpu--crosstool_top などの以前の設定の読み取りを停止する必要があります。これは比較的簡単です。

  2. ツールチェーンのメンテナーは、ツールチェーンを定義し、ユーザーがアクセスできるようにする必要があります(GitHub リポジトリと WORKSPACE エントリ)。技術的には簡単ですが、ユーザー エクスペリエンスを維持するために、インテリジェントに整理する必要があります。

    (Bazel が実行されているマシンと同じマシン用にビルドする場合を除き)プラットフォーム定義も必要です。通常、プロジェクトは独自のプラットフォームを定義する必要があります。

  3. 既存のプロジェクトを移行する必要があります。select()トランジションも移行する必要があります。これが最大の課題です。特に、多言語プロジェクトでは問題が発生しやすくなります(すべての言語で --platforms を読み取れない場合、失敗する可能性があります)。

新しいルールセットを設計する場合は、最初からプラットフォームをサポートする必要があります。これにより、ルールが他のルールやプロジェクトと自動的に互換性を持つようになり、プラットフォーム API が普及するにつれて価値が高まります。

共通のプラットフォーム プロパティ

プロジェクト間で共通のプラットフォーム プロパティ(OSCPU など)は、標準化された一元的な場所で宣言する必要があります。これにより、プロジェクト間および言語間の互換性が促進されます。

たとえば、MyAppconstraint_value @myapp//cpus:armselect() を持ち、SomeCommonLib@commonlib//constraints:armselect() を持つ場合、これらは互換性のない条件で「アーム」モードをトリガーします。

グローバルに共通のプロパティは @platforms リポジトリで宣言されます(上記の例の正規ラベルは @platforms//cpu:arm です)。言語に共通のプロパティは、それぞれの言語のリポジトリで宣言する必要があります。

デフォルトのプラットフォーム

通常、プロジェクト オーナーは、構築するマシンの種類を記述するために、明示的なプラットフォームを定義する必要があります。これらは --platforms でトリガーされます。

--platforms が設定されていない場合、Bazel はデフォルトでローカルビルドマシンを表す platform になります。これは @local_config_platform//:host で自動生成されるため、明示的に定義する必要はありません。ローカルマシンの OSCPU@platforms で宣言された constraint_value にマッピングします。

C++

Bazel の C++ ルールでは、--incompatible_enable_cc_toolchain_resolution#7260)を設定するときに、プラットフォームを使用してツールチェーンを選択します。

つまり、次のように C++ プロジェクトを構成できます。

bazel build //:my_cpp_project --platforms=//:myplatform

ではなく、

bazel build //:my_cpp_project` --cpu=... --crosstool_top=...  --compiler=...

プロジェクトが純粋な C++ で、C++ 以外のプロジェクトに依存していない場合は、select移行が互換性がある限り、プラットフォームを安全に使用できます。詳しくは、#7260C++ ツールチェーンの構成をご覧ください。

このモードはデフォルトでは有効になっていません。これは、Apple プロジェクトが --cpu--crosstool_top)で C++ 依存関係を構成しているためです。そのため、これは Apple のルールがプラットフォームに移行されるかどうかに依存します。

Java

Bazel の Java ルールではプラットフォームを使用します。

これは、以前のフラグ --java_toolchain--host_java_toolchain--javabase--host_javabase に代わるものです。

構成フラグの使用方法については、Bazel と Java のマニュアルをご覧ください。詳細については、設計ドキュメントをご覧ください。

以前のフラグをまだ使用している場合は、問題 #7849 の移行プロセスに沿って移行してください。

Android

Bazel の Android ルールでは、--incompatible_enable_android_toolchain_resolution を設定するときに、プラットフォームを使用してツールチェーンを選択します。

これはデフォルトでは有効になっていません。しかし、移行は順調に進んでいます。

Apple

Bazel の Apple ルールは、Apple ツールチェーンを選択するプラットフォームをまだサポートしていません。

また、C++ ツールチェーンの設定に以前の --crosstool_top を使用しているため、プラットフォーム対応の C++ 依存関係もサポートしていません。移行が完了するまでは、プラットフォーム マッピング)を使用して、Apple プロジェクトとプラットフォーム対応の C++ を混在させることができます。

その他の言語

  • Bazel の Rust ルールは、プラットフォームを完全にサポートしています。
  • Bazel の Go ルールは、プラットフォームを完全にサポートしています(詳細)。

新しい言語のルールを設計する場合は、プラットフォームを使用して言語のツールチェーンを選択します。詳細なチュートリアルについては、ツールチェーンのドキュメントをご覧ください。

select()

プロジェクトは constraint_value ターゲットselect() できますが、プラットフォーム全体ではできません。これは、select() ができるだけ多くの種類のマシンをサポートできるようにするための意図的なものです。ARM 固有のソースを含むライブラリは、より具体的な理由がない限り、すべての ARM 搭載マシンをサポートする必要があります。

1 つ以上の constraint_value を選択するには、次のコマンドを使用します。

config_setting(
    name = "is_arm",
    constraint_values = [
        "@platforms//cpu:arm",
    ],
)

これは、従来の方法で --cpu を選択するのと同じです。

config_setting(
    name = "is_arm",
    values = {
        "cpu": "arm",
    },
)

詳しくはこちらをご覧ください。

--cpu--crosstool_top などの select--platforms を認識しません。プロジェクトをプラットフォームに移行する場合は、constraint_values に変換するか、プラットフォーム マッピングを使用して移行期間中に両方のスタイルをサポートする必要があります。

切り替え効果

Starlark 遷移は、ビルドグラフの一部でフラグを変更します。プロジェクトで --cpu--crossstool_top、または他のレガシー フラグを設定するトランジションを使用している場合、--platforms を読み取るルールではこれらの変更は認識されません。

プロジェクトをプラットフォームに移行する場合は、return { "//command_line_option:cpu": "arm" } などの変更を return { "//command_line_option:platforms": "//:my_arm_platform" } に変換するか、プラットフォーム マッピングを使用して移行期間中に両方のスタイルをサポートする必要があります。

現在のプラットフォームの使用方法

プロジェクトのビルドまたはクロス コンパイルのみを行う場合は、プロジェクトの公式ドキュメントに沿って操作してください。プラットフォームとの統合方法とタイミング、およびそのメリットを判断するのは、言語とプロジェクトのメンテナーです。

プロジェクト、言語、ツールチェーンのメンテナーで、ビルドでデフォルトでプラットフォームを使用しない場合は、次の 3 つのオプションがあります(グローバル移行を待つ以外)。

  1. プロジェクトの言語の「プラットフォームを使用」フラグをオンにして(ある場合)、必要なテストを実施して、対象のプロジェクトが動作するかどうかを確認します。

  2. 対象のプロジェクトが --cpu--crosstool_top などの以前のフラグに依存している場合は、これらのフラグを --platforms とともに使用します。

    bazel build //:my_mixed_project --platforms==//:myplatform --cpu=... --crosstool_top=...

    これにはメンテナンス費用がかかります(設定が一致していることを手動で確認する必要があります)。ただし、不正なトランジションがない場合は、この方法で動作します。

  3. --cpu スタイルの設定を対応するプラットフォームにマッピングし、その逆も行うことで、両方のスタイルをサポートするプラットフォーム マッピングを記述します。

プラットフォーム マッピング

プラットフォーム マッピングは、プラットフォーム駆動型ロジックとレガシー駆動型ロジックを、後者の非推奨期間中に同じビルド内で共存させる一時的な API です。

プラットフォーム マッピングは、platform() と対応する一連の以前のフラグとのマッピング、またはその逆のマッピングです。次に例を示します。

platforms:
  # Maps "--platforms=//platforms:ios" to "--cpu=ios_x86_64 --apple_platform_type=ios".
  //platforms:ios
    --cpu=ios_x86_64
    --apple_platform_type=ios

flags:
  # Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios".
  --cpu=ios_x86_64
  --apple_platform_type=ios
    //platforms:ios

  # Maps "--cpu=darwin --apple_platform_type=macos" to "//platform:macos".
  --cpu=darwin
  --apple_platform_type=macos
    //platforms:macos

Bazel はこれを使用して、プラットフォーム ベースとレガシーの両方のすべての設定が、トランジションを含め、ビルド全体で一貫して適用されるようにします。

デフォルトでは、Bazel はワークスペース ルートの platform_mappings ファイルからマッピングを読み取ります。--platform_mappings=//:my_custom_mapping を設定することもできます。

詳しくは、こちらをご覧ください。

質問

移行のタイムラインに関する一般的なサポートや質問については、bazel-discuss@googlegroups.com または適切なルールのオーナーにお問い合わせください。

プラットフォーム/ツールチェーン API の設計と進化に関する議論については、bazel-dev@googlegroups.com にお問い合わせください。

関連情報