Guia de início rápido de consultas

Informar um problema Ver fonte Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

Neste tutorial, explicamos como trabalhar com o Bazel para rastrear dependências no seu código usando um projeto do Bazel pré-criado.

Para mais detalhes sobre a linguagem e a flag --output, consulte os manuais Referência de consulta do Bazel e Referência de cquery do Bazel. Para receber ajuda no ambiente de desenvolvimento integrado, digite bazel help query ou bazel help cquery na linha de comando.

Objetivo

Este guia mostra um conjunto de consultas básicas que você pode usar para saber mais sobre as dependências de arquivos do seu projeto. Ele é destinado a novos desenvolvedores do Bazel com um conhecimento básico de como o Bazel e os arquivos BUILD funcionam.

Pré-requisitos

Comece instalando o Bazel, se ainda não tiver feito isso. Este tutorial usa o Git para controle de origem. Para ter os melhores resultados, instale também o Git.

Para visualizar gráficos de dependência, use a ferramenta Graphviz, que pode ser baixada para acompanhar.

Acessar o projeto de exemplo

Em seguida, recupere o app de exemplo do repositório de exemplos do Bazel executando o seguinte na ferramenta de linha de comando de sua escolha:

git clone https://github.com/bazelbuild/examples.git

O projeto de amostra deste tutorial está no diretório examples/query-quickstart.

Primeiros passos

O que são consultas do Bazel?

As consultas ajudam você a conhecer um código-fonte do Bazel analisando as relações entre arquivos BUILD e examinando a saída resultante para informações úteis. Este guia mostra algumas funções básicas de consulta, mas para mais opções, consulte o guia de consultas. As consultas ajudam você a aprender sobre dependências em projetos de grande escala sem navegar manualmente pelos arquivos BUILD.

Para executar uma consulta, abra o terminal de linha de comando e insira:

bazel query 'query_function'

Cenário

Imagine um cenário que se aprofunde na relação entre o Café Bazel e o chef de cozinha. Este café vende exclusivamente pizza e macarrão com queijo. Confira abaixo como o projeto está estruturado:

bazelqueryguide
├── BUILD
├── src
│   └── main
│       └── java
│           └── com
│               └── example
│                   ├── customers
│                   │   ├── Jenny.java
│                   │   ├── Amir.java
│                   │   └── BUILD
│                   ├── dishes
│                   │   ├── Pizza.java
│                   │   ├── MacAndCheese.java
│                   │   └── BUILD
│                   ├── ingredients
│                   │   ├── Cheese.java
│                   │   ├── Tomatoes.java
│                   │   ├── Dough.java
│                   │   ├── Macaroni.java
│                   │   └── BUILD
│                   ├── restaurant
│                   │   ├── Cafe.java
│                   │   ├── Chef.java
│                   │   └── BUILD
│                   ├── reviews
│                   │   ├── Review.java
│                   │   └── BUILD
│                   └── Runner.java
└── MODULE.bazel

Ao longo deste tutorial, a menos que seja indicado de outra forma, tente não consultar os arquivos BUILD para encontrar as informações necessárias. Em vez disso, use apenas a função de consulta.

Um projeto consiste em diferentes pacotes que compõem um café. Elas são separadas em: restaurant, ingredients, dishes, customers e reviews. As regras nesses pacotes definem diferentes componentes do Cafe com várias tags e dependências.

Executar um build

Esse projeto contém um método principal em Runner.java que pode ser executado para imprimir um menu do café. Crie o projeto usando o Bazel com o comando bazel build e use : para indicar que o destino se chama runner. Consulte nomes de destino para saber como fazer referência a destinos.

Para criar esse projeto, cole este comando em um terminal:

bazel build :runner

A saída será semelhante a esta se o build for bem-sucedido.

INFO: Analyzed target //:runner (49 packages loaded, 784 targets configured).
INFO: Found 1 target...
Target //:runner up-to-date:
  bazel-bin/runner.jar
  bazel-bin/runner
INFO: Elapsed time: 16.593s, Critical Path: 4.32s
INFO: 23 processes: 4 internal, 10 darwin-sandbox, 9 worker.
INFO: Build completed successfully, 23 total actions

Depois que ele for criado, execute o aplicativo colando este comando:

bazel-bin/runner
--------------------- MENU -------------------------

Pizza - Cheesy Delicious Goodness
Macaroni & Cheese - Kid-approved Dinner

----------------------------------------------------

Isso deixa você com uma lista dos itens de menu e uma breve descrição.

Análise detalhada de destinos

O projeto lista ingredientes e pratos em pacotes próprios. Para usar uma consulta e ver as regras de um pacote, execute o comando bazel query package/…

Nesse caso, você pode usar esse comando para conferir os ingredientes e pratos do café:

bazel query //src/main/java/com/example/dishes/...
bazel query //src/main/java/com/example/ingredients/...

Se você consultar os destinos do pacote de ingredientes, a saída será assim:

//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato

Como encontrar dependências

Quais metas seu corredor usa para correr?

Digamos que você queira se aprofundar na estrutura do seu projeto sem mexer no sistema de arquivos, o que pode ser insustentável para projetos grandes. Quais regras o Café Bazel usa?

Se, como neste exemplo, o destino do seu runner for runner, descubra as dependências dele executando o comando:

bazel query --noimplicit_deps "deps(target)"
bazel query --noimplicit_deps "deps(:runner)"
//:runner
//:src/main/java/com/example/Runner.java
//src/main/java/com/example/dishes:MacAndCheese.java
//src/main/java/com/example/dishes:Pizza.java
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:Cheese.java
//src/main/java/com/example/ingredients:Dough.java
//src/main/java/com/example/ingredients:Macaroni.java
//src/main/java/com/example/ingredients:Tomato.java
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato
//src/main/java/com/example/restaurant:Cafe.java
//src/main/java/com/example/restaurant:Chef.java
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

Na maioria dos casos, use a função de consulta deps() para conferir as dependências de saída individuais de uma meta específica.

Visualizar o gráfico de dependência (opcional)

Nela, descrevemos como visualizar os caminhos de dependência de uma consulta específica. O Graphviz ajuda a ver o caminho como uma imagem de gráfico acíclico dirigido, em vez de uma lista simples. É possível alterar a exibição do gráfico de consulta do Bazel usando várias opções de linha de comando --output. Consulte Formatos de saída para ver as opções.

Comece executando a consulta desejada e adicione a flag --noimplicit_deps para remover dependências excessivas de ferramentas. Em seguida, adicione a flag de saída à consulta e armazene o gráfico em um arquivo chamado graph.in para criar uma representação de texto do gráfico.

Para pesquisar todas as dependências do :runner de destino e formatar a saída como um gráfico:

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph.in

Isso cria um arquivo chamado graph.in, que é uma representação de texto do gráfico de build. O Graphviz usa o dot , uma ferramenta que processa texto em uma visualização, para criar um PNG:

dot -Tpng < graph.in > graph.png

Se você abrir graph.png, vai aparecer algo assim. O gráfico abaixo foi simplificado para deixar mais claros os detalhes essenciais do caminho neste guia.

Diagrama mostrando uma relação da cafeteria ao chef e aos pratos: pizza e macarrão com queijo, que se dividem em ingredientes separados: queijo, tomates, massa e macarrão.

Isso é útil quando você quer ver as saídas das diferentes funções de consulta ao longo deste guia.

Como encontrar dependências inversas

Se você tiver um destino e quiser analisar quais outros destinos o usam, faça uma consulta para examinar quais destinos dependem de uma determinada regra. Isso é chamado de "dependência inversa". Usar o rdeps() pode ser útil ao editar um arquivo em uma base de código que você não conhece e pode evitar que você quebre outros arquivos que dependiam dele sem saber.

Por exemplo, você quer fazer algumas edições no ingrediente cheese. Para evitar problemas no Cafe Bazel, verifique quais pratos dependem de cheese.

Para saber quais destinos dependem de um destino/pacote específico, use rdeps(universe_scope, target). A função de consulta rdeps() usa pelo menos dois argumentos: um universe_scope (o diretório relevante) e um target. O Bazel procura as dependências inversas do destino no universe_scope fornecido. O operador rdeps() aceita um terceiro argumento opcional: um literal inteiro que especifica o limite superior da profundidade da pesquisa.

.

Para procurar dependências inversas do cheese de destino no escopo de todo o projeto "//…", execute o comando:

bazel query "rdeps(universe_scope, target)"
ex) bazel query "rdeps(//... , //src/main/java/com/example/ingredients:cheese)"
//:runner
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

O retorno da consulta mostra que o queijo é usado tanto na pizza quanto no macAndCheese. Que surpresa!

Como encontrar metas com base em tags

Dois clientes entram no Bazel Cafe: Amir e Jenny. Não se sabe nada sobre eles, exceto os nomes. Felizmente, os pedidos deles estão marcados no arquivo BUILD "clientes". Como acessar essa tag?

Os desenvolvedores podem adicionar tags a destinos do Bazel com identificadores diferentes, geralmente para fins de teste. Por exemplo, as tags em testes podem anotar a função de um teste no processo de depuração e lançamento, especialmente para testes em C++ e Python, que não têm capacidade de anotação de tempo de execução. O uso de tags e elementos de tamanho oferece flexibilidade na montagem de conjuntos de testes com base na política de check-in de uma base de código.

Neste exemplo, as tags são pizza ou macAndCheese para representar os itens do menu. Esse comando consulta destinos que têm tags correspondentes ao seu identificador em um determinado pacote.

bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'

Essa consulta retorna todas as metas no pacote "customers" que têm a tag "pizza".

Faça um teste

Use esta consulta para saber o que Jenny quer pedir.

Resposta

Macarrão com queijo

Como adicionar uma dependência

O Café Bazel ampliou o cardápio. Agora os clientes podem pedir um smoothie! Esse smoothie específico é composto pelos ingredientes Strawberry e Banana.

Primeiro, adicione os ingredientes de que o smoothie depende: Strawberry.java e Banana.java. Adicione as classes Java vazias.

src/main/java/com/example/ingredients/Strawberry.java

package com.example.ingredients;

public class Strawberry {

}

src/main/java/com/example/ingredients/Banana.java

package com.example.ingredients;

public class Banana {

}

Em seguida, adicione Smoothie.java ao diretório apropriado: dishes.

src/main/java/com/example/dishes/Smoothie.java

package com.example.dishes;

public class Smoothie {
    public static final String DISH_NAME = "Smoothie";
    public static final String DESCRIPTION = "Yummy and Refreshing";
}

Por fim, adicione esses arquivos como regras nos arquivos BUILD apropriados. Crie uma nova biblioteca Java para cada novo ingrediente, incluindo nome, visibilidade pública e o arquivo "src" recém-criado. O arquivo BUILD atualizado vai ficar assim:

src/main/java/com/example/ingredients/BUILD

java_library(
    name = "cheese",
    visibility = ["//visibility:public"],
    srcs = ["Cheese.java"],
)

java_library(
    name = "dough",
    visibility = ["//visibility:public"],
    srcs = ["Dough.java"],
)

java_library(
    name = "macaroni",
    visibility = ["//visibility:public"],
    srcs = ["Macaroni.java"],
)

java_library(
    name = "tomato",
    visibility = ["//visibility:public"],
    srcs = ["Tomato.java"],
)

java_library(
    name = "strawberry",
    visibility = ["//visibility:public"],
    srcs = ["Strawberry.java"],
)

java_library(
    name = "banana",
    visibility = ["//visibility:public"],
    srcs = ["Banana.java"],
)

No arquivo BUILD para pratos, adicione uma nova regra para Smoothie. Isso inclui o arquivo Java criado para Smoothie como um arquivo "src" e as novas regras criadas para cada ingrediente do smoothie.

src/main/java/com/example/dishes/BUILD

java_library(
    name = "macAndCheese",
    visibility = ["//visibility:public"],
    srcs = ["MacAndCheese.java"],
    deps = [
        "//src/main/java/com/example/ingredients:cheese",
        "//src/main/java/com/example/ingredients:macaroni",
    ],
)

java_library(
    name = "pizza",
    visibility = ["//visibility:public"],
    srcs = ["Pizza.java"],
    deps = [
        "//src/main/java/com/example/ingredients:cheese",
        "//src/main/java/com/example/ingredients:dough",
        "//src/main/java/com/example/ingredients:tomato",
    ],
)

java_library(
    name = "smoothie",
    visibility = ["//visibility:public"],
    srcs = ["Smoothie.java"],
    deps = [
        "//src/main/java/com/example/ingredients:strawberry",
        "//src/main/java/com/example/ingredients:banana",
    ],
)

Por fim, inclua o smoothie como uma dependência no arquivo BUILD do Chef.

src/main/java/com/example/restaurant/BUILD

java\_library(
    name = "chef",
    visibility = ["//visibility:public"],
    srcs = [
        "Chef.java",
    ],

    deps = [
        "//src/main/java/com/example/dishes:macAndCheese",
        "//src/main/java/com/example/dishes:pizza",
        "//src/main/java/com/example/dishes:smoothie",
    ],
)

java\_library(
    name = "cafe",
    visibility = ["//visibility:public"],
    srcs = [
        "Cafe.java",
    ],
    deps = [
        ":chef",
    ],
)

Crie cafe novamente para confirmar que não há erros. Se a build for bem-sucedida, parabéns! Você adicionou uma nova dependência para o "Café". Se não, procure erros de ortografia e nomes de pacotes. Para mais informações sobre como escrever arquivos BUILD, consulte o guia de estilo BUILD.

Agora, visualize o novo gráfico de dependência com a adição do Smoothie para comparar com o anterior. Para maior clareza, nomeie a entrada do gráfico como graph2.in e graph2.png.

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph2.in
dot -Tpng < graph2.in > graph2.png

O mesmo gráfico do primeiro, mas agora há um spoke que vem do destino do chef com smoothie e leva a banana e morango

Analisando graph2.png, você pode ver que Smoothie não tem dependências compartilhadas com outros pratos, mas é apenas outro destino em que Chef se baseia.

somepath() e allpaths()

E se você quiser consultar por que um pacote depende de outro? Mostrar um caminho de dependência entre os dois fornece a resposta.

Duas funções podem ajudar você a encontrar caminhos de dependência: somepath() e allpaths(). Dado um destino inicial S e um ponto final E, encontre um caminho entre S e E usando somepath(S,E).

Conheça as diferenças entre essas duas funções analisando as relações entre os destinos "Chef" e "Cheese". Há diferentes caminhos possíveis para ir de uma meta a outra:

  • Chef → MacAndCheese → Cheese
  • Chef → Pizza → Queijo

somepath() oferece um único caminho entre as duas opções, enquanto "allpaths()" gera todos os caminhos possíveis.

Usando o Cafe Bazel como exemplo, execute o seguinte:

bazel query "somepath(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/ingredients:cheese

A saída segue o primeiro caminho: Cafe → Chef → MacAndCheese → Cheese. Se você usar allpaths(), vai receber:

bazel query "allpaths(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef

Caminho de saída de &quot;café para chef para pizza&quot;, &quot;macarrão com queijo para queijo&quot;

A saída de allpaths() é um pouco mais difícil de ler, já que é uma lista simplificada das dependências. Visualizar esse gráfico usando o Graphviz facilita a compreensão da relação.

Faça um teste

Um dos clientes do Café Bazel fez a primeira avaliação do restaurante. Infelizmente, a avaliação não tem alguns detalhes, como a identidade do avaliador e o prato a que ela se refere. Felizmente, é possível acessar essas informações com o Bazel. O pacote reviews contém um programa que imprime uma avaliação de um cliente misterioso. Crie e execute com:

bazel build //src/main/java/com/example/reviews:review
bazel-bin/src/main/java/com/example/reviews/review

Usando apenas consultas do Bazel, tente descobrir quem escreveu a avaliação e qual prato foi descrito.

Dica

Confira as tags e dependências para informações úteis.

Resposta

A avaliação descrevia a Pizza e Amir era o avaliador. Se você analisar as dependências dessa regra usando bazel query --noimplicit\_deps 'deps(//src/main/java/com/example/reviews:review)' O resultado desse comando revela que Amir é o revisor. Em seguida, como você sabe que o revisor é Amir, use a função de consulta para procurar qual tag Amir tem no arquivo "BUILD" e ver qual prato está lá. O comando bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)' mostra que Amir é o único cliente que pediu uma pizza e é o avaliador, o que nos dá a resposta.

Conclusão

Parabéns! Agora você executou várias consultas básicas, que pode testar nos seus próprios projetos. Para saber mais sobre a sintaxe da linguagem de consulta, consulte a página de referência de consulta. Quer consultas mais avançadas? O guia de consultas mostra uma lista detalhada de mais casos de uso do que os abordados neste guia.