Descripción general de las dependencias externas

Informar un problema Ver fuente

Bazel admite dependencias externas, archivos de origen (de texto y binarios) que se usan en tu compilación y que no son de tu lugar de trabajo. Por ejemplo, pueden ser un conjunto de reglas alojado en un repositorio de GitHub, un artefacto de Maven o un directorio en tu máquina local fuera de tu espacio de trabajo actual.

A partir de Bazel 6.0, existen dos formas de administrar dependencias externas con Bazel: el sistema WORKSPACE tradicional y centrado en el repositorio y el sistema MODULE.bazel más reciente centrado en el módulo (con nombre interno Bzlmod y habilitado con la marca --enable_bzlmod). Los dos sistemas se pueden usar juntos, pero Bzlmod reemplaza el sistema WORKSPACE en versiones futuras de Bazel. Consulta la guía de migración 1Bzlmod}.

En este documento, se explican los conceptos en torno a la administración de dependencias externas en Bazel, antes de entrar en más detalles sobre los dos sistemas en orden.

Conceptos

Repositorio

Un árbol de directorios con un archivo de marcador de límite en la raíz que contiene archivos de origen que se pueden usar en una compilación de Bazel A menudo se abrevia como repo.

Un archivo de marcador de límite del repositorio puede ser MODULE.bazel (lo que indica que este repositorio representa un módulo de Bazel), REPO.bazel (consulta a continuación) o, en contextos heredados, WORKSPACE o WORKSPACE.bazel. Cualquier archivo de marcador de límite del repositorio indicará el límite de un repositorio; varios de esos archivos pueden coexistir en un directorio.

Repositorio principal

El repositorio en el que se ejecuta el comando de Bazel actual.

La raíz del repositorio principal también se conoce como raíz del lugar de trabajo.

Espacio de trabajo

El entorno que comparten todos los comandos de Bazel se ejecuta en el mismo repositorio principal. Abarca el repositorio principal y el conjunto de repositorios externos definidos.

Ten en cuenta que, históricamente, los conceptos de "repositorio" y "lugar de trabajo" se combinaron. El término "espacio de trabajo" a menudo se usa para referirse al repositorio principal y, a veces, incluso se usa como sinónimo de "repositorio".

Nombre del repositorio canónico

El nombre canónico con el que se puede acceder a un repositorio. Dentro del contexto de un lugar de trabajo, cada repositorio tiene un solo nombre canónico. Un destino dentro de un repositorio cuyo nombre canónico es canonical_name se puede abordar mediante la etiqueta @@canonical_name//package:target (ten en cuenta que el @ doble).

El repositorio principal siempre tiene la cadena vacía como nombre canónico.

Nombre aparente del repositorio

El nombre con el que se puede acceder a un repositorio en el contexto de otro repositorio específico. Esto se puede considerar como el "sobrenombre" de un repositorio: el repo con el nombre canónico michael puede tener el nombre aparente mike en el contexto del alice del repositorio, pero puede tener el nombre aparente mickey en el contexto del bob del repositorio. En este caso, la etiqueta @mike//package:target puede abordar un destino dentro de michael en el contexto de alice (ten en cuenta el único @).

Por el contrario, esto se puede interpretar como una asignación de repositorio: cada repositorio mantiene una asignación de "nombre aparente del repositorio" a "nombre de repositorio canónico".

Regla del repositorio

Un esquema para las definiciones de repositorios que le indica a Bazel cómo materializar un repositorio. Por ejemplo, podría ser "descarga un archivo ZIP de una URL determinada y extráelo", o "recupera un artefacto de Maven determinado y haz que esté disponible como un destino java_import" o simplemente "symlink a local directory". Cada repositorio se define mediante una llamada a la regla del repositorio con una cantidad adecuada de argumentos.

Consulta Reglas del repositorio para obtener más información sobre cómo escribir tus propias reglas del repositorio.

Las reglas de repositorio más comunes son http_archive, que descarga un archivo de una URL y lo extrae, y local_repository, que envía symlinks a un directorio local que ya es un repositorio de Bazel.

Recupera un repositorio

La acción de hacer que un repositorio esté disponible en el disco local mediante la ejecución de su regla de repositorio asociada. Los repositorios definidos en un lugar de trabajo no están disponibles en el disco local antes de que se recuperen.

Por lo general, Bazel solo recupera un repo cuando necesita algo del repo y este aún no se recuperó. Si el repositorio ya se recuperó antes, Bazel solo lo vuelve a recuperar si cambió su definición.

El comando fetch se puede usar para iniciar una carga previa de un repositorio, un destino o todos los repositorios necesarios para realizar cualquier compilación. Esta capacidad habilita las compilaciones sin conexión con la opción --nofetch.

La opción --fetch sirve para administrar el acceso a la red. Su valor predeterminado es verdadero. Sin embargo, cuando se configura como falsa (--nofetch), el comando usará cualquier versión almacenada en caché de la dependencia y, si no existe ninguna, el comando generará un error.

Consulta Opciones de recuperación para obtener más información para controlar la recuperación.

Diseño de directorio

Después de recuperarlo, puedes encontrar el repositorio en el subdirectorio external de la base de salida, debajo de su nombre canónico.

Puedes ejecutar el siguiente comando para ver el contenido del repositorio con el nombre canónico canonical_name:

ls $(bazel info output_base)/external/ canonical_name 

Archivo REPO.bazel

El archivo REPO.bazel se usa para marcar el límite superior del árbol de directorios que constituye un repositorio. No necesita contener nada para funcionar como un archivo de límite del repositorio. Sin embargo, también se puede usar con el objetivo de especificar algunos atributos comunes para todos los destinos de compilación dentro del repositorio.

La sintaxis de un archivo REPO.bazel es similar a la de los archivos BUILD, excepto que no se admite ninguna sentencia load y que solo está disponible una función, repo(). repo() toma los mismos argumentos que la función package() en los archivos BUILD, mientras que package() especifica atributos comunes para todos los destinos de compilación dentro del paquete, mientras que repo() lo hace de forma análoga para todos los destinos de compilación dentro del repositorio.

Por ejemplo, puedes especificar una licencia común para todos los destinos de tu repositorio con el siguiente archivo REPO.bazel:

repo(
    default_package_metadata = ["//:my_license"],
)

Cómo administrar dependencias externas con Bzlmod

Bzlmod, el nuevo subsistema de dependencia externa, no funciona directamente con definiciones de repositorio. En cambio, compila un gráfico de dependencia a partir de módulos, ejecuta extensiones sobre el gráfico y define los repositorios según corresponda.

Un módulo de Bazel es un proyecto de Bazel que puede tener varias versiones, cada una de las cuales publica metadatos sobre otros módulos de los que depende. Un módulo debe tener un archivo MODULE.bazel en la raíz del repositorio, junto al archivo WORKSPACE. Este archivo es el manifiesto del módulo, en el que se declara su nombre, versión y lista de dependencias, entre otros datos. El siguiente es un ejemplo básico:

module(name = "my-module", version = "1.0")

bazel_dep(name = "rules_cc", version = "0.0.1")
bazel_dep(name = "protobuf", version = "3.19.0")

Un módulo solo debe enumerar sus dependencias directas, que Bzlmod busca en un registro de Bazel; de forma predeterminada, el Registro Central de Bazel. El registro proporciona los archivos MODULE.bazel de las dependencias, lo que permite que Bazel descubra todo el gráfico de dependencia transitivo antes de realizar la resolución de la versión.

Después de la resolución de la versión, en la que se selecciona una versión para cada módulo, Bazel vuelve a consultar el registro a fin de aprender a definir un repositorio para cada módulo (en la mayoría de los casos, mediante http_archive).

Los módulos también pueden especificar fragmentos de datos personalizados llamados etiquetas, que las extensiones de módulo consumen después de la resolución del módulo para definir repositorios adicionales. Estas extensiones tienen capacidades similares a las reglas de repositorio, lo que les permite realizar acciones como E/S de archivos y enviar solicitudes de red. Entre otras cosas, permiten que Bazel interactúe con otros sistemas de administración de paquetes y, al mismo tiempo, respetan el gráfico de dependencia compilado a partir de los módulos de Bazel.

Define repositorios con WORKSPACE

Históricamente, puedes administrar dependencias externas si defines repositorios en el archivo WORKSPACE (o WORKSPACE.bazel). Este archivo tiene una sintaxis similar a los archivos BUILD y emplea reglas de repositorio en lugar de reglas de compilación.

El siguiente fragmento es un ejemplo de cómo usar la regla del repositorio http_archive en el archivo WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "foo",
    urls = ["https://example.com/foo.zip"],
    sha256 = "c9526390a7cd420fdcec2988b4f3626fe9c5b51e2959f685e8f4d170d1a9bd96",
)

El fragmento define un repositorio cuyo nombre canónico es foo. En el sistema WORKSPACE, de forma predeterminada, el nombre canónico de un repositorio es también el nombre aparente para todos los demás repositorios.

Consulta la lista completa de funciones disponibles en los archivos WORKSPACE.

Deficiencias del sistema de WORKSPACE

En los años desde que se introdujo el sistema WORKSPACE, los usuarios informaron varios problemas, como los siguientes:

  • Bazel no evalúa los archivos WORKSPACE de ninguna dependencia, por lo que todas las dependencias transitivas deben definirse en el archivo WORKSPACE del repositorio principal, además de las dependencias directas.
  • Para solucionar este problema, los proyectos adoptaron el patrón "deps.bzl", en el que definen una macro que, a su vez, define varios repositorios y les piden a los usuarios que la llamen en sus archivos WORKSPACE.
    • Esto tiene sus propios problemas: las macros no pueden load otros archivos .bzl, por lo que estos proyectos deben definir sus dependencias transitivas en esta macro "deps" o solucionar este problema si le piden al usuario que llame a varias macros de "deps" con capas.
    • Bazel evalúa el archivo WORKSPACE de forma secuencial. Además, las dependencias se especifican mediante http_archive con URLs, sin información de la versión. Eso significa que no hay una forma confiable de resolver la versión en el caso de dependencias de diamante (A depende de B y C; B y C dependen de versiones diferentes de D).

Debido a las deficiencias de WORKSPACE, Bzlmod reemplazará el sistema WORKSPACE heredado en futuras versiones de Bazel. Lee la guía de migración de Bzlmod para descubrir cómo migrar a Bzlmod.