Consulta configurable (cquery)

Informa un problema Ver código fuente

cquery es una variante de query que controla correctamente select() y los efectos de las opciones de compilación en el gráfico de compilación.

Para lograrlo, ejecuta los resultados de la fase de análisis de Bazel, que integra estos efectos. Por el contrario, query ejecuta los resultados de la fase de carga de Bazel antes de evaluar las opciones.

Por ejemplo:

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

Cada resultado incluye un identificador único (9f87702) de la configuración con la que se compiló el destino.

Dado que cquery se ejecuta en el grafo de destino configurado, no tiene estadísticas sobre artefactos como las acciones de compilación ni el acceso a las reglas test_suite, ya que no son destinos configurados. Para ver las opciones anteriores, consulta aquery.

Sintaxis básica

Una simple llamada a cquery se ve de la siguiente manera:

bazel cquery "function(//target)"

La expresión de consulta "function(//target)" consta de lo siguiente:

  • function(...) es la función que se ejecutará en el destino. cquery admite la mayoría de las funciones de query, además de algunas nuevas.
  • //target es la expresión que se envía a la función. En este ejemplo, la expresión es un objetivo simple. Sin embargo, el lenguaje de consulta también permite anidar funciones. Consulta la Guía de consultas para ver ejemplos.

cquery requiere un destino para ejecutarse a través de las fases de carga y análisis. A menos que se especifique lo contrario, cquery analiza los destinos que aparecen en la expresión de consulta. Consulta --universe_scope para consultar las dependencias de los destinos de compilación de nivel superior.

Parámetros de configuración

La línea:

//tree:ash (9f87702)

significa que //tree:ash se compiló en una configuración con el ID 9f87702. Para la mayoría de los destinos, este es un hash opaco de los valores de las opciones de compilación que definen la configuración.

Para ver todo el contenido de la configuración, ejecuta lo siguiente:

$ bazel config 9f87702

9f87702 es un prefijo del ID completo. Esto se debe a que los ID completos son hash SHA-256, que son largos y difíciles de seguir. cquery comprende cualquier prefijo válido de un ID completo, similar a los hashes cortos de Git. Para ver los ID completos, ejecuta $ bazel config.

Evaluación del patrón de destino

//foo tiene un significado diferente para cquery y para query. Esto se debe a que cquery evalúa los destinos configurados y el gráfico de compilación puede tener varias versiones configuradas de //foo.

Para cquery, un patrón de destino en la expresión de consulta se evalúa como cada destino configurado con una etiqueta que coincide con ese patrón. El resultado es determinista, pero cquery no garantiza ningún tipo de orden más allá del contrato de ordenamiento de consultas principales.

Esto produce resultados más sutiles para las expresiones de consulta que con query. Por ejemplo, lo siguiente puede generar varios resultados:

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

Si deseas declarar con precisión qué instancia consultar, usa la función config.

Consulta la documentación de patrones de destino de query para obtener más información sobre los patrones de los objetivos.

Funciones

Del conjunto de funciones compatible con query, cquery admite todos excepto visible, siblings, buildfiles y tests.

cquery también presenta las siguientes funciones nuevas:

config

expr ::= config(expr, word)

El operador config intenta encontrar el destino configurado para la etiqueta que indica el primer argumento y la configuración especificada por el segundo.

Los valores válidos para el segundo argumento son null o un hash de configuración personalizado. Los hashes se pueden recuperar de $ bazel config o del resultado anterior de cquery.

Ejemplos:

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

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

Si no todos los resultados del primer argumento se pueden encontrar en la configuración especificada, solo se muestran los que se pueden encontrar. Si no se encuentran resultados en la configuración especificada, la consulta falla.

Opciones

Opciones de compilación

cquery se ejecuta en una compilación normal de Bazel y, por lo tanto, hereda el conjunto de opciones disponibles durante una compilación.

Usa opciones de BigQuery

--universe_scope (lista separada por comas)

A menudo, las dependencias de los destinos configurados pasan por las transiciones, lo que hace que su configuración difiera de la dependiente. Esta marca te permite consultar un destino como si se hubiera creado como dependencia o una dependencia transitiva de otro destino. Por ejemplo:

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

Las genrules configuran sus herramientas en la configuración exec para que las siguientes consultas produzcan los siguientes resultados:

Consulta Objetivo de creación Resultado
Bazel Cquery "//x:tool" //x:tool //x:tool(targetconfig)
Bazel Cquery "//x:tool" --universe_scope="//x:my_gen" //x:my_gen //x:tool(execconfig)

Si se establece esta marca, se compila su contenido. Si no está configurado, se compilan todos los destinos mencionados en la expresión de consulta. El cierre transitivo de los destinos compilados se usa como el universo de la consulta. De cualquier manera, los destinos que se compilarán deben ser compilados en el nivel superior (es decir, compatibles con las opciones del nivel superior). cquery muestra los resultados en el cierre transitivo de estos objetivos de nivel superior.

Incluso si es posible compilar todos los destinos en una expresión de consulta en el nivel superior, puede resultar beneficioso no hacerlo. Por ejemplo, configurar --universe_scope de forma explícita podría impedir la compilación de destinos varias veces en configuraciones que no te interesan. También puede ayudarte a especificar qué versión de configuración de un objetivo estás buscando (dado que, por el momento, no es posible especificarlo de ninguna otra manera). Debes establecer esta marca si tu expresión de consulta es más compleja que deps(//foo).

--implicit_deps (booleano, predeterminado=Verdadero)

Si estableces esta marca como falsa, se filtrarán todos los resultados que no se establezcan de forma explícita en el archivo BUILD y que se configuren en otro lugar mediante Bazel. Esto incluye filtrar las cadenas de herramientas resueltas.

--tool_deps (booleano, predeterminado=Verdadero)

Si estableces esta marca como falsa, se filtrarán todos los destinos configurados para los que se cruza la ruta del destino consultado a una transición entre la configuración de destino y las configuraciones sin objetivo. Si el destino que se consultó está en la configuración de destino, la configuración de --notool_deps solo mostrará los destinos que también estén en la configuración de destino. Si el objetivo consultado se encuentra en una configuración sin objetivo, configurar --notool_deps solo mostrará objetivos también en configuraciones no objetivo. Por lo general, esta configuración no afecta el filtrado de cadenas de herramientas resueltas.

--include_aspects (booleano, predeterminado=Verdadero)

Incluye las dependencias que agregan los aspectos.

Si esta marca está inhabilitada, cquery somepath(X, Y) y cquery deps(X) | grep 'Y' omiten Y si X solo depende de él a través de un aspecto.

Formatos de salida

De forma predeterminada, los resultados de BigQuery generan una lista ordenada de dependencias de etiquetas y pares de configuración. También hay otras opciones para exponer los resultados.

Transiciones

--transitions=lite
--transitions=full

Las transiciones de configuración se usan para compilar destinos por debajo de los objetivos de nivel superior en configuraciones distintas a los de nivel superior.

Por ejemplo, un objetivo puede imponer una transición a la configuración ejecutiva en todas las dependencias de su atributo tools. Estas se conocen como transiciones de atributos. Las reglas también pueden imponer transiciones en sus propias configuraciones, conocidas como transiciones de clase de reglas. Este formato de salida genera información sobre estas transiciones, como qué tipo son y el efecto que tienen en las opciones de compilación.

Este formato de salida se activa mediante la marca --transitions, que se establece de forma predeterminada en NONE. Se puede establecer en los modos FULL o LITE. El modo FULL genera información sobre las transiciones de clases de reglas y las transiciones de atributos, incluida una diferencia detallada de las opciones antes y después de la transición. El modo LITE genera la misma información sin las diferencias de opciones.

Salida de mensaje de protocolo

--output=proto

Esta opción hace que los destinos resultantes se impriman en un formulario de búfer de protocolo binario. La definición del búfer de protocolo se puede encontrar en src/main/protobuf/analysis_v2.proto.

CqueryResult es el mensaje de nivel superior que contiene los resultados de la consulta. Tiene una lista de mensajes ConfiguredTarget y una lista de mensajes Configuration. Cada ConfiguredTarget tiene un configuration_id cuyo valor es igual al del campo id del mensaje Configuration correspondiente.

--[no]proto:include_configurations

De forma predeterminada, los resultados de BigQuery muestran información de configuración como parte de cada destino configurado. Si deseas omitir esta información y obtener un resultado de proto que tenga el mismo formato que el de la salida de proto de la consulta, establece esta marca como falsa.

Consultar la documentación de salida de proto de la consulta para obtener más opciones relacionadas con la salida de proto.

Resultado del gráfico

--output=graph

Esta opción genera resultados como un archivo .dot compatible con Graphviz. Consulta la documentación de salida del grafo de query para obtener más detalles. cquery también admite --graph:node_limit y --graph:factored.

Salida de archivos

--output=files

Esta opción imprime una lista de los archivos de salida generados por cada destino que coincide con la consulta, similar a la lista impresa al final de una invocación bazel build. El resultado contiene solo los archivos anunciados en los grupos de salida solicitados, según lo determinado por la marca --output_groups. Incluye archivos de origen.

Todas las rutas de acceso emitidas por este formato de salida están relacionadas con execroot, que se puede obtener mediante bazel info execution_root. Si el symlink bazel-out de conveniencia existe, las rutas de acceso a los archivos en el repositorio principal también se resuelven en relación con el directorio del lugar de trabajo.

Definición del formato de salida mediante Starlark

--output=starlark

Este formato de salida llama a una función de Starlark para cada destino configurado en el resultado de la consulta y, luego, imprime el valor que muestra la llamada. La marca --starlark:file especifica la ubicación de un archivo de Starlark que define una función llamada format con un solo parámetro, target. Se llama a esta función para cada destino del resultado de la consulta. De forma alternativa, puedes usar la marca --starlark:expr para especificar solo el cuerpo de una función declarada como def format(target): return expr.

Dialecto Starlark "cquery"

El entorno de Starlark de BigQuery difiere de un archivo BUILD o .bzl. Incluye todas las constantes y funciones integradas de Starlark principales, además de algunas específicas de BigQuery que se describen a continuación, pero no (por ejemplo) glob, native o rule, y no admite instrucciones de carga.

build_options(objetivo)

build_options(target) muestra un mapa cuyas claves son identificadores de opciones de compilación (consulta Configuraciones) y cuyos valores son los valores de Starlark. Las opciones de compilación cuyos valores no son valores legales de Starlark se omiten de este mapa.

Si el objetivo es un archivo de entrada, build_options(target) muestra None, ya que los objetivos del archivo de entrada tienen una configuración nula.

proveedores(objetivo)

providers(target) muestra un mapa cuyas claves son nombres de proveedores (por ejemplo, "DefaultInfo") y cuyos valores son los valores de Starlark. Se omiten de este mapa los proveedores cuyos valores no sean valores legales de Starlark.

Ejemplos

Imprime una lista separada por espacios de los nombres base de todos los archivos que produce //foo:

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

Imprime una lista separada por espacios de las rutas de acceso de todos los archivos generados por objetivos rule en //bar y sus subpaquetes:

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

Imprime una lista de los nombres mnemotécnicos de todas las acciones que registró //foo.

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

Imprime una lista de resultados de compilación registrados por un //baz de cc_library.

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

Imprime el valor de la opción de línea de comandos --javacopt cuando compilas //foo.

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

Imprime la etiqueta de cada destino con exactamente una salida. En este ejemplo, se usan funciones de Starlark definidas en un archivo.

  $ 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

Imprime la etiqueta de cada destino que es estrictamente Python 3. En este ejemplo, se usan funciones de Starlark definidas en un archivo.

  $ 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

Extrae un valor de un proveedor definido por el usuario.

  $ 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

BigQuery en comparación con búsqueda

cquery y query se complementan y se destacan en diferentes nichos. Ten en cuenta lo siguiente para decidir cuál es la más adecuada para ti:

  • cquery sigue ramas select() específicas para modelar el grafo exacto que compilas. query no sabe qué rama elige la compilación, por lo que se incluyen todas las ramas para sobreestimarlo.
  • La precisión de cquery requiere que se compile más gráfico que query. Específicamente, cquery evalúa los destinos configurados, mientras que query solo evalúa los objetivos. Esto lleva más tiempo y usa más memoria.
  • La interpretación de cquery del lenguaje de consulta presenta la ambigüedad que query evita. Por ejemplo, si "//foo" existe en dos configuraciones, ¿cuál debe usar cquery "deps(//foo)"? La función config puede ayudar con esto.
  • Como una herramienta más nueva, cquery no es compatible con ciertos casos de uso. Consulta Problemas conocidos para obtener más detalles.

Errores conocidos

Todos los destinos que cquery "compila" deben tener la misma configuración.

Antes de evaluar las consultas, cquery activa una compilación justo antes del punto en el que se ejecutarían las acciones de compilación. Los destinos que “compila” se seleccionan de forma predeterminada entre todas las etiquetas que aparecen en la expresión de consulta (esto se puede anular con --universe_scope). Estas deben tener la misma configuración.

Aunque, por lo general, comparten la configuración "objetivo" de nivel superior, las reglas pueden cambiar su propia configuración con las transiciones perimetrales entrantes. Aquí es donde cquery no es suficiente.

Solución: Si es posible, configura --universe_scope en un alcance más estricto. Por ejemplo:

# 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

No se admite --output=xml.

Resultado no determinista:

cquery no borra automáticamente el gráfico de compilación de comandos anteriores y, por lo tanto, es más propenso a detectar resultados de consultas anteriores. Por ejemplo, genquery ejecuta una transición ejecutiva en su atributo tools, es decir, configura sus herramientas en la configuración de ejecución.

A continuación, puedes ver los efectos que tiene esa transición.

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

Solución alternativa: Cambia cualquier opción de inicio para forzar un nuevo análisis de los destinos configurados. Por ejemplo, agrega --test_arg=&lt;whatever&gt; a tu comando de compilación.

Solución de problemas

Patrones de destino recurrentes (/...)

Si encuentra lo siguiente:

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

Esto sugiere incorrectamente que el paquete //foo no está dentro del alcance, aunque --universe_scope=//foo:app lo incluya. Esto se debe a las limitaciones de diseño en cquery. Como solución alternativa, incluye explícitamente //foo/... en el alcance del universo:

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

Si eso no funciona (por ejemplo, porque algunos destinos de //foo/... no pueden compilar con las marcas de compilación elegidas), divide el patrón manualmente en sus paquetes constituyentes con una consulta de procesamiento previo:

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