Esta página é o manual de referência da linguagem de consulta do Bazel usada
quando você usa bazel query
para analisar dependências de build. Ele também
descreve os formatos de saída com suporte a bazel query
.
Para casos de uso práticos, consulte o Como fazer da consulta do Bazel.
Referência de consulta adicional
Além de query
, que é executado no gráfico de destino da fase pós-carregamento,
o Bazel inclui a consulta de gráfico de ação e a consulta configurável.
Consulta de gráfico de ação
A consulta do gráfico de ações (aquery
) opera no gráfico de destino configurado
pós-análise e expõe informações sobre ações, artefatos e
as relações deles. aquery
é útil quando você tem interesse nas
propriedades das ações/artefatos geradas pelo gráfico de destino configurado.
Por exemplo, os comandos reais são executados e as entradas, saídas e mnemônicas deles.
Para mais detalhes, consulte a referência de aquery.
Consulta configurável
A consulta tradicional do Bazel é executada no gráfico de destino da fase pós-carregamento e,
portanto, não tem o conceito de configurações e conceitos relacionados. Ele não resolve corretamente as instruções de seleção
e, em vez disso, retorna todas as resoluções possíveis de seleção. No entanto, o
ambiente de consulta configurável, cquery
, processa corretamente as configurações, mas
não oferece todas as funcionalidades da consulta original.
Para mais detalhes, consulte a referência de cquery.
Exemplos
Como as pessoas usam bazel query
? Confira alguns exemplos:
Por que a árvore //foo
depende de //bar/baz
?
Mostrar um caminho:
somepath(foo/..., //bar/baz:all)
De quais bibliotecas C++ todos os testes foo
dependem que
o destino foo_bin
não depende?
kind("cc_library", deps(kind(".*test rule", foo/...)) except deps(//foo:foo_bin))
Tokens: a sintaxe lexical
As expressões na linguagem de consulta são compostas pelos seguintes tokens:
Palavras-chave, como
let
. As palavras-chave são as palavras reservadas do idioma, e cada uma delas é descrita abaixo. O conjunto completo de palavras-chave é:Palavras, como "
foo/...
" ou ".*test rule
" ou "//bar/baz:all
". Se uma sequência de caracteres estiver "entre aspas" (começa e termina com aspas simples ' ou começa e termina com aspas duplas "), ela é uma palavra. Se uma sequência de caracteres não estiver entre aspas, ela ainda poderá ser analisada como uma palavra. Palavras sem aspas são sequências de caracteres extraídos dos caracteres do alfabeto A-Za-z, dos algarismos 0-9 e dos caracteres especiais*/@.-_:$~[]
(asterisco, barra, arroba, ponto, traço, sublinhado, dois-pontos, cifrão, til, colchete esquerdo, colchete direito). No entanto, as palavras sem aspas não podem começar com um hífen-
ou asterisco*
, mesmo que os nomes de destino relativos possam começar com esses caracteres.As palavras sem aspas também não podem incluir os caracteres de sinal de adição
+
ou de igualdade=
, mesmo que esses caracteres sejam permitidos em nomes de destino. Ao escrever um código que gera expressões de consulta, os nomes de destino precisam ser citados.As aspas são necessárias ao escrever scripts que constroem expressões de consulta do Bazel com base em valores fornecidos pelo usuário.
//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.
Essa citação é adicional a qualquer citação que possa ser exigida pelo shell, como:
bazel query ' "//foo:bar=wiz" ' # single-quotes for shell, double-quotes for Bazel.
As palavras-chave e os operadores, quando entre aspas, são tratados como palavras comuns. Por exemplo,
some
é uma palavra-chave, mas "algumas" é uma palavra.foo
e "foo" são palavras.No entanto, tenha cuidado ao usar aspas simples ou duplas nos nomes de destino. Ao citar um ou mais nomes de destino, use apenas um tipo de aspas (todas simples ou todas duplas).
Confira a seguir exemplos de como a string de consulta do Java vai ser:
'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
Escolhemos essa sintaxe para que as aspas não sejam necessárias na maioria dos casos. O exemplo (incomum) de
".*test rule"
precisa de aspas: ele começa com um ponto e contém um espaço. Citar"cc_library"
é desnecessário, mas não faz mal.Pontuação, como parênteses
()
, ponto.
e vírgula,
. Palavras com pontuação (exceto as exceções listadas acima) precisam ser colocadas entre aspas.
Os caracteres de espaço em branco fora de uma palavra entre aspas são ignorados.
Conceitos da linguagem de consulta do Bazel
A linguagem de consulta do Bazel é uma linguagem de expressões. Cada expressão é avaliada como um conjunto parcialmente ordenado de destinos ou, de forma equivalente, um gráfico (DAG) de destinos. Esse é o único tipo de dados.
Conjunto e gráfico se referem ao mesmo tipo de dados, mas enfatizam aspectos diferentes dele, por exemplo:
- Conjunto:a ordem parcial das metas não é interessante.
- Gráfico:a ordem parcial das metas é significativa.
Ciclos no gráfico de dependência
Os gráficos de dependência do build precisam ser acíclicos.
Os algoritmos usados pela linguagem de consulta são destinados ao uso em gráficos acíclicos, mas são robustos contra ciclos. Os detalhes de como os ciclos são tratados não são especificados e não devem ser usados.
Dependências implícitas
Além das dependências de build definidas explicitamente em arquivos BUILD
,
o Bazel adiciona outras dependências implícitas às regras. Por exemplo,
cada regra Java depende implicitamente do JavaBuilder. As dependências implícitas
são estabelecidas usando atributos que começam com $
e não
podem ser substituídas em arquivos BUILD
.
Por padrão, bazel query
considera dependências implícitas ao calcular o resultado da consulta. Esse comportamento pode ser alterado com
a opção --[no]implicit_deps
. Como a consulta não considera
configurações, os possíveis toolchains nunca são considerados.
Integridade
As expressões da linguagem de consulta do Bazel operam no gráfico de dependências
de build, que é o gráfico definido implicitamente por todas
as declarações de regras em todos os arquivos BUILD
. É importante entender
que esse gráfico é um pouco abstrato e não constitui uma
descrição completa de como realizar todas as etapas de um build. Para
realizar um build, também é necessária uma configuração.
Consulte a seção configurações
do guia do usuário para mais detalhes.
O resultado da avaliação de uma expressão na linguagem de consulta do Bazel é verdadeiro para todas as configurações, o que significa que pode ser uma aproximação conservadora e não exatamente precisa. Se você usar a ferramenta de consulta para calcular o conjunto de todos os arquivos de origem necessários durante um build, ela poderá informar mais do que o necessário porque, por exemplo, a ferramenta de consulta vai incluir todos os arquivos necessários para oferecer suporte à tradução de mensagens, mesmo que você não pretenda usar esse recurso no build.
Sobre a preservação da ordem do gráfico
As operações preservam todas as restrições de
ordenação herdadas das subexpressões. Pense nisso
como "a lei da conservação da ordem parcial". Considere um
exemplo: se você emitir uma consulta para determinar o fechamento transitivo de
dependências de um destino específico, o conjunto resultante será ordenado
de acordo com o gráfico de dependência. Se você filtrar esse conjunto para
incluir apenas os destinos do tipo file
, a mesma
relação de ordenação parcial transitiva será mantida entre cada
par de destinos no subconjunto resultante, mesmo que nenhum desses pares esteja conectado diretamente no gráfico original.
Não há arestas de arquivo no gráfico de dependência do build.
No entanto, embora todos os operadores preservem a ordem, algumas operações, como as operações de conjunto, não introduzem restrições de ordenação próprias. Considere esta expressão:
deps(x) union y
A ordem do conjunto de resultados finais é garantida para preservar todas as
restrições de ordenação das subexpressões, ou seja, que todas as
dependências transitivas de x
sejam ordenadas corretamente em
relação umas às outras. No entanto, a consulta não garante nada sobre
a ordem das metas em y
nem sobre a
ordem das metas em deps(x)
em relação às de
y
(exceto as metas em
y
que também estão em deps(x)
).
Os operadores que introduzem restrições de ordenação incluem:
allpaths
, deps
, rdeps
, somepath
e os curingas de padrão de destino
package:*
, dir/...
etc.
Consulta do Sky
A consulta Sky é um modo de consulta que opera em um escopo de universo especificado.
Funções especiais disponíveis apenas no SkyQuery
O modo de consulta Sky tem as funções de consulta adicionais allrdeps
e
rbuildfiles
. Essas funções operam em todo o escopo do universo (é por isso que não fazem sentido para consultas normais).
Como especificar um escopo de universo
O modo de consulta do Sky é ativado transmitindo as duas flags a seguir:
(--universe_scope
ou --infer_universe_scope
) e
--order_output=no
.
--universe_scope=<target_pattern1>,...,<target_patternN>
informa à consulta para
carregar previamente o fechamento transitivo do padrão de destino especificado pelos padrões de destino, que podem
ser aditivos e subtrativos. Todas as consultas são avaliadas nesse "escopo". Em particular,
os operadores allrdeps
e
rbuildfiles
só retornam resultados desse escopo.
--infer_universe_scope
informa ao Bazel para inferir um valor para --universe_scope
da expressão de consulta. Esse valor inferido é a lista de padrões de destino exclusivos na expressão de consulta, mas talvez não seja o que você quer. Exemplo:
bazel query --infer_universe_scope --order_output=no "allrdeps(//my:target)"
A lista de padrões de destino exclusivos nesta expressão de consulta é ["//my:target"]
. Portanto, o Bazel trata isso da mesma forma que a invocação:
bazel query --universe_scope=//my:target --order_output=no "allrdeps(//my:target)"
No entanto, o resultado dessa consulta com --universe_scope
é apenas //my:target
.
Nenhuma das dependências reversas de //my:target
está no universo, por
construção. Por outro lado, considere:
bazel query --infer_universe_scope --order_output=no "tests(//a/... + b/...) intersect allrdeps(siblings(rbuildfiles(my/starlark/file.bzl)))"
Essa é uma invocação de consulta significativa que está tentando calcular os destinos de teste na
expansão tests
dos destinos em alguns diretórios que
dependem transitivamente de destinos cuja definição usa um determinado arquivo .bzl
. Aqui,
--infer_universe_scope
é uma conveniência, especialmente no caso em que a escolha de
--universe_scope
exigiria que você analisasse a expressão da consulta.
Portanto, para expressões de consulta que usam operadores de escopo universal, como
allrdeps
e
rbuildfiles
, use
--infer_universe_scope
somente se o comportamento for o que você quer.
A consulta Sky tem algumas vantagens e desvantagens em comparação com a consulta padrão. A principal
desvantagem é que ele não pode ordenar a saída de acordo com a ordem do gráfico. Portanto, alguns
formatos de saída são proibidos. As vantagens são que ele fornece
dois operadores (allrdeps
e
rbuildfiles
) que não estão disponíveis na consulta padrão.
Além disso, a Sky Query faz o trabalho inspecionando o gráfico Skyframe, em vez de criar um novo gráfico, que é o que a implementação padrão faz. Assim, há algumas circunstâncias em que
ele é mais rápido e usa menos memória.
Expressões: sintaxe e semântica da gramática
Esta é a gramática da linguagem de consulta do Bazel, expressa na notação 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 ... ')'
As seções a seguir descrevem cada uma das produções desta gramática em ordem.
Padrões de destino
expr ::= word
Sintaticamente, um padrão de destino é apenas uma palavra. Ele é interpretado como um conjunto (não ordenado) de destinos. O padrão de destino mais simples é um rótulo, que
identifica um único destino (arquivo ou regra). Por exemplo, o padrão de destino
//foo:bar
é avaliado como um conjunto que contém um elemento, o destino, a regra
bar
.
Os padrões de destino generalizam os rótulos para incluir caracteres curinga em pacotes e
destinos. Por exemplo, foo/...:all
(ou apenas foo/...
) é um padrão de destino
que é avaliado como um conjunto que contém todas as regras em cada pacote de forma recursiva
no diretório foo
. bar/baz:all
é um padrão de destino que é avaliado
como um conjunto que contém todas as regras no pacote bar/baz
, mas não nos
subpacotes.
Da mesma forma, foo/...:*
é um padrão de destino que é avaliado como um conjunto contendo
todos os destinos (regras e arquivos) em cada pacote de forma recursiva abaixo do
diretório foo
. bar/baz:*
é avaliado como um conjunto contendo todos os destinos no
pacote bar/baz
, mas não nos subpacotes.
Como o caractere curinga :*
corresponde a arquivos e regras, ele geralmente é mais
útil que :all
para consultas. Por outro lado, o curinga :all
(implícito em
padrões de destino, como foo/...
) geralmente é mais útil para builds.
Os padrões de destino bazel query
funcionam da mesma forma que os destinos de build bazel build
.
Para mais detalhes, consulte Padrões de destino ou
digite bazel help target-syntax
.
Os padrões de destino podem ser avaliados como um conjunto único (no caso de um rótulo), um
conjunto com muitos elementos (como no caso de foo/...
, que tem milhares
de elementos) ou um conjunto vazio, se o padrão de destino não corresponder a nenhum destino.
Todos os nós no resultado de uma expressão de padrão de destino são ordenados corretamente
em relação uns aos outros de acordo com a relação de dependência. Portanto, o resultado de
foo:*
não é apenas o conjunto de destinos no pacote foo
, mas também o
gráfico sobre esses destinos. Não há garantias sobre a ordem relativa
dos nós de resultado em relação a outros nós. Para mais detalhes, consulte a seção
ordem do gráfico.
Variáveis
expr ::= let name = expr1 in expr2
| $name
A linguagem de consulta do Bazel permite definições e referências a
variáveis. O resultado da avaliação de uma expressão let
é o mesmo
que o de expr2, com todas as ocorrências livres
da variável name substituídas pelo valor de
expr1.
Por exemplo, let v = foo/... in allpaths($v, //common) intersect $v
é
equivalente a allpaths(foo/...,//common) intersect foo/...
.
Uma ocorrência de uma referência de variável name
que não esteja em
uma expressão let name = ...
externa é um
erro. Em outras palavras, as expressões de consulta de alto nível não podem ter variáveis
livres.
Nas produções gramaticais acima, name
é semelhante a palavra, mas com a
restrição adicional de ser um identificador válido na linguagem de programação C. As referências à variável precisam ter o caractere "$" no início.
Cada expressão let
define apenas uma variável, mas você pode aninhá-las.
Os padrões de destino e as referências de variáveis consistem em apenas um token, uma palavra, criando uma ambiguidade sintática. No entanto, não há ambiguidade semântica, porque o subconjunto de palavras que são nomes de variáveis legais é disjunto do subconjunto de palavras que são padrões de destino válidos.
Tecnicamente falando, as expressões let
não aumentam
a expressividade da linguagem de consulta: qualquer consulta expressada no
idioma também pode ser expressa sem elas. No entanto, eles
melhoram a concisão de muitas consultas e também podem levar a uma avaliação de consulta mais
eficiente.
Expressões entre parênteses
expr ::= (expr)
Os parênteses associam subexpressões para forçar uma ordem de avaliação. Uma expressão entre parênteses é avaliada como o valor do argumento.
Operações algébricas de conjuntos: interseção, união, diferença de conjuntos
expr ::= expr intersect expr
| expr ^ expr
| expr union expr
| expr + expr
| expr except expr
| expr - expr
Esses três operadores calculam as operações de conjunto habituais sobre os argumentos.
Cada operador tem duas formas: uma nominal, como intersect
, e uma
simbólica, como ^
. As duas formas são equivalentes, mas as simbólicas são
mais rápidas de digitar. Para maior clareza, o restante desta página usa as formas nominais.
Por exemplo,
foo/... except foo/bar/...
avalia o conjunto de metas que correspondem a foo/...
, mas não a foo/bar/...
.
Você pode escrever a mesma consulta como:
foo/... - foo/bar/...
As operações intersect
(^
) e union
(+
) são comutativas (simétricas);
except
(-
) é assimétrica. O analisador trata os três operadores como
associativos à esquerda e de mesma precedência. Portanto, talvez você queira usar parênteses. Por
exemplo, as duas primeiras dessas expressões são equivalentes, mas a terceira não:
x intersect y union z
(x intersect y) union z
x intersect (y union z)
Ler destinos de uma fonte externa: definir
expr ::= set(word *)
O operador set(a b c ...)
calcula a união de um conjunto de zero ou mais
padrões de destino, separados por espaços em branco (sem vírgulas).
Em conjunto com o recurso $(...)
do shell Bourne, o set()
oferece uma
maneira de salvar os resultados de uma consulta em um arquivo de texto comum, manipulando
esse arquivo de texto usando outros programas (como ferramentas de shell UNIX padrão) e, em seguida,
reintroduzindo o resultado na ferramenta de consulta como um valor para processamento
posterior. Exemplo:
bazel query deps(//my:target) --output=label | grep ... | sed ... | awk ... > foo
bazel query "kind(cc_binary, set($(<foo)))"
No próximo exemplo,kind(cc_library, deps(//some_dir/foo:main, 5))
é
calculado filtrando os valores de maxrank
usando um programa awk
.
bazel query 'deps(//some_dir/foo:main)' --output maxrank | awk '($1 < 5) { print $2;} ' > foo
bazel query "kind(cc_library, set($(<foo)))"
Nesses exemplos, $(<foo)
é uma abreviação de $(cat foo)
, mas comandos de shell
diferentes de cat
também podem ser usados, como o comando awk
anterior.
Funções
expr ::= word '(' int | word | expr ... ')'
A linguagem de consulta define várias funções. O nome da função determina o número e o tipo de argumentos que ela exige. As seguintes funções estão disponíveis:
allpaths
attr
buildfiles
rbuildfiles
deps
filter
kind
labels
loadfiles
rdeps
allrdeps
same_pkg_direct_rdeps
siblings
some
somepath
tests
visible
Fechamento transitivo de dependências: deps
expr ::= deps(expr)
| deps(expr, depth)
O operador deps(x)
é avaliado como o gráfico formado
pela interseção transitiva das dependências do conjunto de argumentos
x. Por exemplo, o valor de deps(//foo)
é o
gráfico de dependências com raiz no nó único foo
, incluindo todas as
dependências. O valor de deps(foo/...)
são os gráficos de dependência cujas raízes
são todas as regras em todos os pacotes abaixo do diretório foo
. Nesse contexto,
"dependências" significa apenas regras e destinos de arquivo. Portanto, os arquivos BUILD
e
Starlark necessários para criar esses destinos não estão incluídos aqui. Para isso,
use o operador buildfiles
.
O gráfico resultante é ordenado de acordo com a relação de dependência. Para mais detalhes, consulte a seção sobre a ordem do gráfico.
O operador deps
aceita um segundo argumento opcional, que é um literal
inteiro que especifica um limite superior na profundidade da pesquisa. Assim, deps(foo:*, 0)
retorna todos os destinos no pacote foo
, enquanto
deps(foo:*, 1)
inclui os pré-requisitos diretos de qualquer destino no
pacote foo
, e deps(foo:*, 2)
inclui os nós diretamente
acessíveis dos nós em deps(foo:*, 1)
e assim por diante. Esses números
correspondem às classificações mostradas no formato de saída minrank
.
Se o parâmetro depth for omitido, a pesquisa será
ilimitada: ela vai calcular o fechamento transitivo reflexivo de pré-requisitos.
Fechamento transitivo de dependências reversas: rdeps
expr ::= rdeps(expr, expr)
| rdeps(expr, expr, depth)
O operador rdeps(u, x)
é avaliado como as dependências reversas do conjunto de argumentos
x dentro do fechamento transitivo do conjunto de universo
u.
O gráfico resultante é ordenado de acordo com a relação de dependência. Consulte a seção sobre a ordem do gráfico para mais detalhes.
O operador rdeps
aceita um terceiro argumento opcional, que é um número inteiro
literal que especifica um limite superior na profundidade da pesquisa. O gráfico resultante
inclui apenas nós dentro de uma distância da profundidade especificada de qualquer
nó no conjunto de argumentos. Portanto, rdeps(//foo, //common, 1)
é avaliado para todos os nós
no fechamento transitivo de //foo
que dependem diretamente de //common
. Esses
números correspondem às classificações mostradas no formato de saída
minrank
. Se o parâmetro depth for omitido, a
pesquisa não terá limite.
Fechamento transitivo de todas as dependências reversas: allrdeps
expr ::= allrdeps(expr)
| allrdeps(expr, depth)
O operador allrdeps
se comporta exatamente como o operador rdeps
, exceto que o "conjunto do universo" é o que a flag --universe_scope
avalia, em vez de ser especificado separadamente. Assim, se
--universe_scope=//foo/...
foi transmitido, allrdeps(//bar)
é
equivalente a rdeps(//foo/..., //bar)
.
Dependências reversas diretas no mesmo pacote: same_pkg_direct_rdeps
expr ::= same_pkg_direct_rdeps(expr)
O operador same_pkg_direct_rdeps(x)
é avaliado como o conjunto completo de destinos
que estão no mesmo pacote que um destino no conjunto de argumentos e que dependem diretamente dele.
Como lidar com o pacote de um alvo: irmãos
expr ::= siblings(expr)
O operador siblings(x)
é avaliado para o conjunto completo de destinos que estão no
mesmo pacote que um destino no conjunto de argumentos.
Escolha arbitrária: alguns
expr ::= some(expr)
| some(expr, count )
O operador some(x, k)
seleciona no máximo k alvos de forma arbitrária do conjunto de argumentos
x e avalia um conjunto que contém
apenas esses alvos. O parâmetro k é opcional. Se
ele estiver ausente, o resultado será um conjunto único contendo apenas um destino
selecionado arbitrariamente. Se o tamanho do conjunto de argumentos x for
menor que k, o conjunto de argumentos
x será retornado.
Por exemplo, a expressão some(//foo:main union //bar:baz)
é avaliada como um
conjunto único que contém //foo:main
ou //bar:baz
, mas não está definido qual
deles. A expressão some(//foo:main union //bar:baz, 2)
ou
some(//foo:main union //bar:baz, 3)
retorna //foo:main
e
//bar:baz
.
Se o argumento for um singleton, some
calculará a função de identidade: some(//foo:main)
é
equivalente a //foo:main
.
Ocorrerá um erro se o conjunto de argumentos especificado estiver vazio, como na
expressão some(//foo:main intersect //bar:baz)
.
Operadores de caminho: somepath, allpaths
expr ::= somepath(expr, expr)
| allpaths(expr, expr)
Os operadores somepath(S, E)
e
allpaths(S, E)
calculam
caminhos entre dois conjuntos de destinos. Ambas as consultas aceitam dois
argumentos: um conjunto S de pontos de partida e um conjunto
E de pontos finais. somepath
retorna o
gráfico de nós em algum caminho arbitrário de um destino em
S para um destino em E. allpaths
retorna o gráfico de nós em todos os caminhos de qualquer destino em
S para qualquer destino em E.
Os gráficos resultantes são ordenados de acordo com a relação de dependência. Consulte a seção sobre a ordem do gráfico para mais detalhes.
Filtragem de tipo de destino: kind
expr ::= kind(word, expr)
O operador kind(pattern, input)
aplica um filtro a um conjunto de destinos e descarta aqueles
que não são do tipo esperado. O parâmetro pattern especifica o tipo de meta a ser correspondido.
Por exemplo, os tipos dos quatro destinos definidos pelo arquivo BUILD
(para o pacote p
) mostrado abaixo são ilustrados na tabela:
Código | Destino | Tipo |
---|---|---|
genrule( name = "a", srcs = ["a.in"], outs = ["a.out"], cmd = "...", ) |
//p:a |
regra genrule |
//p:a.in |
arquivo de origem | |
//p:a.out |
arquivo gerado | |
//p:BUILD |
arquivo de origem |
Assim, kind("cc_.* rule", foo/...)
é avaliado como o conjunto
de todos os cc_library
, cc_binary
etc.,
alvos de regra abaixo de foo
, e kind("source file", deps(//foo))
é avaliado como o conjunto de todos os arquivos de origem no fechamento transitivo
de dependências do destino //foo
.
A citação do argumento pattern geralmente é necessária
porque, sem ela, muitas expressões regulares, como source
file
e .*_test
, não são consideradas palavras pelo analisador.
Ao fazer a correspondência com package group
, os destinos que terminam em
:all
podem não gerar resultados. Em vez disso, use :all-targets
.
Filtragem de nome do destino: filtro
expr ::= filter(word, expr)
O operador filter(pattern, input)
aplica um filtro a um conjunto de destinos e descarta os destinos cujos
rótulos (em forma absoluta) não correspondem ao padrão. Ele
é avaliado para um subconjunto da entrada.
O primeiro argumento, pattern, é uma palavra que contém uma
expressão regular sobre nomes de destino. Uma expressão filter
é avaliada como o conjunto que contém todos os alvos x, de modo que
x seja um membro do conjunto input e o
rótulo (em forma absoluta, como //foo:bar
)
de x contenha uma correspondência (não ancorada)
para a expressão regular pattern. Como todos
os nomes de destino começam com //
, ele pode ser usado como uma alternativa
à âncora de expressão regular ^
.
Esse operador geralmente oferece uma alternativa muito mais rápida e robusta ao operador
intersect
. Por exemplo, para conferir todas as
dependências bar
do destino //foo:foo
, é possível
avaliar
deps(//foo) intersect //bar/...
No entanto, essa instrução vai exigir a análise de todos os arquivos BUILD
na
árvore bar
, que será lenta e propensa a erros em
arquivos BUILD
irrelevantes. Uma alternativa seria:
filter(//bar, deps(//foo))
que primeiro calcularia o conjunto de dependências //foo
e
depois filtraria apenas os destinos que correspondem ao padrão fornecido. Em outras
palavras, destinos com nomes que contêm //bar
como uma substring.
Outro uso comum do operador filter(pattern,
expr)
é filtrar arquivos específicos pelo
nome ou pela extensão. Por exemplo,
filter("\.cc$", deps(//foo))
vai fornecer uma lista de todos os arquivos .cc
usados para criar //foo
.
Filtragem de atributos da regra: attr
expr ::= attr(word, word, expr)
O operador attr(name, pattern, input)
aplica um filtro a um conjunto de destinos e descarta destinos que não são regras, destinos de regras que não têm o atributo name definido ou destinos de regras em que o valor do atributo não corresponde à expressão regular pattern fornecida. Ele avalia um subconjunto da entrada.
O primeiro argumento, name, é o nome do atributo
da regra que precisa ser comparado com o padrão de
expressão regular fornecido. O segundo argumento,
pattern, é uma expressão regular sobre os valores
do atributo. Uma expressão attr
é avaliada como o conjunto que contém todos os alvos
x, de modo que x seja um
membro do conjunto input, seja uma regra com o atributo
name definido e o valor do atributo contenha uma
correspondência (não ancorada) para a expressão regular
pattern. Se name for um
atributo opcional e a regra não o especificar explicitamente, o valor do atributo
padrão será usado para comparação. Por exemplo,
attr(linkshared, 0, deps(//foo))
vai selecionar todas as dependências //foo
que podem ter um
atributo linkshared (como a regra cc_binary
) e defini-lo
explicitamente como 0 ou não defini-lo, mas o valor padrão é 0 (como para
regras cc_binary
).
Atributos do tipo lista (como srcs
, data
etc.) são
convertidos em strings do formulário [value<sub>1</sub>, ..., value<sub>n</sub>]
,
começando com um colchete [
, terminando com um colchete ]
e usando ",
" (vírgula, espaço) para delimitar vários valores.
Os identificadores são convertidos em strings usando a forma absoluta do
identificador. Por exemplo, um atributo deps=[":foo",
"//otherpkg:bar", "wiz"]
seria convertido na
string [//thispkg:foo, //otherpkg:bar, //thispkg:wiz]
.
Os colchetes estão sempre presentes. Portanto, a lista vazia usaria o valor de string []
para fins de correspondência. Por exemplo,
attr("srcs", "\[\]", deps(//foo))
vai selecionar todas as regras entre as dependências //foo
que têm um
atributo srcs
vazio, enquanto
attr("data", ".{3,}", deps(//foo))
vai selecionar todas as regras entre as dependências //foo
que especificam pelo menos um valor no atributo data
. Cada rótulo tem pelo menos três caracteres devido ao //
e ao :
.
Para selecionar todas as regras entre as dependências de //foo
com um value
específico em um
atributo do tipo lista, use
attr("tags", "[\[ ]value[,\]]", deps(//foo))
Isso funciona porque o caractere antes de value
será [
ou um espaço, e o
caractere depois de value
será uma vírgula ou ]
.
Filtragem da visibilidade da regra: visível
expr ::= visible(expr, expr)
O operador visible(predicate, input)
aplica um filtro a um conjunto de destinos e descarta os destinos sem a
visibilidade necessária.
O primeiro argumento, predicate, é um conjunto de destinos que todos os destinos na saída precisam estar visíveis. Uma expressão visible é avaliada como o conjunto que contém todos os alvos x, de modo que x seja um membro do conjunto input e para todos os alvos y em predicate, x seja visível para y. Exemplo:
visible(//foo, //bar:*)
vai selecionar todos os destinos no pacote //bar
de que //foo
pode depender sem violar as restrições de visibilidade.
Avaliação dos atributos de regra do tipo rótulo: rótulos
expr ::= labels(word, expr)
O operador labels(attr_name, inputs)
retorna o conjunto de destinos especificados no
atributo attr_name do tipo "rótulo" ou "lista de rótulos" em
alguma regra no conjunto inputs.
Por exemplo, labels(srcs, //foo)
retorna o conjunto de
alvos que aparecem no atributo srcs
da
regra //foo
. Se houver várias regras
com atributos srcs
no conjunto inputs, a
união dos srcs
será retornada.
Expandir e filtrar test_suites: tests
expr ::= tests(expr)
O operador tests(x)
retorna o conjunto de todas as regras
de teste no conjunto x, expandindo todas as regras test_suite
para
o conjunto de testes individuais a que elas se referem e aplicando a filtragem por
tag
e size
.
Por padrão, a avaliação de consulta
ignora todos os destinos que não são de teste em todas as regras test_suite
. Isso pode ser
alterado para erros com a opção --strict_test_suite
.
Por exemplo, a consulta kind(test, foo:*)
lista todas
as regras *_test
e test_suite
no pacote foo
. Todos os resultados são (por
definição) membros do pacote foo
. Por outro lado,
a consulta tests(foo:*)
vai retornar todos os
testes individuais que seriam executados por bazel test
foo:*
: isso pode incluir testes pertencentes a outros pacotes,
que são referenciados diretamente ou indiretamente
por regras test_suite
.
Arquivos de definição de pacotes: buildfiles
expr ::= buildfiles(expr)
O operador buildfiles(x)
retorna o conjunto
de arquivos que definem os pacotes de cada destino no
conjunto x. Em outras palavras, para cada pacote, o arquivo BUILD
,
mais todos os arquivos .bzl que ele referencia por load
. Isso
também retorna os arquivos BUILD
dos pacotes que contêm esses
arquivos load
.
Esse operador geralmente é usado para determinar quais arquivos ou
pacotes são necessários para criar um destino especificado, geralmente em conjunto com
a opção --output package
abaixo. Por exemplo,
bazel query 'buildfiles(deps(//foo))' --output package
Retorna o conjunto de todos os pacotes de que //foo
depende transitivamente.
.bzl
produzido por
buildfiles
tem um destino correspondente (por exemplo, arquivo a/b.bzl
=>
destino //a:b.bzl
), mas isso não é necessariamente o caso.
Arquivos de definição de pacotes: rbuildfiles
expr ::= rbuildfiles(word, ...)
O operador rbuildfiles
recebe uma lista separada por vírgulas de fragmentos de caminho e retorna
o conjunto de arquivos BUILD
que dependem transitivamente desses fragmentos de caminho. Por exemplo, se
//foo
for um pacote, rbuildfiles(foo/BUILD)
vai retornar o
alvo //foo:BUILD
. Se o arquivo foo/BUILD
tiver
load('//bar:file.bzl'...
, o rbuildfiles(bar/file.bzl)
vai
retornar o destino //foo:BUILD
, bem como os destinos de todos os outros arquivos BUILD
que
carregarem //bar:file.bzl
.
O escopo do operador --universe_scope
. Os arquivos que não correspondem diretamente aos arquivos BUILD
e .bzl
não afetam os resultados. Por exemplo, os arquivos de origem (como foo.cc
) são ignorados,
mesmo que sejam mencionados explicitamente no arquivo BUILD
. No entanto, os links simbólicos são respeitados. Portanto,
se foo/BUILD
for um link simbólico para bar/BUILD
,
rbuildfiles(bar/BUILD)
vai incluir //foo:BUILD
nos resultados.
O operador rbuildfiles
é quase o inverso do operador
buildfiles
. No entanto, essa inversão moral
é mais forte em uma direção: as saídas de rbuildfiles
são iguais às
entradas de buildfiles
. A primeira vai conter apenas destinos de arquivo BUILD
em pacotes,
e a segunda pode conter esses destinos. Na outra direção, a correspondência é mais fraca. As
saídas do operador buildfiles
são destinos correspondentes a todos os pacotes e .Arquivos bzl
necessários para uma determinada entrada. No entanto, as entradas do operador rbuildfiles
não são
essas metas, mas sim os fragmentos de caminho que correspondem a elas.
Arquivos de definição de pacotes: loadfiles
expr ::= loadfiles(expr)
O operador loadfiles(x)
retorna o conjunto de
arquivos Starlark necessários para carregar os pacotes de cada destino no
conjunto x. Em outras palavras, para cada pacote, ele retorna os
arquivos .bzl que são referenciados pelos arquivos BUILD
.
Formatos de saída
bazel query
gera um gráfico.
Você especifica o conteúdo, o formato e a ordem em que
bazel query
apresenta esse gráfico
usando a opção de linha de comando --output
.
Quando executado com a Sky Query, apenas formatos de saída compatíveis com saída não ordenada são permitidos. Especificamente, os formatos de saída graph
, minrank
e
maxrank
são proibidos.
Alguns formatos de saída aceitam outras opções. O nome de
cada opção de saída tem o prefixo do formato de saída a que
se aplica. Portanto, --graph:factored
só é aplicado
quando --output=graph
está sendo usado. Ele não tem efeito se
um formato de saída diferente de graph
for usado. Da mesma forma,
--xml:line_numbers
é aplicado apenas quando --output=xml
está sendo usado.
Sobre a ordenação dos resultados
Embora as expressões de consulta sempre sigam a lei de
conservação da ordem do gráfico, a apresentação dos resultados pode ser feita
de maneira ordenada por dependência ou não ordenada. Isso não
influencia os destinos no conjunto de resultados nem como a consulta é calculada. Isso só
afeta a forma como os resultados são impressos no stdout. Além disso, os nós que são
equivalentes na ordem de dependência podem ou não ser ordenados alfabeticamente.
A flag --order_output
pode ser usada para controlar esse comportamento.
A flag --[no]order_results
tem um subconjunto da funcionalidade
da flag --order_output
e foi descontinuada.
O valor padrão dessa flag é auto
, que imprime resultados em ordem
lexicográfica. No entanto, quando somepath(a,b)
é usado, os resultados são impressos na
ordem deps
.
Quando essa flag é no
e --output
é um dos valores
build
, label
, label_kind
, location
, package
, proto
ou
xml
, as saídas serão impressas em ordem arbitrária. Essa é
geralmente a opção mais rápida. No entanto, não há suporte quando
--output
é graph
, minrank
ou
maxrank
: com esses formatos, o Bazel sempre imprime resultados
ordenados pela ordem ou classificação de dependência.
Quando essa flag é deps
, o Bazel imprime os resultados em alguma ordem topológica, ou seja,
as dependências primeiro. No entanto, os nós que não são ordenados pela ordem de dependência
(porque não há um caminho de um para o outro) podem ser impressos em qualquer ordem.
Quando essa flag é full
, o Bazel imprime nós em uma ordem totalmente determinística (total).
Primeiro, todos os nós são classificados em ordem alfabética. Em seguida, cada nó na lista é usado como o início de uma
pesquisa em profundidade pós-ordem em que as arestas de saída para nós não visitados são percorridas em
ordem alfabética dos nós sucessores. Por fim, os nós são impressos na ordem inversa
em que foram visitados.
A impressão de nós nessa ordem pode ser mais lenta. Portanto, ela só deve ser usada quando o determinismo for importante.
Imprime a forma de origem das metas conforme elas aparecem no BUILD
--output build
Com essa opção, a representação de cada destino é como se fosse
escrita à mão na linguagem BUILD. Todas as variáveis e chamadas de função
(como glob, macros) são expandidas, o que é útil para conferir o efeito
das macros do Starlark. Além disso, cada regra efetiva informa um valor de generator_name
e/ou generator_function
, indicando o nome da macro que foi avaliada para produzir a regra efetiva.
Embora a saída use a mesma sintaxe dos arquivos BUILD
, não é
garantido que ela produza um arquivo BUILD
válido.
Imprimir o rótulo de cada destino
--output label
Com essa opção, o conjunto de nomes (ou rótulos) de cada destino
no gráfico resultante é impresso, um rótulo por linha, em
ordem topológica (a menos que --noorder_results
seja especificado. Consulte
notas sobre a ordenação dos resultados).
Uma ordenação topológica é aquela em que um nó de gráfico
aparece antes de todos os sucessores. É claro que
há muitas ordenações topológicas possíveis de um gráfico (a postordem
inversa é apenas uma). A escolha não é especificada.
Ao imprimir a saída de uma consulta somepath
, a ordem
em que os nós são impressos é a ordem do caminho.
Observação: em alguns casos extremos, pode haver dois destinos distintos com
o mesmo rótulo. Por exemplo, uma regra sh_binary
e o
único arquivo srcs
(implícito) podem ser chamados de
foo.sh
. Se o resultado de uma consulta contiver as duas
metas, a saída (no formato label
) vai mostrar
uma cópia. Ao usar o formato label_kind
(veja abaixo), a distinção fica clara: as duas metas têm o mesmo nome, mas uma tem o tipo sh_binary rule
e a outra source file
.
Imprimir o rótulo e o tipo de cada alvo
--output label_kind
Como label
, esse formato de saída imprime os rótulos de
cada destino no gráfico resultante, na ordem topológica, mas
também precede o rótulo pelo kind do destino.
Imprimir destinos no formato de buffer de protocolo
--output proto
Imprime a saída da consulta como um
buffer de protocolo
QueryResult
.
Imprimir alvos no formato de buffer de protocolo delimitado por comprimento
--output streamed_proto
Imprime um fluxo
delimitado por comprimento
de buffers de protocolo
Target
. Isso é útil para (i) contornar
limitações de tamanho
de buffers de protocolo quando há muitos destinos para caber em um único
QueryResult
ou (ii) iniciar o processamento enquanto o Bazel ainda está gerando saída.
Imprimir destinos no formato de proto de texto
--output textproto
Semelhante a --output proto
, imprime o
buffer de protocolo QueryResult
,
mas no
formato de texto.
Imprimir destinos no formato ndjson
--output streamed_jsonproto
Semelhante a --output streamed_proto
, imprime um fluxo de
buffers de protocolo Target
,
mas no formato ndjson.
Imprimir o rótulo de cada alvo, em ordem de classificação
--output minrank --output maxrank
Assim como label
, os formatos de saída minrank
e maxrank
imprimem os rótulos de cada
alvo no gráfico resultante, mas, em vez de aparecer em
ordem topológica, eles aparecem em ordem de classificação, precedidos pelo
número de classificação. Elas não são afetadas pela flag --[no]order_results
de ordenação de resultados (consulte as notas sobre
a ordenação de resultados).
Há duas variantes desse formato: minrank
classifica
cada nó pelo comprimento do caminho mais curto de um nó raiz até ele.
Os nós "raiz" (aqueles que não têm arestas de entrada) têm classificação 0,
os sucessores têm classificação 1 etc. Como sempre, as arestas apontam de um
alvo para os pré-requisitos: os alvos de que ele depende.
O maxrank
classifica cada nó pela extensão do caminho mais longo
de um nó raiz até ele. Novamente, "roots" tem classificação 0, todos os outros
nós têm uma classificação que é um maior do que a classificação máxima de todos
os predecessores.
Todos os nós em um ciclo são considerados de mesma classificação. A maioria dos gráficos é
não cíclica, mas os ciclos ocorrem
simplesmente porque os arquivos BUILD
contêm ciclos incorretos.
Esses formatos de saída são úteis para descobrir a profundidade de um gráfico.
Se usado para o resultado de uma consulta deps(x)
, rdeps(x)
ou allpaths
, o número de classificação é igual ao
comprimento do caminho mais curto (com minrank
) ou mais longo
(com maxrank
) de x
para um nó nessa classificação. O maxrank
pode ser usado para determinar a
sequência mais longa de etapas de build necessárias para criar um destino.
Por exemplo, o gráfico à esquerda gera as saídas à direita
quando --output minrank
e --output maxrank
são especificados, respectivamente.
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 |
Imprimir o local de cada destino
--output location
Como label_kind
, essa opção imprime, para cada
destino no resultado, o tipo e o rótulo do destino, mas tem
um prefixo com uma string que descreve o local desse destino, como um
nome de arquivo e um número de linha. O formato é semelhante à saída de
grep
. Assim, ferramentas que podem analisar o último (como Emacs
ou vi) também podem usar a saída da consulta para passar por uma série de
correspondências, permitindo que a ferramenta de consulta do Bazel seja usada como um "grep para arquivos BUILD" compatível com o gráfico de dependências.
As informações de local variam de acordo com o tipo de destino (consulte o operador kind). Para regras, o
local da declaração da regra no arquivo BUILD
é impresso.
Para arquivos de origem, o local da linha 1 do arquivo real é
impresso. Para um arquivo gerado, o local da regra que
o gera é impresso. A ferramenta de consulta não tem informações
suficientes para encontrar o local real do arquivo gerado e, em qualquer caso, pode não existir se um build ainda não tiver sido realizado.
Imprimir o conjunto de pacotes
--output package
Essa opção mostra o nome de todos os pacotes a que alguns destinos no conjunto de resultados pertencem. Os nomes são impressos em ordem alfabética e as duplicatas são excluídas. Formalmente, essa é uma projeção do conjunto de rótulos (pacote, destino) para pacotes.
Os pacotes em repositórios externos são formatados como
@repo//foo/bar
, enquanto os pacotes no repositório principal são
formatados como foo/bar
.
Em conjunto com a consulta deps(...)
, essa opção de saída
pode ser usada para encontrar o conjunto de pacotes que precisam ser verificados
para criar um determinado conjunto de destinos.
Mostrar um gráfico do resultado
--output graph
Essa opção faz com que o resultado da consulta seja impresso como um gráfico
direcionado no formato GraphViz da AT&T. Normalmente, o
resultado é salvo em um arquivo, como .png
ou .svg
.
Se o programa dot
não estiver instalado na sua estação de trabalho, instale-o
usando o comando sudo apt-get install graphviz
.
Consulte a seção de exemplo abaixo para conferir um exemplo de invocação.
Esse formato de saída é particularmente útil para consultas allpaths
,
deps
ou rdeps
, em que o resultado
inclui um conjunto de caminhos que não podem ser visualizados facilmente quando
renderizados de forma linear, como com --output label
.
Por padrão, o gráfico é renderizado em um formato decomposto. Ou seja,
nós topologicamente equivalentes são mesclados em um único
nó com vários rótulos. Isso torna o gráfico mais compacto
e legível, porque os gráficos de resultados típicos contêm padrões altamente
repetitivos. Por exemplo, uma regra java_library
pode depender de centenas de arquivos de origem Java, todos gerados pelo
mesmo genrule
. No gráfico fatorado, todos esses arquivos
são representados por um único nó. Esse comportamento pode ser desativado
com a opção --nograph:factored
.
--graph:node_limit n
A opção especifica o comprimento máximo da string de rótulo para um
nó de gráfico na saída. Rótulos mais longos serão truncados. O valor -1
desativa o truncamento. Devido à forma fatorada em que os gráficos são
normalmente impressos, os rótulos de nó podem ser muito longos. O GraphViz não pode
processar rótulos com mais de 1.024 caracteres, que é o valor padrão
dessa opção. Essa opção não tem efeito a menos que
--output=graph
esteja sendo usado.
--[no]graph:factored
Por padrão, os gráficos são mostrados na forma fatorada, conforme explicado
acima.
Quando --nograph:factored
é especificado, os gráficos são
impressos sem fatoração. Isso torna a visualização usando o GraphViz
impraticável, mas o formato mais simples pode facilitar o processamento por outras
ferramentas (como grep). Essa opção não tem efeito
a menos que --output=graph
esteja sendo usado.
XML
--output xml
Essa opção faz com que os destinos resultantes sejam impressos em um formato XML. A saída começa com um cabeçalho XML, como este:
<?xml version="1.0" encoding="UTF-8"?>
<query version="2">
e continua com um elemento XML para cada destino no gráfico de resultados, na ordem topológica (a menos que resultados não ordenados sejam solicitados), e termina com um elemento de término
</query>
As entradas simples são emitidas para destinos do tipo file
:
<source-file name='//foo:foo_main.cc' .../>
<generated-file name='//foo:libfoo.so' .../>
No entanto, para regras, o XML é estruturado e contém definições de todos
os atributos da regra, incluindo aqueles cujo valor não foi
especificado explicitamente no arquivo BUILD
da regra.
Além disso, o resultado inclui elementos rule-input
e
rule-output
para que a topologia do
gráfico de dependência possa ser reconstruída sem precisar saber que,
por exemplo, os elementos do atributo srcs
são
dependências futuras (pré-requisitos) e o conteúdo do
atributo outs
são dependências passadas (consumidores).
Os elementos rule-input
para dependências implícitas são suprimidos se
--noimplicit_deps
for especificado.
<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 um destino contém um atributo name
,
cujo valor é o rótulo do destino, e
um atributo location
, cujo valor é o local
do destino, conforme impresso pelo --output location
.
--[no]xml:line_numbers
Por padrão, os locais mostrados na saída XML contêm números de linha.
Quando --noxml:line_numbers
é especificado, os números de linha não são impressos.
--[no]xml:default_values
Por padrão, a saída XML não inclui o atributo de regra cujo valor
é o padrão para esse tipo de atributo (por exemplo, se ele
não foi especificado no arquivo BUILD
ou se o valor padrão foi
fornecido explicitamente). Essa opção faz com que esses valores de atributo sejam
incluídos na saída XML.
Expressões regulares
As expressões regulares na linguagem de consulta usam a biblioteca de regex Java. Assim, você pode usar a
sintaxe completa de
java.util.regex.Pattern
.
Consultar com repositórios externos
Se o build depender de regras de repositórios externos (definidos no
arquivo WORKSPACE), os resultados da consulta vão incluir essas dependências. Por
exemplo, se //foo:bar
depender de //external:some-lib
e //external:some-lib
estiver vinculado a @other-repo//baz:lib
, bazel query 'deps(//foo:bar)'
vai listar @other-repo//baz:lib
e
//external:some-lib
como dependências.
Os repositórios externos não são dependências de um build. Ou seja, no
exemplo acima, //external:other-repo
não é uma dependência. No entanto, ele pode ser consultado como membro do pacote //external
, por exemplo:
# Querying over all members of //external returns the repository.
bazel query 'kind(http_archive, //external:*)'
//external:other-repo
# ...but the repository is not a dependency.
bazel query 'kind(http_archive, deps(//foo:bar))'
INFO: Empty results