構成可能なクエリ(cquery)

cqueryquery のバリアントで、 select() とビルド オプションがビルド グラフに与える影響を正しく処理します。

これは、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's 関数のほとんどと、いくつかの新しい関数をサポートしています。
  • //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 は、cqueryquery で意味が異なります。これは、 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's ターゲット パターン ドキュメントをご覧ください。

関数

関数セット queryでサポートされているうち、cqueryallrdepsbuildfilesrbuildfilessiblingstests、および visible以外のすべてをサポートしています。

cquery には、次の新しい関数も導入されています。

config

expr ::= config(expr, word)

config 演算子は、最初の引数で示されるラベルと、 2 番目の引数で指定された構成の構成済みターゲットを見つけようとします。

2 番目の引数の有効な値は null または a カスタム構成ハッシュです。ハッシュは $ 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"],
)

Genrule は 実行構成 でツールを構成するため、次のクエリでは次の出力が生成されます。

クエリ ビルドされたターゲット 出力
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これらは属性 の切り替えと呼ばれます。ルールは、ルールクラスの切り替えと呼ばれる独自の構成に切り替えを適用することもできます。 この出力形式では、 切り替えの種類やビルド オプションへの影響など、これらの切り替えに関する情報が出力されます。

この出力形式は --transitions フラグによってトリガーされます。このフラグはデフォルトで NONE に設定されています。FULL モードまたは LITE モードに設定できます。FULL モードでは、ルールクラスの切り替えと属性の切り替えに関する情報が出力されます。これには、切り替え前後のオプションの詳細な差分が含まれます。LITE モード では、オプションの差分を除いて同じ情報が出力されます。

プロトコル メッセージの出力

--output=proto

このオプションを指定すると、結果のターゲットがバイナリ プロトコル バッファ形式で出力されます。プロトコル バッファの定義は src/main/protobuf/analysis_v2.proto にあります。

CqueryResult は、cquery の結果を含む最上位メッセージです。 メッセージのリストとConfiguredTargetConfigurationメッセージのリストがあります。各 ConfiguredTarget には configuration_id があり、その値は対応する Configuration メッセージの id フィールドの値と同じです。

--[no]proto:include_configurations

デフォルトでは、cquery の結果は、構成済みターゲットの一部として構成情報を返します。この情報を省略して、クエリの proto 出力とまったく同じ形式の proto 出力を取得する場合は、このフラグを false に設定します。

proto 出力関連のオプションについては、クエリの proto 出力ドキュメント をご覧ください。

グラフ出力

--output=graph

このオプションは、Graphviz 互換の .dot ファイルとして出力を生成します。詳細については、query's グラフ出力ドキュメントをご覧ください。cquery もサポートしています --graph:node_limit--graph:factored

ファイル出力

--output=files

このオプションを指定すると、呼び出しの最後に表示されるリストと同様に、クエリに一致する各ターゲットによって生成された出力ファイルのリストが出力されます。bazel build出力には、リクエストされた 出力グループでアドバタイズされたファイルのみが含まれます。これは、 --output_groups フラグによって決定されます。 ソースファイルは含まれます。

この出力形式で出力されるパスはすべて、 execroot を基準としています。execroot は bazel info execution_root で取得できます。bazel-out の便利なシンボリック リンクが存在する場合、 メイン リポジトリ内のファイルへのパスもワークスペース ディレクトリを基準として解決されます。

Starlark を使用した出力形式の定義

--output=starlark

この出力形式では、クエリ結果の構成済みターゲットごとに Starlark 関数が呼び出され、呼び出しによって返された値 が出力されます。--starlark:file フラグは、 `format` という単一のパラメータ、 `target` という名前の関数を定義する Starlark ファイルの場所を指定します。この関数は、クエリ結果の各ターゲット に対して呼び出されます。または、便宜上、 フラグを使用して、def format(target): return exprとして宣言された関数の本文のみを指定することもできます。--starlark:expr

'cquery' Starlark 言語

cquery Starlark 環境は、BUILD ファイルまたは .bzl ファイルとは異なります。これには、すべてのコア Starlark 組み込み定数と関数に加えて、以下で説明する cquery 固有の定数が含まれますが、globnativerule などは含まれません。また、load ステートメントはサポートされていません。

build_options(target)

build_options(target) は、キーがビルド オプション識別子( 構成を参照)、値が Starlark 値であるマップを返します。値が有効な Starlark 値ではないビルド オプションは、このマップから除外されます。

ターゲットが入力ファイルの場合、build_options(target) は None を返します。入力ファイル ターゲットには null 構成があるためです。

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 とそのサブパッケージに出力します。

  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

ユーザー定義のプロバイダから値を抽出します。

  $ 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

cqueryquery は相互に補完し合い、 異なるニッチで優れています。どちらが適しているかを判断するには、次の点を考慮してください。

  • cquery は特定の select() ブランチに従って、 ビルドする正確なグラフをモデル化します。query はビルドで選択される ブランチを認識しないため、すべてのブランチを含めることで過剰な近似を行います。
  • cquery's 精度を高めるには、 query よりも多くのグラフをビルドする必要があります。具体的には、cquery 構成済みターゲットを評価しますが、queryターゲットのみを評価します。これには時間がかかり、より多くのメモリを使用します。
  • cquery's の解釈では、 クエリ言語 に、 query では回避される曖昧さが生じます。たとえば、 if "//foo" が 2 つの構成に存在する場合、どちらを cquery "deps(//foo)" が使用する必要がありますか? config 関数が役立ちます。
  • cquery は新しいツールであるため、特定のユース ケースをサポートしていません。詳しくは、既知の問題をご覧ください。

既知の問題

cquery が「ビルド」するすべてのターゲットは、同じ構成である必要があります。

クエリを評価する前に、cquery はビルド アクションが実行される直前まで ビルドをトリガーします。「ビルド」するターゲットは、デフォルトではクエリ式に表示されるすべてのラベルから選択されます(これは --universe_scope でオーバーライドできます)。これらは同じ構成である必要があります。

これらは通常、最上位の「ターゲット」構成を共有しますが、 ルールは受信エッジの切り替えによって 独自の構成を変更できます。 これが 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 属性に実行切り替えを適用します。つまり、実行構成でツールを構成します

その切り替えの残存効果は次のとおりです。

$ 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 "+" -))"