Um módulo do Bazel é um projeto do Bazel que pode ter várias versões, cada uma delas publicando metadados sobre outros módulos dos quais depende. Isso é análogo a conceitos conhecidos em outros sistemas de gerenciamento de dependências, como um artefato do Maven, um pacote do npm, um módulo do Go ou uma caixa do Cargo.
Um módulo precisa ter um arquivo MODULE.bazel na raiz do repositório. Esse arquivo é o manifesto do módulo, declarando o nome, a versão, a lista de dependências diretas e outras informações. Para um exemplo básico:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")
Consulte a lista completa de diretivas disponíveis em
MODULE.bazel arquivos.
Para realizar a resolução de módulos, o Bazel começa lendo o arquivo
MODULE.bazel do módulo raiz e, em seguida, solicita repetidamente o arquivo
MODULE.bazel de qualquer dependência de um registro do Bazel até
descobrir todo o gráfico de dependência.
Por padrão, o Bazel então seleciona uma versão de cada módulo a ser usada. O Bazel representa cada módulo com um repositório e consulta o registro novamente para saber como definir cada um dos repositórios.
Formato da versão
O Bazel tem um ecossistema diversificado, e os projetos usam vários esquemas de controle de versões. O
mais popular é o SemVer, mas também há
projetos importantes que usam esquemas diferentes, como o
Abseil, cujas
versões são baseadas em datas, por exemplo, 20210324.2).
Por esse motivo, o Bazel adota uma versão mais flexível da especificação SemVer. As diferenças incluem:
- O SemVer prescreve que a parte "lançamento" da versão precisa consistir em três segmentos:
MAJOR.MINOR.PATCH. No Bazel, esse requisito é flexibilizado para que qualquer número de segmentos seja permitido. - No SemVer, cada um dos segmentos na parte "lançamento" precisa ser apenas dígitos. No Bazel, isso é flexibilizado para permitir letras também, e a semântica de comparação corresponde aos "identificadores" na parte "pré-lançamento".
- Além disso, a semântica de aumentos de versão principal, secundária e de patch não é aplicada.
Qualquer versão SemVer válida é uma versão de módulo do Bazel válida. Além disso, duas
versões SemVer a e b comparam a < b se e somente se o mesmo for válido quando
comparadas como versões de módulo do Bazel.
Por fim, para saber mais sobre o controle de versões de módulos, consulte as MODULE.bazel
Perguntas frequentes.
Seleção da versão
Considere o problema de dependência de diamante, um elemento básico no espaço de gerenciamento de dependências com versões. Suponha que você tenha o gráfico de dependência:
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
Qual versão de D deve ser usada? Para resolver essa questão, o Bazel usa o
algoritmo de seleção de versão mínima
(MVS, na sigla em inglês) introduzido no sistema de módulos Go. O MVS pressupõe que todas as novas versões de um módulo sejam compatíveis com versões anteriores e, portanto, escolhe a versão mais alta especificada por qualquer dependente (D 1.1 no nosso exemplo). Ele é chamado de "mínimo" porque D 1.1 é a versão mais antiga que pode atender aos nossos requisitos. Mesmo que D 1.2 ou mais recente exista, não a selecionamos. O uso do MVS cria um processo de seleção de versão de alta fidelidade e reproduzível.
Versões retiradas
O registro pode declarar determinadas versões como retiradas se elas precisarem ser evitadas (por exemplo, para vulnerabilidades de segurança). O Bazel gera um erro ao selecionar uma versão retirada de um módulo. Para corrigir esse erro, faça upgrade para uma versão mais recente,
não retirada ou use a
--allow_yanked_versions
flag para permitir explicitamente a versão retirada.
Modifica
Especifique modificações no arquivo MODULE.bazel para alterar o comportamento da resolução de módulos do Bazel. Somente as modificações do módulo raiz entram em vigor. Se um módulo for usado como dependência, as modificações serão ignoradas.
Cada modificação é especificada para um determinado nome de módulo, afetando todas as versões dele no gráfico de dependência. Embora apenas as modificações do módulo raiz entrem em vigor, elas podem ser para dependências transitivas das quais o módulo raiz não depende diretamente.
Modificação de versão única
O single_version_override
tem várias finalidades:
- Com o atributo
version, é possível fixar uma dependência em uma versão específica, independentemente de quais versões da dependência sejam solicitadas no gráfico de dependência. - Com o atributo
registry, é possível forçar essa dependência a vir de um registro específico, em vez de seguir o processo normal de seleção de registro. - Com os atributos
patch*, é possível especificar um conjunto de patches a serem aplicados ao módulo transferido por download.
Esses atributos são opcionais e podem ser combinados.
Modificação de várias versões
Uma multiple_version_override
pode ser especificada para permitir que várias versões do mesmo módulo coexistam no
gráfico de dependência resolvido.
Se houver várias versões do mesmo módulo restantes no gráfico de dependência, o Bazel vai escolher a versão mais alta permitida mais próxima para cada dependente.
Por exemplo, se as versões 1.1, 1.3, 1.5, 1.7 e 2.0 existirem no gráfico de dependência antes da resolução:
- Uma modificação de várias versões que permite
1.3,1.7e2.0resulta no upgrade de1.1para1.3,1.5para1.7e outras versões permanecem as mesmas. - Uma modificação de várias versões que permite
1.9e2.0resulta em um erro, já que1.9não está presente no gráfico de dependência antes da resolução.
Além disso, os usuários também podem modificar o registro usando o atributo registry, de maneira semelhante às modificações de versão única.
Modificações não registradas
As modificações não registradas removem completamente um módulo da resolução de versão. O Bazel não solicita esses arquivos MODULE.bazel de um registro, mas do próprio repositório.
O Bazel oferece suporte às seguintes modificações não registradas:
Definir um valor de versão no arquivo de origem MODULE.bazel pode ter desvantagens quando o módulo é modificado com uma modificação não registrada. Para
saber mais sobre isso, consulte as MODULE.bazel
Perguntas frequentes.
Definir repositórios que não representam módulos do Bazel
Com bazel_dep, é possível definir repositórios que representam outros módulos do Bazel.
Às vezes, é necessário definir um repositório que não represente um módulo do Bazel, por exemplo, um que contenha um arquivo JSON simples a ser lido como dados.
Nesse caso, é possível usar a use_repo_rule
diretiva para definir diretamente um repositório
invocando uma regra de repositório. Esse repositório só ficará visível para o módulo em que está definido.
Nos bastidores, isso é implementado usando o mesmo mecanismo das extensões de módulo, que permite definir repositórios com mais flexibilidade.
Nomes de repositório e dependências estritas
O nome aparente de um repositório que faz backup de um
módulo para seus dependentes diretos é o nome do módulo, a menos que o
repo_name atributo da diretiva bazel_dep
diga o contrário. Isso significa que um módulo só pode encontrar as dependências diretas. Isso ajuda a evitar interrupções acidentais devido a mudanças em dependências transitivas.
O nome canônico de um repositório que faz backup de um
módulo é module_name+version (por exemplo, bazel_skylib+1.0.3) ou module_name+ (por exemplo, bazel_features+), dependendo de haver
várias versões do módulo em todo o gráfico de dependência (consulte
multiple_version_override).
Observe que o formato de nome canônico não é uma API da qual você deve depender e
está sujeito a mudanças a qualquer momento. Em vez de codificar o nome canônico, use uma maneira compatível de recebê-lo diretamente do Bazel:
- Em arquivos BUILD e
.bzl, useLabel.repo_nameem uma instânciaLabelconstruída a partir de uma string de rótulo fornecida pelo nome aparente do repositório, por exemplo,Label("@bazel_skylib").repo_name. - Ao pesquisar arquivos de execução, use
$(rlocationpath ...)ou uma das bibliotecas de arquivos de execução em@bazel_tools//tools/{bash,cpp,java}/runfilesou, para um conjunto de regrasrules_foo, em@rules_foo//foo/runfiles. - Ao interagir com o Bazel de uma ferramenta externa, como um ambiente de desenvolvimento integrado ou um servidor de linguagem
, use o comando
bazel mod dump_repo_mappingpara receber o mapeamento de nomes aparentes para nomes canônicos de um determinado conjunto de repositórios.
As extensões de módulo também podem introduzir outros repositórios no escopo visível de um módulo.