O Bazel oferece suporte a dependências externas, arquivos de origem (texto e binários) usados no build que não são do seu espaço de trabalho. Por exemplo, eles podem ser um conjunto de regras hospedado em um repo do GitHub, um artefato do Maven ou um diretório na sua máquina local fora do espaço de trabalho atual.
Este documento oferece uma visão geral do sistema antes de examinar alguns dos conceitos com mais detalhes.
Visão geral do sistema
O sistema de dependência externa do Bazel funciona com base em módulos do Bazel, cada um deles é um projeto do Bazel com versão e repositórios (ou repos), que são árvores de diretórios que contêm arquivos de origem.
O Bazel começa com o módulo raiz, ou seja, o projeto em que você está trabalhando.
Como todos os módulos, ele precisa ter um arquivo MODULE.bazel na raiz do diretório, declarando os metadados básicos e as dependências diretas. Confira abaixo um exemplo básico:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")
A partir daí, o Bazel procura todos os módulos de dependência transitiva em um
registro do Bazel , que, por padrão, é o Registro central
do Bazel. O registro fornece os arquivos MODULE.bazel das dependências, o que permite que o Bazel descubra todo o gráfico de dependência transitiva antes de realizar a resolução de versão.
Após a resolução da versão, em que uma versão é selecionada para cada módulo, o Bazel consulta o registro novamente para saber como definir um repositório para cada módulo , ou seja, como as fontes de cada módulo de dependência precisam ser buscadas. Na maioria das vezes, esses são apenas arquivos baixados da Internet e extraídos.
Os módulos também podem especificar partes personalizadas de dados chamadas tags, que são consumidas por extensões de módulo após a resolução do módulo para definir repositórios adicionais. Essas extensões podem realizar ações como E/S de arquivos e envio de solicitações de rede. Entre outras coisas, elas permitem que o Bazel interaja com outros sistemas de gerenciamento de pacotes, respeitando o gráfico de dependência criado com módulos do Bazel.
Os três tipos de repositórios (o principal, que é a árvore de origem em que você está
trabalhando, os repositórios que representam módulos de dependência transitiva e os repositórios
criados por extensões de módulo) formam o espaço de trabalho juntos.
Os repositórios externos (não principais) são buscados sob demanda, por exemplo, quando são referenciados por rótulos (como @repo//pkg:target) em arquivos BUILD.
Benefícios
O sistema de dependência externa do Bazel oferece uma ampla variedade de benefícios.
Resolução automática de dependências
- Resolução de versão determinística: o Bazel adota o algoritmo de resolução de versão MVS determinístico, minimizando conflitos e resolvendo problemas de dependência de diamante.
- Gerenciamento simplificado de dependências:
MODULE.bazeldeclara apenas dependências diretas dependências, enquanto as dependências transitivas são resolvidas automaticamente, oferecendo uma visão geral mais clara das dependências do projeto. - Visibilidade estrita da dependência: apenas as dependências diretas são visíveis, garantindo a correção e a previsibilidade.
Integração do ecossistema
- Registro central do Bazel: um repositório centralizado para descobrir e gerenciar dependências comuns como módulos do Bazel .
- Adoção de projetos não Bazel: quando um projeto não Bazel (geralmente uma biblioteca C++) é adaptado para o Bazel e disponibilizado no BCR, ele simplifica a integração para toda a comunidade e elimina esforços duplicados e conflitos de arquivos BUILD personalizados.
- Integração unificada com gerenciadores de pacotes específicos do idioma: os conjuntos de regras
simplificam a integração com gerenciadores de pacotes externos para dependências não Bazel, incluindo:
- rules_jvm_external para Maven,
- rules_python para PyPi,
- bazel-gazelle para módulos Go,
- rules_rust para Cargo.
Recursos avançados
- Extensões de módulo: os recursos de extensão de módulo e
use_repo_rulepermitem o uso flexível de regras de repositório personalizadas e lógica de resolução para introduzir dependências não Bazel. bazel modComando: o subcomando oferece maneiras poderosas de inspecionar dependências externas. Você sabe exatamente como uma dependência externa é definida e de onde ela vem.- Modo de fornecedor: pré-busque as dependências externas exatas necessárias para facilitar builds off-line.
- Arquivo de bloqueio: o arquivo de bloqueio melhora a capacidade de reprodução do build e acelera a resolução de dependências.
- (Próximas) Atestações de procedência do BCR: fortaleça a segurança da cadeia de suprimentos garantindo a procedência verificada das dependências.
Conceitos
Esta seção oferece mais detalhes sobre conceitos relacionados a dependências externas.
Módulo
Um projeto do Bazel que pode ter várias versões, cada uma delas com dependências em outros módulos.
Em um espaço de trabalho local do Bazel, um módulo é representado por um repositório.
Para mais detalhes, consulte Módulos do Bazel.
Repositório
Uma árvore de diretórios com um arquivo de marcador de limite na raiz, contendo arquivos de origem que podem ser usados em um build do Bazel. Geralmente abreviado para apenas repo.
Um arquivo de marcador de limite de repositório pode ser MODULE.bazel (indicando que esse repositório
representa um módulo do Bazel), REPO.bazel (consulte abaixo) ou, em
contextos legados, WORKSPACE ou WORKSPACE.bazel. Qualquer arquivo de marcador de limite de repositório vai indicar o limite de um repositório. Vários arquivos desse tipo podem coexistir em um diretório.
Repositório principal
O repositório em que o comando atual do Bazel está sendo executado.
A raiz do repositório principal também é conhecida como raiz do espaço de trabalho.
Espaço de trabalho
O ambiente compartilhado por todos os comandos do Bazel executados no mesmo repositório principal. Ele abrange o repositório principal e o conjunto de todos os repositórios externos definidos.
Historicamente, os conceitos de "repositório" e "espaço de trabalho" foram confundidos. O termo "espaço de trabalho" costuma ser usado para se referir ao repositório principal e, às vezes, até como sinônimo de "repositório".
Nome canônico do repositório
O nome pelo qual um repositório sempre pode ser endereçado. No contexto de um espaço de trabalho, cada repositório tem um único nome canônico. Um destino dentro de um repositório
cujo nome canônico é canonical_name pode ser endereçado pelo rótulo
@@canonical_name//package:target (observe o @ duplo).
O repositório principal sempre tem a string vazia como nome canônico.
Nome aparente do repositório
O nome pelo qual um repositório pode ser endereçado no contexto de outro repositório. Isso pode ser considerado como um "apelido" de um repositório: o repositório com o nome canônico michael pode ter o nome aparente mike no contexto do repositório alice, mas pode ter o nome aparente mickey no contexto do repositório bob. Nesse caso, um destino dentro de michael pode ser endereçado pelo rótulo
@mike//package:target no contexto de alice (observe o @ único).
Por outro lado, isso pode ser entendido como um mapeamento de repositório: cada repositório mantém um mapeamento de "nome aparente do repositório" para um "nome canônico do repositório".
Regra de repositório
Um esquema para definições de repositório que informa ao Bazel como materializar um repositório. Por exemplo, pode ser "baixar um arquivo zip de um determinado URL e extraí-lo", "buscar um determinado artefato do Maven e disponibilizá-lo como um destino java_import" ou simplesmente "criar um link simbólico para um diretório local". Cada repositório é definido chamando uma regra de repositório com um número apropriado de argumentos.
Consulte Regras de repositório para mais informações sobre como escrever suas próprias regras de repositório.
As regras de repositório mais comuns são
http_archive, que baixa um arquivo
de um URL e o extrai, e
local_repository, que cria um link simbólico para um
diretório local que já é um repositório do Bazel.
Buscar um repositório
A ação de disponibilizar um repositório no disco local executando a regra de repositório associada. Os repositórios definidos em um espaço de trabalho não ficam disponíveis no disco local antes de serem buscados.
Normalmente, o Bazel só busca um repositório quando precisa de algo dele e o repositório ainda não foi buscado. Se o repositório já tiver sido buscado antes, o Bazel só o buscará novamente se a definição tiver mudado.
O comando fetch pode ser usado para iniciar uma pré-busca de um repositório, destino ou todos os repositórios necessários para realizar qualquer build. Essa capacidade permite builds off-line usando a opção --nofetch.
A opção --fetch serve para gerenciar o acesso à rede. O valor padrão é "true".
No entanto, quando definido como "false" (--nofetch), o comando vai usar qualquer versão armazenada em cache da dependência e, se nenhuma existir, o comando vai falhar.
Consulte Opções de busca para mais informações sobre como controlar a busca.
Layout do diretório
Depois de buscado, o repositório pode ser encontrado no subdiretório external na
base de saída, com o nome canônico.
Execute o comando a seguir para conferir o conteúdo do repositório com o nome canônico canonical_name:
ls $(bazel info output_base)/external/ canonical_name Arquivo REPO.bazel
O arquivo REPO.bazel é usado para marcar o limite superior
da árvore de diretórios que constitui um repositório. Ele não precisa conter nada para servir como um arquivo de limite de repo. No entanto, ele também pode ser usado para especificar alguns atributos comuns para todos os destinos de build dentro do repo.
A sintaxe de um arquivo REPO.bazel é semelhante aos arquivos BUILD, exceto que nenhuma instrução load é aceita. A função repo() usa os mesmos argumentos da package()
função em arquivos BUILD. Enquanto package()
especifica atributos comuns para todos os destinos de build dentro do pacote, repo()
faz isso de maneira análoga para todos os destinos de build dentro do repositório.
Por exemplo, é possível especificar uma licença comum para todos os destinos no repositório com o seguinte arquivo REPO.bazel:
repo(
default_package_metadata = ["//:my_license"],
)
O sistema WORKSPACE legado
Em versões mais antigas do Bazel (antes da 9.0), as dependências externas eram introduzidas por
definição de repositórios no arquivo WORKSPACE (ou WORKSPACE.bazel). Esse arquivo tem uma sintaxe semelhante aos arquivos BUILD, usando regras de repositório em vez de regras de build.
O snippet a seguir é um exemplo de uso da regra de repositório http_archive no arquivo WORKSPACE:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "foo",
urls = ["https://example.com/foo.zip"],
sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)
O snippet define um repositório cujo nome canônico é foo. No sistema WORKSPACE, por padrão, o nome canônico de um repositório também é o nome aparente para todos os outros repositórios.
Consulte a lista completa de funções disponíveis em
WORKSPACE arquivos.
Limitações do sistema WORKSPACE
Nos anos após a introdução do sistema WORKSPACE, os usuários relataram muitos problemas, incluindo:
- O Bazel não avalia os arquivos
WORKSPACEde nenhuma dependência. Portanto, todas as dependências transitivas precisam ser definidas no arquivoWORKSPACEdo repositório principal, além das dependências diretas. - Para contornar isso, os projetos adotaram o padrão "deps.bzl", em que definem uma macro que, por sua vez, define vários repositórios e pede aos usuários que chamem essa macro nos arquivos
WORKSPACE.- Isso tem problemas próprios: as macros não podem
loadoutros arquivos.bzl. Portanto, esses projetos precisam definir as dependências transitivas nessa macro "deps" ou contornar esse problema fazendo com que o usuário chame várias macros "deps" em camadas. - O Bazel avalia o arquivo
WORKSPACEsequencialmente. Além disso, as dependências são especificadas usandohttp_archivecom URLs, sem informações de versão. Isso significa que não há uma maneira confiável de realizar a resolução de versão no caso de dependências de diamante (Adepende deBeC;BeCdependem de versões diferentes deD).
- Isso tem problemas próprios: as macros não podem
Devido às limitações do WORKSPACE, o novo sistema baseado em módulos (codinome "Bzlmod") substituiu gradualmente o sistema WORKSPACE legado entre o Bazel 6 e 9. Leia o guia de migração do Bzlmod para saber como migrar para o Bzlmod.
Links externos no Bzlmod
- Exemplos de uso do Bzlmod em bazelbuild/examples (link em inglês)
- Revisão das dependências externas do Bazel (documento de design original do Bzlmod, link em inglês)
- Palestra do BazelCon 2021 sobre o Bzlmod (link em inglês)
- Palestra do Bazel Community Day sobre o Bzlmod (link em inglês)