Criar programas com o Bazel

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

Nesta página, abordamos como criar um programa com o Bazel, a sintaxe do comando de build e a sintaxe do padrão de destino.

Guia de início rápido

Para executar o Bazel, acesse o diretório base do espaço de trabalho ou qualquer um dos subdiretórios e digite bazel. Consulte build se você precisar criar um novo espaço de trabalho.

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

Comandos disponíveis

  • analyze-profile: analisa dados de perfil de build.
  • aquery: executa uma consulta no gráfico de ações de análise pós-evento.
  • build: cria os destinos especificados.
  • canonicalize-flags: canonicaliza flags do Bazel.
  • clean: remove arquivos de saída e, opcionalmente, interrompe o servidor.
  • cquery: executa uma consulta de gráfico de dependências de pós-análise.
  • dump: despeja o estado interno do processo do servidor Bazel.
  • help: mostra ajuda para comandos ou o índice.
  • info: mostra informações de tempo de execução sobre o servidor do Bazel.
  • fetch: busca todas as dependências externas de um destino.
  • mobile-install: instala apps em dispositivos móveis.
  • query: executa uma consulta de gráfico de dependência.
  • run: executa o destino especificado.
  • shutdown: interrompe o servidor do Bazel.
  • test: cria e executa os destinos de teste especificados.
  • version: imprime informações da versão do Bazel.

Receber ajuda

  • bazel help command: mostra ajuda e opções para command.
  • bazel helpstartup_options: opções para a JVM que hospeda o Bazel.
  • bazel helptarget-syntax: explica a sintaxe para especificar metas.
  • bazel help info-keys: mostra uma lista de chaves usadas pelo comando "info".

A ferramenta bazel executa muitas funções, chamadas de comandos. Os mais usados são bazel build e bazel test. Você pode navegar pelas mensagens de ajuda on-line usando bazel help.

Criar um destino

Antes de iniciar um build, você precisa de um espaço de trabalho. Um espaço de trabalho é uma árvore de diretórios que contém todos os arquivos de origem necessários para criar seu aplicativo. O Bazel permite realizar uma build de um volume completamente somente leitura.

Para criar um programa com o Bazel, digite bazel build seguido do destino que você quer criar.

bazel build //foo

Depois de emitir o comando para criar //foo, você verá uma saída semelhante a esta:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

Primeiro, o Bazel carrega todos os pacotes no gráfico de dependências do seu destino. Isso inclui dependências declaradas, arquivos listados diretamente no arquivo BUILD do destino e dependências transitivas, arquivos listados nos arquivos BUILD das dependências do destino. Depois de identificar todas as dependências, o Bazel as analisa para verificar se estão corretas e cria as ações de build. Por fim, o Bazel executa os compiladores e outras ferramentas do build.

Durante a fase de execução do build, o Bazel imprime mensagens de progresso. As mensagens de progresso incluem a etapa de build atual (como compilador ou vinculador) quando ela começa, e o número concluído sobre o número total de ações de build. À medida que o build começa, o número total de ações geralmente aumenta à medida que o Bazel descobre todo o gráfico de ações, mas o número se estabiliza em alguns segundos.

No final da build, o Bazel mostra quais destinos foram solicitados, se eles foram criados com sucesso e, em caso afirmativo, onde os arquivos de saída podem ser encontrados. Os scripts que executam builds podem analisar essa saída de maneira confiável. Consulte --show_result para mais detalhes.

Se você digitar o mesmo comando novamente, o build será concluído muito mais rápido.

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

Este é um build nulo. Como nada mudou, não há pacotes para recarregar nem etapas de build para executar. Se algo mudasse em "foo" ou nas dependências dele, o Bazel executaria novamente algumas ações de build ou concluiria um build incremental.

Como criar vários destinos

O Bazel permite várias maneiras de especificar os destinos a serem criados. Coletivamente, eles são conhecidos como padrões de destino. Essa sintaxe é usada em comandos como build, test ou query.

Enquanto os rótulos são usados para especificar destinos individuais, como declarar dependências em arquivos BUILD, os padrões de destino do Bazel especificam vários destinos. Os padrões de segmentação são uma generalização da sintaxe de rótulo para conjuntos de segmentações, usando caracteres curinga. No caso mais simples, qualquer rótulo válido também é um padrão de destino válido, identificando um conjunto de exatamente um destino.

Todos os padrões de destino que começam com // são resolvidos em relação ao espaço de trabalho atual.

//foo/bar:wiz Apenas o destino único //foo/bar:wiz.
//foo/bar É equivalente a //foo/bar:bar.
//foo/bar:all Todos os destinos de regra no pacote foo/bar.
//foo/... Todos os destinos de regra em todos os pacotes abaixo do diretório foo.
//foo/...:all Todos os destinos de regra em todos os pacotes abaixo do diretório foo.
//foo/...:* Todos os destinos (regras e arquivos) em todos os pacotes abaixo do diretório foo.
//foo/...:all-targets Todos os destinos (regras e arquivos) em todos os pacotes abaixo do diretório foo.
//... Todas as metas de regra em pacotes no repositório principal. Não inclui destinos de repositórios externos.
//:all Todos os destinos de regra no pacote de nível superior, se houver um arquivo "BUILD" na raiz do espaço de trabalho.

Os padrões de destino que não começam com // são resolvidos em relação ao diretório de trabalho atual. Estes exemplos pressupõem um diretório de trabalho de foo:

:foo É equivalente a //foo:foo.
bar:wiz É equivalente a //foo/bar:wiz.
bar/wiz Equivalente a:
  • //foo/bar/wiz:wiz se foo/bar/wiz for um pacote
  • //foo/bar:wiz se foo/bar for um pacote
  • Caso contrário, //foo:bar/wiz.
bar:all É equivalente a //foo/bar:all.
:all É equivalente a //foo:all.
...:all É equivalente a //foo/...:all.
... É equivalente a //foo/...:all.
bar/...:all É equivalente a //foo/bar/...:all.

Por padrão, os links simbólicos de diretório são seguidos para padrões de destino recursivos, exceto aqueles que apontam para a base de saída, como os links simbólicos de conveniência criados no diretório raiz do espaço de trabalho.

Além disso, o Bazel não segue links simbólicos ao avaliar padrões de destino recursivos em qualquer diretório que contenha um arquivo com o seguinte nome: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/... é um caractere curinga em packages, indicando todos os pacotes de forma recursiva abaixo do diretório foo (para todas as raízes do caminho do pacote). :all é um curinga em destinos, correspondendo a todas as regras em um pacote. Os dois podem ser combinados, como em foo/...:all, e quando ambos os caracteres curinga são usados, isso pode ser abreviado para foo/....

Além disso, :* (ou :all-targets) é um caractere curinga que corresponde a todos os destinos nos pacotes correspondentes, incluindo arquivos que normalmente não são criados por nenhuma regra, como arquivos _deploy.jar associados a regras java_binary.

Isso implica que :* denota um superconjunto de :all. Embora possa ser confusa, essa sintaxe permite que o caractere curinga :all seja usado para builds típicos, em que não é desejável criar destinos como _deploy.jar.

Além disso, o Bazel permite que uma barra seja usada em vez dos dois-pontos exigidos pela sintaxe de rótulo. Isso é conveniente ao usar a expansão de nomes de arquivos do Bash. Por exemplo, foo/bar/wiz é equivalente a //foo/bar:wiz (se houver um pacote foo/bar) ou a //foo:bar/wiz (se houver um pacote foo).

Muitos comandos do Bazel aceitam uma lista de padrões de destino como argumentos, e todos eles respeitam o operador de negação de prefixo -. Isso pode ser usado para subtrair um conjunto de destinos do conjunto especificado pelos argumentos anteriores. Isso significa que a ordem é importante. Por exemplo,

bazel build foo/... bar/...

significa "criar todos os destinos abaixo de foo e todos os destinos abaixo de bar", enquanto

bazel build -- foo/... -foo/bar/...

significa "criar todas as metas abaixo de foo exceto aquelas abaixo de foo/bar". O argumento -- é obrigatório para evitar que os argumentos subsequentes que começam com - sejam interpretados como opções adicionais.

No entanto, é importante ressaltar que subtrair destinos dessa forma não garante que eles não sejam criados, já que podem ser dependências de destinos que não foram subtraídos. Por exemplo, se houver um destino //foo:all-apis que, entre outros, dependa de //foo/bar:api, o último será criado como parte da criação do primeiro.

Os destinos com tags = ["manual"] não são incluídos em padrões de destino curinga (..., :*, :all etc.) quando especificados em comandos como bazel build e bazel test. No entanto, eles são incluídos em padrões de destino curinga negativos, ou seja, são subtraídos. Especifique esses destinos de teste com padrões de destino explícitos na linha de comando se quiser que o Bazel os crie/teste. Por outro lado, bazel query não realiza esse tipo de filtragem automaticamente, o que prejudicaria o objetivo de bazel query.

Como buscar dependências externas

Por padrão, o Bazel faz o download e cria symlinks de dependências externas durante o build. No entanto, isso pode ser indesejável, seja porque você quer saber quando novas dependências externas são adicionadas ou porque você quer "pré-buscar" dependências (por exemplo, antes de um voo em que você ficará off-line). Se você quiser impedir que novas dependências sejam adicionadas durante os builds, especifique a flag --fetch=false. Essa flag só se aplica a regras de repositório que não apontam para um diretório no sistema de arquivos local. Mudanças, por exemplo, em local_repository, new_local_repository e regras de repositório do SDK e NDK do Android sempre vão entrar em vigor, independente do valor --fetch .

Se você não permitir a busca durante os builds e o Bazel encontrar novas dependências externas, o build vai falhar.

É possível buscar dependências manualmente executando bazel fetch. Se você não permitir a busca durante a criação, execute bazel fetch:

  • Antes de criar pela primeira vez.
  • Depois de adicionar uma nova dependência externa.

Depois de executado, não é necessário executá-lo novamente até que o arquivo MODULE.bazel seja alterado.

fetch usa uma lista de destinos para buscar dependências. Por exemplo, isso buscaria as dependências necessárias para criar //foo:bar e //bar:baz:

bazel fetch //foo:bar //bar:baz

Para buscar todas as dependências externas de um espaço de trabalho, execute:

bazel fetch //...

Com o Bazel 7 ou mais recente, se o Bzlmod estiver ativado, você também poderá buscar todas as dependências externas executando

bazel fetch

Não é necessário executar o bazel fetch se você tiver todas as ferramentas que está usando (de jars de biblioteca ao próprio JDK) na raiz do seu espaço de trabalho. No entanto, se você estiver usando algo fora do diretório do espaço de trabalho, o Bazel vai executar automaticamente bazel fetch antes de executar bazel build.

O cache do repositório

O Bazel tenta evitar buscar o mesmo arquivo várias vezes, mesmo que ele seja necessário em diferentes espaços de trabalho ou se a definição de um repositório externo tenha mudado, mas ainda precise do mesmo arquivo para download. Para isso, o bazel armazena em cache todos os arquivos baixados no cache do repositório que, por padrão, está localizado em ~/.cache/bazel/_bazel_$USER/cache/repos/v1/. O local pode ser mudado pela opção --repository_cache. O cache é compartilhado entre todos os espaços de trabalho e versões instaladas do bazel. Uma entrada é extraída do cache se o Bazel tiver certeza de que tem uma cópia do arquivo correto, ou seja, se a solicitação de download tiver uma soma SHA256 do arquivo especificado e um arquivo com esse hash estiver no cache. Portanto, especificar um hash para cada arquivo externo não é apenas uma boa ideia do ponto de vista da segurança, mas também ajuda a evitar downloads desnecessários.

A cada acerto de cache, o horário de modificação do arquivo no cache é atualizado. Dessa forma, é fácil determinar o último uso de um arquivo no diretório de cache, por exemplo, para limpar o cache manualmente. O cache nunca é limpo automaticamente, porque pode conter uma cópia de um arquivo que não está mais disponível no upstream.

[Descontinuado] Diretórios de arquivos de distribuição

Descontinuado: é preferível usar o cache do repositório para fazer builds off-line.

O diretório de distribuição é outro mecanismo do Bazel para evitar downloads desnecessários. O Bazel pesquisa diretórios de distribuição antes do cache do repositório. A principal diferença é que o diretório de distribuição exige preparação manual.

Usando a opção --distdir=/path/to-directory, é possível especificar outros diretórios somente leitura para procurar arquivos em vez de buscá-los. Um arquivo é extraído de um diretório desse tipo se o nome do arquivo for igual ao nome base do URL e, além disso, o hash do arquivo for igual ao especificado na solicitação de download. Isso só funciona se o hash do arquivo for especificado na declaração da regra do repositório.

Embora a condição no nome do arquivo não seja necessária para a correção, ela reduz o número de arquivos candidatos a um por diretório especificado. Dessa forma, especificar diretórios de arquivos de distribuição continua sendo eficiente, mesmo que o número de arquivos em um diretório aumente muito.

Como executar o Bazel em um ambiente isolado

Para manter o tamanho binário do Bazel pequeno, as dependências implícitas dele são buscadas pela rede durante a primeira execução. Essas dependências implícitas contêm cadeias de ferramentas e regras que podem não ser necessárias para todos. Por exemplo, as ferramentas do Android são desvinculadas e buscadas apenas ao criar projetos Android.

No entanto, essas dependências implícitas podem causar problemas ao executar o Bazel em um ambiente isolado, mesmo que você tenha vendido todas as dependências externas. Para resolver isso, prepare um cache de repositório (com o Bazel 7 ou mais recente) ou um diretório de distribuição (com o Bazel anterior à versão 7) que contenha essas dependências em uma máquina com acesso à rede e transfira para o ambiente isolado usando uma abordagem off-line.

Cache do repositório (com o Bazel 7 ou mais recente)

Para preparar o cache do repositório, use a flag --repository_cache. É necessário fazer isso uma vez para cada nova versão binária do Bazel, já que as dependências implícitas podem ser diferentes para cada lançamento.

Para buscar essas dependências fora do ambiente isolado, primeiro crie um espaço de trabalho vazio:

mkdir empty_workspace && cd empty_workspace
touch MODULE.bazel

Para buscar dependências Bzlmod integradas, execute

bazel fetch --repository_cache="path/to/repository/cache"

Se você ainda usa o arquivo WORKSPACE legado, execute

bazel sync --repository_cache="path/to/repository/cache"

Por fim, ao usar o Bazel no ambiente isolado, transmita a mesma flag --repository_cache. Para facilitar, adicione como uma entrada .bazelrc:

common --repository_cache="path/to/repository/cache"

Além disso, talvez seja necessário clonar o BCR localmente e usar a flag --registry para apontar sua cópia local e impedir que o Bazel acesse o BCR pela Internet. Adicione a linha a seguir ao seu .bazelrc:

common --registry="path/to/local/bcr/registry"
Diretório de distribuição (com Bazel antes da versão 7)

Para preparar o diretório de distribuição, use a sinalização --distdir. É necessário fazer isso uma vez para cada nova versão binária do Bazel, já que as dependências implícitas podem ser diferentes para cada lançamento.

Para criar essas dependências fora do ambiente isolado, primeiro faça o check-out da árvore de origem do Bazel na versão certa:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

Em seguida, crie o tarball com as dependências implícitas de tempo de execução para essa versão específica do Bazel:

bazel build @additional_distfiles//:archives.tar

Exporte esse tarball para um diretório que possa ser copiado para o ambiente isolado. Observe a flag --strip-components, porque --distdir pode ser bem exigente com o nível de aninhamento do diretório:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

Por fim, ao usar o Bazel no ambiente isolado, transmita a flag --distdir apontando para o diretório. Para facilitar, adicione como uma entrada .bazelrc:

build --distdir=path/to/directory

Configurações de build e compilação cruzada

Todas as entradas que especificam o comportamento e o resultado de um build podem ser divididas em duas categorias distintas. O primeiro tipo são as informações intrínsecas armazenadas nos arquivos BUILD do seu projeto: a regra de build, os valores dos atributos e o conjunto completo de dependências transitivas. O segundo tipo são os dados externos ou ambientais, fornecidos pelo usuário ou pela ferramenta de build: a escolha da arquitetura de destino, opções de compilação e vinculação e outras opções de configuração da cadeia de ferramentas. Chamamos um conjunto completo de dados ambientais de configuração.

Em qualquer build, pode haver mais de uma configuração. Considere uma compilação cruzada, em que você cria um executável //foo:bin para uma arquitetura de 64 bits, mas sua estação de trabalho é uma máquina de 32 bits. É claro que o build vai exigir a criação de //foo:bin usando uma cadeia de ferramentas capaz de criar executáveis de 64 bits, mas o sistema de build também precisa criar várias ferramentas usadas durante o próprio build, por exemplo, ferramentas criadas da origem e usadas posteriormente em, digamos, uma genrule. Elas precisam ser criadas para serem executadas na sua estação de trabalho. Assim, podemos identificar duas configurações: a configuração de execução, que é usada para criar ferramentas que são executadas durante o build, e a configuração de destino (ou configuração de solicitação, mas usamos "configuração de destino" com mais frequência, mesmo que essa palavra já tenha muitos significados), que é usada para criar o binário que você solicitou.

Normalmente, há muitas bibliotecas que são pré-requisitos do destino de build solicitado (//foo:bin) e de uma ou mais ferramentas de execução, por exemplo, algumas bibliotecas de base. Essas bibliotecas precisam ser criadas duas vezes, uma para a configuração de execução e outra para a configuração de destino. O Bazel garante que as duas variantes sejam criadas e que os arquivos derivados sejam mantidos separados para evitar interferências. Geralmente, esses destinos podem ser criados simultaneamente, já que são independentes uns dos outros. Se você vir mensagens de progresso indicando que um determinado destino está sendo criado duas vezes, essa é provavelmente a explicação.

A configuração de execução é derivada da configuração de destino da seguinte maneira:

  • Use a mesma versão do Crosstool (--crosstool_top) especificada na configuração da solicitação, a menos que --host_crosstool_top seja especificado.
  • Use o valor de --host_cpu para --cpu (padrão: k8).
  • Use os mesmos valores dessas opções especificados na configuração da solicitação: --compiler, --use_ijars e, se --host_crosstool_top for usado, o valor de --host_cpu será usado para pesquisar um default_toolchain no Crosstool (ignorando --compiler) para a configuração de execução.
  • Use o valor de --host_javabase para --javabase
  • Use o valor de --host_java_toolchain para --java_toolchain
  • Use builds otimizados para código C++ (-c opt).
  • Não gerar informações de depuração (--copt=-g0).
  • Remova informações de depuração de executáveis e bibliotecas compartilhadas (--strip=always).
  • Coloque todos os arquivos derivados em um local especial, diferente daquele usado por qualquer configuração de solicitação possível.
  • Suprimir a inclusão de carimbos em binários com dados de build (consulte as opções --embed_*).
  • Todos os outros valores permanecem nos padrões.

Há muitos motivos para preferir selecionar uma configuração de execução diferente da configuração de solicitação. O mais importante:

Primeiro, ao usar binários otimizados e sem informações desnecessárias, você reduz o tempo gasto vinculando e executando as ferramentas, o espaço em disco ocupado por elas e o tempo de E/S de rede em builds distribuídos.

Em segundo lugar, ao desacoplar as configurações de execução e solicitação em todos os builds, você evita reconstruções muito caras que resultariam de pequenas mudanças na configuração de solicitação (como a mudança de uma opção de vinculador), conforme descrito anteriormente.

Corrigir recriações incrementais

Um dos principais objetivos do projeto Bazel é garantir reconstruções incrementais corretas. As ferramentas de build anteriores, especialmente aquelas baseadas em Make, fazem várias premissas incorretas na implementação de builds incrementais.

Primeiro, os carimbos de data/hora dos arquivos aumentam de forma monotônica. Embora esse seja o caso típico, é muito fácil violar essa proposição. A sincronização com uma revisão anterior de um arquivo faz com que o tempo de modificação dele diminua. Os sistemas baseados em Make não serão recompilados.

Em geral, embora o Make detecte mudanças nos arquivos, ele não detecta mudanças nos comandos. Se você alterar as opções transmitidas ao compilador em uma determinada etapa de build, o Make não vai executar o compilador novamente. É necessário descartar manualmente as saídas inválidas do build anterior usando make clean.

Além disso, o Make não é robusto contra o encerramento sem êxito de um dos subprocessos depois que ele começa a gravar no arquivo de saída. Embora a execução atual do Make falhe, a invocação subsequente vai presumir que o arquivo de saída truncado é válido (porque é mais recente que as entradas) e não será reconstruído. Da mesma forma, se o processo de criação for interrompido, uma situação semelhante poderá ocorrer.

O Bazel evita essas e outras proposições. O Bazel mantém um banco de dados de todo o trabalho feito anteriormente e só omite uma etapa de build se encontrar que o conjunto de arquivos de entrada (e seus carimbos de data/hora) para essa etapa e o comando de compilação para ela correspondem exatamente a um no banco de dados, e que o conjunto de arquivos de saída (e seus carimbos de data/hora) para a entrada do banco de dados correspondem exatamente aos carimbos de data/hora dos arquivos no disco. Qualquer mudança nos arquivos de entrada ou saída, ou no próprio comando, vai causar uma nova execução da etapa de build.

O benefício para os usuários de builds incrementais corretos é: menos tempo perdido devido a confusão. Além disso, menos tempo gasto esperando por reconstruções causadas pelo uso de make clean, seja necessário ou preventivo.

Consistência e builds incrementais

Formalmente, definimos o estado de um build como consistente quando todos os arquivos de saída esperados existem e o conteúdo deles está correto, conforme especificado pelas etapas ou regras necessárias para criá-los. Quando você edita um arquivo de origem, o estado do build é considerado inconsistente e permanece assim até que você execute a ferramenta de build novamente. Descrevemos essa situação como inconsistência instável, porque ela é apenas temporária, e a consistência é restaurada executando a ferramenta de build.

Há outro tipo de inconsistência prejudicial: a inconsistência estável. Se o build atingir um estado estável inconsistente, a invocação repetida e bem-sucedida da ferramenta de build não vai restaurar a consistência. O build vai ficar "preso", e as saídas vão continuar incorretas. Estados estáveis inconsistentes são o principal motivo pelo qual os usuários do Make (e de outras ferramentas de build) digitam make clean. Descobrir que a ferramenta de build falhou dessa maneira (e se recuperar dela) pode levar tempo e ser muito frustrante.

Conceitualmente, a maneira mais simples de conseguir um build consistente é descartar todas as saídas de build anteriores e começar de novo: faça de cada build um build limpo. Essa abordagem é obviamente demorada demais para ser prática (exceto talvez para engenheiros de lançamento). Portanto, para ser útil, a ferramenta de build precisa ser capaz de realizar builds incrementais sem comprometer a consistência.

A análise incremental correta de dependências é difícil e, conforme descrito acima, muitas outras ferramentas de build não evitam estados estáveis inconsistentes durante builds incrementais. Em contraste, o Bazel oferece a seguinte garantia: após uma invocação bem-sucedida da ferramenta de build durante a qual você não fez edições, o build estará em um estado consistente. Se você editar os arquivos de origem durante uma compilação, o Bazel não garante a consistência do resultado da compilação atual. Mas isso garante que os resultados do próximo build vão restaurar a consistência.)

Como em todas as garantias, há algumas letras pequenas: há algumas maneiras conhecidas de entrar em um estado inconsistente estável com o Bazel. Não garantimos investigar problemas decorrentes de tentativas deliberadas de encontrar bugs na análise de dependência incremental, mas vamos investigar e fazer o possível para corrigir todos os estados estáveis inconsistentes decorrentes do uso normal ou "razoável" da ferramenta de build.

Se você detectar um estado estável e inconsistente com o Bazel, informe um bug.

Execução em sandbox

O Bazel usa sandboxes para garantir que as ações sejam executadas de forma hermética e correta. O Bazel executa gerações (em termos gerais: ações) em sandboxes que contêm apenas o conjunto mínimo de arquivos necessários para a ferramenta fazer o trabalho. No momento, o isolamento em sandbox funciona no Linux 3.12 ou mais recente com a opção CONFIG_USER_NS ativada e também no macOS 10.11 ou mais recente.

O Bazel vai mostrar um aviso se o sistema não for compatível com o isolamento em sandbox para alertar que não há garantia de que os builds sejam herméticos e que eles podem afetar o sistema host de maneiras desconhecidas. Para desativar esse aviso, transmita a flag --ignore_unsupported_sandboxing ao Bazel.

Em algumas plataformas, como nós de cluster do Google Kubernetes Engine ou Debian, os namespaces de usuário são desativados por padrão devido a problemas de segurança. Para verificar isso, consulte o arquivo /proc/sys/kernel/unprivileged_userns_clone: se ele existir e contiver um 0, os namespaces de usuário poderão ser ativados com sudo sysctl kernel.unprivileged_userns_clone=1.

Em alguns casos, a sandbox do Bazel não executa regras devido à configuração do sistema. O sintoma geralmente é uma falha que gera uma mensagem semelhante a namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory. Nesse caso, tente desativar o sandbox para genrules com --strategy=Genrule=standalone e para outras regras com --spawn_strategy=standalone. Informe um bug no nosso rastreador de problemas e mencione qual distribuição do Linux você está usando para que possamos investigar e fornecer uma correção em uma versão posterior.

Fases de um build

No Bazel, um build ocorre em três fases distintas. Como usuário, entender a diferença entre elas fornece insights sobre as opções que controlam um build (veja abaixo).

Fase de carregamento

A primeira é o carregamento, em que todos os arquivos BUILD necessários para os targets iniciais e o fechamento transitivo das dependências são carregados, analisados, avaliados e armazenados em cache.

Para o primeiro build depois que um servidor do Bazel é iniciado, a fase de carregamento geralmente leva muitos segundos, já que vários arquivos BUILD são carregados do sistema de arquivos. Em builds subsequentes, principalmente se nenhum arquivo BUILD tiver sido alterado, o carregamento vai ocorrer muito rapidamente.

Os erros informados durante essa fase incluem: pacote não encontrado, destino não encontrado, erros lexicais e gramaticais em um arquivo BUILD e erros de avaliação.

Fase de análise

A segunda fase, análise, envolve a análise semântica e a validação de cada regra de build, a construção de um gráfico de dependência de build e a determinação exata do trabalho a ser feito em cada etapa do build.

Assim como o carregamento, a análise também leva vários segundos quando calculada por completo. No entanto, o Bazel armazena em cache o gráfico de dependências de uma build para a próxima e só reanalisa o que precisa, o que pode tornar as builds incrementais extremamente rápidas no caso em que os pacotes não mudaram desde a build anterior.

Os erros informados nesta etapa incluem: dependências inadequadas, entradas inválidas para uma regra e todas as mensagens de erro específicas da regra.

As fases de carregamento e análise são rápidas porque o Bazel evita E/S de arquivo desnecessárias nessa etapa, lendo apenas arquivos BUILD para determinar o trabalho a ser feito. Isso é proposital e faz do Bazel uma boa base para ferramentas de análise, como o comando query do Bazel, que é implementado na fase de carregamento.

Fase de execução

A terceira e última fase do build é a execução. Essa fase garante que as saídas de cada etapa do build sejam consistentes com as entradas, executando novamente as ferramentas de compilação/vinculação/etc. conforme necessário. É nessa etapa que o build passa a maior parte do tempo, variando de alguns segundos a mais de uma hora para um build grande. Os erros informados durante essa fase incluem: arquivos de origem ausentes, erros em uma ferramenta executada por alguma ação de build ou falha de uma ferramenta em produzir o conjunto esperado de saídas.