Um módulo do Bazel é um projeto que pode ter várias versões, cada uma que publica 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 npm, um módulo Go ou uma caixa Cargo.
Um módulo precisa ter um arquivo MODULE.bazel
na raiz do repositório (ao lado do
WORKSPACE
). Esse arquivo é o manifesto do módulo, que declara o nome dele,
versão, lista de dependências diretas e outras informações. Para um modelo
exemplo:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")
Confira a lista completa de diretivas disponíveis em arquivos MODULE.bazel
.
Para executar a resolução do módulo, o Bazel começa lendo o arquivo
MODULE.bazel
e, em seguida, solicita repetidamente o
MODULE.bazel
de um registro do Bazel até
descobre todo o gráfico de dependência.
Por padrão, o Bazel seleciona uma versão de cada módulo. usar. 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 diverso, e os projetos usam vários esquemas de controle de versões. A
o mais popular é o SemVer, mas há
também projetos de destaque usando diferentes esquemas,
Abseil, cuja
são baseadas em data, por exemplo, 20210324.2
).
Por esse motivo, o Bzlmod adota uma versão mais descontraída das especificações do SemVer. A as diferenças incluem:
- O SemVer determina que a "versão" da versão deve consistir em 3
segmentos:
MAJOR.MINOR.PATCH
. No Bazel, esse requisito é atenuado para que que qualquer número de segmentos é permitido. - No SemVer, cada um dos segmentos do "lançamento" deve ter apenas dígitos. No Bazel, isso é flexibilizado para permitir letras também, e a comparação a semântica corresponde aos "identifiers" no "pré-lançamento", parte.
- Além disso, a semântica dos aumentos principais, secundários e de versão de patch são é aplicada. No entanto, consulte o nível de compatibilidade para detalhes sobre como indicamos a compatibilidade com versões anteriores.
Qualquer versão válida do SemVer é uma versão válida do módulo do Bazel. Além disso, dois
As versões a
e b
do SemVer só comparam a < b
quando o mesmo acontece
elas são comparadas
como versões de módulos do Bazel.
Seleção da versão
Considere o problema da dependência diamante, um item básico da dependência com controle de versões. de gerenciamento de projetos. Suponha que você tenha o gráfico de dependências:
A 1.0
/ \
B 1.0 C 1.1
| |
D 1.0 D 1.1
Qual versão do D
deve ser usada? Para resolver essa questão, o Bzlmod usa o
Seleção de versão mínima
(MVS) introduzido no sistema de módulos Go. O MVS presume que todos os novos
de um módulo são compatíveis com versões anteriores e, por isso, escolhe a versão mais recente
especificado por qualquer dependente (D 1.1
em nosso exemplo). É chamado de "mínimo"
porque D 1.1
é a versão mais antiga que atende aos nossos requisitos,
mesmo que haja uma D 1.2
ou mais recente, não as selecionamos. O uso do MVS cria uma
processo de seleção de versão de alta fidelidade e reprodutível.
Versões Yangue
O registro pode declarar determinadas versões como puxadas se elas precisarem ser evitadas
(por exemplo, para vulnerabilidades de segurança). O Bazel gera um erro ao selecionar um
versão puxada de um módulo. Para corrigir esse erro, faça upgrade para uma versão mais recente
uma versão não puxada ou use o
--allow_yanked_versions
para permitir explicitamente a versão puxada.
Nível de compatibilidade
Em Go, a suposição do MVS sobre compatibilidade com versões anteriores funciona porque trata
versões incompatíveis com versões anteriores de um módulo como um módulo separado. Em termos de
SemVer, o que significa que A 1.x
e A 2.x
são considerados módulos distintos e podem
coexistem no gráfico de dependências resolvida. Isso é, por sua vez, possível
a versão principal no caminho do pacote em Go. Portanto, não há nenhuma
conflitos de tempo de compilação
ou de vinculação.
No entanto, o Bazel não oferece essas garantias, por isso precisa da "versão principal".
para detectar versões incompatíveis com versões anteriores. Esse número é chamado
o nível de compatibilidade e é especificado por cada versão de módulo nos respectivos
module()
. Com essas informações, o Bazel pode gerar um erro ao
detecta que versões do mesmo módulo com diferentes níveis de compatibilidade
existem no gráfico de dependências resolvidas.
Modifica
Especificar substituições no arquivo MODULE.bazel
para mudar o comportamento do Bazel
e resolução do módulo. Somente as substituições do módulo raiz entram em vigor — se um módulo for
usado como dependência, as substituições dele serão ignoradas.
Cada substituição é especificada para um determinado nome de módulo, o que afeta todos os no gráfico de dependências. Embora apenas as substituições do módulo raiz levem podem ser para dependências transitivas que o módulo raiz não depender diretamente.
Substituição de versão única
O single_version_override
atende a vários propósitos:
- Com o atributo
version
, é possível fixar uma dependência em um versão, independentemente de quais versões da dependência são solicitadas no gráfico de dependências. - Com o atributo
registry
, é possível forçar essa dependência a vir de um registro específico, em vez de seguir o registro normal no processo de seleção. - Com os atributos
patch*
, é possível especificar um conjunto de patches a serem aplicados módulo baixado.
Esses atributos são opcionais e podem ser misturados e combinados.
Substituição de várias versões
Uma multiple_version_override
pode ser especificado para permitir que várias versões do mesmo módulo coexistam no
gráfico de dependências resolvido.
Você pode especificar uma lista explícita de versões permitidas para o módulo, que deve estejam presentes no gráfico de dependências antes da resolução. É preciso que haja alguma dependência transitiva dependendo de cada versão permitida. Depois resolução, apenas as versões permitidas do módulo permanecem, enquanto o Bazel faz upgrade outras versões do módulo para a versão mais alta permitida mais próxima com o mesmo nível de compatibilidade. Se nenhuma versão superior permitida com a mesma compatibilidade existir, o Bazel lançará um erro.
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, e a versão principal é o bloco
nível:
- Uma substituição de várias versões que permite
1.3
,1.7
e2.0
resulta em Upgrade de1.1
para1.3
, upgrade de1.5
para1.7
e outros versões permanecem as mesmas. - Uma substituição de várias versões que permite
1.5
e2.0
resulta em erro, assim como O1.7
não tem uma versão mais recente com o mesmo nível de compatibilidade para fazer upgrade. - Uma substituição de várias versões que permite
1.9
e2.0
resulta em erro, assim como1.9
não está presente no gráfico de dependência antes da resolução.
Além disso, os usuários também podem substituir o registro usando o registry
da mesma forma que as substituições de versão única.
Substituições que não são do registro
As substituições que não são do registro removem completamente um módulo da resolução de versão. Júlio
não solicita esses arquivos MODULE.bazel
de um registro, mas de
o repositório em si.
O Bazel oferece suporte às seguintes substituições que não são do registro:
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, é preciso definir um repositório que não represente um arquivo do Bazel
module; por exemplo, um que contenha um arquivo JSON simples para ser lido como dados.
Nesse caso, é possível usar o método use_repo_rule
diretiva para definir diretamente um repositório
invocando uma regra de repositório. Este repositório só será visível para o módulo a que
definido.
Internamente, isso é implementado usando o mesmo mecanismo do module padrão, que permitem definir repositórios com mais flexibilidade.
Nomes de repositório e dependências estritas
O nome aparente de um repositório que faz
módulo para seus dependentes diretos, o padrão é o nome do módulo, a menos que o
Atributo repo_name
do bazel_dep
diretiva diz o contrário. Isso significa que um módulo só pode encontrar o próprio
dependências. Isso ajuda a evitar quebras acidentais devido a alterações
dependências transitivas.
O nome canônico de um repositório que apoia uma
módulo é module_name~version
(por exemplo, bazel_skylib~1.0.3
) ou module_name~
(por exemplo, bazel_features~
), dependendo se há
várias versões do módulo em todo o gráfico de dependências (consulte
multiple_version_override
).
Observe que o formato de nome canônico não é uma API de que 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 obtê-lo diretamente do Bazel:
* Nos arquivos BUILD e .bzl
, use
Label.repo_name
em uma instância Label
construído a partir de uma string de rótulo dada pelo nome aparente do repositório, por exemplo,
Label("@bazel_skylib").repo_name
:
* Ao procurar arquivos de execução, use
$(rlocationpath ...)
ou uma das bibliotecas do runfiles do
@bazel_tools//tools/{bash,cpp,java}/runfiles
ou, para um conjunto de regras rules_foo
,
em @rules_foo//foo/runfiles
.
* Ao interagir com o Bazel usando uma ferramenta externa, como um ambiente de desenvolvimento integrado ou uma linguagem
servidor, use o comando bazel mod dump_repo_mapping
para receber o mapeamento do
nomes aparentes a nomes canônicos para 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.