ルール

<ph type="x-smartling-placeholder"></ph> 問題を報告する ソースを表示 夜間 · 7.3 · 7.2 · 7.1 · 7.0 · 6.5

ルールは、Bazel で実行される一連のアクションを定義します。 出力のセットを生成するための入力です。この出力は、 ルールの ID によって返されるプロバイダ 実装関数。たとえば、C++ バイナリルールの例:

  1. .cpp ソースファイル(入力)のセットを取得します。
  2. ソースファイルに対して g++ を実行します(アクション)。
  3. 実行可能出力と他のファイルを含めて DefaultInfo プロバイダを返して、ランタイムで使用できるようにします。
  4. 取得した C++ 固有の情報を使用して CcInfo プロバイダを返します。 その依存関係を定義します。

Bazel の観点では、g++ と標準 C++ ライブラリもこのルールの入力です。ルール作成者は、ルールへのユーザー提供入力だけでなく、アクションの実行に必要なすべてのツールとライブラリも考慮する必要があります。

ルールを作成または変更する前に、Bazel のビルドフェーズをよく理解してください。データ アナリストは、 (読み込み、分析、実行)。ルールとマクロの違いを理解するには、マクロについても学習することをおすすめします。使用を開始するには、まずルールのチュートリアルをご覧ください。 このページを参照用として使用します。

Bazel 自体には、いくつかのルールが組み込まれています。次のようなネイティブ ルールcc_libraryjava_binary は、特定の言語のコアサポートを提供します。 独自のルールを定義することで、言語とツールに対する同様のサポートを追加できます。 Bazel でネイティブにサポートされていないバージョンです。

Bazel には、Starlark 言語を使用してルールを記述するための拡張モデルが用意されています。これらのルールは .bzl ファイルで記述され、BUILD ファイルから直接読み込むことができます。

独自のルールを定義するときは、ルールでサポートする属性と、ルールで実行できる属性と 出力の生成方法を確認できます。

ルールの implementation 関数は、ルールの 分析フェーズ。この関数は何も実行せず 使用できます。代わりに、後で実行フェーズでルールの出力を構築するために使用されるアクションを登録します(必要に応じて)。

ルールの作成

.bzl ファイルで、rule 関数を使用して新しい 結果をグローバル変数に格納します。rule の呼び出しでは、 属性実装関数:

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "deps": attr.label_list(),
        ...
    },
)

example_library という名前のルールの種類を定義します。

rule の呼び出しでは、ルールが実行可能ファイルの出力(executable=True を使用)を作成するのか、特にテスト実行可能ファイル(test=True を使用)を作成するのかを指定する必要があります。後者の場合、ルールはテストルールであり、ルールの名前は _test で終わる必要があります。

ターゲットのインスタンス化

ルールは BUILD ファイルで読み込みして呼び出すことができます。

load('//some/pkg:rules.bzl', 'example_library')

example_library(
    name = "example_target",
    deps = [":another_target"],
    ...
)

ビルドルールへの呼び出しは値を返しませんが、ターゲットを定義する副作用があります。これはルールのインスタンス化と呼ばれます。これにより、新しいターゲットの名前と、ターゲットの属性の値を指定します。

ルールは Starlark 関数から呼び出して、.bzl ファイルに読み込むこともできます。ルールを呼び出す Starlark 関数は、Starlark マクロと呼ばれます。Starlark のマクロは、最終的に BUILD ファイルから呼び出す必要があります。 読み込みフェーズBUILD が呼び出されたとき ターゲットをインスタンス化するために評価されます。

属性

属性はルール引数です。属性は、ターゲットの実装に特定の値を提供できます。また、他のターゲットを参照して、依存関係のグラフを作成することもできます。

ルール固有の属性(srcsdeps など)は、マップを渡すことで定義します。 属性名からスキーマ(attr を使用して作成)に モジュール)を ruleattrs パラメータに追加します。 一般的な属性: namevisibility は、すべてのルールに暗黙的に追加されます。追加属性は、特に実行可能ルールとテストルールに暗黙的に追加されます。ルールに暗黙的に追加される属性は、attrs に渡される辞書に含めることはできません。

依存関係属性

通常、ソースコードを処理するルールでは、 さまざまな種類の依存関係があります。

  • srcs は、ターゲットのアクションによって処理されるソースファイルを指定します。多くの場合、 属性スキーマは、並べ替えに必要なファイル拡張子を指定します ソースファイルを指定します。ヘッダー ファイルを使用する言語のルール 通常は、商品またはサービスによって処理されるヘッダーに、個別の hdrs 属性を 把握することです
  • deps は、ターゲットのコード依存関係を指定します。属性スキーマは それらの依存関係が提供する必要があるプロバイダを指定します。(たとえば、cc_libraryCcInfo を提供します)。
  • data は、実行時に任意の実行可能ファイルで使用できるようにするファイルを指定します。 これはターゲットによって異なりますこれにより、任意のファイルを あります。
example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        "srcs": attr.label_list(allow_files = [".example"]),
        "hdrs": attr.label_list(allow_files = [".header"]),
        "deps": attr.label_list(providers = [ExampleInfo]),
        "data": attr.label_list(allow_files = True),
        ...
    },
)

以下は、依存関係属性の例です。次を指定する属性 入力ラベル( attr.label_list attr.label、または attr.label_keyed_string_dict) 依存関係を指定するために ターゲットと、そのラベル(または対応する Label オブジェクト)は、ターゲットが属性の 定義します。これらのラベルのリポジトリとパスは、定義されたターゲットに対して相対的に解決されます。

example_library(
    name = "my_target",
    deps = [":other_target"],
)

example_library(
    name = "other_target",
    ...
)

この例では、other_targetmy_target の依存関係であるため、 other_target が最初に分析されます。期間に周期がある場合はエラーになります。 ターゲットの依存関係グラフ。

非公開属性と暗黙的な依存関係

依存関係属性にデフォルト値を設定すると、暗黙的な依存関係が作成されます。これは、 ユーザーが指定していないターゲット グラフの一部であるため、暗黙的に指定されます。 BUILD ファイルで指定する。暗黙的な依存関係は、ルールとツール(コンパイラなどのビルド時依存関係)の関係をハードコードする場合に便利です。ほとんどの場合、ユーザーはルールで使用するツールを指定することに関心がありません。これはルールの実装関数内では 他の依存関係と同じです。

ユーザーに許可せずに暗黙的な依存関係を提供する場合 その値をオーバーライドするには、属性に名前を指定して private にできます アンダースコア(_)で始まる。非公開属性にはデフォルトの 使用できます。通常、プライベート属性は暗黙的な依存関係にのみ使用します。

example_library = rule(
    implementation = _example_library_impl,
    attrs = {
        ...
        "_compiler": attr.label(
            default = Label("//tools:example_compiler"),
            allow_single_file = True,
            executable = True,
            cfg = "exec",
        ),
    },
)

この例では、example_library タイプのすべてのターゲットに、コンパイラ //tools:example_compiler に対する暗黙的な依存関係があります。これにより、 呼び出すアクションを生成する example_library の実装関数 ユーザーが自身のラベルを入力として渡さなかった場合でも、コンパイルされます。以降 _compiler は非公開属性であり、ctx.attr._compiler に従います。 このルールのすべてのターゲットで、常に //tools:example_compiler を指します。 あります。または、アンダースコアなしで属性に compiler という名前を付け、デフォルト値を保持することもできます。これにより、必要に応じて別のコンパイラを代用できますが、コンパイラのラベルを意識する必要はありません。

暗黙的な依存関係は、通常、ルールの実装と同じリポジトリにあるツールに使用されます。ツールが 実行プラットフォームまたは別のリポジトリを使用する場合は、 ルールは、ツールチェーンからそのツールを取得する必要があります。

出力属性

出力属性attr.outputattr.output_list など)は、ターゲットが生成する出力ファイルを宣言します。これらは、次の 2 つの点で依存関係属性と異なります。

  • 定義されたターゲットを参照する代わりに、出力ファイル ターゲットを定義します。 できます。
  • 出力ファイル ターゲットは、インスタンスではなく、インスタンス化されたルール ターゲットに依存します。 その逆に変更できます。

通常、出力属性は、ターゲット名に基づかないユーザー定義名を持つ出力をルールで作成する必要がある場合にのみ使用されます。ルールに出力属性が 1 つある場合、通常の名前は out または outs です。

出力属性は、宣言された出力を作成するためのおすすめの方法です。 具体的に依存するか、 コマンドラインでリクエストできます。

実装関数

すべてのルールには implementation 関数が必要です。これらの関数は分析フェーズで厳密に実行され、読み込みフェーズで生成されたターゲットのグラフを、実行フェーズで実行されるアクションのグラフに変換します。そのため、実装関数は実際にファイルを読み書きできません。

ルール実装関数は通常非公開(先頭にアンダースコアが付いた名前)です。慣例的には、ルールと同じ名前が付けられますが、 _impl で。

実装関数は、ルール コンテキスト(通常は ctx という名前)という 1 つのパラメータのみを取ります。プロバイダのリストが返されます。

ターゲット

依存関係は、分析時に Target オブジェクトとして表されます。これらのオブジェクトには、ターゲットの実装関数が実行されたときに生成されたプロバイダが含まれています。

ctx.attr には、それぞれの名前に対応するフィールドがあります。 各直接的な関係を表す Target オブジェクトを含む その属性を介して依存関係を設定します。label_list 属性の場合、これは Targets のリストです。label 属性の場合、これは単一の Target または None です。

プロバイダ オブジェクトのリストは、ターゲットの実装関数によって返されます。

return [ExampleInfo(headers = depset(...))]

これらには、インデックス表記([])を使用してアクセスできます。プロバイダのタイプをキーとして指定します。Starlark で定義されたカスタム プロバイダか、 ネイティブ ルールのプロバイダ(Starlark として利用可能) 使用します。

たとえば、ルールで hdrs 属性を使用してヘッダー ファイルを取得し、 ターゲットとそのコンシューマのコンパイル アクションに 次のように収集します。

def _example_library_impl(ctx):
    ...
    transitive_headers = [hdr[ExampleInfo].headers for hdr in ctx.attr.hdrs]

プロバイダ オブジェクトのリストではなく、ターゲットの実装関数から struct が返される従来スタイルの場合:

return struct(example_info = struct(headers = depset(...)))

プロバイダは、Target オブジェクトの対応するフィールドから取得できます。

transitive_headers = [hdr.example_info.headers for hdr in ctx.attr.hdrs]

このスタイルは使用しないことを強くおすすめします。ルールを 移行したということです。

ファイル

ファイルは File オブジェクトで表されます。Bazel は分析フェーズ中にファイル I/O を実行しないため、これらのオブジェクトを使用してファイル コンテンツを直接読み書きすることはできません。代わりに、アクションを生成関数(ctx.actions を参照)に渡して、アクショングラフの一部を構築します。

File は、ソースファイルまたは生成されたファイルのいずれかです。生成される各ファイルは、1 つのアクションの出力である必要があります。ソースファイルは、 できます。

依存関係属性ごとに、ctx.files の対応するフィールドには、その属性を介したすべての依存関係のデフォルト出力のリストが含まれます。

def _example_library_impl(ctx):
    ...
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    ...

ctx.file には、次の要素に対する単一の File または None が含まれています。 仕様で allow_single_file=True が設定されている依存関係属性。 ctx.executablectx.file と同じように動作しますが、仕様で executable=True が設定されている依存関係属性のフィールドのみが含まれます。

出力の宣言

分析フェーズでは、ルールの実装関数によって出力が作成される場合があります。読み込みフェーズですべてのラベルを認識する必要があるため、 出力にラベルはありません。出力の File オブジェクトは、ctx.actions.declare_filectx.actions.declare_directory を使用して作成できます。多くの場合、出力の名はターゲットの名前 ctx.label.name に基づいています。

def _example_library_impl(ctx):
  ...
  output_file = ctx.actions.declare_file(ctx.label.name + ".output")
  ...

事前に宣言された出力(Google 広告用に作成された出力など) 出力属性がありますが、代わりに File オブジェクトを取得できます。 ctx.outputs の対応するフィールドから取得できます。

操作

アクションは、一連の入力値から一連の出力を生成する方法を (例: 「hello.c で gcc を実行し、hello.o を取得する」)。アクションを作成すると、Bazel でアクション コマンドはすぐには実行されません。アクションは別のアクションの出力に依存する可能性があるため、依存関係のグラフに登録されます。たとえば、C では、リンカーはコンパイラ後に呼び出す必要があります。

アクションを作成する汎用関数は、 ctx.actions:

ctx.actions.args を使用すると、アクションの引数を効率的に蓄積できます。次の時点までデプセットのフラット化を回避 実行時間:

def _example_library_impl(ctx):
    ...

    transitive_headers = [dep[ExampleInfo].headers for dep in ctx.attr.deps]
    headers = depset(ctx.files.hdrs, transitive=transitive_headers)
    srcs = ctx.files.srcs
    inputs = depset(srcs, transitive=[headers])
    output_file = ctx.actions.declare_file(ctx.label.name + ".output")

    args = ctx.actions.args()
    args.add_joined("-h", headers, join_with=",")
    args.add_joined("-s", srcs, join_with=",")
    args.add("-o", output_file)

    ctx.actions.run(
        mnemonic = "ExampleCompile",
        executable = ctx.executable._compiler,
        arguments = [args],
        inputs = inputs,
        outputs = [output_file],
    )
    ...

アクションは入力ファイルのリストまたは依存関係を受け取り、入力ファイルのリスト(空でない)を生成します。 出力ファイルです。入力ファイルと出力ファイルのセットは、 分析フェーズに進みましょう。依存関係のプロバイダなど、属性の値に依存する場合があります。ただし、実行結果に依存することはできません。たとえば、アクションで unzip コマンドを実行する場合、 では、インフレートするファイルを(unzip を実行する前に)指定する必要があります。 内部で可変数のファイルを内部に作成するアクションでは、それらを (zip、tar、その他のアーカイブ形式など)に圧縮できます。

アクションはすべての入力を列挙する必要があります。使用されていない入力を一覧表示することは許可されていますが、効率的ではありません。

アクションの出力はすべて作成する必要があります。他のファイルを書き込むこともありますが、 出力に含まれないデータはコンシューマに 提供されません宣言された出力はすべて、なんらかのアクションによって書き込まれる必要があります。

アクションは純粋関数に似ています。提供された入力にのみ依存し、コンピュータ情報、ユーザー名、時計、ネットワーク、I/O デバイスへのアクセスを避けるべきです(入力の読み取りと出力の書き込みを除く)。これは、 出力がキャッシュに保存されて再利用されるため、重要です。

依存関係は Bazel によって解決され、Bazel が実行するアクションを決定します。依存関係グラフにサイクルがある場合はエラーになります。アクションを作成しても、それが実行されるとは限りません。これは、その出力がビルドに必要かどうかによって異なります。

プロバイダ

プロバイダは、ルールによって他のルールに公開され、 依存します。このデータには、出力ファイル、ライブラリ、ツールのコマンドライン経由で渡すパラメータ、ターゲットのコンシューマが知っておくべきその他のデータが含まれます。

ルールの実装関数で読み取ることができるのは インスタンス化されたターゲットの直接の依存関係のため、 依存関係からの情報(ターゲットの側で認識しておく必要がある) 消費者に対してリクエストできます。通常は、それを depset に蓄積することで行います。

ターゲットのプロバイダは、Provider 実装します。

古い実装関数は、実装関数がプロバイダ オブジェクトのリストではなく struct を返すレガシー スタイルで記述することもできます。このスタイルは使用しないことを強くおすすめします。ルールを 移行したということです。

デフォルトの出力

ターゲットのデフォルトの出力は、 コマンドラインでビルドのターゲットがリクエストされます。たとえば、java_library ターゲット //pkg:foo のデフォルト出力は foo.jar であるため、bazel build //pkg:foo コマンドでビルドされます。

デフォルトの出力は、DefaultInfofiles パラメータで指定します。

def _example_library_impl(ctx):
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        ...
    ]

ルール実装または files によって DefaultInfo が返されない場合 パラメータが指定されていない場合、DefaultInfo.files はデフォルトですべてに設定されます。 事前に宣言された出力(通常は outputs によって作成される出力) 属性)。

アクションを実行するルールは、その出力であっても、デフォルトの出力を提供する必要がある 直接使用することは想定されていません。グラフにないアクションは 出力がプルーニングされます。出力がターゲットのコンシューマによってのみ使用される場合、 ターゲットが独立して構築されている場合、これらのアクションは実行されません。この 失敗したターゲットのみをビルドし直さないため、デバッグが困難になります。 失敗を再現します

Runfiles

実行ファイルは、(ビルドではなく)実行時にターゲットが使用するファイルのセット あります。実行フェーズで、Bazel は runfile を指すシンボリック リンクを含むディレクトリ ツリー。これにより、バイナリの環境がステージングされ、実行時にランファイルにアクセスできるようになります。

Runfile はルールの作成時に手動で追加できます。 runfiles オブジェクトは runfiles メソッドで作成できます。 ルールのコンテキスト(ctx.runfiles)で指定され、 DefaultInforunfiles パラメータ。実行可能な出力は、 実行可能ルールは暗黙的に runfile に追加されます。

一部のルールでは、属性を指定する属性で、通常は data: この出力は ターゲットrunfiles.Runfile も data からマージする必要があります。 最終的に実行するためのコードを提供する可能性のある属性から srcsdata が関連付けられた filegroup 個のターゲットを含む場合があります)と deps

def _example_library_impl(ctx):
    ...
    runfiles = ctx.runfiles(files = ctx.files.data)
    transitive_runfiles = []
    for runfiles_attr in (
        ctx.attr.srcs,
        ctx.attr.hdrs,
        ctx.attr.deps,
        ctx.attr.data,
    ):
        for target in runfiles_attr:
            transitive_runfiles.append(target[DefaultInfo].default_runfiles)
    runfiles = runfiles.merge_all(transitive_runfiles)
    return [
        DefaultInfo(..., runfiles = runfiles),
        ...
    ]

カスタム プロバイダ

プロバイダは、provider 関数を使用してルール固有の情報を伝達します。

ExampleInfo = provider(
    "Info needed to compile/link Example code.",
    fields={
        "headers": "depset of header Files from transitive dependencies.",
        "files_to_link": "depset of Files from compilation.",
    })

その後、ルール実装関数でプロバイダ インスタンスを作成して返すことができます。

def _example_library_impl(ctx):
  ...
  return [
      ...
      ExampleInfo(
          headers = headers,
          files_to_link = depset(
              [output_file],
              transitive = [
                  dep[ExampleInfo].files_to_link for dep in ctx.attr.deps
              ],
          ),
      )
  ]
プロバイダのカスタム初期化

カスタムの前処理ロジックと検証ロジックを使用して、プロバイダのインスタンス化を保護できます。これにより、すべてのプロバイダ インスタンスが特定の不変性に従うことを保証したり、インスタンスを取得するためのクリーンな API をユーザーに提供したりできます。

これを行うには、init コールバックを provider 関数を使用します。このコールバックが渡されると、 provider() の戻り値の型が、プロバイダ シンボル(init が使用されていないときの通常の戻り値)と、"raw" あります。

この場合、プロバイダ シンボルが呼び出されたときに、元のアイテムが 引数が init コールバックに転送されます。「 コールバックの戻り値は、フィールド名(文字列)を値にマッピングする辞書である必要があります。 これは新しいインスタンスのフィールドを初期化するために使用されます。コールバックには任意のシグネチャを指定できます。引数がシグネチャと一致しない場合、コールバックが直接呼び出されたようにエラーが報告されます。

一方、未加工のコンストラクタは init コールバックをバイパスします。

次の例では、init を使用して引数の前処理と検証を行います。

# //pkg:exampleinfo.bzl

_core_headers = [...]  # private constant representing standard library files

# It's possible to define an init accepting positional arguments, but
# keyword-only arguments are preferred.
def _exampleinfo_init(*, files_to_link, headers = None, allow_empty_files_to_link = False):
    if not files_to_link and not allow_empty_files_to_link:
        fail("files_to_link may not be empty")
    all_headers = depset(_core_headers, transitive = headers)
    return {'files_to_link': files_to_link, 'headers': all_headers}

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init)

export ExampleInfo

ルールの実装では、次のようにプロバイダをインスタンス化できます。

    ExampleInfo(
        files_to_link=my_files_to_link,  # may not be empty
        headers = my_headers,  # will automatically include the core headers
    )

未加工のコンストラクタを使用して、代替のパブリック ファクトリ関数を定義できます。 init ロジックを経由しない新しい API です。たとえば、exampleinfo.bzl で次のように定義できます。

def make_barebones_exampleinfo(headers):
    """Returns an ExampleInfo with no files_to_link and only the specified headers."""
    return _new_exampleinfo(files_to_link = depset(), headers = all_headers)

通常、未加工のコンストラクタは、名前がアンダースコア(上記の _new_exampleinfo)で始まる変数にバインドされるため、ユーザーコードがそれを読み込んで任意のプロバイダ インスタンスを生成することはできません。

init のもう 1 つの使用方法は、単にユーザーがプロバイダを呼び出せないようにする場合です。 代わりにファクトリ関数を使用するように強制します。

def _exampleinfo_init_banned(*args, **kwargs):
    fail("Do not call ExampleInfo(). Use make_exampleinfo() instead.")

ExampleInfo, _new_exampleinfo = provider(
    ...
    init = _exampleinfo_init_banned)

def make_exampleinfo(...):
    ...
    return _new_exampleinfo(...)

実行可能ルールとテストルール

実行可能ルールは、bazel run コマンドで呼び出せるターゲットを定義します。テストルールは特別な実行可能ルールで、そのターゲットも bazel test コマンドで呼び出されます。実行可能なルールとテストのルールは、 それぞれの executable または rule の呼び出しの Truetest 引数を追加します。

example_binary = rule(
   implementation = _example_binary_impl,
   executable = True,
   ...
)

example_test = rule(
   implementation = _example_binary_impl,
   test = True,
   ...
)

テストルールの名前は、_test で終わる必要があります。(ターゲット名も頻繁にテストする) 慣例により末尾は _test ですが、必須ではありません)。テスト以外のルールにはこの接尾辞を付けないでください。

どちらの種類のルールでも、run または test コマンドで呼び出される実行可能出力ファイル(事前宣言されている場合も、されていない場合もあります)を生成する必要があります。伝えるには 実行可能なファイルとして使用するルールの出力を Bazel で渡します。 返された DefaultInfoexecutable 引数 接続します。この executable は、ルールのデフォルト出力に追加されます(そのため、executablefiles の両方に渡す必要はありません)。また、runfiles にも暗黙的に追加されます。

def _example_binary_impl(ctx):
    executable = ctx.actions.declare_file(ctx.label.name)
    ...
    return [
        DefaultInfo(executable = executable, ...),
        ...
    ]

このファイルを生成するアクションでは、ファイルで実行可能ビットを設定する必要があります。対象 ctx.actions.run または この操作が必要な ctx.actions.run_shell 件のアクション ベースとなるツールによってのみ取得されます。1 つの ctx.actions.write アクションの場合は、is_executable=True を渡します。

従来の動作として、実行可能なルールには 事前定義された特殊な ctx.outputs.executable 出力。このファイルは、DefaultInfo を使用して指定しない場合のデフォルトの実行可能ファイルとして機能します。それ以外の場合は使用しないでください。この出力メカニズムは、 実行可能ファイル名を解析時にカスタマイズできます。

サンプル 実行可能ルール および test Rule です。

実行可能なルールテストルールには、追加の 属性に加え、暗黙的に定義された属性も すべてのルール。デフォルトの 暗黙的に追加された属性は変更できませんが、回避することはできます Starlark マクロでプライベート ルールをラップして、 default:

def example_test(size="small", **kwargs):
  _example_test(size=size, **kwargs)

_example_test = rule(
 ...
)

Runfile の場所

bazel run(または test)を指定して実行可能ターゲットを実行すると、 runfiles ディレクトリは実行可能ファイルに隣接しているからです。パスは次のように関係します。

# Given launcher_path and runfile_file:
runfiles_root = launcher_path.path + ".runfiles"
workspace_name = ctx.workspace_name
runfile_path = runfile_file.short_path
execution_root_relative_path = "%s/%s/%s" % (
    runfiles_root, workspace_name, runfile_path)

runfiles ディレクトリの下の File へのパスは、 File.short_path

bazel によって直接実行されるバイナリは、 runfiles ディレクトリ。ただし、実行ファイルから呼び出されるバイナリは、 考えてみましょう。これを軽減するために、各バイナリは、 環境またはコマンドラインを使用して、runfiles ルートをパラメータとして受け取る 指定することもできます。これにより、バイナリは正規の runfile ルートを正しく渡せるようになります。 バインドします。設定されていない場合、バイナリはそれが 最初のバイナリが呼び出され、隣接する runfiles ディレクトリを探します。

高度なトピック

出力ファイルのリクエスト

1 つのターゲットに複数の出力ファイルを含めることができます。bazel build コマンドが コマンドに指定されたターゲットの出力の一部が リクエストできます。Bazel は、リクエストされたファイルと、それらが直接的または間接的に依存するファイルのみをビルドします。(アクション グラフの観点から見ると、Bazel は 依存関係として到達可能なアクションを実行します。 表示されます)。

デフォルトの出力に加えて、事前宣言された出力をコマンドラインから明示的にリクエストできます。ルールでは、出力属性を使用して事前宣言された出力を指定できます。その場合、ユーザーはルールをインスタンス化するときに出力のラベルを明示的に選択します。出力属性の File オブジェクトを取得するには、ctx.outputs の対応する属性を使用します。ルールでできること 事前に宣言された出力を暗黙的に定義する も使用できますが、この機能は非推奨です。

デフォルトの出力の他に、出力グループがあります。これは、 まとめてリクエストできる出力ファイルの数を減らすことができます。これらは --output_groups でリクエストできます。たとえば、ターゲット //pkg:mytargetdebug_files 出力グループを持つルールタイプの場合、これらのファイルは bazel build //pkg:mytarget --output_groups=debug_files を実行してビルドできます。事前に宣言されていない出力にはラベルがないため、 出力は、デフォルトの出力または出力コードで できます。

出力グループは OutputGroupInfo プロバイダで指定できます。多くのモデルとは異なり 組み込みプロバイダ。OutputGroupInfo は任意の名前のパラメータを受け入れることができます。 その名前で出力グループを定義します。

def _example_library_impl(ctx):
    ...
    debug_file = ctx.actions.declare_file(name + ".pdb")
    ...
    return [
        DefaultInfo(files = depset([output_file]), ...),
        OutputGroupInfo(
            debug_files = depset([debug_file]),
            all_files = depset([output_file, debug_file]),
        ),
        ...
    ]

また、ほとんどのプロバイダとは異なり、OutputGroupInfo は、同じ出力グループを定義しない限り、アスペクトと、そのアスペクトが適用されるルール ターゲットの両方から返すことができます。その場合、結果のプロバイダが統合されます。

通常、OutputGroupInfo は、ターゲットからコンシューマのアクションに特定の種類のファイルを伝達するために使用しないでください。代わりに、ルール固有のプロバイダを定義します。

構成

別のアーキテクチャ用の C++ バイナリをビルドするとします。「 構築は複雑で、複数のステップを必要とする場合があります。中級者向けには、 バイナリは、コンパイラやコード生成ツールなどのバイナリを 実行プラットフォーム(ホスト、 リモート エグゼキュータなど)。最終出力などの一部のバイナリは、ターゲット アーキテクチャ用にビルドする必要があります。

このため、Bazel には「構成」と遷移のコンセプトがあります。「 最上位のターゲット(コマンドラインでリクエストされたもの)は、 &quot;target&quot;実行プラットフォームで実行する必要のあるツールは 「exec」ロールでビルドされるできます。ルールは、コンパイラに渡される CPU アーキテクチャを変更するなど、構成に応じて異なるアクションを生成できます。場合によっては、同じライブラリが複数の異なるタスクに対して できます。その場合、分析され、複数回ビルドされる可能性があります。

デフォルトでは、Bazel はターゲットの依存関係を つまり、遷移なしでターゲット自体が作成されます。依存関係が 必要な場合は、対応する属性を exec 構成への移行を指定しています。これにより、ツールとすべてのリソースが 実行プラットフォーム用にビルドされます

依存関係属性ごとに、cfg を使用して、依存関係を同じ構成でビルドするか、exec 構成に移行するかを決定できます。依存関係属性にフラグ executable=True がある場合は、cfg を明示的に設定する必要があります。これは、誤った目的のために誤ってツールを作成しないようにするためです。 できます。 例を見る

通常、実行時に必要なソース、依存ライブラリ、実行可能ファイルは同じ構成を使用できます。

ビルドの一部として実行されるツール(コンパイラやコード生成ツールなど)は、exec 構成用にビルドする必要があります。この場合は、属性に cfg="exec" を指定します。

それ以外の場合は、実行時に使用される実行可能ファイル(テストの一部など)は、ターゲット構成用にビルドする必要があります。この場合、cfg="target" を 作成します。

cfg="target" は実際には何もしません。これは、ルール設計者が意図を明確にするために使用するコンビニエンス値にすぎません。executable=False の場合、 つまり、cfg は省略可能で、読みやすくする場合にのみ設定してください。

cfg=my_transition を使用してユーザー定義の遷移を使用することもできます。これにより、ルール作成者は構成を柔軟に変更できますが、ビルドグラフが大きくなり、わかりにくくなるという欠点があります。

: 従来、Bazel には実行プラットフォームの概念がなく、 代わりに、すべてのビルド アクションがホストマシンで実行されると見なされていました。 このため、インフラストラクチャの「host」というラベルが付きます。移行 ホスト構成で依存関係を構築するために使用できます。多くのルールでは、ツールに「host」遷移が引き続き使用されていますが、これは現在非推奨であり、可能な限り「exec」遷移を使用するように移行されています。

「ホスト」クラスタと「exec」は構成:

  • 「host」「exec」はターミナル依存関係が「host」フォルダ内にあると、 それ以上の移行は許可されません。さらに アプリケーションの構成をできます。
  • 「host」モノリシックであり「host」は 1 つだけ構成、 別の「エグゼクティブ」と構成する必要があります 説明します。
  • 「host」は、Bazel と同じマシンまたは非常に類似したマシンでツールを実行することを前提としています。これはもはや当てはまらず、 ローカル マシン、リモート エグゼキュータで実行できるアクションが リモート エグゼキュータの CPU と OS がローカル あります。

「exec」と「host」の両方の構成で、同じオプションの変更が適用されます(--host_compilation_mode から --compilation_mode を設定する、--host_cpu から --cpu を設定するなど)。違いは、「host」構成は他のすべてのフラグのデフォルト値で開始されるのに対し、「exec」構成はターゲット構成に基づいてフラグの現在の値で開始されることです。

構成フラグメント

ルールによりアクセスできる 構成フラグメント(例: cppjavajvm。ただし、必要なフラグメントはすべて、 次の点を確認してください。

def _impl(ctx):
    # Using ctx.fragments.cpp leads to an error since it was not declared.
    x = ctx.fragments.java
    ...

my_rule = rule(
    implementation = _impl,
    fragments = ["java"],      # Required fragments of the target configuration
    host_fragments = ["java"], # Required fragments of the host configuration
    ...
)

ctx.fragments がターゲットの構成フラグメントのみを提供する できます。ホスト設定のフラグメントにアクセスするには、次のコマンドを使用します。 代わりに ctx.host_fragments を使用してください。

通常、runfiles ツリー内のファイルの相対パスは、 ソースツリーまたは生成された出力ツリー内のそのファイルの相対パス。なんらかの理由でこれらを区別する必要がある場合は、root_symlinks または symlinks 引数を指定できます。root_symlinks は、パスをマッピングする辞書です。 ここで、パスは runfiles ディレクトリのルートからの相対パスです。「 symlinks ディクショナリは同じですが、パスの先頭には暗黙的に ワークスペース名を指定します。

    ...
    runfiles = ctx.runfiles(
        root_symlinks = {"some/path/here.foo": ctx.file.some_data_file2}
        symlinks = {"some/path/here.bar": ctx.file.some_data_file3}
    )
    # Creates something like:
    # sometarget.runfiles/
    #     some/
    #         path/
    #             here.foo -> some_data_file2
    #     <workspace_name>/
    #         some/
    #             path/
    #                 here.bar -> some_data_file3

symlinks または root_symlinks を使用する場合は、2 つの異なるファイルを runfiles ツリー内の同じパスにマッピングしないように注意してください。これにより、競合を説明するエラーでビルドが失敗します。これを修正するには、ctx.runfiles 引数を変更して競合を解消する必要があります。このチェックは そのルールを使用するすべてのターゲットと、それらのルールに依存する できます。ツールが推移的に使用される可能性が高い場合、これは特に危険です 分析できます。シンボリック リンク名はツールのランファイル全体で一意である必要があり、 依存関係がすべて含まれます。

コード カバレッジ

coverage コマンドを実行すると、特定のターゲットに対してカバレッジ計測を追加する必要がある場合があります。ビルドでは、計測対象のソースファイルのリストも収集されます。検討対象となるターゲットのサブセットは、--instrumentation_filter フラグで制御されます。次の場合を除き、テスト ターゲットは除外されます。 --instrument_test_targets 指定されています。

ルールの実装でビルド時にカバレッジ計測を追加する場合は、 実装関数でそれを考慮する必要があります ctx.coverage_instrumented は true を返します ターゲットのソースを計測可能にする場合は、カバレッジ モードを選択します。

# Are this rule's sources instrumented?
if ctx.coverage_instrumented():
  # Do something to turn on coverage for this compile action

カバレッジ モードで常にオンにする必要があるロジック(ターゲットのソース (特にインストゥルメント化されているかどうか)に ctx.configuration.coverage_enabled.

コンパイル前にルールが依存関係のソース(ヘッダー ファイルなど)を直接含める場合、依存関係のソースを計測する必要がある場合は、コンパイル時計測を有効にする必要があります。

# Are this rule's sources or any of the sources for its direct dependencies
# in deps instrumented?
if (ctx.configuration.coverage_enabled and
    (ctx.coverage_instrumented() or
     any([ctx.coverage_instrumented(dep) for dep in ctx.attr.deps]))):
    # Do something to turn on coverage for this compile action

ルールでは、どの属性が関連する属性なのかを InstrumentedFilesInfo プロバイダによるカバレッジ。 coverage_common.instrumented_files_infoinstrumented_files_infodependency_attributes パラメータは、 すべてのランタイム依存関係属性(depsdata などのデータ依存関係。source_attributes パラメータは、 カバレッジ インストルメンテーションを追加できる場合は、ルールのソースファイルの属性を指定します。

def _example_library_impl(ctx):
    ...
    return [
        ...
        coverage_common.instrumented_files_info(
            ctx,
            dependency_attributes = ["deps", "data"],
            # Omitted if coverage is not supported for this rule:
            source_attributes = ["srcs", "hdrs"],
        )
        ...
    ]

InstrumentedFilesInfo が返されない場合、それぞれのデフォルト テーブルが作成されます。 ツール以外の依存関係属性が設定されていない 属性スキーマの cfg"host" または "exec"dependency_attributes。(属性を指定するため、これは理想的な動作ではありません。 dependency_attributessrcs のように source_attributes ではなく、 すべてのルールに対して明示的なカバレッジ構成を 必要があります。)

検証アクション

ビルドに関する検証が必要な場合、その検証に必要な情報はアーティファクト(ソースファイルまたは生成ファイル)でのみ利用できます。この情報はアーティファクトに含まれているため、ルールはファイルを読み取ることができないため、分析時にこの検証を行うことはできません。代わりに、アクションは実行時にこの検証を行う必要があります。検証に失敗すると、アクションが失敗し、ビルドも失敗します。

実行される検証の例としては、静的分析、リンティング、依存関係と整合性のチェック、スタイルチェックなどがあります。

検証アクションは、アーティファクトのビルドに不要なアクションの一部を個別のアクションに移動することで、ビルドのパフォーマンスを改善することもできます。たとえば、コンパイルとリンティングを行う単一のアクションをコンパイル アクションとリンティング アクションに分割できる場合、リンティング アクションは検証アクションとして実行し、他のアクションと並行して実行できます。

これらの「検証アクション」は他の場所で使用されるものは生成されず、 出力についてアサートするだけでよいため、ビルドに組み込みます。ただし、この方法には問題があります。検証アクションがビルドの他の場所で使用されるものを生成しない場合、ルールはどのようにしてアクションを実行させるのでしょうか。従来のアプローチでは、検証アクションで空のファイルを出力し、その出力をビルド内の他の重要なアクションの入力に人為的に追加していました。

これは、Bazel がコンパイル アクションの実行時に常に検証アクションを実行するため機能しますが、重大な欠点があります。

  1. 検証アクションがビルドのクリティカル パスにある。Bazel は、コンパイル アクションの実行に空の出力が必要であると判断するため、コンパイル アクションが入力を無視するにもかかわらず、まず検証アクションを実行します。これにより、並列処理が減り、ビルドが遅くなります。

  2. コンパイル アクションの代わりに、ビルド内の他のアクションが実行される可能性がある場合は、検証アクションの空の出力もそれらのアクションに追加する必要があります(java_library のソース jar 出力など)。これは、コンパイル アクションの代わりに実行される可能性のある新しいアクションが後で追加され、空の検証出力が誤って省略された場合にも問題になります。

この問題を解決するには、Validations 出力グループを使用します。

検証出力グループ

検証出力グループは、検証アクションの使用されない出力を保持するように設計された出力グループです。これにより、他のアクションの入力に人為的に追加する必要がなくなります。

このグループは、特定のリソースに関係なく、常に出力がリクエストされる --output_groups フラグの値。ターゲットの状態は問いません。 たとえば、コマンドライン、依存関係として、あるいは ターゲットの暗黙的な出力)。通常のキャッシュと増分処理は引き続き適用されます。検証アクションへの入力が変更されておらず、検証アクションが以前に成功した場合、検証アクションは実行されません。

この出力グループを使用する場合も、検証アクションでなんらかのファイルを出力する必要があります。 空のオブジェクトも作成できます。そのため、通常はサポートされないツールをラップする必要が ファイルを作成するように出力を作成します。

次の 3 つのケースでは、ターゲットの検証アクションは実行されません。

  • ターゲットがツールとして依存している場合
  • ターゲットが暗黙的な依存関係( 「_」で始まる属性)
  • ターゲットがホスト構成または exec 構成でビルドされている場合。

これらのターゲットには、それぞれ固有の ビルドとテストを分離し、検証の失敗を発見できるようにします。

検証出力グループを使用する

検証出力グループは _validation という名前で、他のテストと同様に使用されます。 出力グループ:

def _rule_with_validation_impl(ctx):

  ctx.actions.write(ctx.outputs.main, "main output\n")

  ctx.actions.write(ctx.outputs.implicit, "implicit output\n")

  validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
  ctx.actions.run(
      outputs = [validation_output],
      executable = ctx.executable._validation_tool,
      arguments = [validation_output.path])

  return [
    DefaultInfo(files = depset([ctx.outputs.main])),
    OutputGroupInfo(_validation = depset([validation_output])),
  ]


rule_with_validation = rule(
  implementation = _rule_with_validation_impl,
  outputs = {
    "main": "%{name}.main",
    "implicit": "%{name}.implicit",
  },
  attrs = {
    "_validation_tool": attr.label(
        default = Label("//validation_actions:validation_tool"),
        executable = True,
        cfg = "exec"),
  }
)

検証出力ファイルは DefaultInfo または フィルタできます。このルール種類のターゲットの検証アクション ターゲットがラベルまたはターゲットの 暗黙的な出力は、直接的または間接的に依存しています。

通常、検証アクションの出力は、Cloud Storage バケットに 他のアクションの入力には追加されず、他のアクションの入力には 並列処理のメリットを失う可能性があります。ただし、Bazel は現在、 これを適用するための特別なチェックがあります。そのため 検証アクションの出力が、任意のアクションの入力に追加されることが Starlark ルール用のテストです。例:

load("@bazel_skylib//lib:unittest.bzl", "analysistest")

def _validation_outputs_test_impl(ctx):
  env = analysistest.begin(ctx)

  actions = analysistest.target_actions(env)
  target = analysistest.target_under_test(env)
  validation_outputs = target.output_groups._validation.to_list()
  for action in actions:
    for validation_output in validation_outputs:
      if validation_output in action.inputs.to_list():
        analysistest.fail(env,
            "%s is a validation action output, but is an input to action %s" % (
                validation_output, action))

  return analysistest.end(env)

validation_outputs_test = analysistest.make(_validation_outputs_test_impl)

検証アクション フラグ

検証アクションの実行は --run_validations コマンドラインで制御されます。 デフォルトは true です。

サポートが終了した機能

非推奨の事前宣言済み出力

事前宣言された出力を使用する方法は、非推奨の 2 つあります。

  • ruleoutputs パラメータでは、 出力属性名と文字列テンプレート間のマッピングを 出力ラベルを使用します。事前宣言されていない出力を使用し、出力を DefaultInfo.files に明示的に追加することをおすすめします。ルールのターゲットの 入力としてラベルを使用するルールの場合、これは事前に宣言された あります。

  • 実行可能ルールの場合、ctx.outputs.executable は、ルール ターゲットと同じ名前の宣言済み実行可能出力を参照します。出力は明示的に宣言することをおすすめします。たとえば、 ctx.actions.declare_file(ctx.label.name) です。このコマンドは、 実行可能なファイルを生成するため、実行を許可するように権限を設定します。実行可能ファイルの出力を DefaultInfoexecutable パラメータに明示的に渡します。

避けるべき Runfile の機能

ctx.runfiles 型と runfiles 型には複雑な機能セットがあり、その多くは以前の理由から保持されています。複雑さを軽減するには、次の推奨事項に沿って対応してください。

  • ctx.runfilescollect_data モードと collect_default モードの使用を避けてください。これらのモードは、 特定のハードコードされた依存関係エッジで runfile を実行することで、混乱を招きます。 代わりに、ctx.runfilesfiles または transitive_files パラメータを使用してファイルを追加するか、依存関係の runfile を runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles) でマージします。

  • DefaultInfo コンストラクタの data_runfilesdefault_runfiles の使用を避ける。代わりに DefaultInfo(runfiles = ...) を指定してください。 「デフォルト」と「データ」のランファイルの区別は、以前の理由から維持されています。たとえば、一部のルールでは、デフォルトの出力が data_runfiles に配置されますが、default_runfiles には配置されません。ルールでは、data_runfiles を使用する代わりに、デフォルトの出力を両方含め、ランファイルを提供する属性(多くの場合 data)から default_runfiles をマージする必要があります。

  • DefaultInfo から runfiles を取得する場合(通常は、現在のルールとその依存関係間での実行ファイルのマージの場合のみ)、DefaultInfo.data_runfiles ではなく DefaultInfo.default_runfiles を使用します。

以前のプロバイダからの移行

従来、Bazel プロバイダは Target オブジェクト上の単純なフィールドでした。これらのフィールドにはドット演算子を使用してアクセスし、ルールの実装関数によって返された構造体にフィールドを配置して作成しました。

このスタイルはサポートが終了しているため、新しいコードでは使用しないでください。詳しくは、 移行に役立つ情報をご確認ください。新しいプロバイダ メカニズムでは、名前が 防ぐことができます。また、プロバイダ インスタンスにアクセスするコードにプロバイダ シンボルを使用してプロバイダを取得するよう要求することで、データの非公開化もサポートしています。

現時点では、従来のプロバイダは引き続きサポートされます。1 つのルールで、 簡単に説明します。

def _old_rule_impl(ctx):
  ...
  legacy_data = struct(x="foo", ...)
  modern_data = MyInfo(y="bar", ...)
  # When any legacy providers are returned, the top-level returned value is a
  # struct.
  return struct(
      # One key = value entry for each legacy provider.
      legacy_info = legacy_data,
      ...
      # Additional modern providers:
      providers = [modern_data, ...])

このルールのインスタンスの結果の Target オブジェクトが dep である場合、 プロバイダとそのコンテンツは dep.legacy_info.x として取得でき、 dep[MyInfo].y

返される構造体には、providers に加えて、特別な意味を持つ他のフィールドもいくつか指定できます(対応するレガシー プロバイダは作成されません)。

  • フィールド filesrunfilesdata_runfilesdefault_runfilesexecutable は、DefaultInfo の同名のフィールドに対応しています。DefaultInfo プロバイダを返すときに、これらのフィールドのいずれかを指定することはできません。

  • フィールド output_groups は構造体値を受け取り、OutputGroupInfo に対応します。

ルールの provides 宣言と、依存関係属性の providers 宣言では、従来のプロバイダは文字列として渡され、最新のプロバイダは *Info シンボルで渡されます。移行する際は、文字列からシンボルに変更してください。更新が困難な複雑なルールセットや大規模なルールセットの場合 すべてのルールをアトミックに実行するため、 手順:

  1. レガシー プロバイダを生成するルールを変更して、レガシー プロバイダと 最新のプロバイダをサポートしています。宣言するルールでは、 以前のプロバイダを返す場合は、宣言を更新して、以前のプロバイダと サポートしています。

  2. レガシー プロバイダを使用するルールを変更して、代わりにモダン プロバイダを使用するようにします。以前のプロバイダを必要とする属性宣言がある場合は、 最新のプロバイダを要求するように更新することもできます。必要に応じて、 この作業とステップ 1 をインターリーブするために、コンシューマがどちらかの provider: 以前のプロバイダが存在するかどうかをテストします。 hasattr(target, 'foo')、または FooInfo in target を使用する新しいプロバイダ。

  3. すべてのルールからレガシー プロバイダを完全に削除します。