cquery
es una variante de query
que controla correctamente los efectos de select()
y las opciones de compilación en el gráfico de compilación.
Para ello, se ejecuta sobre los resultados de la fase de análisis de Bazel, que integra estos efectos. Por el contrario, query
se ejecuta en los resultados de la fase de carga de Bazel, antes de que se evalúen 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 gráfico de destino configurado, no tiene estadísticas sobre artefactos, como acciones de compilación, ni acceso a las reglas [test_suite](/versions/6.1.0/reference/be/general#test_suite)
, ya que no son objetivos configurados. Para lo primero, consulta [aquery](/versions/6.1.0/docs/aquery)
.
Sintaxis básica
Una llamada cquery
simple 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
es compatible con la mayoría de las functions dequery
, además de algunas nuevas.//target
es la expresión que se pasa 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 práctica de consultas para ver ejemplos.
cquery
requiere un objetivo para ejecutar las fases de carga y análisis. A menos que se especifique lo contrario, cquery
analiza los destinos que se enumeran en la expresión de consulta. Consulta --universe_scope
para consultar las dependencias de los destinos de compilación de nivel superior.
Configuraciones
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 objetivos, este es un hash opaco de los valores de la opción de compilación que definen la configuración.
Para ver el contenido completo de la configuración, ejecuta lo siguiente:
$ bazel config 9f87702
La configuración del host usa el ID especial (HOST)
. Los archivos de origen no generados, como los que se suelen encontrar en srcs
, usan el ID especial (null)
(porque no es necesario configurarlos).
9f87702
es un prefijo del ID completo. Esto se debe a que los IDs completos son hashes 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 IDs completos, ejecuta $ bazel config
.
Evaluación del patrón de objetivo
//foo
tiene un significado diferente para cquery
que 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 para cada destino configurado con una etiqueta que coincida con ese patrón. El resultado es determinista, pero cquery
no garantiza el 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 producir varios resultados:
# 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)
Si quieres declarar con precisión sobre qué instancia consultar, usa la función config
.
Consulta la documentación del patrón de destino de query
para obtener más información sobre los patrones de destino.
Funciones
Del conjunto de funciones que admite query
, cquery
admite todas, excepto allrdeps
, buildfiles
, rbuildfiles
, siblings
, tests
y visible
.
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 que especifica el segundo argumento.
Los valores válidos para el segundo argumento son target
, host
, null
o un hash de configuración personalizado. Los valores hash se pueden recuperar de $
bazel config
o de un resultado anterior de cquery
.
Ejemplos:
$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (HOST) //baz (3732cc8) $ bazel cquery "config(//baz, 3732cc8)"
Si no se pueden encontrar todos los resultados del primer argumento en la configuración especificada, solo se muestran los que se pueden encontrar. Si no se pueden encontrar resultados en la configuración especificada, la consulta fallará.
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 cquery
--universe_scope
(lista separada por comas)
A menudo, las dependencias de los destinos configurados pasan por transiciones, lo que hace que su configuración difiera de la de su dependiente. Esta marca te permite consultar un destino como si se compilara como una 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_library( name = "tool", )
Genrules configura sus herramientas en la configuración del host, por lo que las siguientes consultas generarían los siguientes resultados:
Consulta | Se compiló el destino | Salida |
---|---|---|
bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
bazel cquery "//x:tool" --universe_scope="//x:my_gen" | //x:my_gen | //x:tool(hostconfig) |
Si se establece esta marca, se compila su contenido. Si no se establece, se compilan todos los destinos mencionados en la expresión de consulta. El cierre transitivo de los objetivos compilados se usa como el universo de la consulta. De cualquier manera, los destinos que se compilarán deben poder compilarse en el nivel superior (es decir, ser compatibles con las opciones de nivel superior). cquery
muestra 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 ser beneficioso no hacerlo. Por ejemplo, configurar --universe_scope
de forma explícita podría evitar la compilación de destinos varias veces en configuraciones que no te interesan. También podría ayudar a especificar qué versión de configuración de un objetivo estás buscando (ya que, actualmente, no es posible especificar esto por completo de ninguna otra manera). Debes establecer esta marca si la expresión de consulta es más compleja que deps(//foo)
.
--implicit_deps
(booleano, predeterminado=True)
Si estableces esta marca como falsa, se filtran todos los resultados que no se establecen de forma explícita en el archivo BUILD y, en su lugar, Bazel los establece en otro lugar. Esto incluye filtrar las cadenas de herramientas resueltas.
--tool_deps
(booleano, predeterminado=True)
Si estableces esta marca como falsa, se filtran todos los destinos configurados para los que la ruta desde el destino consultado hasta ellos cruza una transición entre la configuración de destino y las configuraciones que no son de destino.
Si el destino consultado está en la configuración de destino, configurar --notool_deps
solo mostrará los destinos que también están en la configuración de destino. Si el objetivo consultado está en una configuración no objetivo, la configuración de --notool_deps
solo mostrará objetivos también en configuraciones no objetivo. Por lo general, este parámetro de configuración no afecta el filtrado de cadenas de herramientas resueltas.
--include_aspects
(booleano, predeterminado=True)
Los aspectos pueden agregar dependencias adicionales a una compilación. De forma predeterminada, cquery
no sigue aspectos porque hacen que el gráfico consultable sea más grande, lo que usa más memoria. Sin embargo, seguirlos produce resultados más precisos.
Si no te preocupa el impacto en la memoria de las consultas grandes, habilita esta marca de forma predeterminada en tu bazelrc.
Si consultas con los aspectos inhabilitados, puedes experimentar un problema en el que el destino X falla mientras se compila el destino Y, pero cquery somepath(Y, X)
y cquery deps(Y) | grep 'X'
no muestran resultados porque la dependencia se produce a través de un aspecto.
Formatos de salida
De forma predeterminada, cquery muestra los resultados en una lista ordenada por dependencia de pares de etiquetas y 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 debajo de los destinos de nivel superior en configuraciones diferentes a las de los destinos de nivel superior.
Por ejemplo, un objetivo podría imponer una transición a la configuración del host en todas las dependencias de su atributo tools
. Estos 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 el tipo que son y el efecto que tienen en las opciones de
compilación.
Este formato de salida se activa con la marca --transitions
, que de forma predeterminada se establece en NONE
. Se puede configurar en modo FULL
o LITE
. El modo FULL
muestra información sobre las transiciones de clase de reglas y 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 la diferencia de opciones.
Salida del mensaje de protocolo
--output=proto
Esta opción hace que los destinos resultantes se impriman en un formato de búfer de protocolo binario. Puedes encontrar la definición del búfer de protocolo en src/main/protobuf/analysis.proto.
CqueryResult
es el mensaje de nivel superior que contiene los resultados de la cquery. 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 cquery muestran información de configuración como parte de cada objetivo configurado. Si deseas omitir esta información y obtener un resultado de proto que tenga el mismo formato que el resultado de proto de la consulta, establece esta marca como "false".
Consulta la documentación de salida de proto de la consulta para obtener más opciones relacionadas con la salida de proto.
Salida del gráfico
--output=graph
Esta opción genera un resultado como un archivo .dot compatible con Graphviz. Consulta la documentación de salida de gráficos de query
para obtener más información. 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 que produce cada destino que coincide con la consulta, similar a la lista impresa al final de una invocación de bazel build
. El resultado solo contiene los archivos anunciados en los grupos de salida solicitados, según lo determine la marca --output_groups
.
Sí incluye archivos fuente.
Cómo definir el formato de salida con Starlark
--output=starlark
Este formato de salida llama a una función Starlark
para cada objetivo configurado en el resultado de la consulta y muestra el valor
que devuelve la llamada. La marca --starlark:file
especifica la ubicación de un archivo Starlark que define una función llamada format
con un solo parámetro, target
. Se llama a esta función para cada objetivo en el resultado de la consulta. Como alternativa, para mayor comodidad, puedes especificar solo el cuerpo de una función declarada como def format(target): return expr
con la marca --starlark:expr
.
Dialecto Starlark de "cquery"
El entorno de Starlark de cquery difiere de un archivo BUILD o .bzl. Incluye todas las constantes y funciones integradas principales de Starlark, además de algunas específicas de cquery que se describen a continuación, pero no (por ejemplo) glob
, native
ni rule
, y no admite instrucciones de carga.
build_options(target)
build_options(target)
muestra un mapa cuyas claves son identificadores de opciones de compilación (consulta Parámetros de configuración) y cuyos valores son sus valores de Starlark. Las opciones de compilación cuyos valores no son legales para Starlark se omiten de este mapa.
Si el destino es un archivo de entrada, build_options(target)
muestra None, ya que los destinos de archivos de entrada tienen una configuración nula.
providers(target)
providers(target)
muestra un mapa cuyas claves son nombres de proveedores (por ejemplo, "DefaultInfo"
) y cuyos valores son sus valores de Starlark. Los proveedores cuyos valores no son valores legales de Starlark se omiten de este mapa.
Ejemplos
Imprime una lista de los nombres de base de todos los archivos que produce //foo
separados por espacios:
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 que producen los destinos de 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 mnemónicos de todas las acciones registradas por //foo
.
bazel cquery //foo --output=starlark \ --starlark:expr="[a.mnemonic for a in target.actions]"
Imprime una lista de los resultados de compilación registrados por un //baz
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 compiles //foo
.
bazel cquery //foo --output=starlark \ --starlark:expr="build_options(target)['//command_line_option:javacopt']"
Imprime la etiqueta de cada objetivo con exactamente un resultado. 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 sea 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
cquery en comparación con query
cquery
y query
se complementan y se destacan en diferentes nichos. Ten en cuenta lo siguiente para decidir cuál es la opción adecuada para ti:
cquery
sigue ramasselect()
específicas para simular el gráfico exacto que compilas.query
no sabe qué rama elige la compilación, por lo que incluye todas las ramas.- La precisión de
cquery
requiere compilar más del gráfico quequery
. Específicamente,cquery
evalúa los destinos configurados, mientras quequery
solo evalúa los destinos. Esto lleva más tiempo y usa más memoria. - La interpretación de
cquery
del lenguaje de consulta introduce ambigüedades quequery
evita. Por ejemplo, si"//foo"
existe en dos configuraciones, ¿cuál debería usarcquery "deps(//foo)"
? La función[config](#config)
puede ayudarte con esto. - Como es una herramienta más reciente,
cquery
no es compatible con ciertos casos de uso. Consulta Problemas conocidos para obtener más información.
Problemas conocidos
Todos los destinos que cquery
"compila" deben tener la misma configuración.
Antes de evaluar las consultas, cquery
activa una compilación hasta poco antes del punto en el que se ejecutarían las acciones de compilación. Los destinos que “compila” se seleccionan de forma predeterminada de todas las etiquetas que aparecen en la expresión de consulta (esto se puede anular con --universe_scope
). Estos deben tener la misma configuración.
Si bien, por lo general, comparten la configuración "objetivo" de nivel superior, las reglas pueden cambiar su propia configuración con transiciones de borde entrantes.
Aquí es donde cquery
no es suficiente.
Solución alternativa: Si es posible, establece --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 determinístico.
cquery
no borra automáticamente el gráfico de compilación de los comandos anteriores y, por lo tanto, es propenso a detectar resultados de consultas anteriores. Por ejemplo, genquery
ejerce una transición de host en su atributo tools
, es decir, configura sus herramientas en la configuración del host.
A continuación, puedes ver los efectos persistentes de 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 (host_config) ... $ bazel cquery "//foo:tool" tool(host_config)
Solución: Cambia cualquier opción de inicio para forzar el nuevo análisis de los objetivos configurados.
Por ejemplo, agrega --test_arg=<whatever>
a tu comando de compilación.
Solución de problemas
Patrones de segmentación recursivos (/...
)
Si encuentras lo siguiente, haz 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 de forma incorrecta que el paquete //foo
no está dentro del alcance, aunque --universe_scope=//foo:app
lo incluye. Esto se debe a limitaciones de diseño en cquery
. Como solución alternativa, incluye //foo/...
de forma explícita en el alcance del universo:
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
Si eso no funciona (por ejemplo, porque algún destino en //foo/...
no se puede compilar con las marcas de compilación elegidas), desenvuelve el patrón de forma manual 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 "+" -))"