このページでは、アスペクトの基本的な使い方とメリットについて説明します。また、 簡単な例と高度な例で説明します。
アスペクトを使用すると、ビルド依存関係グラフを追加情報で拡張できる あります。アスペクトが役立つ一般的なシナリオは次のとおりです。
- 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 つの動作で「シャドウ グラフ」が生成されます/ ターゲットの元の依存関係グラフを次の図に示します。
図 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 つの引数を取ります。
実装関数は、次のコードを使用してターゲット ルールの属性にアクセスできます。
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
を介して再帰的にアクセスできるすべてのターゲットが含まれます。