アスペクト

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

このページでは、アスペクトの基本的な使い方とメリットについて説明します。また、 簡単な例と高度な例で説明します。

アスペクトを使用すると、ビルド依存関係グラフを追加情報で拡張できる あります。アスペクトが役立つ一般的なシナリオは次のとおりです。

  • Bazel を統合する IDE は、アスペクトを使用して、 できます。
  • コード生成ツールは、さまざまな側面を活用して、 ターゲットに依存しないようにする必要があります。たとえば、BUILD ファイルで階層を指定できます。 protobuf ライブラリ 言語固有のルールでは、アスペクトを使用して、 特定の言語の protobuf サポートコードを生成するアクション。

Aspect の基本

BUILD ファイルには、プロジェクトのソースコードの説明(ソースは何か)が記述されています。 ファイルがプロジェクトの一部である場合、どのアーティファクト(ターゲット)をビルドするか Bazel が使用するファイル、それらのファイル間の依存関係などです。 ビルドを実行するために必要なアクション セットが (コンパイラやリンカーの実行など)と、 アクションを実行します。Bazel では、依存関係を作成して グラフを参照し、このグラフにアクセスしてアクションを収集します。

次の BUILD ファイルについて考えてみましょう。

java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)

この BUILD ファイルは、次の図に示す依存関係グラフを定義します。

グラフを作成する

図 1. BUILD ファイルの依存関係グラフ。

Bazel は、次の実装関数を呼び出して、この依存関係グラフを分析します。 ルールごとに対応するルール(この場合は「java_library」)、 指定します。ルール実装関数は、ルールによって実行されるアクションを .jar ファイルなどのビルド アーティファクトと、場所などのパス情報 それらのターゲットの逆依存関係まで プロバイダ

アスペクトは、次の実装関数を備えているという点で、ルールと類似しています。 アクションを生成してプロバイダを返します。しかし、その力は 依存関係グラフの作成方法も変わってきます。アスペクトには実装が含まれます 反映されるすべての属性のリストです。アスペクト A について考えてみましょう。 「deps」という名前の属性に沿って伝播される。このアスペクトは ターゲット X となり、アスペクト アプリケーション ノード A(X) が生成されます。その適用中に アスペクト A は、X が「deps」で参照するすべてのターゲットに再帰的に適用される 属性(A の伝播リスト内のすべての属性)で定義されます。

したがって、アスペクト A をターゲット X に適用する 1 つの動作で「シャドウ グラフ」が生成されます/ ターゲットの元の依存関係グラフを次の図に示します。

Aspect を使用してグラフを作成する

図 2. アスペクトを含むグラフを作成する。

影になっているエッジは、 伝播セット。そのため、この例では runtime_deps エッジがシャドーイングされません。 例です。次に、アスペクト実装関数が、システム内のすべてのノードで シャドウグラフは、ノードでルール実装が呼び出される方法に類似しています。 必要があります。

簡単な例

この例では、Google 検索のアプリケーションのソースファイルを再帰的に出力する方法を示します。 ルールと、deps 属性を持つすべての依存関係。内容 アスペクトの実装、アスペクトの定義、アスペクトの呼び出し方法 実行することもできます。

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

例を複数の部分に分割し、それぞれを個別に見てみましょう。

アスペクトの定義

print_aspect = aspect(
    implementation = _print_aspect_impl,
    attr_aspects = ['deps'],
)

アスペクト定義はルール定義と似ており、 aspect 関数

ルールと同様に、アスペクトには実装関数があります。この場合、実装関数があります。 _print_aspect_impl

attr_aspects は、アスペクトが伝播されるルール属性のリストです。 この場合、アスペクトは deps 属性に沿って伝播されます。 適用できます。

attr_aspects のもう 1 つの一般的な引数は ['*'] であり、 適用できます。

Aspect の実装

def _print_aspect_impl(target, ctx):
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the files that make up the sources and
        # print their paths.
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                print(f.path)
    return []

Aspect 実装関数はルールの実装と類似 使用できます。この関数ではプロバイダが返され、 actions で、次の 2 つの引数を取ります。

  • target: アスペクトが適用されるターゲット
  • ctx: 属性へのアクセスに使用できる ctx オブジェクト 出力とアクションを生成できます

実装関数は、次のコードを使用してターゲット ルールの属性にアクセスできます。 ctx.rule.attr。対応しているプロバイダを 適用先のターゲットから提供される target 引数。

プロバイダのリストを返すには、アスペクトが必要です。この例では、アスペクトは は何も指定しないので、空のリストを返します。

コマンドラインを使用してアスペクトを呼び出す

アスペクトを適用する最も簡単な方法は、コマンドラインから --aspects 渡します。上記のアスペクトが print.bzl という名前のファイルに定義されていると仮定します。 これを次のように使用します。

bazel build //MyExample:example --aspects print.bzl%print_aspect

print_aspect を、ターゲットの example と、 deps 属性を使用して再帰的にアクセス可能なターゲット ルール。

--aspects フラグは 1 つの引数を取ります。これはアスペクトの仕様です。 形式は <extension file label>%<aspect top-level name> です。

高度な例

次の例は、ターゲット ルールのアスペクトを使用する方法を示しています。 は、ターゲット内のファイルをカウントします。拡張機能でフィルタすることもできます。 プロバイダを使用して値を返す方法、パラメータを使用して 引数、アスペクトの実装への引数、ルールからアスペクトを呼び出す方法。

file_count.bzl ファイル:

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

BUILD.bazel ファイル:

load('//:file_count.bzl', 'file_count_rule')

cc_library(
    name = 'lib',
    srcs = [
        'lib.h',
        'lib.cc',
    ],
)

cc_binary(
    name = 'app',
    srcs = [
        'app.h',
        'app.cc',
        'main.cc',
    ],
    deps = ['lib'],
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

アスペクトの定義

file_count_aspect = aspect(
    implementation = _file_count_aspect_impl,
    attr_aspects = ['deps'],
    attrs = {
        'extension' : attr.string(values = ['*', 'h', 'cc']),
    }
)

次の例は、deps 属性を通じてアスペクトが伝播される方法を示しています。

attrs はアスペクトの属性のセットを定義します。公開アスペクトの属性 string 型で、パラメータと呼ばれます。パラメータには values が必要です 属性を指定します。この例には extension というパラメータがあります。 「*」、「h」、「cc」のいずれかを含めることができます。指定します。

アスペクトのパラメータ値は、同じ属性を持つ文字列属性から取得されます。 アスペクトをリクエストしているルールの名前(file_count_rule の定義を参照)。 パラメータを含むアスペクトは、コマンドラインで使用することはできません。 パラメータを定義する必要があります。

アスペクトには、label または label_list。限定公開ラベル属性を使用すると、リソースへの依存関係を アスペクトによって生成されたアクションに必要なツールやライブラリ。 この例で定義されている非公開属性ですが、次のコード スニペットは、 は、ツールをアスペクトに渡す方法を示しています。

...
    attrs = {
        '_protoc' : attr.label(
            default = Label('//tools:protoc'),
            executable = True,
            cfg = "exec"
        )
    }
...

Aspect の実装

FileCountInfo = provider(
    fields = {
        'count' : 'number of files'
    }
)

def _file_count_aspect_impl(target, ctx):
    count = 0
    # Make sure the rule has a srcs attribute.
    if hasattr(ctx.rule.attr, 'srcs'):
        # Iterate through the sources counting files
        for src in ctx.rule.attr.srcs:
            for f in src.files.to_list():
                if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
                    count = count + 1
    # Get the counts from our dependencies.
    for dep in ctx.rule.attr.deps:
        count = count + dep[FileCountInfo].count
    return [FileCountInfo(count = count)]

ルール実装関数と同様に、アスペクト実装関数は 依存関係にアクセスできるプロバイダの構造体を返します。

この例では、FileCountInfo は、次のものを持つプロバイダとして定義されています。 フィールド count。推奨される方法は、変数のフィールドを明示的に定義し、 fields 属性を使用するプロバイダ。

アスペクト アプリケーション A(X)のプロバイダの集合は、プロバイダの集合である ターゲット X のルールの実装によって発生する、 アスペクト A の実装。ルールの実装が伝播されるプロバイダ アスペクトの適用前に作成され、固定され、 あります。ターゲットとそれに適用されるアスペクトのそれぞれに 同じタイプのプロバイダを指定する必要があります。ただし、 OutputGroupInfo (ただし、同じ Pod が 異なる出力グループを指定します)、 InstrumentedFilesInfo (アスペクトから取得します)。つまり アスペクトの実装は DefaultInfo を返さない。

パラメータと非公開属性は、API 呼び出しの ctx。この例では、extension パラメータを参照して、 カウントするファイルを指定します

返されるプロバイダの場合、返される属性に付随する アスペクトが(attr_aspects リストから)伝播されると、 それらのアスペクトの適用結果を 返すことができますたとえば、ターゲットが X の依存関係には Y と Z があり、A(X) の ctx.rule.attr.deps は [A(Y), A(Z)] になります。 この例の ctx.rule.attr.deps は、ターゲット オブジェクトであり、 「deps」にアスペクトを適用した結果元のターゲットと同じく、 適用されたことを示します。

この例では、アスペクトは FileCountInfo プロバイダにアクセスします。 ターゲットの依存関係を計算し、一時的なファイルの総数を累積します。

ルールからのアスペクトの呼び出し

def _file_count_rule_impl(ctx):
    for dep in ctx.attr.deps:
        print(dep[FileCountInfo].count)

file_count_rule = rule(
    implementation = _file_count_rule_impl,
    attrs = {
        'deps' : attr.label_list(aspects = [file_count_aspect]),
        'extension' : attr.string(default = '*'),
    },
)

ルールの実装では、FileCountInfo にアクセスする方法を示します。 ctx.attr.deps 経由。

ルール定義は、パラメータ(extension)を定義する方法を示しています。 デフォルト値(*)を指定します。デフォルト値を使用して 「cc」、「h」、「*」のいずれでもないエラーになりますが、 アスペクト定義のパラメータに適用された制限。

ターゲット ルールによるアスペクトの呼び出し

load('//:file_count.bzl', 'file_count_rule')

cc_binary(
    name = 'app',
...
)

file_count_rule(
    name = 'file_count',
    deps = ['app'],
    extension = 'h',
)

これは、extension パラメータをアスペクトに渡す方法を示しています。 表示されます。extension パラメータには、 extension は省略可能なパラメータと見なされます。

file_count ターゲットが作成されると、アスペクトは次のように評価されます。 deps を介して再帰的にアクセスできるすべてのターゲットが含まれます。