Las extensiones de módulo permiten a los usuarios extender el sistema de módulos leyendo datos de entrada de módulos en el gráfico de dependencias, realizando la lógica necesaria para resolver las dependencias y, por último, la creación de repositorios llamando a las reglas de repositorio. Estas extensiones Tienen capacidades similares a las reglas del repositorio, lo que les permite realizar operaciones de E/S de archivos. enviar solicitudes de red, etcétera. Entre otras cosas, permiten que Bazel interactuar con otros sistemas de administración de paquetes y, al mismo tiempo, respetar el gráfico de dependencias compilado a partir de módulos de Bazel.
Puedes definir extensiones de módulo en archivos .bzl
, al igual que las reglas del repositorio. Son
no se invoca directamente; más bien, cada módulo especifica fragmentos de datos llamados etiquetas
para que lean las extensiones. Bazel ejecuta la resolución del módulo
antes de evaluar cualquier
extensiones. La extensión lee todas las etiquetas que le pertenecen en toda
gráfico de dependencias.
Uso de extensiones
Las extensiones se alojan en módulos de Bazel. Para usar una extensión en un
primero agrega un bazel_dep
en el módulo que aloja la extensión y, luego,
llama a la función integrada use_extension
para ponerlo dentro del alcance. Considera el siguiente ejemplo, un fragmento de un
MODULE.bazel
para usar “maven” de Compute Engine, como se define en el
rules_jvm_external
módulo:
bazel_dep(name = "rules_jvm_external", version = "4.5")
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
De esta manera, se vincula el valor que se muestra de use_extension
a una variable, lo que permite que la
el usuario debe usar la sintaxis de puntos para especificar etiquetas para la extensión. Las etiquetas deben seguir
el esquema definido por las clases de etiquetas correspondientes especificadas en el
definición de la extensión. Para ver un ejemplo que especifique algunas
Etiquetas maven.install
y 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"])
Usa la directiva use_repo
para incorporar repositorios.
generada por la extensión en el alcance del módulo actual.
use_repo(maven, "maven")
Los repositorios que genera una extensión forman parte de su API. En este ejemplo,
“maven” extensión del módulo promete generar un repositorio llamado maven
. Con la
de la declaración anterior, la extensión resuelve correctamente etiquetas como las siguientes:
@maven//:org_junit_junit
para apuntar al repositorio que generó "maven"
.
Definición de la extensión
Puedes definir extensiones de módulo de manera similar a las reglas del repositorio con las
función module_extension
. Sin embargo,
Las reglas de repositorio tienen varios atributos,
tag_class
, cada una de las cuales tiene varias características
atributos. Las clases de etiquetas definen esquemas para las etiquetas que usa esta extensión. Para
ejemplo, el "maven" extensión anterior podría definirse de la siguiente manera:
# @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},
)
Estas declaraciones muestran que las etiquetas maven.install
y maven.artifact
se pueden
se especifica con el esquema de atributos especificado.
La función de implementación de las extensiones de módulo es similar a la de repo.
de la app, excepto que obtienen un objeto module_ctx
,
que otorga acceso a todos los módulos que usan la extensión y a todas las etiquetas pertinentes.
Luego, la función de implementación llama a las reglas de repositorio para generar repositorios.
# @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)
Identidad de la extensión
Las extensiones de módulo se identifican por el nombre y el archivo .bzl
que aparece
en la llamada a use_extension
. En el siguiente ejemplo, la extensión maven
se identifica con el archivo .bzl
@rules_jvm_external//:extension.bzl
y el
nombre maven
:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Si vuelves a exportar una extensión desde un archivo .bzl
diferente, se le otorga una nueva identidad.
y, si se usan ambas versiones de la extensión en el gráfico del módulo transitivo,
Luego, se evaluarán por separado y solo verán las etiquetas asociadas.
con esa identidad en particular.
Como autor de la extensión, debes asegurarte de que los usuarios solo usen tus
del módulo a partir de un único archivo .bzl
.
Nombres y visibilidad de repositorios
Los repositorios generados por extensiones tienen nombres canónicos con el formato module_repo_canonical_name+extension_name+repo_name
. Ten en cuenta que el nombre canónico
no es una API en la que deba confiar y está sujeto a cambios en cualquier momento.
Esta política de nombres implica que cada extensión tiene su propio "espacio de nombres del repositorio". dos
distintas extensiones pueden definir un repositorio con el mismo nombre sin arriesgarse
de conflictos. También significa que repository_ctx.name
informa el nombre canónico
del repositorio, que no es igual al nombre especificado en la regla del repositorio
llamada.
Si se tienen en cuenta los repositorios generados por extensiones de módulo, hay algunas varias reglas de visibilidad del repositorio:
- Un repositorio de módulos de Bazel puede ver todos los repositorios ingresados en su archivo
MODULE.bazel
. a través debazel_dep
yuse_repo
- Un repo generado por una extensión de módulo puede ver todos los repos visibles para el
que aloja la extensión, además de todos los demás repositorios generados por la
con la misma extensión de módulo (con los nombres especificados en las llamadas a la regla del repositorio
sus nombres aparentes).
- Esto podría generar un conflicto. Si el módulo repo puede ver un repo con
el nombre aparente
foo
, y la extensión generará un repositorio con el el nombre especificadofoo
; luego, para todos los repositorios generados por esa extensiónfoo
se refiere al primero.
- Esto podría generar un conflicto. Si el módulo repo puede ver un repo con
el nombre aparente
Prácticas recomendadas
En esta sección, se describen las prácticas recomendadas para escribir extensiones de modo que sean fáciles de usar, fáciles de mantener y adaptarse bien a los cambios con el tiempo.
Colocar cada extensión en un archivo separado
Cuando las extensiones se encuentran en archivos diferentes, permite que una extensión se cargue. de recursos generados por otra extensión. Incluso si no la usas es conveniente ubicarlas en archivos separados en caso de que la necesites más adelante. Esto se debe a que la identidad de la extensión se basa en su archivo, así que la extensión en otro archivo luego cambia tu API pública y es una cambio incompatible para tus usuarios.
Especifica la reproducibilidad
Si tu extensión siempre define los mismos repositorios con las mismas entradas
(etiquetas de extensión, archivos que lee, etc.) y, en particular, no se basa en
cualquier descarga que no esté protegida por
una suma de comprobación, considera devolver
extension_metadata
con
reproducible = True
Esto permite que Bazel omita esta extensión cuando escriba en
el archivo de bloqueo.
Especifica el sistema operativo y la arquitectura
Si tu extensión se basa en el sistema operativo o su tipo de arquitectura,
asegúrate de indicarlo en la definición de la extensión mediante el atributo os_dependent
y arch_dependent
. Esto garantiza que Bazel reconozca el
la necesidad de reevaluación si hay cambios en cualquiera de ellos.
Debido a que este tipo de dependencia del host dificulta el mantenimiento la entrada del archivo de bloqueo para esta extensión, considera marcar la extensión como reproducible, si es posible.
Solo el módulo raíz debería afectar directamente los nombres del repositorio
Recuerda que cuando una extensión crea repositorios, estos se crean
el espacio de nombres de la extensión. Esto significa que pueden producirse colisiones si se
módulos usan la misma extensión y terminan creando un repositorio con la misma
de la fuente de datos. A menudo, esto se manifiesta como el tag_class
de la extensión de un módulo que tiene un name
que se pasa como el valor name
de una regla de repositorio.
Por ejemplo, supongamos que el módulo raíz, A
, depende del módulo B
. Ambos módulos
depender del módulo mylang
. Si A
y B
llaman
mylang.toolchain(name="foo")
, ambos intentarán crear un repositorio llamado
foo
en el módulo mylang
, y se producirá un error.
Para evitar esto, quita la capacidad de establecer el nombre del repositorio directamente o que solo permita que lo haga el módulo raíz. Es posible permitir que el módulo raíz capacidad porque nada dependerá de ella, y no tiene que preocuparse por otro módulo, lo que crea un nombre en conflicto.