このページでは、Starlark 構成の利点と基本的な使用方法について説明します。 プロジェクトのビルド方法をカスタマイズするための Bazel の API。また、Kubernetes の ビルド設定について説明し、サンプルを示します。
これにより、次のことが可能になります。
- プロジェクトのカスタムフラグを定義できます。これにより、
--define
- 書き込み
Transitions で依存関係を設定します。
構成が異なる場合があります。
(
--compilation_mode=opt
、--cpu=arm
など) - より適切なデフォルトをルールに埋め込む(
//my:android_app
を自動的にビルドするなど) 使用)
など、すべて .bzl ファイルから作成できます(Bazel リリースは必要ありません)。詳しくは、
bazelbuild/examples
リポジトリ
例をご覧ください。
ユーザー定義のビルド設定
ビルド設定は
構成
情報です。構成は Key-Value マップのようなものです。--cpu=ppc
の設定
--copt="-DFoo"
は、次のような構成を生成します。
{cpu: ppc, copt: "-DFoo"}
。各エントリはビルド設定です。
cpu
や copt
などの従来のフラグはネイティブ設定です。
キーが定義され、値がネイティブの bazel Java コード内で設定されます。
Bazel ユーザーは、コマンドラインでのみ読み取りと書き込みを行える
などの API がネイティブに維持されます。ネイティブ フラグと API の変更
公開するには bazel リリースが必要です。ユーザー定義ビルド
設定は .bzl
ファイルで定義されるため、設定に bazel リリースは必要ありません
変更を登録します)。コマンドラインでも設定できます。
(flags
として指定されている場合、詳細は下記を参照)が、
ユーザー定義の遷移で設定できます。
ビルド設定の定義
build_setting
rule()
パラメータ
ビルド設定は他のルールと同様のルールであり、
Starlark の rule()
関数の build_setting
属性。
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
build_setting
属性は、変数の型を指定する関数を
ビルド設定を指定します。このタイプは、次のような基本的な Starlark のタイプに限定されます。
bool
と string
。config
モジュールを確認する
ドキュメントをご覧ください。より複雑な入力は
指定することもできます。詳しくは以下をご覧ください。
config
モジュールの関数は、オプションのブール値パラメータ flag
を受け取ります。
デフォルトで false に設定されています。flag
が true に設定されている場合、ビルド設定は
ユーザーがコマンドラインで設定することも、内部でルール作成者が設定することも可能
デフォルト値と遷移によって識別されます。
すべての設定をユーザーが設定できるようにする必要はありません。たとえば
ライターには、テストルール内で有効にしたいデバッグモードがあります。
この機能を無差別に有効にしたり
使用できます。
gsuite.build_setting_value の使用
すべてのルールと同様に、ビルド設定ルールにも実装関数があります。
ビルド設定の基本的な Starlark 型の値は、
ctx.build_setting_value
メソッドを使用します。この方法は、
ビルド設定ルールの ctx
オブジェクト。この実装は
メソッドはビルド設定の値を直接転送したり、ファイルに対して
型チェックや、より複雑な構造体の作成などです。これを
enum
型のビルド設定を実装します。
# example/buildsettings/build_settings.bzl
TemperatureProvider = provider(fields = ['type'])
temperatures = ["HOT", "LUKEWARM", "ICED"]
def _impl(ctx):
raw_temperature = ctx.build_setting_value
if raw_temperature not in temperatures:
fail(str(ctx.label) + " build setting allowed to take values {"
+ ", ".join(temperatures) + "} but was set to unallowed value "
+ raw_temperature)
return TemperatureProvider(type = raw_temperature)
temperature = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
複数セットの文字列フラグの定義
文字列の設定には、allow_multiple
パラメータが追加されており、
コマンドラインまたは bazelrcs でフラグを複数回設定することもできます。デフォルトの
値は文字列型属性を使用して設定されます。
# example/buildsettings/build_settings.bzl
allow_multiple_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "allow_multiple_flag")
allow_multiple_flag(
name = "roasts",
build_setting_default = "medium"
)
フラグの各設定は、1 つの値として扱われます。
$ bazel build //my/target --//example:roasts=blonde \
--//example:roasts=medium,dark
上記は {"//example:roasts": ["blonde", "medium,dark"]}
に解析され、
ctx.build_setting_value
はリスト ["blonde", "medium,dark"]
を返します。
ビルド設定をインスタンス化する
build_setting
パラメータで定義されたルールには暗黙的な必須要素があります。
build_setting_default
属性。この属性は
build_setting
パラメータで宣言されます。
# example/buildsettings/build_settings.bzl
FlavorProvider = provider(fields = ['type'])
def _impl(ctx):
return FlavorProvider(type = ctx.build_setting_value)
flavor = rule(
implementation = _impl,
build_setting = config.string(flag = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
事前定義の設定
「 Skylib ライブラリには事前定義された設定のセットが用意されており、何も設定しなくてもインスタンス化できる カスタム Starlark を作成します。
たとえば、一部の文字列値を受け入れる設定を定義するには、次のようにします。
# example/BUILD
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
string_flag(
name = "myflag",
values = ["a", "b", "c"],
build_setting_default = "a",
)
完全なリストについては、 一般的なビルド設定のルール。
ビルド設定の使用
ビルド設定によって異なる
ターゲットが構成情報を読み取る場合は、 通常の属性依存関係を介してビルド設定に直接依存しています。
# example/rules.bzl
load("//example/buildsettings:build_settings.bzl", "FlavorProvider")
def _rule_impl(ctx):
if ctx.attr.flavor[FlavorProvider].type == "ORANGE":
...
drink_rule = rule(
implementation = _rule_impl,
attrs = {
"flavor": attr.label()
}
)
# example/BUILD
load("//example:rules.bzl", "drink_rule")
load("//example/buildsettings:build_settings.bzl", "flavor")
flavor(
name = "favorite_flavor",
build_setting_default = "APPLE"
)
drink_rule(
name = "my_drink",
flavor = ":favorite_flavor",
)
言語によっては、ビルド設定の正規のセットを作成することをおすすめします。
言語に依存しますfragments
のネイティブ コンセプトはもはや、
Starlark の構成にハードコードされたオブジェクトとして存在します。
共通の暗黙属性のセットを使用するという考え方です次に例を示します。
# kotlin/rules.bzl
_KOTLIN_CONFIG = {
"_compiler": attr.label(default = "//kotlin/config:compiler-flag"),
"_mode": attr.label(default = "//kotlin/config:mode-flag"),
...
}
...
kotlin_library = rule(
implementation = _rule_impl,
attrs = dicts.add({
"library-attr": attr.string()
}, _KOTLIN_CONFIG)
)
kotlin_binary = rule(
implementation = _binary_impl,
attrs = dicts.add({
"binary-attr": attr.label()
}, _KOTLIN_CONFIG)
コマンドラインでビルド設定を使用する
ほとんどのネイティブ フラグと同様に、コマンドラインを使用してビルド設定を指定できます。
ビルド
設定名は、name=value
構文を使用したターゲットのフルパスです。
$ bazel build //my/target --//example:string_flag=some-value # allowed
$ bazel build //my/target --//example:string_flag some-value # not allowed
特別なブール構文がサポートされています。
$ bazel build //my/target --//example:boolean_flag
$ bazel build //my/target --no//example:boolean_flag
ビルド設定エイリアスの使用
読みやすくするために、ビルド設定のターゲット パスにエイリアスを設定できます。 指定します。エイリアスはネイティブ フラグと同様に機能し、 オプションの構文を示します。
--flag_alias=ALIAS_NAME=TARGET_PATH
を追加してエイリアスを設定する
.bazelrc
へ。たとえば、エイリアスを coffee
に設定するには、次のようにします。
# .bazelrc
build --flag_alias=coffee=//experimental/user/starlark_configurations/basic_build_setting:coffee-temp
ベスト プラクティス: エイリアスを複数回設定すると、最新の どちらかが優先されます意図しない解析結果を避けるため、一意のエイリアス名を使用します。
エイリアスを使用するには、ビルド設定のターゲット パスの代わりにこのエイリアスを入力します。
上記の coffee
の例では、ユーザーの .bazelrc
で次のように設定しています。
$ bazel build //my/target --coffee=ICED
これは、以下を置き換えるものです。
$ bazel build //my/target --//experimental/user/starlark_configurations/basic_build_setting:coffee-temp=ICED
ベスト プラクティス: コマンドラインでエイリアスを設定することはできますが、エイリアスは残しておきます。
.bazelrc
に含めると、コマンドラインがすっきりします。
ラベルタイプのビルド設定
他のビルド設定とは異なり、ラベルタイプの設定は、
build_setting
ルール パラメータ。代わりに、bazel には 2 つのルールが組み込まれています。
label_flag
と label_setting
。これらのルールは、ファイアウォール ルールの
ビルド設定が行われる実際のターゲット。label_flag
、
label_setting
は遷移によって読み書きでき、label_flag
を設定可能
他の build_setting
ルールと同様に、ユーザーにより制限されます。唯一の違いは、
カスタム定義はできません
ラベルタイプの設定が最終的には遅延バインド機能に置き換わる
できます。遅延制限のあるデフォルト属性は、ラベル型の属性で、
最終的な値が構成の影響を受ける場合があります。Starlark では、
configuration_field
API
# example/rules.bzl
MyProvider = provider(fields = ["my_field"])
def _dep_impl(ctx):
return MyProvider(my_field = "yeehaw")
dep_rule = rule(
implementation = _dep_impl
)
def _parent_impl(ctx):
if ctx.attr.my_field_provider[MyProvider].my_field == "cowabunga":
...
parent_rule = rule(
implementation = _parent_impl,
attrs = { "my_field_provider": attr.label() }
)
# example/BUILD
load("//example:rules.bzl", "dep_rule", "parent_rule")
dep_rule(name = "dep")
parent_rule(name = "parent", my_field_provider = ":my_field_provider")
label_flag(
name = "my_field_provider",
build_setting_default = ":dep"
)
ビルド設定と select()
ユーザーは以下を使用してビルド設定の属性を構成できます。
select()
。ビルド設定ターゲットは、次の flag_values
属性に渡すことができます:
config_setting
。構成に一致する値は
次に、String
が照合用のビルド設定のタイプに解析されます。
config_setting(
name = "my_config",
flag_values = {
"//example:favorite_flavor": "MANGO"
}
)
ユーザー定義の遷移
構成 移行 変換が、構成済みのターゲットから別のターゲット グラフを作成します。
定義
移行では、ルール間の構成の変更を定義します。たとえば リクエストが 「親とは異なる CPU の依存関係をコンパイルする」などによって処理されます。 説明します。
形式的には、遷移とは入力構成から 1 つ以上の構成への関数である
設定します。「入力をオーバーライドする」など、ほとんどの遷移は 1 対 1 です。
--cpu=ppc
で構成」と記述します。1 対 2 以降の移行も可能だが、
特別な制限が適用されます
Starlark では、遷移はルールとよく似ており、
transition()
関数
実装関数が必要です
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//example:favorite_flavor" : "MINT"}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
transition()
関数は、実装関数を受け取ります。
読み取るビルド設定(inputs
)と書き込むビルド設定のセット
(outputs
)。この実装関数には、settings
と 2 つのパラメータがあります。
attr
。settings
は宣言されたすべての設定の辞書 {String
:Object
} です
inputs
パラメータで transition()
に設定します。
attr
は、ルールを適用する属性と値の辞書です。
適用できます。として接続した場合
送信エッジ遷移の場合、これらの値は
属性は、select() の後に解決するように設定されています。次のように接続した場合:
受信エッジへの遷移。attr
では、
セレクタを使用して値を解決するすべての属性を含めるもし
--foo
の着信エッジ遷移が属性 bar
を読み取り、さらに
--foo
を選択して属性 bar
を設定すると、
入力エッジ遷移で誤った bar
値を読み取る。
実装関数は、ディクショナリ(または
場合は、
複数の出力構成を持つ遷移の場合)
適用する新しいビルド設定値のリストです。返される辞書の鍵セットは、
outputs
に渡されたビルド設定のセットのみが含まれる
パラメータを使用します。これは、ビルド設定が
その元の値は移行の過程で変更されないため、
明示的に渡されます。
1 対 2 以上の遷移の定義
出力エッジ遷移は単一の入力をマッピングできる 複数の出力構成に結合できます。これは マルチアーキテクチャコードをバンドルします
1 対 2 以上の遷移は、 します。
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return [
{"//example:favorite_flavor" : "LATTE"},
{"//example:favorite_flavor" : "MOCHA"},
]
coffee_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
また、ルールの実装機能で使用するカスタムキーも設定できます。 個々の依存関係を読み取るには:
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
遷移を追加する
遷移は、受信エッジと送信エッジの 2 か所に接続できます。 これは実質的に、ルールによって独自の構成(受信 それらの依存関係を移行したり、構成( など)があります。
注: 現在のところ、Starlark の遷移をネイティブ ルールに適用する方法はありません。 この操作を行う必要がある場合は、 bazel-discuss@googlegroups.com をご利用ください。
入力エッジ遷移
入力エッジ遷移をアクティブにするには、transition
オブジェクトをアタッチします
(transition()
によって作成)を rule()
の cfg
パラメータに追加します。
# example/rules.bzl
load("example/transitions:transitions.bzl", "hot_chocolate_transition")
drink_rule = rule(
implementation = _impl,
cfg = hot_chocolate_transition,
...
受信エッジの遷移は 1 対 1 の遷移である必要があります。
発信エッジ遷移
transition
オブジェクトをアタッチして発信エッジ遷移をアクティブにする
(transition()
によって作成)を属性の cfg
パラメータに追加します。
# example/rules.bzl
load("example/transitions:transitions.bzl", "coffee_transition")
drink_rule = rule(
implementation = _impl,
attrs = { "dep": attr.label(cfg = coffee_transition)}
...
発信エッジ遷移は、1:1 または 1:2 以上です。
遷移による属性へのアクセスをご覧ください。 ご覧ください
ネイティブ オプションの切り替え
Starlark 遷移では、ネイティブ ビルドでの読み取りと書き込みの宣言も可能 特別な接頭辞でオプション名に付加します。
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {"//command_line_option:cpu": "k8"}
cpu_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
サポートされていないネイティブ オプション
Bazel は、以下を含む --define
での遷移をサポートしていません。
"//command_line_option:define"
。代わりに、カスタム
ビルド設定。一般的に、
ビルド設定を優先して --define
を使用することは推奨されません。
Bazel は、--config
での移行をサポートしていません。これは、--config
が
「拡大」他のフラグに展開されます。
重要な点として、--config
にはビルド構成に影響しないフラグが含まれる場合があります。
--spawn_strategy
をタップします。Bazel は設計上、このようなフラグを個々のターゲットにバインドできません。つまり
遷移に適用するための一貫した方法がありません。
回避策として、
おすすめします。そのためには、--config
の
UI の問題として知られています。
複数のビルド設定を許可する場合の遷移
依存関係を設定すると、 複数の値を許可する場合は、 リストを使用して設定する必要があります。
# example/buildsettings/build_settings.bzl
string_flag = rule(
implementation = _impl,
build_setting = config.string(flag = True, allow_multiple = True)
)
# example/BUILD
load("//example/buildsettings:build_settings.bzl", "string_flag")
string_flag(name = "roasts", build_setting_default = "medium")
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
# Using a value of just "dark" here will throw an error
return {"//example:roasts" : ["dark"]},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:roasts"]
)
NoOps の遷移
遷移が {}
、[]
、または None
を返す場合、これはすべての
元の値のまま維持できますこれは、明示的に宣言するよりも
各出力をそれ自体に設定します。
# example/transitions/transitions.bzl
def _impl(settings, attr):
_ignore = (attr)
if settings["//example:already_chosen"] is True:
return {}
return {
"//example:favorite_flavor": "dark chocolate",
"//example:include_marshmallows": "yes",
"//example:desired_temperature": "38C",
}
hot_chocolate_transition = transition(
implementation = _impl,
inputs = ["//example:already_chosen"],
outputs = [
"//example:favorite_flavor",
"//example:include_marshmallows",
"//example:desired_temperature",
]
)
遷移による属性へのアクセス
送信エッジへの移行を接続する場合
(遷移が 1:1 または 1:2+ のいずれであるかにかかわらず)、ctx.attr
は強制的にリストになります。
まだ行っていない場合ですこのリストに含まれる要素の順序は指定されていません。
# example/transitions/rules.bzl
def _transition_impl(settings, attr):
return {"//example:favorite_flavor" : "LATTE"},
coffee_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//example:favorite_flavor"]
)
def _rule_impl(ctx):
# Note: List access even though "dep" is not declared as list
transitioned_dep = ctx.attr.dep[0]
# Note: Access doesn't change, other_deps was already a list
for other_dep in ctx.attr.other_deps:
# ...
coffee_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = coffee_transition)
"other_deps": attr.label_list(cfg = coffee_transition)
})
遷移が 1:2+
でカスタムキーを設定した場合は、ctx.split_attr
を使用できます。
次のようにして、キーごとに個別の依存関係を読み取ることができます。
# example/transitions/rules.bzl
def _impl(settings, attr):
_ignore = (settings, attr)
return {
"Apple deps": {"//command_line_option:cpu": "ppc"},
"Linux deps": {"//command_line_option:cpu": "x86"},
}
multi_arch_transition = transition(
implementation = _impl,
inputs = [],
outputs = ["//command_line_option:cpu"]
)
def _rule_impl(ctx):
apple_dep = ctx.split_attr.dep["Apple deps"]
linux_dep = ctx.split_attr.dep["Linux deps"]
# ctx.attr has a list of all deps for all keys. Order is not guaranteed.
all_deps = ctx.attr.dep
multi_arch_rule = rule(
implementation = _rule_impl,
attrs = {
"dep": attr.label(cfg = multi_arch_transition)
})
完全な例を見る 見てみましょう。
プラットフォームとツールチェーンとの統合
現在、--cpu
や --crosstool_top
など、現在多くのネイティブ フラグは以下に関連しています。
ツールチェーンの解決。将来的には、このような Google Workspace の
フラグが置き換えられる可能性があるので、Dataflow からの
ターゲット プラットフォーム。
メモリとパフォーマンスに関する考慮事項
ビルドに遷移(つまり新しい構成を追加)を追加する場合は、 コスト: ビルドグラフが大きく、ビルドグラフがわかりにくく、低速 説明します。これらの費用を検討する際には ビルドルールで遷移を使用します。以下は、移行プロセスが ビルドグラフが急激に増加することがあります
不正な動作のビルド: ケーススタディ
図 1. 最上位のターゲットとその依存関係を示すスケーラビリティ グラフ。
このグラフは、トップレベルのターゲット //pkg:app
を示しています。これは、
//pkg:1_0
と //pkg:1_1
。どちらのターゲットも、//pkg:2_0
と
//pkg:2_1
。これらのターゲットはどちらも、//pkg:3_0
と //pkg:3_1
の 2 つのターゲットに依存します。
これは、単一の要素に依存する //pkg:n_0
と //pkg:n_1
まで続きます。
ターゲット、//pkg:dep
。
//pkg:app
を構築するには \(2n+2\) ターゲット:
//pkg:app
//pkg:dep
- \(i\) \([1..n]\)で
//pkg:i_0
と//pkg:i_1
フラグを実装しているとします。
--//foo:owner=<STRING>
と //pkg:i_b
が適用されます
depConfig = myConfig + depConfig.owner="$(myConfig.owner)$(b)"
つまり、//pkg:i_b
は、すべてのテーブルで --owner
の古い値に b
を付加します。
依存関係があります。
これにより、次の構成済みターゲットが生成されます。
//pkg:app //foo:owner=""
//pkg:1_0 //foo:owner=""
//pkg:1_1 //foo:owner=""
//pkg:2_0 (via //pkg:1_0) //foo:owner="0"
//pkg:2_0 (via //pkg:1_1) //foo:owner="1"
//pkg:2_1 (via //pkg:1_0) //foo:owner="0"
//pkg:2_1 (via //pkg:1_1) //foo:owner="1"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_0) //foo:owner="00"
//pkg:3_0 (via //pkg:1_0 → //pkg:2_1) //foo:owner="01"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_0) //foo:owner="10"
//pkg:3_0 (via //pkg:1_1 → //pkg:2_1) //foo:owner="11"
...
//pkg:dep
が \(2^n\) 構成されたターゲット config.owner=
を生成します。
「\(b_0b_1...b_n\)」の \(b_i\) \(\{0,1\}\)
これにより、ビルドグラフがターゲット グラフより指数関数的に大きくなり、 メモリやパフォーマンスにも影響します
TODO: これらの問題の測定と軽減のための戦略を追加する。
関連情報
ビルド構成の変更について詳しくは、以下をご覧ください。
- Starlark ビルド構成
- Bazel の構成可能性のロードマップ
- エンドツーエンドのサンプルの完全なセット