As extensões de módulo permitem que os usuários estendam o sistema de módulos lendo dados de entrada. dos módulos em todo o gráfico de dependências, executando a lógica necessária para resolver dependências e, por fim, criar repositórios chamando regras de repo. Essas extensões têm recursos semelhantes às regras de repositório, que permitem realizar E/S de arquivos, enviar solicitações de rede e assim por diante. Entre outras coisas, eles permitem que o Bazel interagem com outros sistemas de gerenciamento de pacotes, ao mesmo tempo em que respeitam gráfico de dependência criado com base em módulos do Bazel.
É possível definir extensões de módulos em arquivos .bzl
, assim como as regras de repositório. São
não invocadas diretamente, em vez disso, cada módulo especifica partes de dados chamadas tags.
para as extensões lerem. O Bazel executa a resolução do módulo antes de avaliar qualquer
extensões. A extensão lê todas as tags pertencentes a ela em todo o
gráfico de dependências.
Uso da extensão
As extensões são hospedadas nos próprios módulos do Bazel. Para usar uma extensão em um
primeiro adicione um bazel_dep
no módulo que hospeda a extensão e, em seguida,
Chamar a função integrada use_extension
para trazê-lo ao escopo. Considere o exemplo a seguir — um snippet de uma
MODULE.bazel
para usar o "maven" extensão definida no
rules_jvm_external
módulo:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Isso vincula o valor de retorno de use_extension
a uma variável, o que permite que o
que o usuário use a sintaxe ponto para especificar tags para a extensão. As tags devem seguir
o esquema definido pelas classes de tag correspondentes especificadas na
definição de extensão. Para um exemplo especificando algumas
Tags maven.install
e maven.artifact
:
maven.install(artifacts = ["org.junit:junit:4.13.2"])
maven.artifact(group = "com.google.guava",
artifact = "guava",
version = "27.0-jre",
exclusions = ["com.google.j2objc:j2objc-annotations"])
Use a diretiva use_repo
para trazer repositórios
gerada pela extensão no escopo do módulo atual.
use_repo(maven, "maven")
Os repositórios gerados por uma extensão fazem parte da API dela. Neste exemplo,
"maven" de extensão de módulo promete gerar um repositório chamado maven
. Com o
acima, a extensão resolverá adequadamente os rótulos como
@maven//:org_junit_junit
para apontar para o repositório gerado pelo "maven".
.
Definição de extensão
É possível definir extensões de módulos de forma semelhante às regras de repositório, usando o
função module_extension
. No entanto,
enquanto as regras de repositório têm uma série de atributos, as extensões de módulo têm
tag_class
es, cada um com uma série de
atributos. As classes de tag definem os esquemas das tags usadas por essa extensão. Para
por exemplo, o "maven" extensão acima pode ser definida assim:
# @rules_jvm_external//:extensions.bzl
_install = tag_class(attrs = {"artifacts": attr.string_list(), ...})
_artifact = tag_class(attrs = {"group": attr.string(), "artifact": attr.string(), ...})
maven = module_extension(
implementation = _maven_impl,
tag_classes = {"install": _install, "artifact": _artifact},
)
Essas declarações mostram que as tags maven.install
e maven.artifact
podem ser
especificado usando o esquema de atributos especificado.
A função de implementação das extensões de módulo é semelhante às do repo
com exceção de um objeto module_ctx
,
que concede acesso a todos os módulos usando a extensão e todas as tags pertinentes.
Em seguida, a função de implementação chama as regras de repo para gerar repositórios.
# @rules_jvm_external//:extensions.bzl
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") # a repo rule
def _maven_impl(ctx):
# This is a fake implementation for demonstration purposes only
# collect artifacts from across the dependency graph
artifacts = []
for mod in ctx.modules:
for install in mod.tags.install:
artifacts += install.artifacts
artifacts += [_to_artifact(artifact) for artifact in mod.tags.artifact]
# call out to the coursier CLI tool to resolve dependencies
output = ctx.execute(["coursier", "resolve", artifacts])
repo_attrs = _process_coursier_output(output)
# call repo rules to generate repos
for attrs in repo_attrs:
http_file(**attrs)
_generate_hub_repo(name = "maven", repo_attrs)
Identidade da extensão
As extensões do módulo são identificadas pelo nome e pelo arquivo .bzl
exibido.
na chamada para use_extension
. No exemplo a seguir, a extensão maven
é identificado pelo arquivo .bzl
@rules_jvm_external//:extension.bzl
e pelo
nome maven
:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
A reexportação de uma extensão de um arquivo .bzl
diferente dá a ela uma nova identidade.
e, se as duas versões da extensão forem usadas no gráfico do módulo transitivo,
eles serão avaliados separadamente e verão apenas as tags associadas
com essa identidade específica.
Como autor de uma extensão, você deve garantir que os usuários usem apenas seus
extensão de módulo de um único arquivo .bzl
.
Nomes e visibilidade dos repositórios
Os repositórios gerados por extensões têm nomes canônicos no formato de module_repo_canonical_name+extension_name+repo_name
. O nome canônico
não é uma API da qual você deve depender. Ele está sujeito a alterações a qualquer momento.
Essa política de nomenclatura significa que cada extensão tem seu próprio "namespace repo"; dois
extensões diferentes podem definir um repositório com o mesmo nome sem o risco
conflitos. Isso também significa que repository_ctx.name
informa o nome canônico
do repositório, que não é o mesmo nome especificado na regra de repositório
a chamada.
Considerando os repositórios gerados por extensões de módulo, há várias regras de visibilidade do repositório:
- Um repositório de módulo do Bazel pode conferir todos os repositórios introduzidos no arquivo
MODULE.bazel
. viabazel_dep
euse_repo
- Um repositório gerado por uma extensão de módulo pode ver todos os repositórios visíveis para o
módulo que hospeda a extensão, além de todos os outros repositórios gerados pelo
mesma extensão de módulo (usando os nomes especificados nas chamadas de regra de repo como
seus nomes aparentes).
- Isso pode resultar em um conflito. Se o repositório do módulo puder ver um repositório com
o nome aparente
foo
e a extensão gera um repositório com o nome especificadofoo
, e para todos os repositórios gerados por essa extensãofoo
refere-se ao primeiro.
- Isso pode resultar em um conflito. Se o repositório do módulo puder ver um repositório com
o nome aparente
Práticas recomendadas
Esta seção descreve as práticas recomendadas ao criar extensões para que elas sejam simples de usar, manter e se adaptar bem às mudanças ao longo do tempo.
Colocar cada extensão em um arquivo separado
Quando as extensões estão em arquivos diferentes, uma extensão pode ser carregada repositórios gerados por outra extensão. Mesmo que você não use isso é melhor colocá-los em arquivos separados, caso necessário mais tarde. Isso ocorre porque a identificação da extensão é baseada em seu arquivo, portanto, mover a extensão em outro arquivo posteriormente altera sua API pública e é uma incompatível com os usuários.
Especificar a reprodutibilidade
Se a extensão sempre define os mesmos repositórios com as mesmas entradas
(tags de extensão, arquivos lidos etc.) e, em particular, não depende
todos os downloads que não são protegidos por
uma soma de verificação, considere retornar
extension_metadata
com
reproducible = True
. Isso permite que o Bazel pule essa extensão ao gravar em
o arquivo de bloqueio.
Especificar o sistema operacional e a arquitetura
Se sua extensão depende do sistema operacional ou do tipo de arquitetura,
indique isso na definição da extensão usando o os_dependent
.
e os atributos booleanos arch_dependent
. Isso garante que o Bazel reconheça
necessidade de reavaliação em caso de alterações em alguma delas.
Como esse tipo de dependência do host dificulta a manutenção a entrada de lockfile dessa extensão, considere marcando a extensão como reproduzível, se possível.
Somente o módulo raiz afeta diretamente os nomes dos repositórios
Lembre-se de que, quando uma extensão cria repositórios, eles são criados dentro
o namespace da extensão. Isso significa que podem ocorrer colisões se diferentes
módulos usam a mesma extensão e acabam criando um repositório com a mesma
nome. Isso geralmente se manifesta como o tag_class
de uma extensão de módulo com um name
.
que é transmitido como o valor name
de uma regra de repositório.
Por exemplo, digamos que o módulo raiz, A
, dependa do módulo B
. Ambos os módulos
dependem do módulo mylang
. Se A
e B
chamarem
mylang.toolchain(name="foo")
, os dois tentarão criar um repositório com o nome
foo
no módulo mylang
. Um erro vai ocorrer.
Para evitar isso, remova a capacidade de definir o nome do repositório diretamente, ou permitir que apenas o módulo raiz faça isso. Você pode permitir que o módulo raiz capacidade porque nada vai depender dela, por isso não é preciso ou outro módulo criando um nome conflitante.