Módulos do Bazel

Informar um problema Mostrar fonte Por noite · 7,3 · 7,2 · 7,1 · 7,0 · 6,5

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 e 2.0 resulta em Upgrade de 1.1 para 1.3, upgrade de 1.5 para 1.7 e outros versões permanecem as mesmas.
  • Uma substituição de várias versões que permite 1.5 e 2.0 resulta em erro, assim como O 1.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 e 2.0 resulta em erro, assim como 1.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.