構成可能なクエリ(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](/versions/6.3.0/reference/be/general#test_suite) ルールへのアクセスは構成されません。これらは構成済みのターゲットではないためです。前者については、[aquery](/versions/6.3.0/docs/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

ホスト構成では、特殊な ID (HOST) が使用されます。生成されていないソースファイル(srcs でよく見られるものなど)は特別な ID (null) を使用します(構成する必要がないため)。

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 a host-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 (HOST)

クエリを実行するインスタンスを正確に宣言するには、config 関数を使用します。

ターゲット パターンの詳細については、queryターゲット パターンのドキュメントをご覧ください。

関数

query でサポートされている関数セットのうち、cqueryvisiblesiblingsbuildfilestests を除くすべての関数をサポートします。

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

config

expr ::= config(expr, word)

config 演算子は、最初の引数で示されるラベルと 2 番目の引数で指定された構成で構成されたターゲットの検索を試みます。

2 番目の引数の有効な値は、targethostnull、またはカスタム構成ハッシュです。ハッシュは、$ bazel config または以前の cquery の出力から取得できます。

例:

$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)"
//bar (HOST)
//baz (3732cc8)

$ 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_library(
    name = "tool",
)

genrules はホスト構成でツールを構成するため、次のクエリでは次の出力が生成されます。

クエリ ターゲットのビルド 出力
bazel cquery 「//x:ツール」 //x:tool //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scope="x:my_gen" //x:my_gen //x:tool(hostconfig)

このフラグが設定されている場合、その内容はビルドされます。設定されていない場合、代わりにクエリ式に指定されているすべてのターゲットがビルドされます。構築されたターゲットの推移的クロージャがクエリの世界として使用されます。いずれの場合も、ビルドするターゲットはトップレベルでビルド可能(つまり、トップレベル オプションと互換性がある)である必要があります。cquery は、これらのトップレベル ターゲットを推移的に閉じて結果を返します。

トップレベルのクエリ式内のすべてのターゲットをビルドできる場合でも、そうしないほうが良い場合があります。たとえば、--universe_scope を明示的に設定すると、不要な構成でターゲットを複数回ビルドできなくなる可能性があります。また、探しているターゲットの構成バージョンを指定するのにも使用できます(現時点では、他の方法では完全に指定できないため)。クエリ式が deps(//foo) よりも複雑な場合は、このフラグを設定する必要があります。

--implicit_deps(ブール値、デフォルト=True)

このフラグを false に設定すると、BUILD ファイルで明示的に設定されず、Bazel によって他の場所に設定された結果がすべて除外されます。これには、解決されたツールチェーンのフィルタリングが含まれます。

--tool_deps(ブール値、デフォルト=True)

このフラグを false に設定すると、クエリされたターゲットからそれらのターゲットへのパスがターゲット構成とターゲット以外の構成間の移行を通過する構成済みターゲットがすべてフィルタされます。クエリ対象のターゲットがターゲット構成にある場合、--notool_deps を設定すると、ターゲット構成にも存在するターゲットのみが返されます。クエリ対象のターゲットがターゲット以外の構成にある場合、--notool_deps を設定すると、ターゲット以外の構成のターゲットのみが返されます。通常、この設定は解決済みのツールチェーンのフィルタリングには影響しません。

--include_aspects(ブール値、デフォルト=True)

アスペクトにより、ビルドに依存関係を追加できます。デフォルトでは、cquery はアスペクトをフォローしません。これは、クエリ可能なグラフが大きくなり、より多くのメモリを使用するためです。より正確な結果が得られます

大規模なクエリのメモリへの影響を懸念しない場合は、bazelrc でこのフラグをデフォルトで有効にしてください。

アスペクトを無効にしてクエリを実行すると、ターゲット Y のビルド中にターゲット X が失敗し、cquery somepath(Y, X)cquery deps(Y) | grep 'X' はアスペクトを通じて依存関係が発生するため、結果を返さないという問題が発生する可能性があります。

出力形式

デフォルトでは、cquery の出力結果は、ラベルと構成のペアの依存関係順のリストになります。結果を公開する方法は他にもあります。

切り替え効果

--transitions=lite
--transitions=full

構成遷移は、トップレベル ターゲットとは異なる構成でトップレベル ターゲットの下にターゲットを作成するために使用されます。

たとえば、あるターゲットによって、その tools 属性のすべての依存関係でホスト構成への移行が適用されることがあります。これは属性遷移と呼ばれます。ルールでは、独自の構成に遷移を適用することもできます(これをルールクラスの遷移と呼びます)。この出力形式は、遷移の種類やビルド オプションへの影響など、遷移に関する情報を出力します。

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

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

--output=proto

このオプションを使用すると、結果のターゲットがバイナリ プロトコル バッファ形式で出力されます。プロトコル バッファの定義は、src/main/protobuf/analysis.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 フラグで指定した、リクエストされた出力グループでアドバタイズされたファイルのみが含まれます。これにはソースファイルが含まれます。

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 固有のものが含まれますが、globnativerule などは含まれず、読み込みステートメントはサポートされていません。

build_options(target)

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

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

プロバイダ(ターゲット)

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

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

  $ 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 の精度を高くするには、query よりも多くのグラフを作成する必要があります。具体的には、cquery構成されたターゲットを評価しますが、queryターゲットのみを評価します。この場合は時間とメモリ使用量が増えます。
  • cquery によるクエリ言語の解釈により、query では回避されるあいまいさが生じます。たとえば、"//foo" が 2 つの構成が存在する場合、cquery "deps(//foo)" ではどちらを使用すべきでしょうか。これには [config](#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 は、前のコマンドからビルドグラフを自動的にワイプしないため、過去のクエリから結果が取得されやすくなります。たとえば、genquerytools 属性でホストを遷移します。つまり、ホスト構成でツールを構成します。

その移行による効果を下記に示します。

$ 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 (host_config)
...

    $ bazel cquery "//foo:tool"
tool(host_config)

回避策: 起動オプションを変更して、構成されたターゲットを強制的に再分析します。たとえば、ビルドコマンドに --test_arg=&lt;whatever&gt; を追加します。

トラブルシューティング

再帰ターゲット パターン(/...

次のようなエラーに遭遇した場合:

$ 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.

これは、パッケージ //foo--universe_scope=//foo:app に含まれているにもかかわらず、対象範囲外であると誤って提案します。これは、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 "+" -))"