Referencia de la consulta de Bazel

Informar un problema Ver fuente

Esta página es el manual de referencia del lenguaje de consulta de Bazel que se usa cuando utilizas bazel query para analizar dependencias de compilación. También se describen los formatos de salida compatibles con bazel query.

Para casos de uso prácticos, consulta el instructivo de consultas de Bazel.

Referencia de consulta adicional

Además de query, que se ejecuta en el gráfico de destino de la fase posterior a la carga, Bazel incluye la consulta de gráfico de acciones y la consulta configurable.

Consulta del gráfico de acción

La consulta de gráfico de acción (aquery) opera en el gráfico de destino configurado posterior al análisis y expone información sobre acciones, artefactos y sus relaciones. aquery es útil cuando te interesan las propiedades de las acciones o los artefactos generados a partir del gráfico de destino configurado. Por ejemplo, los comandos reales se ejecutan y sus entradas, salidas y mnemotécnicas.

Para obtener más detalles, consulta la referencia de aquery.

Consulta configurable

La consulta de Bazel tradicional se ejecuta en el grafo de destino de la fase posterior a la carga y, por lo tanto, no tiene un concepto de configuración ni sus conceptos relacionados. En particular, no resuelve correctamente las instrucciones selectas y, en su lugar, muestra todas las resoluciones posibles de las selecciones. Sin embargo, el entorno de consulta configurable, cquery, controla correctamente las configuraciones, pero no proporciona todas las funcionalidades de esta consulta original.

Para obtener más detalles, consulta la referencia de cquery.

Ejemplos

¿Cómo utilizan las personas bazel query? Estos son ejemplos típicos:

¿Por qué el árbol //foo depende de //bar/baz? Muestra una ruta:

somepath(foo/..., //bar/baz:all)

¿De qué bibliotecas de C++ dependen todas las pruebas de foo de las que no depende el destino foo_bin?

kind("cc_library", deps(kind(".*test rule", foo/...)) except deps(//foo:foo_bin))

Tokens: La sintaxis léxica

Las expresiones en el lenguaje de consulta se componen de los siguientes tokens:

  • Palabras clave, como let. Las palabras clave son palabras reservadas del idioma y cada una de ellas se describe a continuación. El conjunto completo de palabras clave es:

  • Palabras, como "foo/...", ".*test rule" o "//bar/baz:all". Si una secuencia de caracteres está "entrecomillada" (comienza y termina con una comilla simple "o comienza y termina con una comilla doble "), es una palabra. Si una secuencia de caracteres no tiene comillas, aún se puede analizar como una palabra. Las palabras sin comillas son secuencias de caracteres extraídas de los caracteres del alfabeto A-Za-z, los números del 0 al 9 y los caracteres especiales */@.-_:$~[] (asterisco, barra diagonal, en, punto, guion, guion bajo, dos puntos, signo de dólar, virgulilla, llave de apertura cuadrada izquierda, llave de cuadrado derecha). Sin embargo, las palabras sin comillas no pueden comenzar con un guion - o un asterisco *, aunque los nombres de destino relativos pueden comenzar con esos caracteres.

    Es posible que las palabras sin comillas tampoco incluyan los caracteres del signo más + o el signo igual =, aunque esos caracteres estén permitidos en los nombres de destino. Cuando se escribe un código que genera expresiones de consulta, los nombres de los objetivos deben estar entre comillas.

    La cotización es necesaria cuando se escriben secuencias de comandos que construyen expresiones de consulta de Bazel a partir de valores proporcionados por el usuario.

     //foo:bar+wiz    # WRONG: scanned as //foo:bar + wiz.
     //foo:bar=wiz    # WRONG: scanned as //foo:bar = wiz.
     "//foo:bar+wiz"  # OK.
     "//foo:bar=wiz"  # OK.
    

    Ten en cuenta que estas comillas se agregan a las que requiera la shell, por ejemplo:

    bazel query ' "//foo:bar=wiz" '   # single-quotes for shell, double-quotes for Bazel.
    

    Las palabras clave y los operadores, cuando se citan, se tratan como palabras comunes. Por ejemplo, some es una palabra clave, pero "algunos" es una palabra. Tanto foo como "foo" son palabras.

    Sin embargo, ten cuidado cuando uses comillas simples o dobles en los nombres de destino. Cuando entrecomillas uno o más nombres de destino, usa solo un tipo de comillas (todas o solo comillas simples o dobles).

    Estos son ejemplos de cómo será la cadena de consulta de Java:

      'a"'a'         # WRONG: Error message: unclosed quotation.
      "a'"a"         # WRONG: Error message: unclosed quotation.
      '"a" + 'a''    # WRONG: Error message: unexpected token 'a' after query expression '"a" + '
      "'a' + "a""    # WRONG: Error message: unexpected token 'a' after query expression ''a' + '
      "a'a"          # OK.
      'a"a'          # OK.
      '"a" + "a"'    # OK
      "'a' + 'a'"    # OK
    

    Elegimos esta sintaxis para que las comillas no sean necesarias en la mayoría de los casos. El ejemplo (inusual) ".*test rule" necesita comillas: comienza con un punto y contiene un espacio. Citar el elemento "cc_library" es innecesario, pero inofensivo.

  • Puntuación, como paréntesis (), punto . y coma ,. Las palabras que contengan signos de puntuación (excepto las excepciones mencionadas anteriormente) deben estar entre comillas.

Se ignoran los caracteres de espacio en blanco que no se encuentran en las palabras entrecomilladas.

Conceptos del lenguaje de consulta de Bazel

El lenguaje de consulta de Bazel es un lenguaje de expresiones. Cada expresión se evalúa como un conjunto parcialmente ordenado de objetivos o, de manera equivalente, un gráfico (DAG) de destinos. Este es el único tipo de dato.

El conjunto y el gráfico hacen referencia al mismo tipo de datos, pero enfatizan diferentes aspectos, por ejemplo:

  • Conjunto: El orden parcial de los destinos no es interesante.
  • Gráfico: El orden parcial de los objetivos es significativo.

Ciclos en el gráfico de dependencias

Los grafos de dependencia de compilación deben ser acíclicos.

Los algoritmos usados por el lenguaje de consulta están diseñados para su uso en grafos acíclicos, pero son sólidos frente a los ciclos. Los detalles de cómo se tratan los ciclos no se especifican y no se deben confiar.

Dependencias implícitas

Además de compilar dependencias que se definen de forma explícita en archivos BUILD, Bazel agrega dependencias implícitas adicionales a las reglas. Por ejemplo, cada regla de Java depende implícitamente de JavaBuilder. Las dependencias implícitas se establecen mediante atributos que comienzan con $ y no se pueden anular en archivos BUILD.

De forma predeterminada, bazel query tiene en cuenta las dependencias implícitas cuando se calcula el resultado de la consulta. Este comportamiento se puede cambiar con la opción --[no]implicit_deps. Ten en cuenta que, como la consulta no considera las configuraciones, nunca se consideran las cadenas de herramientas potenciales.

Exhaustividad

Las expresiones de lenguaje de consulta de Bazel operan sobre el gráfico de dependencia de compilación, que es el grafo definido de manera implícita por todas las declaraciones de reglas en todos los archivos BUILD. Es importante comprender que este gráfico es algo abstracto y no constituye una descripción completa de cómo realizar todos los pasos de una compilación. Para realizar una compilación, también se requiere una configuración. Consulta la sección de configuraciones de la Guía del usuario para obtener más detalles.

El resultado de evaluar una expresión en el lenguaje de consulta de Bazel es verdadero para todas las configuraciones, lo que significa que puede ser una sobreaproximación conservadora y no exactamente precisa. Si usas la Herramienta de consultas para calcular el conjunto de archivos de origen necesarios durante una compilación, es posible que informe más de los necesarios porque, por ejemplo, la Herramienta de consultas incluirá todos los archivos necesarios para admitir la traducción de mensajes, aunque no tengas la intención de usar esa función en tu compilación.

La preservación del orden del gráfico

Las operaciones conservan cualquier restricción de orden heredada de sus subexpresiones. Puedes entender esto como "la ley de conservación del orden parcial". Considera un ejemplo: si emites una consulta para determinar el cierre transitivo de dependencias de un destino en particular, el conjunto resultante se ordena de acuerdo con el gráfico de dependencias. Si filtras ese conjunto para incluir solo los destinos de la categoría file, se mantiene la misma relación de orden parcial transitivo entre cada par de destinos en el subconjunto resultante, aunque ninguno de estos pares esté conectado directamente en el gráfico original. (No hay bordes del archivo de archivo en el gráfico de dependencia de compilación).

Sin embargo, si bien todos los operadores conservan el orden, algunas operaciones, como las operaciones de conjuntos, no presentan ninguna restricción de orden propia. Considera esta expresión:

deps(x) union y

Se garantiza que el orden del conjunto de resultados final conserve todas las restricciones de orden de sus subexpresiones, es decir, que todas las dependencias transitivas de x estén ordenadas correctamente entre sí. Sin embargo, la consulta no garantiza nada sobre el orden de los destinos en y ni sobre el orden de los destinos en deps(x) en relación con los de y (excepto en los destinos en y que también están en deps(x)).

Los operadores que introducen restricciones de orden incluyen los siguientes: allpaths, deps, rdeps, somepath y los comodines de patrón de destino package:*, dir/..., etcétera.

Consulta de Sky

Sky Query es un modo de consulta que opera en un alcance universal especificado.

Funciones especiales disponibles solo en SkyQuery

El modo de consulta de Sky tiene las funciones de consulta adicionales allrdeps y rbuildfiles. Estas funciones operan en todo el alcance universal (por eso no tienen sentido para una consulta normal).

Especificación de un alcance del universo

El modo de consulta de Sky se activa cuando se pasan las siguientes dos marcas: (--universe_scope o --infer_universe_scope) y --order_output=no. --universe_scope=<target_pattern1>,...,<target_patternN> le indica a la consulta que precargue el cierre transitivo del patrón de destino especificado por los patrones objetivo, que pueden ser aditivos y sustractivos. Luego, todas las consultas se evalúan en este “permiso”. En particular, los operadores allrdeps y rbuildfiles solo muestran resultados de este alcance. --infer_universe_scope le indica a Bazel que infiera un valor para --universe_scope a partir de la expresión de consulta. Este valor inferido es la lista de patrones de objetivo únicos en la expresión de consulta, pero podría no ser lo que deseas. Por ejemplo:

bazel query --infer_universe_scope --order_output=no "allrdeps(//my:target)"

La lista de patrones de destino únicos en esta expresión de consulta es ["//my:target"], por lo que Bazel lo trata de la misma manera que la invocación:

bazel query --universe_scope=//my:target --order_output=no "allrdeps(//my:target)"

Sin embargo, el resultado de esa consulta con --universe_scope es solo //my:target; ninguna de las dependencias inversas de //my:target está en el universo, por construcción. Por otro lado, considera lo siguiente:

bazel query --infer_universe_scope --order_output=no "tests(//a/... + b/...) intersect allrdeps(siblings(rbuildfiles(my/starlark/file.bzl)))"

Esta es una invocación de consulta significativa que intenta calcular los destinos de prueba en la expansión tests de los destinos en algunos directorios que dependen de forma transitiva de destinos cuya definición usa un archivo .bzl determinado. Aquí, --infer_universe_scope es conveniente, en especial cuando la elección de --universe_scope requeriría que analices la expresión de consulta tú mismo.

Por lo tanto, para las expresiones de consulta que usan operadores con alcance universal, como allrdeps y rbuildfiles, asegúrate de usar --infer_universe_scope solo si su comportamiento es lo que deseas.

Sky Query tiene algunas ventajas y desventajas en comparación con la consulta predeterminada. La principal desventaja es que no puede ordenar los resultados según el orden del gráfico y, por lo tanto, no se permiten ciertos formatos de salida. Sus ventajas son que proporciona dos operadores (allrdeps y rbuildfiles) que no están disponibles en la consulta predeterminada. Además, Sky Query hace su trabajo mediante la introspección del gráfico Skyframe en lugar de crear uno nuevo, que es lo que hace la implementación predeterminada. Por lo tanto, hay algunas circunstancias en las que es más rápido y usa menos memoria.

Expresiones: Sintaxis y semántica de la gramática

Esta es la gramática del lenguaje de consulta de Bazel, expresada en notación EBNF:

expr ::= word
       | let name = expr in expr
       | (expr)
       | expr intersect expr
       | expr ^ expr
       | expr union expr
       | expr + expr
       | expr except expr
       | expr - expr
       | set(word *)
       | word '(' int | word | expr ... ')'

Las siguientes secciones describen cada una de las producciones de esta gramática en orden.

Patrones de objetivos

expr ::= word

En términos sintácticos, un patrón objetivo es solo una palabra. Se interpreta como un conjunto (desordenado) de objetivos. El patrón de destino más simple es una etiqueta, que identifica un solo objetivo (archivo o regla). Por ejemplo, el patrón de destino //foo:bar se evalúa como un conjunto que contiene un elemento, el objetivo, la regla bar.

Los patrones de destino generalizan las etiquetas para incluir comodines sobre paquetes y objetivos. Por ejemplo, foo/...:all (o solo foo/...) es un patrón de destino que se evalúa como un conjunto que contiene todas las reglas en cada paquete de forma recurrente debajo del directorio foo. bar/baz:all es un patrón de destino que se evalúa como un conjunto que contiene todas las reglas del paquete bar/baz, pero no sus subpaquetes.

De manera similar, foo/...:* es un patrón de objetivos que se evalúa como un conjunto que contiene todos los destinos (reglas y archivos) en cada paquete de forma recurrente debajo del directorio foo; bar/baz:* evalúa como un conjunto que contiene todos los destinos en el paquete bar/baz, pero no sus subpaquetes.

Debido a que el comodín :* coincide con los archivos y las reglas, suele ser más útil que :all para las consultas. Por el contrario, el comodín :all (implícito en patrones de destino como foo/...) suele ser más útil para las compilaciones.

Los patrones de destino bazel query funcionan igual que los objetivos de compilación bazel build. Para obtener más información, consulta Patrones de destino o escribe bazel help target-syntax.

Los patrones de destino pueden evaluar como un conjunto singleton (en el caso de una etiqueta), un conjunto que contiene muchos elementos (como el caso de foo/..., que tiene miles de elementos) o el conjunto vacío, si el patrón de destino no coincide con ningún objetivo.

Todos los nodos que resultan de una expresión de patrón de destino se ordenan de manera correcta entre sí según la relación de dependencia. Por lo tanto, el resultado de foo:* no es solo el conjunto de objetivos en el paquete foo, también es el gráfico sobre esos objetivos. (No se garantiza el orden relativo de los nodos de resultado en comparación con otros nodos). Para obtener más detalles, consulta la sección Orden de los gráficos.

Variables

expr ::= let name = expr1 in expr2
       | $name

El lenguaje de consulta de Bazel permite definir variables y hacer referencias a ellas. El resultado de la evaluación de una expresión let es el mismo que el de expr2, y todos los casos gratuitos de la variable name se reemplazan por el valor de expr1.

Por ejemplo, let v = foo/... in allpaths($v, //common) intersect $v es equivalente a allpaths(foo/...,//common) intersect foo/....

Un caso de una referencia de variable name que no sea una expresión let name = ... que la contiene es un error. En otras palabras, las expresiones de consulta de nivel superior no pueden tener variables libres.

En las producciones gramaticales anteriores, name es como word, pero con la restricción adicional de que es un identificador legal en el lenguaje de programación C. Las referencias a la variable deben estar precedidas por el carácter "$".

Cada expresión let define solo una variable, pero puedes anidarlas.

Los patrones de objetivo y las referencias variables constan de un solo token, una palabra, lo que crea una ambigüedad sintáctica. Sin embargo, no hay ambigüedad semántica, ya que el subconjunto de palabras que son nombres legales de variables no está separado del subconjunto de palabras que son patrones objetivo legales.

En términos técnicos, las expresiones let no aumentan la expresividad del lenguaje de consulta. Cualquier consulta que se pueda expresar en el lenguaje también se puede expresar sin ellas. Sin embargo, mejoran la brevedad de muchas consultas y pueden generar una evaluación más eficiente de las consultas.

Expresiones entre paréntesis

expr ::= (expr)

Los paréntesis asocian subexpresiones para forzar un orden de evaluación. Una expresión entre paréntesis se evalúa según el valor de su argumento.

Operaciones de conjuntos algebraicos: intersección, unión, diferencia de conjunto

expr ::= expr intersect expr
       | expr ^ expr
       | expr union expr
       | expr + expr
       | expr except expr
       | expr - expr

Estos tres operadores calculan las operaciones de conjuntos habituales sobre sus argumentos. Cada operador tiene dos formas: una nominal, como intersect, y una simbólica, como ^. Ambas formas son equivalentes; las formas simbólicas son más rápidas de escribir. (Para mayor claridad, el resto de esta página utiliza las formas nominales).

Por ejemplo:

foo/... except foo/bar/...

Evalúa el conjunto de objetivos que coincide con foo/..., pero no con foo/bar/....

Puedes escribir la misma consulta de la siguiente manera:

foo/... - foo/bar/...

Las operaciones intersect (^) y union (+) son conmutativas (simétricas), mientras que except (-) es asimétrica. El analizador trata los tres operadores como asociativos a la izquierda y de igual prioridad, por lo que quizás quieras paréntesis. Por ejemplo, las dos primeras expresiones son equivalentes, pero la tercera no lo es:

x intersect y union z
(x intersect y) union z
x intersect (y union z)

Leer destinos desde una fuente externa: establecer

expr ::= set(word *)

El operador set(a b c ...) calcula la unión de un conjunto de cero o más patrones de destino separados por espacios en blanco (sin comas).

Junto con la función $(...) de la shell Bourne, set() proporciona un medio para guardar los resultados de una consulta en un archivo de texto normal, manipular ese archivo de texto con otros programas (como herramientas shell de UNIX estándar) y, luego, volver a ingresar el resultado en la herramienta de consultas como un valor para su procesamiento posterior. Por ejemplo:

bazel query deps(//my:target) --output=label | grep ... | sed ... | awk ... > foo
bazel query "kind(cc_binary, set($(<foo)))"

En el siguiente ejemplo,kind(cc_library, deps(//some_dir/foo:main, 5)) se calcula filtrando los valores maxrank con un programa awk.

bazel query 'deps(//some_dir/foo:main)' --output maxrank | awk '($1 < 5) { print $2;} ' > foo
bazel query "kind(cc_library, set($(<foo)))"

En estos ejemplos, $(<foo) es una abreviatura de $(cat foo), pero también se pueden usar comandos de shell que no sean cat, como el comando awk anterior.

Funciones

expr ::= word '(' int | word | expr ... ')'

El lenguaje de consulta define varias funciones. El nombre de la función determina la cantidad y el tipo de argumentos que requiere. Están disponibles las siguientes funciones:

Cierre transitivo de dependencias: dependencias

expr ::= deps(expr)
       | deps(expr, depth)

El operador deps(x) evalúa el gráfico formado por el cierre transitivo de dependencias de su conjunto de argumentos x. Por ejemplo, el valor de deps(//foo) es el gráfico de dependencias que tiene la raíz en el foo de un solo nodo, incluidas todas sus dependencias. El valor de deps(foo/...) son los gráficos de dependencia cuyas raíces representan todas las reglas de cada paquete que se encuentra debajo del directorio foo. En este contexto, "dependencias" hace referencia solo a los objetivos de reglas y archivos, por lo que no se incluyen aquí los archivos BUILD y Starlark necesarios para crear estos destinos. Para eso, debes usar el operador buildfiles.

El gráfico resultante se ordena según la relación de dependencia. Para obtener más información, consulta la sección sobre el orden de los gráficos.

El operador deps acepta un segundo argumento opcional, que es un literal de número entero que especifica un límite superior para la profundidad de la búsqueda. Por lo tanto, deps(foo:*, 0) muestra todos los destinos en el paquete foo, mientras que deps(foo:*, 1) incluye, además, los requisitos previos directos de cualquier objetivo en el paquete foo, y deps(foo:*, 2) incluye, además, los nodos a los que se puede acceder directamente desde los nodos en deps(foo:*, 1), y así sucesivamente. (Estos números corresponden a las clasificaciones que se muestran en el formato de salida minrank). Si se omite el parámetro depth, la búsqueda no tiene límites: calcula el cierre transitivo reflexivo de los requisitos previos.

Cierre transitivo de dependencias inversas: rdeps

expr ::= rdeps(expr, expr)
       | rdeps(expr, expr, depth)

El operador rdeps(u, x) evalúa las dependencias inversas del conjunto de argumentos x dentro del cierre transitivo del conjunto de universos u.

El gráfico resultante se ordena según la relación de dependencia. Consulta la sección sobre el orden de los gráficos para obtener más detalles.

El operador rdeps acepta un tercer argumento opcional, que es un literal de número entero que especifica un límite superior para la profundidad de la búsqueda. El gráfico resultante solo incluye nodos a una distancia de la profundidad especificada desde cualquier nodo en el conjunto de argumentos. Por lo tanto, rdeps(//foo, //common, 1) se evalúa en todos los nodos del cierre transitivo de //foo que dependen directamente de //common. (Estos números corresponden a las clasificaciones que se muestran en el formato de salida minrank). Si se omite el parámetro depth, la búsqueda no está delimitada.

Cierre transitivo de todas las dependencias inversas: allrdeps

expr ::= allrdeps(expr)
       | allrdeps(expr, depth)

El operador allrdeps se comporta igual que el operador rdeps, con la excepción de que el "conjunto universal" es el valor con el que se haya evaluado la marca --universe_scope, en lugar de especificarse por separado. Por lo tanto, si se pasó --universe_scope=//foo/..., allrdeps(//bar) es equivalente a rdeps(//foo/..., //bar).

Dependencias inversas directas en el mismo paquete: same_pkg_direct_rdeps

expr ::= same_pkg_direct_rdeps(expr)

El operador same_pkg_direct_rdeps(x) evalúa el conjunto completo de destinos que están en el mismo paquete que un objetivo en el conjunto de argumentos y que dependen directamente de él.

Cómo tratar con un paquete de destino: hermanos

expr ::= siblings(expr)

El operador siblings(x) evalúa el conjunto completo de destinos que se encuentran en el mismo paquete que un objetivo en el conjunto de argumentos.

Elección arbitraria: algunas

expr ::= some(expr)
       | some(expr, count )

El operador some(x, k) selecciona, como máximo, k destinos de manera arbitraria de su conjunto de argumentos x y se evalúa como un conjunto que contiene solo esos objetivos. El parámetro k es opcional. Si falta, el resultado será un conjunto singleton que contenga solo un objetivo seleccionado de forma arbitraria. Si el tamaño del conjunto de argumentos x es menor que k, se mostrará todo el conjunto de argumentos x.

Por ejemplo, la expresión some(//foo:main union //bar:baz) se evalúa como un conjunto singleton que contiene //foo:main o //bar:baz, aunque no está definido. La expresión some(//foo:main union //bar:baz, 2) o some(//foo:main union //bar:baz, 3) muestra //foo:main y //bar:baz.

Si el argumento es un singleton, some calcula la función de identidad: some(//foo:main) es equivalente a //foo:main.

Es un error si el conjunto de argumentos especificado está vacío, como en la expresión some(//foo:main intersect //bar:baz).

Operadores de ruta de acceso: somepath, allpaths

expr ::= somepath(expr, expr)
       | allpaths(expr, expr)

Los operadores somepath(S, E) y allpaths(S, E) calculan rutas entre dos conjuntos de objetivos. Ambas consultas aceptan dos argumentos: un conjunto S de puntos de partida y un conjunto E de puntos de finalización. somepath muestra el gráfico de nodos en alguna ruta arbitraria de un destino en S a un destino en E. allpaths muestra el gráfico de nodos en todas las rutas de acceso desde cualquier destino en S hacia cualquier destino en E.

Los gráficos resultantes se ordenan según la relación de dependencia. Consulta la sección sobre el orden de los gráficos para obtener más detalles.

Algún camino
somepath(S1 + S2, E), un resultado posible.
Algún camino
somepath(S1 + S2, E), otro resultado posible.
Todas las rutas
allpaths(S1 + S2, E)

Filtrado de tipos objetivo: tipo

expr ::= kind(word, expr)

El operador kind(pattern, input) aplica un filtro a un conjunto de destinos y descarta los destinos que no son del tipo esperado. El parámetro pattern especifica con qué tipo de objetivo debe coincidir.

Por ejemplo, en la tabla, se ilustran los tipos de los cuatro destinos definidos por el archivo BUILD (para el paquete p) que se muestran a continuación:

Código Diana Tipo
        genrule(
            name = "a",
            srcs = ["a.in"],
            outs = ["a.out"],
            cmd = "...",
        )
      
//p:a regla genrule
//p:a.in archivo fuente
//p:a.out archivo generado
//p:BUILD archivo fuente

Por lo tanto, kind("cc_.* rule", foo/...) evalúa el conjunto de todos los objetivos de regla cc_library, cc_binary, etc., debajo de foo, y kind("source file", deps(//foo)) evalúa el conjunto de todos los archivos de origen en el cierre transitivo de dependencias del destino //foo.

A menudo, se requiere la cita del argumento pattern porque, sin él, el analizador no considera palabras a muchas expresiones regulares, como source file y .*_test.

Cuando realizas coincidencias para package group, es posible que los objetivos que terminan en :all no muestren ningún resultado. Utiliza :all-targets en lugar de esta función.

Filtrado de nombres de destino: filtro

expr ::= filter(word, expr)

El operador filter(pattern, input) aplica un filtro a un conjunto de destinos y descarta los destinos cuyas etiquetas (en forma absoluta) no coinciden con el patrón; se evalúa en un subconjunto de su entrada.

El primer argumento, pattern, es una palabra que contiene una expresión regular sobre los nombres de destino. Una expresión filter se evalúa según el conjunto que contiene todos los destinos x de modo que x sea miembro del conjunto input y la etiqueta (en forma absoluta, como //foo:bar) de x contenga una coincidencia (sin anclar) con la expresión regular pattern. Dado que todos los nombres de destino comienzan con //, se puede usar como alternativa al ancla de expresión regular ^.

A menudo, este operador proporciona una alternativa mucho más rápida y sólida al operador intersect. Por ejemplo, para ver todas las dependencias bar del destino //foo:foo, se podría evaluar

deps(//foo) intersect //bar/...

Sin embargo, esta sentencia requerirá el análisis de todos los archivos BUILD del árbol bar, lo que será lento y propenso a errores en archivos BUILD irrelevantes. Una alternativa sería:

filter(//bar, deps(//foo))

que primero calcularía el conjunto de dependencias de //foo y, luego, filtraría solo los objetivos que coincidan con el patrón proporcionado. En otras palabras, los objetivos con nombres que contengan //bar como substring.

Otro uso común del operador filter(pattern, expr) es filtrar archivos específicos por su nombre o extensión. Por ejemplo:

filter("\.cc$", deps(//foo))

Proporciona una lista de todos los archivos .cc que se usaron para compilar //foo.

Filtrado de atributos de la regla: attr

expr ::= attr(word, word, expr)

El operador attr(name, pattern, input) aplica un filtro a un conjunto de objetivos y descarta los objetivos que no son reglas, los objetivos de reglas que no tienen definido el atributo name o los objetivos de reglas en los que el valor del atributo no coincide con la expresión regular pattern proporcionada. Se evalúa como un subconjunto de su entrada.

El primer argumento, name, es el nombre del atributo de la regla que debe coincidir con el patrón de expresión regular proporcionado. El segundo argumento, pattern, es una expresión regular sobre los valores de atributos. Una expresión attr se evalúa como el conjunto que contiene todos los destinos x, de manera que x es un miembro del conjunto input, es una regla con el atributo definido name y el valor del atributo contiene una coincidencia (sin anclar) para la expresión regular pattern. Si name es un atributo opcional y la regla no lo especifica de forma explícita, se usará el valor del atributo predeterminado para la comparación. Por ejemplo:

attr(linkshared, 0, deps(//foo))

Se seleccionarán todas las dependencias //foo que pueden tener un atributo linkshared (como una regla cc_binary) y se lo establecerá de forma explícita en 0 o no lo establecerá en absoluto, pero el valor predeterminado es 0 (como para las reglas cc_binary).

Los atributos de tipo de lista (como srcs, data, etc.) se convierten en cadenas con el formato [value<sub>1</sub>, ..., value<sub>n</sub>], que comienzan con un corchete [, terminan con un corchete ] y usan "," (coma, espacio) para delimitar varios valores. Las etiquetas se convierten en strings mediante la forma absoluta de la etiqueta. Por ejemplo, un atributo deps=[":foo", "//otherpkg:bar", "wiz"] se convertiría en la cadena [//thispkg:foo, //otherpkg:bar, //thispkg:wiz]. Los corchetes siempre están presentes, por lo que la lista vacía usaría el valor de string [] para fines de coincidencia. Por ejemplo:

attr("srcs", "\[\]", deps(//foo))

seleccionará todas las reglas entre las dependencias //foo que tengan un atributo srcs vacío, mientras que

attr("data", ".{3,}", deps(//foo))

Se seleccionarán todas las reglas entre las dependencias //foo que especifiquen al menos un valor en el atributo data (cada etiqueta tiene al menos 3 caracteres debido a // y :).

Para seleccionar todas las reglas entre dependencias //foo con un value particular en un atributo de tipo de lista, usa

attr("tags", "[\[ ]value[,\]]", deps(//foo))

Esto funciona porque el carácter antes de value será [ o un espacio, y el carácter después de value será una coma o ].

Filtrado de visibilidad de la regla: visible

expr ::= visible(expr, expr)

El operador visible(predicate, input) aplica un filtro a un conjunto de objetivos y descarta los objetivos sin la visibilidad requerida.

El primer argumento, predicate, es un conjunto de destinos para el cual todos los destinos del resultado deben estar visibles. Se evalúa una expresión visible en el conjunto que contiene todos los destinos de x, de modo que x sea miembro del conjunto input y, para todos los destinos, y en predicate x sea visible para y. Por ejemplo:

visible(//foo, //bar:*)

Se seleccionarán todos los objetivos del paquete //bar de los que puede depender //foo sin infringir las restricciones de visibilidad.

Evaluación de atributos de regla de la etiqueta de tipo: etiquetas

expr ::= labels(word, expr)

El operador labels(attr_name, inputs) muestra el conjunto de objetivos especificados en el atributo attr_name del tipo “etiqueta” o “lista de etiquetas” en alguna regla del conjunto inputs.

Por ejemplo, labels(srcs, //foo) muestra el conjunto de destinos que aparece en el atributo srcs de la regla //foo. Si hay varias reglas con atributos srcs en el conjunto inputs, se muestra la unión de su srcs.

Expande y filtra test_suites: pruebas

expr ::= tests(expr)

El operador tests(x) muestra el conjunto de todas las reglas de prueba en el conjunto x, expande cualquier regla test_suite al conjunto de pruebas individuales al que hace referencia y aplica el filtrado por tag y size.

De forma predeterminada, la evaluación de la consulta ignora los destinos que no sean de prueba en todas las reglas test_suite. Esto se puede cambiar a errores con la opción --strict_test_suite.

Por ejemplo, la consulta kind(test, foo:*) enumera todas las reglas *_test y test_suite en el paquete foo. Todos los resultados son miembros (por definición) del paquete foo. Por el contrario, la consulta tests(foo:*) mostrará todas las pruebas individuales que ejecutaría bazel test foo:*: esto puede incluir pruebas que pertenecen a otros paquetes, a los que se hace referencia directa o indirectamente a través de las reglas test_suite.

Archivos de definición del paquete: buildfiles

expr ::= buildfiles(expr)

El operador buildfiles(x) muestra el conjunto de archivos que definen los paquetes de cada destino en el conjunto x; en otras palabras, para cada paquete, su archivo BUILD, más cualquier archivo .bzl al que haga referencia a través de load. Ten en cuenta que esto también muestra los archivos BUILD de los paquetes que contienen estos archivos load.

Por lo general, este operador se usa para determinar qué archivos o paquetes se requieren para compilar un destino específico, a menudo en combinación con la opción --output package que aparece a continuación. Por ejemplo:

bazel query 'buildfiles(deps(//foo))' --output package

muestra el conjunto de todos los paquetes de los que //foo depende transitivamente.

Archivos de definición de paquete: rbuildfiles

expr ::= rbuildfiles(word, ...)

El operador rbuildfiles toma una lista separada por comas de fragmentos de ruta de acceso y muestra el conjunto de archivos BUILD que dependen de forma transitiva de estos fragmentos de ruta. Por ejemplo, si //foo es un paquete, rbuildfiles(foo/BUILD) mostrará el destino //foo:BUILD. Si el archivo foo/BUILD contiene load('//bar:file.bzl'..., rbuildfiles(bar/file.bzl) mostrará el destino //foo:BUILD, así como los destinos de cualquier otro archivo BUILD que se cargue //bar:file.bzl.

El alcance del operador rbuildfiles es el universo que especifica la marca --universe_scope. Los archivos que no corresponden directamente a los archivos BUILD y .bzl no afectan los resultados. Por ejemplo, los archivos de origen (como foo.cc) se ignoran, incluso si se mencionan explícitamente en el archivo BUILD. Sin embargo, se respetan los symlinks, de modo que si foo/BUILD es un symlink a bar/BUILD, entonces rbuildfiles(bar/BUILD) incluirá //foo:BUILD en sus resultados.

El operador rbuildfiles es casi moralmente inverso al operador buildfiles. Sin embargo, esta inversión moral se mantiene más firme en una dirección: los resultados de rbuildfiles son como las entradas de buildfiles; el primero solo contendrá destinos de archivos BUILD en paquetes y el último puede contener esos destinos. En la otra dirección, la correspondencia es más débil. Los resultados del operador buildfiles son objetivos correspondientes a todos los paquetes y .bzl necesarios para una entrada dada. Sin embargo, las entradas del operador rbuildfiles no son esos objetivos, sino los fragmentos de la ruta de acceso que corresponden a esos objetivos.

Archivos de definición del paquete: loadfiles

expr ::= loadfiles(expr)

El operador loadfiles(x) muestra el conjunto de archivos de Starlark que se necesitan para cargar los paquetes de cada destino en el conjunto x. En otras palabras, muestra los archivos .bzl a los que se hace referencia desde sus archivos BUILD para cada paquete.

Formatos de salida

bazel query genera un gráfico. Especifica el contenido, el formato y el orden mediante los cuales bazel query presenta este gráfico mediante la opción de línea de comandos --output.

Cuando se ejecuta con Sky Query, solo se permiten los formatos de salida compatibles con resultados desordenados. Específicamente, no están permitidos los formatos de salida graph, minrank y maxrank.

Algunos de los formatos de salida aceptan opciones adicionales. El nombre de cada opción de salida tiene el prefijo del formato de salida al que se aplica, por lo que --graph:factored se aplica solo cuando se usa --output=graph. No tiene ningún efecto si se usa un formato de salida distinto de graph. Del mismo modo, --xml:line_numbers solo se aplica cuando se usa --output=xml.

Sobre el orden de los resultados

Aunque las expresiones de consulta siempre siguen la “ley de conservación del orden de los gráficos”, presentar los resultados se pueden realizar de forma ordenada según la dependencia o desordenada. Esto no influye en los objetivos en el conjunto de resultados ni en la forma en que se calcula la consulta. Solo afecta la forma en que se imprimen los resultados en stdout. Además, los nodos que son equivalentes en el orden de dependencia pueden o no estar ordenados alfabéticamente. Se puede usar la marca --order_output para controlar este comportamiento. (La marca --[no]order_results tiene un subconjunto de funciones de la marca --order_output y está obsoleta).

El valor predeterminado de esta marca es auto, que imprime los resultados en un orden lexicográfico. Sin embargo, cuando se usa somepath(a,b), los resultados se imprimirán en orden deps.

Cuando esta marca es no y --output es build, label, label_kind, location, package, proto o xml, los resultados se imprimirán en un orden arbitrario. Por lo general, esta es la opción más rápida. Sin embargo, no se admite cuando --output es uno de graph, minrank o maxrank: con estos formatos, Bazel siempre imprime resultados ordenados por el orden o la clasificación de la dependencia.

Cuando esta marca es deps, Bazel imprime los resultados en algún orden topoológico, es decir, primero las dependencias y las dependencias. Sin embargo, los nodos que no están ordenados por el orden de dependencia (porque no hay una ruta de acceso de uno a otro) se pueden imprimir en cualquier orden.

Cuando esta marca es full, Bazel imprime los nodos en un orden completamente determinista (total). Primero, todos los nodos se ordenan alfabéticamente. Luego, cada nodo de la lista se usa como el inicio de una búsqueda de prioridad de profundidad posterior al orden, en la que las aristas salientes y los nodos no visitados se recorren en orden alfabético de los nodos sucesores. Por último, los nodos se imprimen en orden inverso al orden en que se visitaron.

La impresión de los nodos en este orden puede ser más lenta, por lo que solo se debe usar cuando el determinismo sea importante.

Imprime el formulario de origen de los destinos como aparecerían en BUILD.

--output build

Con esta opción, la representación de cada destino es como si estuviera escrita a mano en el lenguaje BUILD. Todas las variables y las llamadas a funciones (como glob y macros) se expanden, lo que es útil para ver el efecto de las macros de Starlark. Además, cada regla vigente informa un valor generator_name o generator_function, lo que indica el nombre de la macro que se evaluó para producir la regla vigente.

Aunque el resultado usa la misma sintaxis que los archivos BUILD, no se garantiza que produzca un archivo BUILD válido.

--output label

Con esta opción, se imprime el conjunto de nombres (o etiquetas) de cada objetivo en el gráfico resultante, una etiqueta por línea, en orden topoológico (a menos que se especifique --noorder_results; consulta las notas sobre el orden de los resultados). (En un orden topológico, el nodo de un gráfico aparece antes que todos sus sucesores). Por supuesto que existen muchos órdenes topoológicos posibles de un grafo (orden posterior inverso es solo uno); no se especifica cuál se elige.

Cuando se imprime el resultado de una consulta somepath, el orden en que se imprimen los nodos es el orden de la ruta de acceso.

Advertencia: En algunos casos límite, puede haber dos destinos distintos con la misma etiqueta. Por ejemplo, una regla sh_binary y su único archivo srcs (implícito) pueden llamarse foo.sh. Si el resultado de una consulta contiene ambos destinos, el resultado (en formato label) parecerá contener un duplicado. Cuando se usa el formato label_kind (consulta a continuación), la distinción es clara: los dos destinos tienen el mismo nombre, pero uno tiene el tipo sh_binary rule y el otro tipo source file.

--output label_kind

Al igual que label, este formato de salida imprime las etiquetas de cada destino en el gráfico resultante, en orden topoológico, pero además antepone a la etiqueta por el tipo del destino.

--output proto

Imprime el resultado de la consulta como un búfer de protocolo QueryResult.

--output streamed_proto

Imprime un flujo delimitado por longitud de búferes de protocolo Target. Esto es útil para (i) eludir las limitaciones de tamaño de los búferes de protocolo cuando hay demasiados destinos para caber en un solo QueryResult o (ii) a fin de comenzar a procesar mientras Bazel aún genera salidas.

--output textproto

Al igual que --output proto, imprime el búfer de protocolo QueryResult, pero en formato de texto.

--output streamed_jsonproto

Al igual que --output streamed_proto, imprime una transmisión de búferes de protocolo Target, pero en formato ndjson.

--output minrank --output maxrank

Al igual que label, los formatos de salida minrank y maxrank imprimen las etiquetas de cada objetivo en el gráfico resultante, pero, en lugar de aparecer en orden topoológico, aparecen en orden de clasificación, precedidos por su número de rango. Estos no se ven afectados por la marca --[no]order_results sobre el orden de los resultados (consulta las notas sobre el orden de los resultados).

Hay dos variantes de este formato: minrank clasifica cada nodo según la longitud de la ruta más corta desde un nodo raíz hasta él. Los nodos "raíz" (aquellos que no tienen aristas entrantes) son de rango 0, sus sucesores son de rango 1, etc. (Como siempre, las aristas apuntan desde un objetivo a sus requisitos previos, es decir, los objetivos de los que depende).

maxrank clasifica cada nodo según la longitud de la ruta más larga desde un nodo raíz hasta él. Nuevamente, las "raíces" tienen una clasificación 0, todos los demás nodos tienen una clasificación superior al rango máximo de todos sus predecesores.

Todos los nodos de un ciclo se consideran de la misma clasificación. (La mayoría de los grafos son acíclicos, pero los ciclos ocurren simplemente porque los archivos BUILD contienen ciclos erróneos).

Estos formatos de salida son útiles para descubrir la profundidad de un gráfico. Si se usa para el resultado de una consulta deps(x), rdeps(x) o allpaths, el número de clasificación es igual a la longitud de la ruta más corta (con minrank) o más larga (con maxrank) desde x hasta un nodo en esa clasificación. maxrank se puede usar para determinar la secuencia más larga de pasos de compilación necesaria para compilar un destino.

Por ejemplo, el gráfico de la izquierda produce los resultados de la derecha cuando se especifican --output minrank y --output maxrank, respectivamente.

Mejor clasificación
      minrank

      0 //c:c
      1 //b:b
      1 //a:a
      2 //b:b.cc
      2 //a:a.cc
      
      maxrank

      0 //c:c
      1 //b:b
      2 //a:a
      2 //b:b.cc
      3 //a:a.cc
      
--output location

Al igual que label_kind, esta opción imprime, para cada objetivo en el resultado, el tipo y la etiqueta del destino, pero tiene el prefijo de una cadena que describe la ubicación de ese destino, como un nombre de archivo y un número de línea. El formato se parece al resultado de grep. Por lo tanto, las herramientas que pueden analizar esta última (como Emacs o vi) también pueden usar el resultado de la consulta para recorrer una serie de coincidencias, lo que permite que la herramienta de consultas de Bazel se use como un "grep para archivos BUILD" con reconocimiento de gráfico de dependencias.

La información de la ubicación varía según el tipo de segmentación (consulta el operador kind). Para las reglas, se muestra la ubicación de la declaración de la regla dentro del archivo BUILD. En el caso de los archivos de origen, se imprime la ubicación de la línea 1 del archivo real. Para un archivo generado, se imprime la ubicación de la regla que lo genera. (La Herramienta de consultas no cuenta con información suficiente para encontrar la ubicación real del archivo generado y, en cualquier caso, es posible que no exista si aún no se realizó una compilación).

--output package

Esta opción imprime el nombre de todos los paquetes a los que pertenece algún destino del conjunto de resultados. Los nombres se imprimen en orden lexicográfico; los duplicados están excluidos. De manera formal, esta es una proyección del conjunto de etiquetas (paquete, destino) en paquetes.

Los paquetes en repositorios externos tienen el formato @repo//foo/bar, mientras que los paquetes del repositorio principal tienen el formato foo/bar.

Junto con la consulta deps(...), esta opción de resultado se puede usar para encontrar el conjunto de paquetes que se deben pagar a fin de compilar un conjunto determinado de destinos.

Cómo mostrar un gráfico del resultado

--output graph

Esta opción hace que el resultado de la consulta se imprima como un gráfico dirigido en el popular formato de GraphViz de AT&T. Por lo general, el resultado se guarda en un archivo, como .png o .svg. (Si el programa dot no está instalado en tu estación de trabajo, puedes instalarlo con el comando sudo apt-get install graphviz). Consulta la sección de ejemplos a continuación para ver una invocación de muestra.

Este formato de salida es particularmente útil para las consultas allpaths, deps o rdeps, en las que el resultado incluye un conjunto de rutas que no se puede visualizar fácilmente cuando se renderiza en forma lineal, como con --output label.

De forma predeterminada, el gráfico se renderiza de forma factorizada. Es decir, los nodos equivalentes topológicamente se combinan en un solo nodo con varias etiquetas. Esto hace que el gráfico sea más compacto y legible, ya que los gráficos de resultados típicos contienen patrones muy repetitivos. Por ejemplo, una regla java_library puede depender de cientos de archivos de origen Java generados por el mismo genrule. En el gráfico factorizado, todos estos archivos están representados por un solo nodo. Este comportamiento se puede inhabilitar con la opción --nograph:factored.

--graph:node_limit n

La opción especifica la longitud máxima de la string de etiqueta para un nodo gráfico en el resultado. Las etiquetas más largas se truncarán; -1 inhabilita el truncamiento. Debido a la forma factorizada en la que se suelen imprimir los gráficos, las etiquetas de los nodos pueden ser muy largas. GraphViz no puede procesar etiquetas que superen los 1,024 caracteres, que es el valor predeterminado de esta opción. Esta opción no tiene efecto, a menos que se use --output=graph.

--[no]graph:factored

De forma predeterminada, los gráficos se muestran de forma factorizada, como se explicó anteriormente. Cuando se especifica --nograph:factored, se imprimen los gráficos sin factorizar. Esto hace que la visualización con GraphViz sea poco práctica, pero el formato más simple puede facilitar el procesamiento por parte de otras herramientas (como grep). Esta opción no tiene efecto, a menos que se use --output=graph.

XML

--output xml

Esta opción hace que los destinos resultantes se impriman en un formato XML. El resultado comienza con un encabezado XML como el que se muestra a continuación.

  <?xml version="1.0" encoding="UTF-8"?>
  <query version="2">

Luego, continúa con un elemento XML para cada destino en el gráfico de resultados, en orden topoológico (a menos que se soliciten resultados desordenados), y termina con una terminación

</query>

Se emiten entradas simples para destinos de tipo file:

  <source-file name='//foo:foo_main.cc' .../>
  <generated-file name='//foo:libfoo.so' .../>

Sin embargo, en el caso de las reglas, el archivo XML está estructurado y contiene definiciones de todos los atributos de la regla, incluidos aquellos cuyo valor no se especificó explícitamente en el archivo BUILD de la regla.

Además, el resultado incluye elementos rule-input y rule-output para que la topología del gráfico de dependencia se pueda reconstruir sin tener que saber que, por ejemplo, los elementos del atributo srcs son dependencias futuras (requisitos previos) y el contenido del atributo outs son dependencias inversas (consumidores).

Los elementos rule-input para las dependencias implícitas se suprimen si se especifica --noimplicit_deps.

  <rule class='cc_binary rule' name='//foo:foo' ...>
    <list name='srcs'>
      <label value='//foo:foo_main.cc'/>
      <label value='//foo:bar.cc'/>
      ...
    </list>
    <list name='deps'>
      <label value='//common:common'/>
      <label value='//collections:collections'/>
      ...
    </list>
    <list name='data'>
      ...
    </list>
    <int name='linkstatic' value='0'/>
    <int name='linkshared' value='0'/>
    <list name='licenses'/>
    <list name='distribs'>
      <distribution value="INTERNAL" />
    </list>
    <rule-input name="//common:common" />
    <rule-input name="//collections:collections" />
    <rule-input name="//foo:foo_main.cc" />
    <rule-input name="//foo:bar.cc" />
    ...
  </rule>

Cada elemento XML de un destino contiene un atributo name, cuyo valor es la etiqueta del destino, y un atributo location, cuyo valor es la ubicación del destino tal como imprime la --output location.

--[no]xml:line_numbers

De forma predeterminada, las ubicaciones que se muestran en el resultado del XML contienen números de línea. Cuando se especifica --noxml:line_numbers, no se imprimen los números de línea.

--[no]xml:default_values

De forma predeterminada, el resultado del archivo XML no incluye un atributo de regla cuyo valor sea el valor predeterminado para ese tipo de atributo (por ejemplo, si no se especificó en el archivo BUILD o se proporcionó de manera explícita el valor predeterminado). Esta opción hace que esos valores de atributos se incluyan en el resultado del XML.

Expresiones regulares

Las expresiones regulares en el lenguaje de consulta usan la biblioteca de regex de Java, por lo que puedes usar la sintaxis completa para java.util.regex.Pattern.

Consulta con repositorios externos

Si la compilación depende de reglas de repositorios externos, los resultados de la consulta incluirán estas dependencias. Por ejemplo, si //foo:bar depende de @other-repo//baz:lib, bazel query 'deps(//foo:bar)' mostrará @other-repo//baz:lib como dependencia.