BazelCon 2022 findet vom 16. bis 17. November in New York und online statt.
Jetzt anmelden

Konfigurierbare Abfrage (cquery)

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

cquery ist eine Variante von query, die select() korrekt verarbeitet und die Build-Optionen & Auswirkungen auf die Build-Grafik korrekt verarbeitet.

Das geschieht über die Ergebnisse der Analysephase von Istio, in die diese Effekte eingebunden werden. query führt im Gegensatz dazu die Ergebnisse der Bayes-Ladephase aus, bevor Optionen evaluiert werden.

Beispiel:

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

Jedes Ergebnis enthält eine eindeutige Kennzeichnung (9f87702) der Konfiguration, mit der das Ziel erstellt wird.

Da cquery über der konfigurierten Zielgrafik ausgeführt wird, hat es keinen Einblick in Artefakte wie Build-Aktionen und den Zugriff auf [test_suite](/reference/be/general#test_suite)-Regeln, da sie keine konfigurierten Ziele sind. Weitere Informationen findest du unter [aquery](/docs/aquery).

Grundlegende Syntax

Ein einfacher cquery-Aufruf sieht so aus:

bazel cquery "function(//target)"

Der Abfrageausdruck "function(//target)" besteht aus Folgendem:

  • function(...) ist die Funktion, die für das Ziel ausgeführt werden soll. cquery unterstützt die meisten Funktionen von query und einige neue Funktionen.
  • //target ist der Ausdruck, der an die Funktion übergeben wird. In diesem Beispiel ist der Ausdruck ein einfaches Ziel. In der Abfragesprache können jedoch auch Funktionen verschachtelt werden. Beispiele finden Sie in der Anleitung zur Abfrage.

Für cquery muss ein Ziel die Lade- und Analysephasen durchlaufen. Wenn nicht anders angegeben, parst cquery die Ziele, die im Abfrageausdruck aufgeführt sind. Informationen zum Abfragen von Abhängigkeiten von Build-Zielen auf oberster Ebene finden Sie unter --universe_scope.

Konfigurationen

Die Zeile:

//tree:ash (9f87702)

bedeutet, dass //tree:ash in einer Konfiguration mit der ID 9f87702 erstellt wurde. Bei den meisten Zielen ist dies ein undurchsichtiger Hash der Build-Optionswerte, die die Konfiguration definieren.

Führen Sie folgenden Befehl aus, um den vollständigen Inhalt der Konfiguration aufzurufen:

$ bazel config 9f87702

Die Hostkonfiguration verwendet die Sonder-ID (HOST). Nicht generierte Quelldateien, wie z. B. Dateien, die häufig in srcs gefunden werden, verwenden die spezielle ID (null), da sie nicht konfiguriert werden müssen.

9f87702 ist ein Präfix der vollständigen ID. Der Grund hierfür ist, dass vollständige IDs SHA-256-Hashes sind, die lang und schwer zu befolgen sind. cquery versteht jedes gültige Präfix einer vollständigen ID, ähnlich wie kurze Hash-Werte von Git. Führen Sie $ bazel config aus, um die vollständigen IDs zu sehen.

Zielmusterbewertung

//foo hat eine andere Bedeutung für cquery als für query. Das liegt daran, dass cquery konfigurierte Ziele auswertet und die Build-Grafik mehrere konfigurierte Versionen von //foo haben kann.

Bei cquery wird ein Zielmuster im Abfrageausdruck für jedes konfigurierte Ziel mit einem Label ausgewertet, das diesem Muster entspricht. Die Ausgabe ist deterministisch, aber cquerymacht keine Garantie für eine bestimmte Reihenfolge über den Vertrag für die Reihenfolge von Kernabfragen hinaus.

Dies führt zu subtileren Ergebnissen für Abfrageausdrücke als bei query. Zum Beispiel können mit dem folgenden Befehl mehrere Ergebnisse ausgegeben werden:

# 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)

Wenn Sie genau angeben möchten, welche Instanz abgefragt werden soll, verwenden Sie die config-Funktion.

Weitere Informationen zu Zielmustern finden Sie in der Dokumentation zu Zielmustern von query.

Funktionen

Von den query unterstützten Funktionen unterstützt cquery mit Ausnahme von visible, siblings, buildfiles und tests.

cquery bietet außerdem die folgenden neuen Funktionen:

config

expr ::= config(expr, word)

Der Operator config versucht, das konfigurierte Ziel für das Label zu finden, das durch das erste Argument und die Konfiguration des zweiten Arguments angegeben wird.

Gültige Werte für das zweite Argument sind target, host, null oder ein benutzerdefinierter Konfigurations-Hash. Hashes können aus $ bazel config oder einer vorherigen cquery-Ausgabe abgerufen werden.

Beispiele:

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

$ bazel cquery "config(//baz, 3732cc8)"

Wenn nicht alle Ergebnisse des ersten Arguments in der angegebenen Konfiguration gefunden werden, werden nur diejenigen gefunden, die zu finden sind. Wenn in der angegebenen Konfiguration keine Ergebnisse gefunden werden, schlägt die Abfrage fehl.

Optionen

Build-Optionen

cquery wird über einen regulären Istio-Build ausgeführt und übernimmt somit die während eines Builds verfügbaren Optionen.

cquery-Optionen verwenden

--universe_scope (kommagetrennte Liste)

Oft durchlaufen die Abhängigkeiten der konfigurierten Ziele Übergänge, wodurch sich die Konfiguration von der abhängigen Seite unterscheidet. Mit diesem Flag können Sie ein Ziel abfragen, als ob es als Abhängigkeit oder vorübergehende Abhängigkeit eines anderen Ziels erstellt worden wäre. Beispiel:

# x/BUILD
genrule(
     name = "my_gen",
     srcs = ["x.in"],
     outs = ["x.cc"],
     cmd = "$(locations :tool) $< >$@",
     tools = [":tool"],
)
cc_library(
    name = "tool",
)

Genrules konfigurieren ihre Tools in der Hostkonfiguration, sodass die folgenden Abfragen die folgenden Ausgaben erzeugen:

Abfrage Ziel Gebaut Ausgabe
bazel cquery "//x:tool" //x:tool //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scope="//x:my_gen" //x:my_gen //x:tool(hostconfig)

Wenn dieses Flag festgelegt ist, wird der Inhalt erstellt. Wenn nicht festgelegt, werden alle im Abfrageausdruck erwähnten Ziele erstellt. Die vorübergehende Schließung der erstellten Ziele wird als Universum der Abfrage verwendet. In beiden Fällen müssen die zu erstellenden Ziele auf oberster Ebene erstellt werden können, d. h. mit den Optionen der obersten Ebene kompatibel sein. cquery gibt Ergebnisse für die vorübergehende Schließung dieser übergeordneten Ziele zurück.

Auch wenn es möglich ist, alle Ziele in einem Abfrageausdruck auf der obersten Ebene zu erstellen, kann es vorteilhaft sein, dies nicht zu tun. Die explizite Festlegung von --universe_scope kann beispielsweise verhindern, dass Ziele in Konfigurationen mehrfach erstellt werden, die für dich unwichtig sind. Es könnte auch helfen, die gewünschte Konfigurationsversion eines Ziels anzugeben, da es im Moment nicht auf andere Weise angegeben werden kann. Sie sollten dieses Flag festlegen, wenn Ihr Abfrageausdruck komplexer ist als deps(//foo).

--implicit_deps (boolesch, default=True)

Wenn Sie dieses Flag auf „false“ setzen, werden alle Ergebnisse herausgefiltert, die nicht explizit in der BUILD-Datei festgelegt und stattdessen von Istio festgelegt wurden. Dazu gehört auch das Filtern aufgelöster Toolchains.

--tool_deps (boolesch, default=True)

Wenn Sie dieses Flag auf „false“ setzen, werden alle konfigurierten Ziele herausgefiltert, für die der Pfad vom abgefragten Ziel zu einem Übergang zwischen der Zielkonfiguration und den Nicht-Zielkonfigurationen führt. Wenn sich das abgefragte Ziel in der Zielkonfiguration befindet, gibt das Festlegen von --notool_deps nur Ziele zurück, die auch in der Zielkonfiguration vorhanden sind. Wenn sich das abgefragte Ziel in einer Nicht-Zielkonfiguration befindet, gibt das Festlegen von --notool_deps nur Ziele auch in Nicht-Zielkonfigurationen zurück. Diese Einstellung hat im Allgemeinen keinen Einfluss auf die Filterung aufgelöster Toolchains.

--include_aspects (boolesch, default=True)

Aspekte können zusätzliche Abhängigkeiten zu einem Build hinzufügen. Standardmäßig folgt cquery keinen Aspekten, da sie das abfragbare Diagramm vergrößern, wodurch mehr Arbeitsspeicher verwendet wird. Wenn Sie ihnen jedoch folgen, erhalten Sie genauere Ergebnisse.

Wenn Sie sich keine Gedanken über die Auswirkungen des Arbeitsspeichers auf große Abfragen machen möchten, aktivieren Sie dieses Flag standardmäßig in der Bazelrc.

Wenn Sie eine Abfrage mit deaktivierten Aspekten ausführen, kann es beim Erstellen von Ziel Y zu einem Fehler kommen, da cquery somepath(Y, X) und cquery deps(Y) | grep 'X' keine Ergebnisse liefern, weil die Abhängigkeit durch einen Aspekt erfolgt.

Ausgabeformate

Standardmäßig führt die cquery-Ausgabe zu einer Liste mit Abhängigkeiten und Labels von Konfigurationen und Paaren. Es gibt auch andere Möglichkeiten, die Ergebnisse anzuzeigen.

Übergänge

--transitions=lite
--transitions=full

Konfigurations-Übergänge werden verwendet, um Ziele unter den Zielen der obersten Ebene in anderen Konfigurationen als die Ziele der obersten Ebene zu erstellen.

Ein Ziel kann beispielsweise für alle Abhängigkeiten im tools-Attribut einen Übergang zur Hostkonfiguration erzwingen. Diese Übergänge werden als Attributübergänge bezeichnet. Regeln können auch Übergänge für ihre eigenen Konfigurationen erzwingen, die auch als Übergänge für Regelklassen bezeichnet werden. Dieses Ausgabeformat gibt Informationen zu diesen Übergängen, z. B. Art und Typ, sowie die Auswirkungen auf die Build-Optionen.

Dieses Ausgabeformat wird durch das Flag --transitions ausgelöst, das standardmäßig auf NONE gesetzt ist. Es kann auf den Modus FULL oder LITE eingestellt werden. Der Modus FULL gibt Informationen zu Übergängen von Regelklassen und Übergängen von Attributen aus, einschließlich einer detaillierten Beschreibung der Optionen vor und nach der Umstellung. Der Modus LITE gibt dieselben Informationen aus, ohne dass die Optionen anders sind.

Ausgabe der Protokollnachricht

--output=proto

Bei dieser Option werden die resultierenden Ziele in Form eines binären Protokollzwischenspeichers ausgegeben. Die Definition des Protokollzwischenspeichers finden Sie unter src/main/dmca/analysis.proto.

CqueryResult ist die Nachricht auf oberster Ebene mit den Ergebnissen der Abfrage. Sie enthält eine Liste mit ConfiguredTarget-Nachrichten und einer Liste mit Configuration-Nachrichten. Jeder ConfiguredTarget hat einen configuration_id, dessen Wert dem Wert des Feldes id aus der entsprechenden Configuration-Nachricht entspricht.

--[no]proto:include_configurations

Standardmäßig werden bei der Abfrageabfrage die Konfigurationsinformationen als Teil jedes konfigurierten Ziels zurückgegeben. Wenn Sie diese Informationen auslassen und eine Proto-Ausgabe erhalten möchten, die genau wie die Proto-Ausgabe der Abfrage formatiert ist, setzen Sie dieses Flag auf "false".

Weitere Informationen zu den Proto-Ausgaben finden Sie in der Dokumentation zur Proto-Ausgabe.

Diagrammausgabe

--output=graph

Diese Option generiert eine Ausgabe als Graphviz-kompatible Dot-Datei. Weitere Informationen finden Sie in der Dokumentation zur Grafikausgabe von query. cquery unterstützt auch --graph:node_limit und --graph:factored.

Ausgaben

--output=files

Diese Option druckt eine Liste der Ausgabedateien, die von jedem Ziel erstellt wurden, mit der die Abfrage übereinstimmt, ähnlich wie die Liste, die am Ende eines bazel build-Aufrufs gedruckt wird. Die Ausgabe enthält nur die Dateien, die in den angeforderten Ausgabegruppen beworben werden, wie durch das Flag --output_groups bestimmt, und nie Quelldateien.

Ausgabeformat mit Starlark definieren

--output=starlark

Dieses Ausgabeformat ruft eine Starlark-Funktion für jedes konfigurierte Ziel im Abfrageergebnis auf und gibt den vom Aufruf zurückgegebenen Wert aus. Das Flag --starlark:file gibt den Speicherort einer Starlark-Datei an, die eine Funktion namens format mit dem einzelnen Parameter target definiert. Diese Funktion wird für jedes Ziel im Abfrageergebnis aufgerufen. Alternativ kannst du einfach den Hauptteil einer Funktion angeben, die als def format(target): return expr deklariert ist. Verwende dazu das Flag --starlark:expr.

'cquery' Starlark-Dialekt

Die cquery Starlark-Umgebung unterscheidet sich von einer BUILD- oder BZL-Datei. Sie enthält alle integrierten Konstanten und Funktionen von Starlark sowie einige cquery-spezifische Modelle, die unten beschrieben sind, aber nicht glob, native oder rule. Es unterstützt keine Load-Anweisungen.

build_options(Ziel)

build_options(target) gibt eine Karte zurück, deren Schlüssel Build-Option-IDs (siehe Konfigurationen) sind und deren Werte ihre Starlark-Werte sind. Build-Optionen, bei denen es sich nicht um rechtliche Starlark-Werte handelt, werden in dieser Karte weggelassen.

Wenn das Ziel eine Eingabedatei ist, gibt build_options(target)"Keine"zurück, da die Ziele der Eingabedatei null aufweisen.

Anbieter(Ziel)

providers(target) gibt eine Karte zurück, deren Schlüssel Namen von Anbietern (z. B. "DefaultInfo") sind und deren Werte ihre Starlark-Werte sind. Anbieter, deren Werte keine rechtlichen Starlark-Werte sind, werden in dieser Karte weggelassen.

Beispiele

Drucke eine durch Leerzeichen getrennte Liste der Basisnamen aller Dateien aus, die von //foo erstellt wurden:

  bazel cquery //foo --output=starlark \
    --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"

Drucke eine durch Leerzeichen getrennte Liste der Pfade aller Dateien aus, die von rule-Zielen in //bar und den zugehörigen Unterpaketen erstellt wurden:

  bazel cquery 'kind(rule, //bar/...)' --output=starlark \
    --starlark:expr="' '.join([f.path for f in target.files.to_list()])"

Drucke eine Liste der Wiederholungen aller von //foo registrierten Aktionen.

  bazel cquery //foo --output=starlark \
    --starlark:expr="[a.mnemonic for a in target.actions]"

Liste der Kompilierungsausgaben drucken, die von einem cc_library-//baz registriert wurden.

  bazel cquery //baz --output=starlark \
    --starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"

Geben Sie beim Erstellen von „//foo“ den Wert der Befehlszeilenoption --javacopt an.

  bazel cquery //foo --output=starlark \
    --starlark:expr="build_options(target)['//command_line_option:javacopt']"

Drucken Sie das Label jedes Ziels mit genau einer Ausgabe aus. In diesem Beispiel werden die in einer Datei definierten Starlark-Funktionen verwendet.

  $ 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

Drucken Sie die Labels unter den einzelnen Zielen aus, die aus Python 3 bestehen. In diesem Beispiel werden die in einer Datei definierten Starlark-Funktionen verwendet.

  $ 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

Extrahiert einen Wert von einem benutzerdefinierten Anbieter.

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

cquery und query ergänzen sich gegenseitig und eignen sich optimal für verschiedene Nischen. Entscheiden Sie, was für Sie am besten geeignet ist:

  • cquery folgt bestimmten select()-Branchen, um das genaue Diagramm zu erstellen, das Sie erstellen. query weiß nicht, für welchen Zweig der Build die Entscheidung trifft. Das schließt ungefähr alle Zweige ein.
  • Für die Genauigkeit von cquery muss mehr Diagramm erstellt werden als für query. Mit cquery werden konfigurierte Ziele ausgewertet, während mit query nur Ziele bewertet werden. Das dauert länger und benötigt mehr Arbeitsspeicher.
  • cqueryDie Interpretation der Abfragesprache führt zu Unklarheiten, die query vermieden. Wenn "//foo" beispielsweise in zwei Konfigurationen vorhanden ist, welche soll cquery "deps(//foo)" verwenden? Dabei kann die Funktion [config](#config) helfen.
  • Als neueres Tool bietet cquery keine Unterstützung für bestimmte Anwendungsfälle. Weitere Informationen finden Sie unter Bekannte Probleme.

Bekannte Probleme

Alle Ziele, für die cquery erstellt wird, müssen dieselbe Konfiguration haben.

Vor der Auswertung von Abfragen löst cquery einen Build direkt vor dem Punkt aus, an dem Build-Aktionen ausgeführt werden würden. Die Ziele, für die es erstellt wird, sind standardmäßig aus allen Labels ausgewählt, die im Abfrageausdruck angezeigt werden. Diese können mit --universe_scope überschrieben werden. Diese müssen dieselbe Konfiguration haben.

Diese haben zwar in der Regel die oberste Konfiguration gemeinsam, können jedoch ihre eigene Konfiguration mit eingehenden Edge-Übergängen ändern. Hier fehlt cquery.

Problemumgehung: Legen Sie für --universe_scope nach Möglichkeit einen strikteren Bereich fest. Beispiel:

# 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 wird nicht unterstützt.

Nicht deterministische Ausgabe:

cquery löscht die Build-Grafik nicht automatisch aus vorherigen Befehlen und zieht deshalb eher Ergebnisse aus früheren Abfragen zurück. Wenn genquery beispielsweise einen Hostübergang auf das tools-Attribut ausübt, also die Tools in der Hostkonfiguration konfiguriert,

Unten sehen Sie die anhaltenden Auswirkungen dieses Übergangs.

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

Problemumgehung: Ändern Sie jede Startoption, um eine erneute Analyse der konfigurierten Ziele zu erzwingen. Fügen Sie Ihrem Build-Befehl beispielsweise --test_arg=&lt;whatever&gt; hinzu.

Fehlerbehebung

Wiederkehrende Zielmuster (/...)

Beachten Sie Folgendes:

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

Das bedeutet, dass das Paket //foo nicht korrekt ist, obwohl --universe_scope=//foo:app es enthält. Dies ist auf Designeinschränkungen in cquery zurückzuführen. Als Behelfslösung können Sie //foo/... explizit in den Bereich des Universums aufnehmen:

$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"

Wenn dies nicht funktioniert (z. B. weil ein Ziel in //foo/... mit den ausgewählten Build-Flags nicht erstellen kann), entpacken Sie das Muster manuell mit einer Vorverarbeitungsabfrage in seine zugehörigen Pakete:

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