Starlark は、Python に似た
Bazel で使用するために開発され、その後採用されました。
分析できますBazel の BUILD
ファイルと .bzl
ファイルは、次の言語で記述します。
Starlark は「Build Language」としてよく知られていますが、単に
「Starlark」と呼ばれます。特に、ある特徴が
組み込みまたは「ネイティブ」ではなく、Build Language で表現されます。部品
使用されます。Bazel は多くのビルド関連機能でコア言語を強化
glob
、genrule
、java_binary
など。
詳しくは、 Bazel と Starlark のドキュメント: 詳細情報が表示され、 ルール SIG テンプレートを 新しいルールセットの作成を開始できます。
空のルール
最初のルールを作成するには、foo.bzl
ファイルを作成します。
def _foo_binary_impl(ctx):
pass
foo_binary = rule(
implementation = _foo_binary_impl,
)
rule
関数を呼び出すと、次の処理が行われます。
コールバック関数を定義する必要があります。ロジックはここで行いますが、
今のところは関数を空のままにしておくことができます。ctx
引数
ターゲットに関する情報を提供します。
ルールは、BUILD
ファイルから読み込んで使用できます。
同じディレクトリに BUILD
ファイルを作成します。
load(":foo.bzl", "foo_binary")
foo_binary(name = "bin")
これで、ターゲットをビルドできるようになりました。
$ bazel build bin
INFO: Analyzed target //:bin (2 packages loaded, 17 targets configured).
INFO: Found 1 target...
Target //:bin up-to-date (nothing to build)
このルールは何も行いませんが、すでに他のルールと同じように動作します。つまり、
必須の名前。visibility
、testonly
、
tags
。
評価モデル
先に進む前に、コードがどのように評価されているかを理解することが重要です。
print ステートメントを使用して foo.bzl
を更新します。
def _foo_binary_impl(ctx):
print("analyzing", ctx.label)
foo_binary = rule(
implementation = _foo_binary_impl,
)
print("bzl file evaluation")
BUILD:
load(":foo.bzl", "foo_binary")
print("BUILD file")
foo_binary(name = "bin1")
foo_binary(name = "bin2")
ctx.label
分析するターゲットのラベルに対応します。ctx
オブジェクトには、
多くの有用なフィールドとメソッドがあります。詳細は、
API リファレンス。
コードをクエリします。
$ bazel query :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:8:1: bzl file evaluation
DEBUG: /usr/home/bazel-codelab/BUILD:2:1: BUILD file
//:bin2
//:bin1
いくつか観察してみましょう。
- "bzl ファイルの評価"が最初に出力されます。
BUILD
ファイルを評価する前に、 Bazel は、読み込まれるすべてのファイルを評価します。複数のBUILD
ファイルが読み込まれている場合 foo.bzl の場合、「bzl file Evaluation」は 1 件しか表示されません。なぜなら Bazel は評価結果をキャッシュに保存します。 - コールバック関数
_foo_binary_impl
は呼び出されません。Bazel クエリの読み込みBUILD
ファイルですが、ターゲットは分析されません。
ターゲットを分析するには、cquery
(構成済み
クエリ)、または build
コマンドを使用します。
$ bazel build :all
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin1
DEBUG: /usr/home/bazel-codelab/foo.bzl:2:5: analyzing //:bin2
INFO: Analyzed 2 targets (0 packages loaded, 0 targets configured).
INFO: Found 2 targets...
ご覧のとおり、_foo_binary_impl
は 2 回(ターゲットごとに 1 回)呼び出されるようになりました。
「bzl file Evaluation」は「BUILD ファイル」と再度出力されて
foo.bzl
の評価は bazel query
の呼び出し後にキャッシュに保存されるためです。
Bazel は、実際に実行された場合にのみ print
ステートメントを出力します。
ファイルの作成
ルールをより有用なものにするには、ファイルを生成するようにルールを更新してください。まず、 名前を付けます。この例では、「 ターゲット:
ctx.actions.declare_file(ctx.label.name)
ここで bazel build :all
を実行すると、エラーが発生します。
The following files have no generating action:
bin2
ファイルを宣言するたびに、ファイルの生成方法を Bazel に指示する必要があります。
作成します。ctx.actions.write
を使用する。
指定した内容のファイルを作成します。
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello\n",
)
コードは有効ですが、何の動作も起こりません。
$ bazel build bin1
Target //:bin1 up-to-date (nothing to build)
ctx.actions.write
関数が Bazel を学習させるアクションを登録しました
ファイルの生成方法を説明します。ただし、Bazel は、次の状態になるまでファイルを作成しません。
確認できます。最後に、Bazel にファイルを
ルールの出力であり、ルール内で使用される一時ファイルではない
説明します。
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello!\n",
)
return [DefaultInfo(files = depset([out]))]
後で DefaultInfo
関数と depset
関数を確認します。現在のところ、
最後の行がルールの出力を選択する方法であると仮定します。
次に、Bazel を実行します。
$ bazel build bin1
INFO: Found 1 target...
Target //:bin1 up-to-date:
bazel-bin/bin1
$ cat bazel-bin/bin1
Hello!
ファイルが正常に生成されました。
属性
ルールをより有用なものにするには、
attr
モジュールを実行してルール定義を更新します。
username
という文字列属性を追加します。
foo_binary = rule(
implementation = _foo_binary_impl,
attrs = {
"username": attr.string(),
},
)
次に、BUILD
ファイルで設定します。
foo_binary(
name = "bin",
username = "Alice",
)
コールバック関数の値にアクセスするには、ctx.attr.username
を使用します。次に例を示します。
def _foo_binary_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.write(
output = out,
content = "Hello {}!\n".format(ctx.attr.username),
)
return [DefaultInfo(files = depset([out]))]
この属性は必須にすることも、デフォルト値を設定することもできます。注目すべき点は、
attr.string
のドキュメントをご覧ください。
ブール値など、他のタイプの属性を使用することもできます。
または整数のリスト。
依存関係
依存関係属性(attr.label
など)
および attr.label_list
、
属性を所有するターゲットから、属性を所有するターゲットへの依存関係を宣言します。
属性値に出現するラベルを表します。この種の属性は
表します。
BUILD
ファイルでは、ターゲット ラベルは次のような文字列オブジェクトとして表示されます。
//pkg:name
。実装関数では、ターゲットは
Target
オブジェクト。たとえば、返されたファイルを表示して
Target.files
を使用してターゲットごとに設定できます。
複数のファイル
デフォルトでは、ルールによって作成されたターゲットのみが依存関係として表示されます(
foo_library()
ターゲット)。属性で許容されるターゲットの指定が
入力ファイル(リポジトリ内のソースファイルなど)を使用する場合、
allow_files
で、使用可能なファイル拡張子のリスト(または True
任意のファイル拡張子を許可する)を選択します。
"srcs": attr.label_list(allow_files = [".java"]),
ファイルのリストには ctx.files.<attribute name>
でアクセスできます。対象
たとえば、srcs
属性のファイルのリストには、
ctx.files.srcs
単一ファイル
必要なファイルが 1 つだけの場合は、allow_single_file
を使用します。
"src": attr.label(allow_single_file = [".java"])
このファイルは ctx.file.<attribute name>
でアクセスできます。
ctx.file.src
テンプレートを使用してファイルを作成する
テンプレートに基づいて .cc ファイルを生成するルールを作成できます。また、
ctx.actions.write
を使用して、ルールで作成された文字列を出力できます。
ただし、2 つの問題があります。まず、テンプレートに
大きければ、別のファイルに入れてメモリ効率が上がり、
長い文字列を構築することです。2 つ目は、別の Pod に
ユーザーにとっては便利です。代わりに、
ctx.actions.expand_template
テンプレートファイルの置換を実行します
template
属性を作成してテンプレートへの依存関係を宣言する
ファイル:
def _hello_world_impl(ctx):
out = ctx.actions.declare_file(ctx.label.name + ".cc")
ctx.actions.expand_template(
output = out,
template = ctx.file.template,
substitutions = {"{NAME}": ctx.attr.username},
)
return [DefaultInfo(files = depset([out]))]
hello_world = rule(
implementation = _hello_world_impl,
attrs = {
"username": attr.string(default = "unknown person"),
"template": attr.label(
allow_single_file = [".cc.tpl"],
mandatory = True,
),
},
)
ユーザーはルールを次のように使用できます。
hello_world(
name = "hello",
username = "Alice",
template = "file.cc.tpl",
)
cc_binary(
name = "hello_bin",
srcs = [":hello"],
)
テンプレートをエンドユーザーに公開したくない場合は、必ず デフォルト値を設定して、属性を非公開にすることもできます。
"_template": attr.label(
allow_single_file = True,
default = "file.cc.tpl",
),
アンダースコアで始まる属性は非公開であり、
BUILD
ファイル。テンプレートが暗黙的な依存関係になりました。すべての hello_world
target はこのファイルと依存関係があります。このファイルは必ず公開してください
他のパッケージに追加するには、BUILD
ファイルを更新し、
exports_files
:
exports_files(["file.cc.tpl"])
さらに先へ
- ルールのリファレンス ドキュメントをご覧ください。
- depset について十分に理解してください。
- サンプル リポジトリを確認する ルールの例が表示されます。