Reglas del repositorio

En esta página, se explica cómo definir reglas de repositorio y se proporcionan ejemplos para obtener más detalles.

Un repositorio externo es un árbol de directorios, que contiene archivos fuente que se pueden usar en una compilación de Bazel, que se genera a pedido cuando se ejecuta su regla de repositorio correspondiente. Los repositorios se pueden definir de muchas maneras, pero, en última instancia, cada repositorio se define invocando una regla de repositorio, al igual que los destinos de compilación se definen invocando reglas de compilación. Se pueden usar para depender de bibliotecas de terceros (como bibliotecas empaquetadas de Maven), pero también para generar archivos BUILD específicos para el host en el que se ejecuta Bazel.

Definición de regla de repositorio

En un archivo .bzl, usa la repository_rule para definir una nueva regla de repositorio y almacenarla en una variable global. Una vez que se define una regla de repositorio, se puede invocar como una función para definir repositorios. Por lo general, esta invocación se realiza desde una función de implementación de extensión de módulo.

Los dos componentes principales de una definición de regla de repositorio son su esquema de atributos y su función de implementación. El esquema de atributos determina los nombres y los tipos de atributos que se pasan a una invocación de regla de repositorio, y la función de implementación se ejecuta cuando se debe recuperar el repositorio.

Atributos

Los atributos son argumentos que se pasan a la invocación de la regla de repositorio. El esquema de atributos aceptado por una regla de repositorio se especifica con el argumento attrs cuando la regla de repositorio se define con una llamada a repository_rule. Un ejemplo que define los atributos url y sha256 como cadenas:

http_archive = repository_rule(
    implementation=_impl,
    attrs={
        "url": attr.string(mandatory=True),
        "sha256": attr.string(mandatory=True),
    }
)

Para acceder a un atributo dentro de la función de implementación, usa repository_ctx.attr.<attribute_name>:

def _impl(repository_ctx):
    url = repository_ctx.attr.url
    checksum = repository_ctx.attr.sha256

Todas las repository_rule tienen el atributo name definido de forma implícita. Este es un atributo de cadena que se comporta de forma algo mágica: cuando se especifica como entrada para una invocación de regla de repositorio, toma un nombre de repositorio aparente; pero cuando se lee desde la función de implementación de la regla de repositorio con repository_ctx.attr.name, devuelve el nombre de repositorio canónico.

Función de implementación

Cada regla de repositorio requiere una función implementation. Contiene la lógica real de la regla y se ejecuta estrictamente en la fase de carga.

La función tiene exactamente un parámetro de entrada, repository_ctx. La función devuelve None para indicar que la regla es reproducible con los parámetros especificados o un diccionario con un conjunto de parámetros para esa regla que la convertirían en una regla reproducible que genera el mismo repositorio. Por ejemplo, para una regla que hace un seguimiento de un repositorio de Git, esto significaría devolver un identificador de confirmación específico en lugar de una rama flotante que se especificó originalmente.

El parámetro de entrada repository_ctx se puede usar para acceder a valores de atributos y funciones no herméticas (buscar un objeto binario, ejecutar un objeto binario, crear un archivo en el repositorio o descargar un archivo de Internet). Consulta la documentación de la API para obtener más contexto. Ejemplo:

def _impl(repository_ctx):
  repository_ctx.symlink(repository_ctx.attr.path, "")

local_repository = repository_rule(
    implementation=_impl,
    ...)

¿Cuándo se ejecuta la función de implementación?

La función de implementación de una regla de repositorio se ejecuta cuando Bazel necesita un destino de ese repositorio, por ejemplo, cuando otro destino (en otro repositorio) depende de él o si se menciona en la línea de comandos. Luego, se espera que la función de implementación cree el repositorio en el sistema de archivos. Esto se denomina "recuperar" el repositorio.

A diferencia de los destinos normales, los repositorios no se vuelven a recuperar necesariamente cuando cambia algo que haría que el repositorio sea diferente. Esto se debe a que hay elementos para los que Bazel no puede detectar cambios o que causarían una sobrecarga excesiva en cada compilación (por ejemplo, elementos que se recuperan de la red). Por lo tanto, los repositorios se vuelven a recuperar solo si cambia uno de los siguientes elementos:

  • Los atributos que se pasan a la invocación de la regla de repositorio.
  • El código de Starlark que comprende la implementación de la regla de repositorio.
  • El valor de cualquier variable de entorno que se pase al método de repository_ctx's getenv() o que se declare con el atributo environ de repository_rule. Los valores de estas variables de entorno se pueden conectar de forma directa en la línea de comandos con la --repo_env marca.
  • La existencia, el contenido y el tipo de cualquier ruta de acceso que se watched en la implementación función de la regla de repositorio.
    • Otros métodos de repository_ctx con un parámetro watch, como read(), execute() y extract(), también pueden hacer que se observen las rutas de acceso.
    • Del mismo modo, repository_ctx.watch_tree y path.readdir pueden hacer que se observen las rutas de acceso de otras maneras.
  • Cuando se ejecuta bazel fetch --force.

Hay dos parámetros de repository_rule que controlan cuándo se vuelven a recuperar los repositorios:

  • Si se configura la marca configure, el repositorio se vuelve a recuperar en bazel fetch --force --configure (los repositorios que no son configure no se vuelven a recuperar).
  • Si se configura la marca local, además de los casos anteriores, el repositorio también se vuelve a recuperar cuando se reinicia el servidor de Bazel.

Forzar la recuperación de repositorios externos

A veces, un repositorio externo puede quedar obsoleto sin que se produzca ningún cambio en su definición o dependencias. Por ejemplo, un repositorio que recupera fuentes puede seguir una rama en particular de un repositorio de terceros, y hay confirmaciones nuevas disponibles en esa rama. En este caso, puedes pedirle a Bazel que vuelva a recuperar todos los repositorios externos de forma incondicional llamando a bazel fetch --force --all.

Además, algunas reglas de repositorio inspeccionan la máquina local y podrían quedar obsoletas si esta se actualizó. Aquí puedes pedirle a Bazel que solo vuelva a recuperar aquellos repositorios externos en los que la repository_rule definición tenga configurado el atributo configure, usa bazel fetch --force --configure.

Ejemplos

  • Cadena de herramientas configurada automáticamente de C++: Usa una regla de repositorio para crear automáticamente los archivos de configuración de C++ para Bazel buscando el compilador local de C++, el entorno y las marcas que admite el compilador de C++.

  • Los repositorios de Go usan varias repository_rule para definir la lista de dependencias necesarias para usar las reglas de Go.

  • rules_jvm_external crea un repositorio externo llamado @maven de forma predeterminada que genera destinos de compilación para cada artefacto de Maven en el árbol de dependencias transitivas.