cquery は、ビルドグラフに対する select() とビルド オプションの効果を正しく処理する query のバリアントです。
これは、これらの効果を統合する Bazel の分析フェーズの結果を実行することで実現されます。対照的に、query はオプションが評価される前に、Bazel の読み込みフェーズの結果に対して実行されます。
次に例を示します。
$ cat > tree/BUILD <<EOF
sh_library(
name = "ash",
deps = select({
":excelsior": [":manna-ash"],
":americana": [":white-ash"],
"//conditions:default": [":common-ash"],
}),
)
sh_library(name = "manna-ash")
sh_library(name = "white-ash")
sh_library(name = "common-ash")
config_setting(
name = "excelsior",
values = {"define": "species=excelsior"},
)
config_setting(
name = "americana",
values = {"define": "species=americana"},
)
EOF
# Traditional query: query doesn't know which select() branch you will choose, # so it conservatively lists all of possible choices, including all used config_settings. $ bazel query "deps(//tree:ash)" --noimplicit_deps //tree:americana //tree:ash //tree:common-ash //tree:excelsior //tree:manna-ash //tree:white-ash # cquery: cquery lets you set build options at the command line and chooses # the exact dependencies that implies (and also the config_setting targets). $ bazel cquery "deps(//tree:ash)" --define species=excelsior --noimplicit_deps //tree:ash (9f87702) //tree:manna-ash (9f87702) //tree:americana (9f87702) //tree:excelsior (9f87702)
各結果には、ターゲットのビルドに使用された構成の一意の識別子 (9f87702) が含まれます。
cquery は構成されたターゲット グラフで実行されるため、ビルド アクションなどのアーティファクトに関する分析情報がなく、test_suite ルールにもアクセスできません。これらのルールは構成されたターゲットではないためです。前者については、aquery をご覧ください。
基本的な構文
簡単な cquery 呼び出しは次のようになります。
bazel cquery "function(//target)"
クエリ式 "function(//target)" は次の要素で構成されています。
function(...)は、ターゲットで実行する関数です。cqueryは、queryのほとんどの関数と、いくつかの新しい関数をサポートしています。//targetは、関数に渡される式です。この例では、式はシンプルなターゲットです。ただし、クエリ言語では関数のネストも可能です。例については、クエリガイドをご覧ください。
cquery では、読み込みと分析フェーズを実行するターゲットが必要です。特に指定しない限り、cquery はクエリ式にリストされているターゲットを解析します。トップレベルのビルド ターゲットの依存関係をクエリするには、--universe_scope をご覧ください。
構成
次の行をご覧ください。
//tree:ash (9f87702)
は、//tree:ash が ID 9f87702 の構成でビルドされたことを意味します。ほとんどのターゲットでは、これは構成を定義するビルド オプション値の不透明なハッシュです。
構成の完全な内容を表示するには、次のコマンドを実行します。
$ bazel config 9f87702
9f87702 は、完全な ID の接頭辞です。これは、完全な ID が SHA-256 ハッシュであり、長くて追跡が困難なためです。cquery は、Git の短いハッシュと同様に、完全な ID の有効な接頭辞を理解します。完全な ID を表示するには、$ bazel config を実行します。
ターゲット パターンの評価
//foo は、cquery と query で意味が異なります。これは、cquery が構成されたターゲットを評価し、ビルドグラフに //foo の構成済みバージョンが複数存在する可能性があるためです。
cquery の場合、クエリ式のターゲット パターンは、そのパターンに一致するラベルを持つ構成済みのすべてのターゲットに評価されます。出力は決定的ですが、cquery はコアクエリの順序付けコントラクトを超える順序付けを保証しません。
これにより、query を使用した場合よりもクエリ式に対してより微妙な結果が生成されます。たとえば、次のクエリでは複数の結果が生成される可能性があります。
# Analyzes //foo in the target configuration, but also analyzes # //genrule_with_foo_as_tool which depends on an exec-configured # //foo. So there are two configured target instances of //foo in # the build graph. $ bazel cquery //foo --universe_scope=//foo,//genrule_with_foo_as_tool //foo (9f87702) //foo (exec)
クエリするインスタンスを正確に宣言する場合は、config 関数を使用します。
ターゲット パターンの詳細については、query のターゲット パターンのドキュメントをご覧ください。
関数
query でサポートされている関数セットのうち、cquery は allrdeps、buildfiles、rbuildfiles、siblings、tests、visible を除くすべてをサポートしています。
cquery では、次の新しい関数も導入されています。
config
expr ::= config(expr, word)
config 演算子は、最初の引数で示されるラベルと 2 番目の引数で指定される構成に対して、構成されたターゲットを見つけようとします。
2 番目の引数に有効な値は、null またはカスタム構成ハッシュです。ハッシュは $
bazel config または以前の cquery の出力から取得できます。
例:
$ bazel cquery "config(//bar, 3732cc8)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (exec) //baz (exec) $ bazel cquery "config(//baz, 3732cc8)"
最初の引数の結果が指定された構成にすべて見つからない場合は、見つかったものだけが返されます。指定された構成で結果が見つからない場合、クエリは失敗します。
オプション
ビルド オプション
cquery は通常の Bazel ビルドで実行されるため、ビルド中に使用可能なオプションのセットを継承します。
cquery オプションの使用
--universe_scope(カンマ区切りのリスト)
多くの場合、構成されたターゲットの依存関係は移行を経るため、構成が依存関係と異なります。このフラグを使用すると、ターゲットが別のターゲットの依存関係または推移的依存関係としてビルドされたかのように、ターゲットをクエリできます。次に例を示します。
# x/BUILD
genrule(
name = "my_gen",
srcs = ["x.in"],
outs = ["x.cc"],
cmd = "$(locations :tool) $< >$@",
tools = [":tool"],
)
cc_binary(
name = "tool",
srcs = ["tool.cpp"],
)
Genrules は、実行構成でツールを構成するため、次のクエリは次の出力を生成します。
| クエリ | ターゲットがビルドされました | 出力 |
|---|---|---|
| bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
| bazel cquery "//x:tool" --universe_scope="//x:my_gen" | //x:my_gen | //x:tool(execconfig) |
このフラグが設定されている場合、その内容はビルドされます。設定されていない場合は、クエリ式で言及されているすべてのターゲットが代わりにビルドされます。ビルドされたターゲットの推移閉包がクエリのユニバースとして使用されます。いずれにしても、ビルドするターゲットは最上位でビルド可能(つまり、最上位のオプションと互換性がある)でなければなりません。cquery は、これらのトップレベル ターゲットの推移閉包の結果を返します。
クエリ式の最上位ですべてのターゲットをビルドできる場合でも、そうしない方がよいことがあります。たとえば、--universe_scope を明示的に設定すると、不要な構成でターゲットが複数回ビルドされるのを防ぐことができます。また、探しているターゲットの構成バージョンを指定するのにも役立ちます(現時点では、他の方法で完全に指定することはできません)。クエリ式が deps(//foo) よりも複雑な場合は、このフラグを設定する必要があります。
--implicit_deps(ブール値、デフォルト=True)
このフラグを false に設定すると、BUILD ファイルで明示的に設定されておらず、Bazel によって他の場所で設定されているすべての結果が除外されます。これには、解決済みのツールチェーンのフィルタリングも含まれます。
--tool_deps(ブール値、デフォルト=True)
このフラグを false に設定すると、クエリされたターゲットからそれらへのパスがターゲット構成と非ターゲット構成間の移行を通過する、構成済みのすべてのターゲットが除外されます。クエリ対象のターゲットがターゲット構成に含まれている場合、--notool_deps を設定すると、ターゲット構成にも含まれているターゲットのみが返されます。クエリされたターゲットがターゲット以外の構成にある場合、--notool_deps を設定すると、ターゲット以外の構成にあるターゲットのみが返されます。この設定は通常、解決済みのツールチェーンのフィルタリングには影響しません。
--include_aspects(ブール値、デフォルト=True)
アスペクトによって追加された依存関係を含めます。
このフラグが無効になっている場合、cquery somepath(X, Y) と cquery deps(X) | grep 'Y' は、X がアスペクトを介してのみ Y に依存している場合に Y を省略します。
出力形式
デフォルトでは、cquery はラベルと構成のペアの依存関係順のリストで結果を出力します。結果を公開する他の方法もあります。
切り替え効果
--transitions=lite --transitions=full
構成の切り替えは、最上位のターゲットとは異なる構成で、最上位のターゲットの下にあるターゲットをビルドするために使用されます。
たとえば、ターゲットは tools 属性のすべての依存関係に exec 構成への移行を課す場合があります。これらは属性の切り替えと呼ばれます。ルールは、ルールクラスの移行と呼ばれる独自の構成に移行を適用することもできます。この出力形式では、これらの遷移に関する情報(遷移のタイプやビルド オプションへの影響など)が出力されます。
この出力形式は --transitions フラグによってトリガーされます。このフラグはデフォルトで NONE に設定されています。FULL モードまたは LITE モードに設定できます。FULL モードでは、ルールのクラスの移行と属性の移行に関する情報が出力されます。これには、移行前後のオプションの詳細な差分が含まれます。LITE モードでは、オプションの差分を除いた同じ情報が出力されます。
プロトコル メッセージの出力
--output=proto
このオプションを指定すると、結果のターゲットがバイナリ プロトコル バッファ形式で出力されます。プロトコル バッファの定義は、src/main/protobuf/analysis_v2.proto で確認できます。
CqueryResult は、cquery の結果を含む最上位レベルのメッセージです。ConfiguredTarget メッセージのリストと Configuration メッセージのリストがあります。各 ConfiguredTarget には、対応する Configuration メッセージの id フィールドの値と等しい値を持つ configuration_id があります。
--[no]proto:include_configurations
デフォルトでは、cquery の結果は、構成された各ターゲットの一部として構成情報を返します。この情報を省略して、クエリの proto 出力とまったく同じ形式の proto 出力を取得する場合は、このフラグを false に設定します。
proto 出力関連のその他のオプションについては、クエリの proto 出力ドキュメントをご覧ください。
グラフ出力
--output=graph
このオプションは、Graphviz 互換の .dot ファイルとして出力を生成します。詳しくは、query のグラフ出力ドキュメントをご覧ください。cquery は、--graph:node_limit と --graph:factored もサポートしています。
出力ファイル
--output=files
このオプションは、クエリで一致した各ターゲットによって生成された出力ファイルのリストを、bazel build 呼び出しの最後に表示されるリストと同様の形式で出力します。出力には、--output_groups フラグで指定された、リクエストされた出力グループでアドバタイズされたファイルのみが含まれます。ソースファイルは含まれています。
この出力形式で出力されるすべてのパスは execroot を基準としており、bazel info execution_root を介して取得できます。bazel-out の便利なシンボリック リンクが存在する場合、メイン リポジトリ内のファイルへのパスもワークスペース ディレクトリからの相対パスとして解決されます。
Starlark を使用して出力形式を定義する
--output=starlark
この出力形式では、クエリ結果で構成された各ターゲットに対して Starlark 関数が呼び出され、呼び出しによって返された値が出力されます。--starlark:file フラグは、単一のパラメータ target を持つ format という名前の関数を定義する Starlark ファイルの場所を指定します。この関数は、クエリ結果の各 Target に対して呼び出されます。または、便宜上、--starlark:expr フラグを使用して、def format(target): return expr として宣言された関数の本体のみを指定することもできます。
「cquery」Starlark 言語
cquery Starlark 環境は、BUILD ファイルや .bzl ファイルとは異なります。これには、すべてのコア Starlark の組み込み定数と関数、および後述の cquery 固有の定数と関数が含まれますが、glob、native、rule などは含まれません。また、load ステートメントはサポートされていません。
build_options(target)
build_options(target) は、キーがビルド オプション識別子(構成を参照)で、値が Starlark 値であるマップを返します。値が有効な Starlark 値ではないビルド オプションは、このマップから除外されます。
ターゲットが入力ファイルの場合、入力ファイル ターゲットの構成は null であるため、build_options(target) は None を返します。
providers(target)
providers(target) は、キーがプロバイダの名前("DefaultInfo" など)で、値が Starlark 値であるマップを返します。値が有効な Starlark 値ではないプロバイダは、このマップから除外されます。
例
//foo によって生成されたすべてのファイルのベース名のスペース区切りリストを出力します。
bazel cquery //foo --output=starlark \
--starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
//bar とそのサブパッケージの rule ターゲットによって生成されたすべてのファイルのパスのスペース区切りリストを出力します。
bazel cquery 'kind(rule, //bar/...)' --output=starlark \
--starlark:expr="' '.join([f.path for f in target.files.to_list()])"
//foo によって登録されたすべてのアクションのニーモニックのリストを出力します。
bazel cquery //foo --output=starlark \
--starlark:expr="[a.mnemonic for a in target.actions]"
cc_library //baz によって登録されたコンパイル出力のリストを出力します。
bazel cquery //baz --output=starlark \
--starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"
//foo をビルドするときに、コマンドライン オプション --javacopt の値を出力します。
bazel cquery //foo --output=starlark \
--starlark:expr="build_options(target)['//command_line_option:javacopt']"
出力が 1 つしかない各ターゲットのラベルを出力します。この例では、ファイルで定義された Starlark 関数を使用します。
$ cat example.cquery
def has_one_output(target):
return len(target.files.to_list()) == 1
def format(target):
if has_one_output(target):
return target.label
else:
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
厳密に Python 3 である各ターゲットのラベルを出力します。この例では、ファイルで定義された Starlark 関数を使用します。
$ cat example.cquery
def format(target):
p = providers(target)
py_info = p.get("PyInfo")
if py_info and py_info.has_py3_only_sources:
return target.label
else:
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
ユーザー定義の Provider から値を抽出します。
$ cat some_package/my_rule.bzl
MyRuleInfo = provider(fields={"color": "the name of a color"})
def _my_rule_impl(ctx):
...
return [MyRuleInfo(color="red")]
my_rule = rule(
implementation = _my_rule_impl,
attrs = {...},
)
$ cat example.cquery
def format(target):
p = providers(target)
my_rule_info = p.get("//some_package:my_rule.bzl%MyRuleInfo'")
if my_rule_info:
return my_rule_info.color
return ""
$ bazel cquery //baz --output=starlark --starlark:file=example.cquery
cquery と query
cquery と query は互いに補完し合い、異なるニッチで優れています。どちらが適しているかを判断するには、次の点を考慮してください。
cqueryは特定のselect()ブランチに従って、構築するグラフを正確にモデル化します。queryはビルドがどのブランチを選択するかを認識していないため、すべてのブランチを含めて過大評価します。cqueryの精度では、queryよりも多くのグラフを構築する必要があります。具体的には、cqueryは構成されたターゲットを評価しますが、queryはターゲットのみを評価します。この場合、より多くの時間とメモリが使用されます。cqueryの クエリ言語の解釈には、queryが回避する曖昧さが含まれています。たとえば、"//foo"が 2 つの構成に存在する場合、cquery "deps(//foo)"はどちらを使用する必要がありますか?config関数を使用すると、この処理を簡単に行うことができます。- 新しいツールである
cqueryは、特定のユースケースをサポートしていません。詳細については、既知の問題をご覧ください。
既知の問題
cquery が「ビルド」するすべてのターゲットは、同じ構成である必要があります。
クエリを評価する前に、cquery はビルド アクションが実行される直前までビルドをトリガーします。「ビルド」するターゲットは、デフォルトではクエリ式に表示されるすべてのラベルから選択されます(これは --universe_scope でオーバーライドできます)。これらは同じ構成である必要があります。
これらは通常、最上位の「target」構成を共有しますが、ルールは着信エッジ遷移で独自の構成を変更できます。cquery の欠点はここにあります。
回避策: 可能であれば、--universe_scope をより厳格なスコープに設定します。次に例を示します。
# This command attempts to build the transitive closures of both //foo and # //bar. //bar uses an incoming edge transition to change its --cpu flag. $ bazel cquery 'somepath(//foo, //bar)' ERROR: Error doing post analysis query: Top-level targets //foo and //bar have different configurations (top-level targets with different configurations is not supported) # This command only builds the transitive closure of //foo, under which # //bar should exist in the correct configuration. $ bazel cquery 'somepath(//foo, //bar)' --universe_scope=//foo
--output=xml はサポートされていません。
非決定論的出力。
cquery は、以前のコマンドからビルドグラフを自動的に消去しないため、過去のクエリの結果を取得する可能性があります。たとえば、genrule は tools 属性に exec 遷移を適用します。つまり、exec 構成でツールを構成します。
移行による影響は、以下のとおりです。
$ cat > foo/BUILD <<<EOF
genrule(
name = "my_gen",
srcs = ["x.in"],
outs = ["x.cc"],
cmd = "$(locations :tool) $< >$@",
tools = [":tool"],
)
cc_library(
name = "tool",
)
EOF
$ bazel cquery "//foo:tool"
tool(target_config)
$ bazel cquery "deps(//foo:my_gen)"
my_gen (target_config)
tool (exec_config)
...
$ bazel cquery "//foo:tool"
tool(exec_config)
回避策: 起動オプションを変更して、構成されたターゲットの再分析を強制します。たとえば、ビルドコマンドに --test_arg=<whatever> を追加します。
トラブルシューティング
再帰的なターゲット パターン(/...)
次のような場合は、以下の手順をお試しください。
$ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, //foo/...)" ERROR: Error doing post analysis query: Evaluation failed: Unable to load package '[foo]' because package is not in scope. Check that all target patterns in query expression are within the --universe_scope of this query.
これは、--universe_scope=//foo:app に含まれているにもかかわらず、パッケージ //foo がスコープ外であることを誤って示しています。これは、cquery の設計上の制限によるものです。回避策として、ユニバース スコープに //foo/... を明示的に含めます。
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
(たとえば、//foo/... の一部のターゲットが選択したビルドフラグでビルドできないなどの理由で)うまくいかない場合は、前処理クエリを使用して、パターンを構成パッケージに手動で展開します。
# Replace "//foo/..." with a subshell query call (not cquery!) outputting each package, piped into # a sed call converting "<pkg>" to "//<pkg>:*", piped into a "+"-delimited line merge. # Output looks like "//foo:*+//foo/bar:*+//foo/baz". # $ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, $(bazel query //foo/... --output=package | sed -e 's/^/\/\//' -e 's/$/:*/' | paste -sd "+" -))"