ツールチェーン

問題を報告する ソースを表示 夜間 · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

このページでは、ツールチェーン フレームワークについて説明します。ツールチェーン フレームワークは、ルールの作成者がルールの作成を プラットフォーム ベースのツールの選択肢からルール ロジックを切り離しています。内容 ルールプラットフォームを確認することをおすすめします。 してから続行してください。このページでは、ツールチェーンが必要な理由と、 Bazel がそれに基づいて適切なツールチェーンを選択する仕組み プラットフォームの制約があります。

目的

まず、ツールチェーンで解決できる問題を見ていきましょう。たとえば、 「バー」をサポートするためのルールを説明します。bar_binary ルールでは、barc コンパイラを使用して *.bar ファイルをコンパイルします。このコンパイラは、 ワークスペースの別のターゲットとして作成されます。bar_binary を記述するユーザーは ターゲットでコンパイラへの依存関係を指定する必要がない場合は、 暗黙的な依存関係をプライベート属性としてルール定義に追加することで、暗黙的に依存関係を作成できます。

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        "_compiler": attr.label(
            default = "//bar_tools:barc_linux",  # the compiler running on linux
            providers = [BarcInfo],
        ),
    },
)

//bar_tools:barc_linux はすべての bar_binary ターゲットの依存関係になるようになったため、 bar_binary ターゲットの前にビルドされます。ファイアウォール ルールの 他の属性と同様に実装できます。

BarcInfo = provider(
    doc = "Information about how to invoke the barc compiler.",
    # In the real world, compiler_path and system_lib might hold File objects,
    # but for simplicity they are strings for this example. arch_flags is a list
    # of strings.
    fields = ["compiler_path", "system_lib", "arch_flags"],
)

def _bar_binary_impl(ctx):
    ...
    info = ctx.attr._compiler[BarcInfo]
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

ここでの問題は、コンパイラのラベルが bar_binary にハードコードされているにもかかわらず、 ターゲットが異なれば、プラットフォームによって必要なコンパイラも異なる どのプラットフォーム上に構築されているか それぞれ「ターゲット プラットフォーム」と「実行プラットフォーム」があります。また、このルールは、 作成者は必ずしも利用可能なすべてのツールとプラットフォームを知っているとは限らないため、 ルールの定義にハードコードすることはできません。

理想的とは言えない解決策は、次のようにしてユーザーの負担を軽減することです。 _compiler 属性は非非公開にします。個々のターゲットは ハードコードされています。

bar_binary(
    name = "myprog_on_linux",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_linux",
)

bar_binary(
    name = "myprog_on_windows",
    srcs = ["mysrc.bar"],
    compiler = "//bar_tools:barc_windows",
)

select を使用して compiler を選択すると、このソリューションを改善できます。 プラットフォームに応じて:

config_setting(
    name = "on_linux",
    constraint_values = [
        "@platforms//os:linux",
    ],
)

config_setting(
    name = "on_windows",
    constraint_values = [
        "@platforms//os:windows",
    ],
)

bar_binary(
    name = "myprog",
    srcs = ["mysrc.bar"],
    compiler = select({
        ":on_linux": "//bar_tools:barc_linux",
        ":on_windows": "//bar_tools:barc_windows",
    }),
)

しかし、これは面倒で、bar_binary ユーザー一人ひとりに依頼しなければならない作業量が少しだけ多くなります。 ワークスペース全体でこのスタイルを一貫して使用しないと、 単一のプラットフォームで問題なく動作するが、プロジェクトに拡張すると失敗する マルチプラットフォームのシナリオで対応できますまた、サポートの追加という問題に対処することもありません。 新しいプラットフォームやコンパイラに対応。既存のルールやターゲットを変更する必要はありません。

ツールチェーン フレームワークは、この問題を解消するために、 あります。基本的に、ルールに抽象的な依存関係があることを ターゲット ファミリー(ツールチェーン タイプ)の一部のメンバー、Bazel これは、出力値に基づいて、自動的に特定のターゲット(ツールチェーン)に解決されます。 適用できます。ルール作成者でもターゲット作成者でもない 利用可能なプラットフォームとツールチェーンの完全なセットを 知る必要があります。

ツールチェーンを使用するルールを作成する

ツールチェーン フレームワークでは、ルールがツールに直接依存するのではなく、 ツールチェーン タイプに依存します。ツールチェーン型は単純なターゲット 異なるサービスに対して同じ役割を担うツールのクラスを表す 説明します。たとえば、バーを表す型を宣言し、 コンパイラ:

# By convention, toolchain_type targets are named "toolchain_type" and
# distinguished by their package path. So the full path for this would be
# //bar_tools:toolchain_type.
toolchain_type(name = "toolchain_type")

前のセクションのルール定義は、 コンパイラを属性として受け取る場合、 //bar_tools:toolchain_type ツールチェーン。

bar_binary = rule(
    implementation = _bar_binary_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = True),
        ...
        # No `_compiler` attribute anymore.
    },
    toolchains = ["//bar_tools:toolchain_type"],
)

実装関数は、ctx.toolchains でこの依存関係にアクセスするようになりました。 ctx.attr ではなく、ツールチェーン タイプをキーとして使用します。

def _bar_binary_impl(ctx):
    ...
    info = ctx.toolchains["//bar_tools:toolchain_type"].barcinfo
    # The rest is unchanged.
    command = "%s -l %s %s" % (
        info.compiler_path,
        info.system_lib,
        " ".join(info.arch_flags),
    )
    ...

ctx.toolchains["//bar_tools:toolchain_type"] は、 ToolchainInfo プロバイダ ツールチェーンの依存関係が解決されたターゲット Bazel によるものです。このフィールドの ToolchainInfo オブジェクトは、基盤となるツールのルールによって設定されます。以内 セクション内では、このルールは、値をラップする barcinfo フィールドが BarcInfo オブジェクト。

ツールチェーンをターゲットに解決する Bazel の手順 下記をご覧ください。解決されたツールチェーン ターゲットのみが 候補の領域全体ではなく、bar_binary ターゲットに依存した 統合できます。

必須およびオプションのツールチェーン

デフォルトでは、ルールがベアラベルを使用してツールチェーン型の依存関係を表現している場合 (上記のように)ツールチェーン タイプは必須と見なされます。Bazel の場合 一致するツールチェーンが見つからない(参照: 下記のツールチェーンの解決策を参照)の必須のツールチェーン これがエラーとなり分析が停止します。

代わりに、次のように、オプションのツールチェーン型の依存関係を宣言することもできます。 次のようになります。

bar_binary = rule(
    ...
    toolchains = [
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

オプションのツールチェーン タイプを解決できない場合、分析が続行され、 ctx.toolchains["//bar_tools:toolchain_type"] の結果は None です。

config_common.toolchain_type 関数はデフォルトで必須になります。

以下の形式を使用できます。

  • 必須のツールチェーン タイプ:
    • toolchains = ["//bar_tools:toolchain_type"]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type")]
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = True)]
  • オプションのツールチェーン タイプ:
    • toolchains = [config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False)]
bar_binary = rule(
    ...
    toolchains = [
        "//foo_tools:toolchain_type",
        config_common.toolchain_type("//bar_tools:toolchain_type", mandatory = False),
    ],
)

同じルールでフォームを組み合わせることもできます。ただし、 ツールチェーン型が複数回リストされている場合、最も厳密なバージョンが使用されます。 必須のフィールドは省略できます

ツールチェーンを使用するアスペクトの記述

アスペクトはルールと同じツールチェーン API にアクセスできます。必要な コンテキストを介してツールチェーンにアクセスし、それらを使用して新しいツールチェーンを生成する 使用してアクションを実行できます。

bar_aspect = aspect(
    implementation = _bar_aspect_impl,
    attrs = {},
    toolchains = ['//bar_tools:toolchain_type'],
)

def _bar_aspect_impl(target, ctx):
  toolchain = ctx.toolchains['//bar_tools:toolchain_type']
  # Use the toolchain provider like in a rule.
  return []

ツールチェーンの定義

特定のツールチェーン タイプのツールチェーンを定義するには、次の 3 つが必要です。

  1. ツールやツールスイートの種類を表す言語固有のルール。方法 ルール名の末尾には「_ツールチェーン」が付きます。

    1. 注: \_toolchain ルールではビルド アクションを作成できません。 代わりに、他のルールからアーティファクトを収集し、 ツールチェーンを使用するルールです。このルールにより、すべてのイベント 作成できます。
  2. ツールまたはツールのバージョンを表す、このルールタイプの複数のターゲット さまざまなプラットフォームに 対応しています

  3. このようなターゲットごとに、汎用 toolchain ツールチェーン フレームワークで使用されるメタデータを提供します。このtoolchain ターゲットは、このツールチェーンに関連付けられた toolchain_type も参照します。 つまり、特定の _toolchain ルールを任意のルールに関連付けることができます。 toolchain_type と、それを使用する toolchain インスタンス内のみ この _toolchain ルールでは、ルールが toolchain_type に関連付けられている必要があります。

実行例における bar_toolchain ルールの定義は次のとおりです。Google コンパイラしかありませんが、リンカーなどの他のツールも その下にグループ化されます。

def _bar_toolchain_impl(ctx):
    toolchain_info = platform_common.ToolchainInfo(
        barcinfo = BarcInfo(
            compiler_path = ctx.attr.compiler_path,
            system_lib = ctx.attr.system_lib,
            arch_flags = ctx.attr.arch_flags,
        ),
    )
    return [toolchain_info]

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler_path": attr.string(),
        "system_lib": attr.string(),
        "arch_flags": attr.string_list(),
    },
)

ルールは ToolchainInfo プロバイダを返す必要があります。このプロバイダは、 使用するルールは、ctx.toolchains と ツールチェーン タイプ。ToolchainInfostruct など)は、任意のフィールド値を保持できます。 あります。ToolchainInfo に追加されるフィールドの正確な仕様 ツールチェーン タイプに明記する必要があります。この例では、 上で定義したスキーマを再利用するために、BarcInfo オブジェクトにラップされた戻り値を返す。 スタイルは検証やコードの再利用に役立ちます。

特定の barc コンパイラのターゲットを定義できるようになりました。

bar_toolchain(
    name = "barc_linux",
    arch_flags = [
        "--arch=Linux",
        "--debug_everything",
    ],
    compiler_path = "/path/to/barc/on/linux",
    system_lib = "/usr/lib/libbarc.so",
)

bar_toolchain(
    name = "barc_windows",
    arch_flags = [
        "--arch=Windows",
        # Different flags, no debug support on windows.
    ],
    compiler_path = "C:\\path\\on\\windows\\barc.exe",
    system_lib = "C:\\path\\on\\windows\\barclib.dll",
)

最後に、2 つの bar_toolchain ターゲットの toolchain 定義を作成します。 これらの定義により、言語固有のターゲットがツールチェーン タイプにリンクされ、 ツールチェーンが失敗したときに Bazel に指示する制約情報を提供します 選択することもできます

toolchain(
    name = "barc_linux_toolchain",
    exec_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:linux",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_linux",
    toolchain_type = ":toolchain_type",
)

toolchain(
    name = "barc_windows_toolchain",
    exec_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    target_compatible_with = [
        "@platforms//os:windows",
        "@platforms//cpu:x86_64",
    ],
    toolchain = ":barc_windows",
    toolchain_type = ":toolchain_type",
)

上記の相対パス構文を使用していることから、これらの定義はすべて 言語固有のツールチェーン タイプや、 ツールチェーン ターゲットと toolchain 定義ターゲットをすべて別々の場所に配置することはできません 提供します。

go_toolchain をご覧ください。 をご覧ください。

ツールチェーンと構成

ルール作成者にとって重要な問題は、bar_toolchain のターゲットが どの構成が認識するか、どのような遷移が 依存関係に使うべきでしょうか?上記の例では文字列属性を使用していますが、 他のターゲットに依存する複雑なツールチェーンではどうなるでしょうか。 あるでしょうか。

bar_toolchain のより複雑なバージョンを見てみましょう。

def _bar_toolchain_impl(ctx):
    # The implementation is mostly the same as above, so skipping.
    pass

bar_toolchain = rule(
    implementation = _bar_toolchain_impl,
    attrs = {
        "compiler": attr.label(
            executable = True,
            mandatory = True,
            cfg = "exec",
        ),
        "system_lib": attr.label(
            mandatory = True,
            cfg = "target",
        ),
        "arch_flags": attr.string_list(),
    },
)

attr.label の使い方は標準ルールの場合と同じですが、 cfg パラメータの意味は若干異なります。

ターゲット(「親」)からツールチェーンを介したツールチェーンへの依存関係 「ツールチェーン」と呼ばれる特別な構成遷移を使用します。 移行します。ツールチェーンの遷移では、構成は変わりませんが、 ツールチェーンの実行プラットフォームを 親(そうしないと、ツールチェーンのツールチェーンの解決で任意の 親と必ずしも一致しません)。この これにより、ツールチェーンの exec 依存関係も ビルド アクションを実行できます。cfg = "target" を使用する(または「target」がデフォルトであるため cfg を指定しない)ツールチェーンの依存関係は次のとおりです。 親と同じターゲット プラットフォーム向けにビルドされます。これにより、ツールチェーン ルールで ライブラリ(上記の system_lib 属性)とツール( compiler 属性など)を、それらを必要とするビルドルールに追加します。システム ライブラリ 最終アーティファクトにリンクされているため、同じものとして作成する必要があります。 一方、コンパイラはビルド時に呼び出されるツールであり、 実行プラットフォーム上で実行できます

ツールチェーンを使用した登録とビルド

すべての構成要素が完成しました Bazel の解決手順で使用可能なツールチェーン。これは、 MODULE.bazel ファイルで、または register_toolchains() を使用するか、ツールチェーンの使用すると、 この行を --extra_toolchains フラグで指定しています。

register_toolchains(
    "//bar_tools:barc_linux_toolchain",
    "//bar_tools:barc_windows_toolchain",
    # Target patterns are also permitted, so you could have also written:
    # "//bar_tools:all",
    # or even
    # "//bar_tools/...",
)

ターゲット パターンを使用してツールチェーンを登録する場合、 個々のツールチェーンは、次のルールによって決まります。

  • パッケージのサブパッケージで定義されたツールチェーンは、パッケージの ツールチェーンも使用できます。
  • パッケージ内では、ツールチェーンは できます。

ツールチェーン タイプに依存するターゲットをビルドする場合は、 ツールチェーンは、ターゲット プラットフォームと実行プラットフォームに基づいて選択されます。

# my_pkg/BUILD

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

bar_binary(
    name = "my_bar_binary",
    ...
)
bazel build //my_pkg:my_bar_binary --platforms=//my_pkg:my_target_platform

Bazel は、//my_pkg:my_bar_binary が以下のプラットフォームでビルドされていることを確認します。 @platforms//os:linux が含まれているため、 //bar_tools:barc_linux_toolchain への //bar_tools:toolchain_type 参照。 これにより //bar_tools:barc_linux がビルドされますが、 //bar_tools:barc_windows

ツールチェーンの解像度

ツールチェーンを使用するターゲットごとに、Bazel のツールチェーンの解決手順 ターゲットの具体的なツールチェーンの依存関係を決定します。プロシージャは 必要なツールチェーン タイプ、ターゲット プラットフォーム、 ツールチェーンのリストを参照してください。出力 ツールチェーン タイプごとに選択されたツールチェーンと、選択された実行です 現在のターゲットのプラットフォームを選択します。

使用可能な実行プラットフォームとツールチェーンは、 外部依存関係グラフを register_execution_platforms および register_toolchains 件の着信 MODULE.bazel ファイル。 追加の実行プラットフォームとツールチェーンも、 コマンドラインで --extra_execution_platforms および --extra_toolchains。 ホスト プラットフォームは、使用可能な実行プラットフォームとして自動的に含まれます。 利用可能なプラットフォームとツールチェーンが、決定論の順序付きリストとして追跡されている。 リスト内の前のアイテムが優先されます。

利用可能なツールチェーンのセットは、優先順位に従って、 --extra_toolchainsregister_toolchains:

  1. --extra_toolchains を使用して登録されたツールチェーンが最初に追加されます。( 最後のツールチェーンが最も優先されます)。
  2. 推移的外部で register_toolchains を使用して登録されたツールチェーン 依存関係グラフを次の順序で並べます。(これらのうち、最初の ツールチェーンが最も優先順位が高いと述べています)。
    1. ルート モジュールによって登録されたツールチェーン(MODULE.bazel workspace root);
    2. ユーザーの WORKSPACE ファイルに登録されているツールチェーン( 呼び出すことができます。
    3. 非ルート モジュールによって登録されたツールチェーン( ルート モジュール、その依存関係など)
    4. 「WORKSPACE サフィックス」に登録されているツールチェーンこれは 特定のネイティブ ルールが Bazel インストールにバンドルされています。

注: :all:*/... は Bazel パッケージで並べ替えられています 読み込みメカニズムを使用します。

解決手順は次のとおりです。

  1. target_compatible_with 句または exec_compatible_with 句が次に一致する リスト内の constraint_value ごとにプラットフォームに その constraint_value を(明示的に、またはデフォルトとして)設定します。

    プラットフォームに constraint_setting からの constraint_value がある場合、 照合には影響しません。

  2. ビルド中のターゲットで exec_compatible_with 属性 (またはそのルール定義では、 exec_compatible_with 引数)、 使用可能な実行プラットフォームのリストは 実行制約に一致しないすべてのリソースが含まれます。

  3. 使用可能なツールチェーンのリストがフィルタされ、すべてのツールチェーンが削除されます。 現在の構成と一致しない target_settings を指定しています。

  4. 使用可能な実行プラットフォームごとに、各ツールチェーン タイプを この実行と互換性のある最初のツールチェーン(存在する場合) ターゲットプラットフォームによって異なります

  5. 互換性のある必須ツールチェーンを検出できなかった実行プラットフォーム 除外されます。残りのプラットフォームのうち 最初のものが現在のターゲットの実行プラットフォームになり、 ツールチェーン(存在する場合)はターゲットの依存関係になります。

選択した実行プラットフォームは、ターゲット ユーザーが 生成します。

同じターゲットを複数の構成( 同じビルド内で異なる CPU の場合は、解決手順が適用されます。 ターゲットのバージョンごとに独立して動作します。

ルールで実行グループを使用している場合、各実行は グループはツールチェーンの解決を個別に実行し、それぞれに独自の実行があります。 ツールチェーンを紹介します

ツールチェーンのデバッグ

既存のルールにツールチェーン サポートを追加する場合は、 --toolchain_resolution_debug=regex フラグ。ツールチェーンの解決中に、このフラグは 正規表現変数に一致するツールチェーン タイプまたはターゲット名の詳細出力を提供します。マイページ .* を使用してすべての情報を出力できます。Bazel はツールチェーンの名前を出力します。 チェックを行い、解決プロセスでスキップします。

ツールチェーンに由来する cquery 依存関係を確認する場合 cquery--transitions フラグを使用します。

# Find all direct dependencies of //cc:my_cc_lib. This includes explicitly
# declared dependencies, implicit dependencies, and toolchain dependencies.
$ bazel cquery 'deps(//cc:my_cc_lib, 1)'
//cc:my_cc_lib (96d6638)
@bazel_tools//tools/cpp:toolchain (96d6638)
@bazel_tools//tools/def_parser:def_parser (HOST)
//cc:my_cc_dep (96d6638)
@local_config_platform//:host (96d6638)
@bazel_tools//tools/cpp:toolchain_type (96d6638)
//:default_host_platform (96d6638)
@local_config_cc//:cc-compiler-k8 (HOST)
//cc:my_cc_lib.cc (null)
@bazel_tools//tools/cpp:grep-includes (HOST)

# Which of these are from toolchain resolution?
$ bazel cquery 'deps(//cc:my_cc_lib, 1)' --transitions=lite | grep "toolchain dependency"
  [toolchain dependency]#@local_config_cc//:cc-compiler-k8#HostTransition -> b6df211