ルールは、Bazel が入力に対して実行して一連の出力を生成する一連のアクションを定義します。これらの出力は、ルールの実装関数から返されるプロバイダで参照されます。たとえば、C++ バイナリルールは次のようになります。
.cpp
ソースファイル(入力)のセットを取得します。- ソースファイル(アクション)で
g++
を実行します。 - 実行可能出力と他のファイルを含めて
DefaultInfo
プロバイダを返して、ランタイムで使用できるようにします。 - 取得した C++ 固有の情報を使用して
CcInfo
プロバイダを返します。 その依存関係を定義します。
Bazel の観点では、g++
と標準 C++ ライブラリもこのルールの入力です。ルール作成者は、ユーザーが指定したリソースだけでなく、
ルールの実行に必要なツールとライブラリも
できます。
ルールを作成または変更する前に、Bazel の ビルドフェーズ。データ アナリストは、 (読み込み、分析、実行)。ルールとマクロの違いを理解するために、マクロについても学習することをおすすめします。使用を開始するには、まずルールのチュートリアルをご覧ください。 その後、このページを参照してください。
Bazel 自体には、いくつかのルールが組み込まれています。次のようなネイティブ ルールが
cc_library
と java_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の出力(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
ファイルが評価されてターゲットがインスタンス化される読み込みフェーズ中のみです。
属性
属性はルールの引数です。属性は、ターゲットの実装に特定の値を提供できます。また、他のターゲットを参照して、依存関係のグラフを作成することもできます。
srcs
や deps
などのルール固有の属性は、属性名からスキーマ(attr
モジュールを使用して作成)へのマップを rule
の attrs
パラメータに渡すことで定義します。一般的な属性:
name
と visibility
は、すべてのルールに暗黙的に追加されます。追加
各属性が暗黙的に追加され、
実行可能ルールとテストルールを具体的に指定します。属性
暗黙的にルールに追加され、
attrs
。
依存関係属性
ソースコードを処理するルールでは、通常、さまざまな依存関係のタイプを処理するために次の属性を定義します。
srcs
は、ターゲットのアクションによって処理されるソースファイルを指定します。多くの場合、 属性スキーマは、並べ替えに必要なファイル拡張子を指定します ソースファイルを指定します。ヘッダー ファイルを使用する言語のルールでは、通常、ターゲットとそのコンシューマによって処理されるヘッダーに個別のhdrs
属性を指定します。deps
は、ターゲットのコード依存関係を指定します。属性スキーマは、 それらの依存関係が提供する必要があるプロバイダを指定します。( たとえば、cc_library
はCcInfo
を提供します)。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_target
は my_target
の依存関係であるため、other_target
が最初に分析されます。期間に周期がある場合はエラーになります。
ターゲットの依存関係グラフ。
非公開属性と暗黙的な依存関係
依存関係属性にデフォルト値を設定すると、暗黙的な依存関係が作成されます。ユーザーが BUILD
ファイルで指定しないターゲット グラフの一部であるため、暗黙的です。暗黙的な依存関係は、ルールとツール(コンパイラなどのビルド時依存関係)の関係をハードコードする場合に便利です。ほとんどの場合、ユーザーはルールで使用するツールを指定することに関心がありません。ルールの実装関数内では、これは他の依存関係と同じように扱われます。
ユーザーがその値をオーバーライドできないように暗黙的な依存関係を指定するには、属性にアンダースコア(_
)で始まる名前を付けて、属性を非公開にします。非公開属性にはデフォルト値が必要です。通常、暗黙的な属性ではプライベート属性を使用するのが適切です。
確認します。
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.output
、
attr.output_list
を指定するため、Cloud Storage バケットの
生成されます。これらは、依存関係属性とは次の 2 つの点で異なります。
- 他の場所で定義されたターゲットを参照するのではなく、出力ファイル ターゲットを定義します。
- 出力ファイル ターゲットは、インスタンス化されたルール ターゲットではなく、その逆に依存します。
通常、出力属性は、ターゲット名に基づかないユーザー定義名を持つ出力をルールで作成する必要がある場合にのみ使用されます。ルールにすでに
1 つの出力属性で、通常は out
または outs
という名前が付けられます。
出力属性は、事前宣言された出力を作成する際に推奨される方法です。この出力は、特定の依存関係として使用したり、コマンドラインからリクエストしたりできます。
実装関数
すべてのルールに implementation
関数が必要です。これらの関数は
分析フェーズに置いて、トレーニング コードを変換し、
読み込みフェーズで生成されたターゲットのグラフを、
実行フェーズで実行するアクション。そのため、実装関数は実際にファイルを読み書きできません。
ルール実装関数は通常非公開(先頭にアンダースコアが付いた名前)です。慣例的には、ルールと同じ名前が付けられますが、
_impl
で。
実装関数は 1 つのパラメータを取ります。
rule context(通常は ctx
という名前)このメソッドは、
プロバイダ
ターゲット
依存関係は、分析時に 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
には、仕様で allow_single_file=True
が設定されている依存関係属性の単一の File
または None
が含まれます。ctx.executable
は ctx.file
と同じように動作しますが、
仕様に executable=True
が設定されている依存関係属性のフィールドが含まれています。
出力の宣言
分析フェーズでは、ルールの実装関数によって出力が作成される場合があります。読み込みフェーズではすべてのラベルを認識する必要があるため、
出力にラベルはありません。出力用の File
オブジェクトは、次のコマンドを使用して作成できます。
ctx.actions.declare_file
および
ctx.actions.declare_directory
。多くの場合、出力の名はターゲットの名前 ctx.label.name
に基づいています。
def _example_library_impl(ctx):
...
output_file = ctx.actions.declare_file(ctx.label.name + ".output")
...
出力属性用に作成されたものなど、事前宣言された出力の場合、代わりに ctx.outputs
の対応するフィールドから File
オブジェクトを取得できます。
操作
アクションは、一連の入力から一連の出力を生成する方法を表します(例: 「hello.c で gcc を実行して hello.o を取得する」)。アクションを作成すると、Bazel でアクション コマンドはすぐには実行されません。依存関係のグラフに登録します。 あるアクションが別のアクションの出力に依存する可能性があるためです。たとえば、C では、リンカーはコンパイラ後に呼び出す必要があります。
アクションを作成する汎用関数は、
ctx.actions
:
ctx.actions.run
: 実行可能ファイルを実行します。ctx.actions.run_shell
: シェルコマンドを実行します。ctx.actions.write
: 文字列をファイルに書き込む。ctx.actions.expand_template
: テンプレートからファイルを生成します。
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 の実行前に)。内部で可変数のファイルを作成するアクションは、それらを 1 つのファイル(zip、tar、その他のアーカイブ形式など)にラップできます。
アクションはすべての入力を列挙する必要があります。使用されていない入力を一覧表示することは許可されていますが、効率的ではありません。
アクションの出力はすべて作成する必要があります。他のファイルを書き込むこともありますが、 出力に含まれないものは コンシューマは利用できなくなります宣言された出力はすべて、なんらかのアクションによって書き込まれる必要があります。
アクションは純粋関数に似ています。提供された入力にのみ依存し、コンピュータ情報、ユーザー名、時計、ネットワーク、I/O デバイスにアクセスしないでください(入力の読み取りと出力の書き込みを除く)。これは、 出力がキャッシュに保存されて再利用されるため、重要です。
依存関係は Bazel によって解決され、Bazel が実行するアクションを決定します。依存関係グラフにサイクルがある場合はエラーになります。アクションを作成しても、それが実行されるとは限りません。これは、その出力がビルドに必要かどうかによって異なります。
プロバイダ
プロバイダは、ルールによって他のルールに公開され、 依存します。このデータには、出力ファイル、ライブラリ、ツールのコマンドライン経由で渡すパラメータ、ターゲットのコンシューマが知っておくべきその他のデータが含まれます。
ルールの実装関数で読み取ることができるのは
ターゲットに即時に依存関係があり、依存関係を
依存関係からの情報(ターゲットの側で認識しておく必要がある)
消費者に対してリクエストできます。通常は、それを depset
に蓄積することで行います。
ターゲットのプロバイダは、Provider
実装します。
古い実装関数は、実装関数がプロバイダ オブジェクトのリストではなく struct
を返すレガシー スタイルで記述することもできます。このスタイルは使用しないことを強くおすすめします。ルールを
移行したということです。
デフォルトの出力
ターゲットのデフォルトの出力は、
コマンドラインでビルドのターゲットがリクエストされます。たとえば、
java_library
のターゲット //pkg:foo
にはデフォルトの出力として foo.jar
があるため、
コマンド bazel build //pkg:foo
でビルドされます。
デフォルトの出力は、DefaultInfo
の files
パラメータで指定します。
def _example_library_impl(ctx):
...
return [
DefaultInfo(files = depset([output_file]), ...),
...
]
ルール実装または files
によって DefaultInfo
が返されない場合
パラメータが指定されていない場合、DefaultInfo.files
はデフォルトですべてに設定されます。
事前に宣言された出力(通常は outputs によって作成される出力)
属性)。
アクションを実行するルールでは、出力が直接使用されないことが想定されていても、デフォルトの出力を指定する必要があります。グラフにないアクションは 出力がプルーニングされます。出力がターゲットのコンシューマによってのみ使用される場合、 ターゲットが独立して構築されている場合、これらのアクションは実行されません。この 失敗したターゲットのみを再構築することはできないため、デバッグが困難になります。 失敗を再現します
Runfiles
Runfile は、(ビルド時ではなく)実行時にターゲットによって使用される一連のファイルです。実行フェーズで、Bazel は runfiles を指すシンボリック リンクを含むディレクトリ ツリーを作成します。これにより、 実行時に実行ファイルにアクセスできるようにします。
ランファイルは、ルールの作成時に手動で追加できます。runfiles
オブジェクトは runfiles
メソッドで作成できます。
ルールのコンテキスト(ctx.runfiles
)で指定され、
DefaultInfo
の runfiles
パラメータ。実行可能ルールの実行可能出力は、暗黙的に実行ファイルに追加されます。
一部のルールでは、通常は data
という名前の属性を指定します。この属性の出力は、ターゲットのランファイルに追加されます。Runfile は、data
からだけでなく、最終的な実行用のコードを提供する可能性のある属性(通常は srcs
(data
に関連付けられた 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()
の戻り値の型が、次の 2 つの値のタプルになります。
シンボル(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
ロジックを経由しない新しい関数を作成できます。たとえば、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
コマンドで呼び出されます。実行可能ルールとテストルールは、rule
の呼び出しでそれぞれの executable
または test
引数を True
に設定することで作成されます。
example_binary = rule(
implementation = _example_binary_impl,
executable = True,
...
)
example_test = rule(
implementation = _example_binary_impl,
test = True,
...
)
テストルールの名前は、_test
で終わる必要があります。(ターゲット名も頻繁にテストする)
慣例により末尾は _test
ですが、必須ではありません)。テスト以外のルールでは、
という接尾辞が付いています。
どちらの種類のルールでも、実行可能な出力ファイルを生成する必要があります(このファイルは、
事前に宣言されているものなど)であり、run
または test
コマンドによって呼び出されます。この実行可能ファイルとして使用するルールの出力を Bazel に指示するには、返された DefaultInfo
プロバイダの executable
引数として渡します。この executable
は、ルールのデフォルト出力に追加されます(そのため、executable
と files
の両方に渡す必要はありません)。また、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
を使用して指定しない限り、デフォルトの実行可能ファイルとして機能します。それ以外の場合は使用しないでください。この出力メカニズムは、分析時に実行可能ファイルの名前をカスタマイズできないため、非推奨になりました。
実行可能ルールとテストルールには、すべてのルールに追加される属性に加えて、暗黙的に定義された追加の属性があります。暗黙的に追加された属性のデフォルトは変更できませんが、デフォルトを変更する Starlark マクロでプライベート ルールをラップすることで回避できます。
def example_test(size="small", **kwargs):
_example_test(size=size, **kwargs)
_example_test = rule(
...
)
実行ファイルの場所
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
ディレクトリのルートに隣接しています。ただし、実行ファイルから呼び出されるバイナリは、
考えてみましょう。これを軽減するには、各バイナリで、環境変数またはコマンドライン引数 / フラグを使用して、ランファイルのルートをパラメータとして受け入れる方法を提供する必要があります。これにより、バイナリは呼び出すバイナリに正しい標準の runfile ルートを渡すことができます。設定されていない場合、バイナリはそれが
最初のバイナリが呼び出され、隣接する runfiles ディレクトリを探します。
高度なトピック
出力ファイルのリクエスト
1 つのターゲットに複数の出力ファイルを含めることができます。bazel build
コマンドが実行されると、コマンドに指定されたターゲットの出力の一部がリクエストされたと見なされます。Bazel は、リクエストされたファイルと、それらが直接的または間接的に依存するファイルのみをビルドします。(アクション グラフの観点から見ると、Bazel は
依存関係として到達可能なアクションを実行します。
。)
デフォルトの出力に加え、事前に宣言された出力では、
コマンドラインで明示的にリクエストできます。ルールでは、出力属性を使用して事前宣言された出力を指定できます。その場合、ユーザーは
ルールをインスタンス化する際に、出力のラベルを明示的に選択します。出力属性の File
オブジェクトを取得するには、ctx.outputs
の対応する属性を使用します。ルールでは、ターゲット名に基づいて事前宣言された出力を暗黙的に定義することもできますが、この機能は非推奨です。
デフォルトの出力の他に、出力グループがあります。これは、
まとめてリクエストできる出力ファイルの数を減らすことができます。これらは --output_groups
でリクエストできます。たとえば、ターゲット //pkg:mytarget
が debug_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 には「構成」と遷移のコンセプトがあります。最上位のターゲット(コマンドラインでリクエストされたターゲット)は「target」構成でビルドされますが、実行プラットフォームで実行する必要があるツールは「exec」構成でビルドされます。ルールは、コンパイラに渡される CPU アーキテクチャを変更するなど、構成に応じて異なるアクションを生成する場合があります。場合によっては、同じライブラリが複数の異なるタスクに対して できます。その場合、分析され、複数回ビルドされる可能性があります。
デフォルトでは、Bazel はターゲットの依存関係を つまり、遷移なしでターゲット自体が作成されます。依存関係がターゲットのビルドに必要なツールである場合は、対応する属性で exec 構成への移行を指定する必要があります。これにより、ツールとそのすべての情報が 実行プラットフォーム用にビルドされます
依存関係属性ごとに、cfg
を使用して、
同じ構成でビルドするか exec 構成に移行する必要があります。
依存関係属性にフラグ executable=True
がある場合は、cfg
を明示的に設定する必要があります。これは、誤った目的のために誤ってツールを作成しないようにするためです。
できます。
例を表示
一般的に、開発時に必要になるソース、依存ライブラリ、実行可能ファイルは 同じ構成を使用できます。
ビルドの一環として実行されるツール(コンパイラやコード生成ツールなど)
exec 構成用にビルドする必要があります。この場合は、属性に cfg="exec"
を指定します。
それ以外の場合は、実行時に使用される実行可能ファイル(テストの一部など)は、ターゲット構成用にビルドする必要があります。この場合、cfg="target"
を
作成します。
cfg="target"
は実際には何もしません。これは、ルール設計者が意図を明示するのを助ける、純粋な便宜上の値です。executable=False
(cfg
は省略可)の場合は、判読性が本当に向上する場合にのみ設定します。
cfg=my_transition
を使用してユーザー定義の遷移を使用することもできます。これにより、ルール作成者は構成を柔軟に変更できますが、ビルドグラフが大きくなり、わかりにくくなるという欠点があります。
注: 従来、Bazel には実行プラットフォームの概念がなく、 代わりに、すべてのビルド アクションがホストマシンで実行されると見なされていました。Bazel バージョン 6.0 より前のバージョンでは、これを表す構成です 「host」への言及がある場合コードや古いドキュメントで 参照します。この追加の概念を回避するために、Bazel 6.0 以降を使用することをおすすめします。 オーバーヘッドを軽減できます。
構成フラグメント
ルールによりアクセスできる
構成フラグメント(例:
cpp
、java
、jvm
。ただし、必要なフラグメントはすべて、
次の点を確認してください。
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
...
)
Runfiles のシンボリック リンク
通常、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
また、ルールには、coverage_common.instrumented_files_info
を使用して構築された InstrumentedFilesInfo
プロバイダのカバレッジに関連する属性に関する情報も提供する必要があります。instrumented_files_info
の dependency_attributes
パラメータは、
すべてのランタイム依存関係属性(deps
や
data
などのデータ依存関係。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
。(これは、srcs
などの属性が source_attributes
ではなく dependency_attributes
に配置されるため、理想的な動作ではありませんが、依存関係チェーン内のすべてのルールに明示的なカバレッジ構成を必要としません)。
検証アクション
ビルドに関する検証が必要な場合、その検証に必要な情報はアーティファクト(ソースファイルまたは生成ファイル)でのみ利用できます。この情報はアーティファクトに含まれているため、ルールはファイルを読み取ることができないため、分析時にこの検証を行うことはできません。代わりに、アクションは実行時にこの検証を行う必要があります。日時 検証が失敗するとアクションも失敗し、ビルドも失敗します。
実行される検証の例としては、静的分析、リンティング、依存関係と整合性のチェック、スタイルチェックなどがあります。
検証アクションは、部品を動かしてビルドのパフォーマンスを向上させるのにも役立ちます。 必要ないアクションを 1 つにまとめることができます。 たとえば、コンパイルとリンティングを行う単一のアクションをコンパイル アクションとリンティング アクションに分割できる場合、リンティング アクションは検証アクションとして実行し、他のアクションと並行して実行できます。
これらの「検証アクション」は、入力に関することをアサートする必要があるだけであるため、ビルドの他の場所で使用されるものは生成しません。ただし、この方法には問題があります。検証アクションがビルドの他の場所で使用されるものを生成しない場合、ルールはどのようにしてアクションを実行させるのでしょうか。これまでは、検証アクションで空の文字列を出力する方法が採用されてきました。 その出力を、他の重要な入力要素に人為的に追加し、 アクションを実行します。
これは、Bazel がコンパイル アクションの実行時に常に検証アクションを実行するため機能しますが、重大な欠点があります。
検証アクションがビルドのクリティカル パスにある。Bazel は、コンパイル アクションの実行に空の出力が必要であると判断するため、コンパイル アクションが入力を無視するにもかかわらず、まず検証アクションを実行します。これにより、並列処理が減り、ビルドが遅くなります。
コンパイル アクションの代わりに、ビルド内の他のアクションが実行される可能性がある場合は、検証アクションの空の出力もそれらのアクションに追加する必要があります(
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 つの方法があります(非推奨)。
rule
のoutputs
パラメータでは、 出力属性名と文字列テンプレート間のマッピングを 出力ラベルを使用します。事前定義された宣言されていない出力とDefaultInfo.files
に出力を明示的に追加する。ルールのターゲットの 入力としてラベルを使用するルールの場合、これは事前に宣言された あります。実行可能ルールの場合、
ctx.outputs.executable
は ルールのターゲットと同じ名前の、事前に宣言された実行可能な出力に変換できます。 出力は明示的に宣言することをおすすめします。たとえば、ctx.actions.declare_file(ctx.label.name)
です。このコマンドは、 実行可能なファイルを生成するため、実行を許可するように権限を設定します。明示的 実行可能な出力をDefaultInfo
のexecutable
パラメータに渡します。
避けるべき実行ファイルの機能
ctx.runfiles
と runfiles
タイプには複雑な機能セットがあり、その多くは以前の理由で保持されています。
次の推奨事項は複雑さの軽減に役立ちます。
次の
collect_data
モードとcollect_default
モードは使用しないctx.runfiles
。これらのモードでは、特定のハードコードされた依存関係エッジ全体で、混乱を招く方法でランファイルを暗黙的に収集します。代わりに、ctx.runfiles
のfiles
またはtransitive_files
パラメータを使用してファイルを追加するか、runfiles = runfiles.merge(dep[DefaultInfo].default_runfiles)
を使用して依存関係のランファイルをマージします。DefaultInfo
コンストラクタのdata_runfiles
とdefault_runfiles
の使用を避ける。代わりにDefaultInfo(runfiles = ...)
を指定してください。 「デフォルト」と「データ」のランファイルの区別は、以前の理由から維持されています。たとえば、一部のルールでは、デフォルトの出力がdata_runfiles
に配置されますが、default_runfiles
には配置されません。Google Cloud のdata_runfiles
: ルールにデフォルトの出力を含めることと、マージする両方が必要 実行ファイルを提供する属性からのdefault_runfiles
(data
)。DefaultInfo
からrunfiles
を取得するとき(通常は統合のためのみ) 現在のルールとその依存関係の間のファイルを実行する場合は、DefaultInfo.data_runfiles
ではなくDefaultInfo.default_runfiles
です。
以前のプロバイダからの移行
これまで、Bazel プロバイダは Target
オブジェクトの単純なフィールドでした。Google
ドット演算子を使用してアクセスされており、
ルールの実装関数によって返される構造体です。
このスタイルはサポートが終了しているため、新しいコードでは使用しないでください。詳しくは、 移行に役立つ情報をご確認ください。新しいプロバイダ メカニズムでは、名前が 防ぐことができます。データの非表示もサポートします。つまり、すべてのコードが プロバイダ シンボルを使用して取得します。
現時点では、従来のプロバイダは引き続きサポートされます。ルールは、次のように従来のプロバイダと最新のプロバイダの両方を返すことができます。
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, ...])
dep
がこのルールのインスタンスの結果として生成された Target
オブジェクトの場合、プロバイダとそのコンテンツは dep.legacy_info.x
と dep[MyInfo].y
として取得できます。
返される構造体には、providers
以外にも、
特殊な意味を持つ(対応するレガシー モデルが作成されない)
あります。
フィールド
files
、runfiles
、data_runfiles
、default_runfiles
、executable
は、DefaultInfo
の同名のフィールドに対応しています。DefaultInfo
プロバイダを返すときに、これらのフィールドのいずれかを指定することはできません。フィールド
output_groups
は構造体値を取り、OutputGroupInfo
。
ルールの provides
宣言と、依存関係属性の providers
宣言では、従来のプロバイダは文字列として渡され、最新のプロバイダは *Info
シンボルで渡されます。移行する際は、文字列からシンボルに変更してください。更新が困難な複雑なルールセットや大規模なルールセットの場合
すべてのルールをアトミックに実行するため、
手順:
上記の構文を使用して、レガシー プロバイダを生成するルールを変更し、レガシー プロバイダとモダン プロバイダの両方を生成します。以前のプロバイダを返すことを宣言するルールの場合は、その宣言を更新して、以前のプロバイダと最新のプロバイダの両方を含めます。
レガシー プロバイダを使用するルールを変更して、代わりにモダン プロバイダを使用するようにします。属性宣言で以前のプロバイダが必要な場合は、代わりに最新のプロバイダを必要とするように更新します。必要に応じて、 この作業とステップ 1 をインターリーブするために、コンシューマがどちらかの provider: 以前のプロバイダが存在するかどうかをテストします。
hasattr(target, 'foo')
、またはFooInfo in target
を使用する新しいプロバイダ。以前のプロバイダをすべてのルールから完全に削除します。