Bazel admite dependencias externas, archivos fuente (texto y binarios) que se usan en tu compilación y que no son de tu espacio de trabajo. Por ejemplo, podrían 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.
En este documento, se proporciona una descripción general del sistema antes de examinar algunos de los conceptos con más detalle.
Descripción general del sistema
El sistema de dependencias externas de Bazel funciona en función de los módulos de Bazel, cada uno de los cuales es un proyecto de Bazel con versiones, y los repositorios (o repos), que son árboles de directorios que contienen archivos fuente.
Bazel comienza desde el módulo raíz, es decir, el proyecto en el que trabajas.
Al igual que todos los módulos, debe tener un archivo MODULE.bazel en su raíz de directorio,
que declare sus metadatos básicos y dependencias directas. El siguiente es un ejemplo básico:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")
A partir de ahí, Bazel busca todos los módulos de dependencia transitiva 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 dependencias transitivas antes de realizar la resolución de versiones.
Después de la resolución de versiones, en la que se selecciona una versión para cada módulo, Bazel vuelve a consultar el registro para aprender a definir un repositorio para cada módulo , es decir, cómo se deben recuperar las fuentes de cada módulo de dependencia. La mayoría de las veces, estos son solo archivos descargados de Internet y extraídos.
Los módulos también pueden especificar fragmentos de datos personalizados llamados etiquetas, que consumen las extensiones de módulo después de la resolución del módulo para definir repositorios adicionales. Estas extensiones pueden 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, respete el gráfico de dependencias creado a partir de los módulos de Bazel.
Los tres tipos de repositorios (el repositorio principal, que es el árbol de origen en el que estás
trabajando, los repositorios que representan módulos de dependencia transitiva y los repositorios
creados por extensiones de módulo) forman el espacio de trabajo en conjunto.
Los repositorios externos (repositorios no principales) se recuperan a pedido, por ejemplo, cuando las etiquetas (como @repo//pkg:target) hacen referencia a ellos
en los archivos BUILD.
Beneficios
El sistema de dependencias externas de Bazel ofrece una amplia gama de beneficios.
Resolución de dependencia automática
- Resolución de versiones determinista: Bazel adopta el algoritmo de resolución de versiones MVS determinista, lo que minimiza los conflictos y aborda los problemas de dependencia de diamantes.
- Administración de dependencias simplificada:
MODULE.bazeldeclara solo dependencias directas dependencias, mientras que las dependencias transitivas se resuelven automáticamente, lo que proporciona una descripción general más clara de las dependencias del proyecto. - Visibilidad estricta de las dependencias: Solo las dependencias directas son visibles, lo que garantiza la exactitud y la previsibilidad.
Integración en el ecosistema
- Registro central de Bazel: Un repositorio centralizado para descubrir y administrar dependencias comunes como módulos de Bazel .
- Adopción de proyectos que no son de Bazel: Cuando un proyecto que no es de Bazel (por lo general, una biblioteca de C++ ) se adapta a Bazel y está disponible en BCR, se optimiza su integración para toda la comunidad y se eliminan los esfuerzos duplicados y los conflictos de los archivos BUILD personalizados.
- Integración unificada con administradores de paquetes específicos del lenguaje: Los conjuntos de reglas
optimizan la integración con administradores de paquetes externos para dependencias que no son de Bazel, incluidos los siguientes:
- rules_jvm_external para Maven
- rules_python para PyPi
- bazel-gazelle para módulos de Go
- rules_rust para Cargo
Funciones avanzadas
- Extensiones de módulo: Las
use_repo_ruley las funciones de extensión de módulo permiten el uso flexible de reglas de repositorio personalizadas y lógica de resolución para introducir cualquier dependencia que no sea de Bazel. bazel modComando: El subcomando ofrece formas potentes de inspeccionar dependencias externas. Sabes exactamente cómo se define una dependencia externa y de dónde proviene.- Modo de proveedor: Obtén previamente las dependencias externas exactas que necesitas para facilitar las compilaciones sin conexión.
- Archivo de bloqueo: El archivo de bloqueo mejora la reproducibilidad de la compilación y acelera la resolución de dependencias.
- Certificaciones de procedencia de BCR(próximamente): Fortalece la seguridad de la cadena de suministro asegurándote de que se verifique la procedencia de las dependencias.
Conceptos
En esta sección, se proporcionan más detalles sobre los conceptos relacionados con las dependencias externas.
Módulo
Un proyecto de Bazel que puede tener varias versiones, cada una de las cuales puede tener dependencias de otros módulos.
En un espacio de trabajo local de Bazel, un módulo está representado por un repositorio.
Para obtener más detalles, consulta Módulos de Bazel.
Repositorio
Un árbol de directorios con un archivo de marcador de límite en su raíz, que contiene archivos fuente que se pueden usar en una compilación de Bazel. A menudo, se abrevia como repo.
Un archivo de marcador de límite de 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 de repositorio
indicará el límite de un repositorio; varios de estos 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 la raíz del espacio de trabajo.
Espacio de trabajo
El entorno compartido por todos los comandos de Bazel que se ejecutan en el mismo repositorio principal. Abarca el repositorio principal y el conjunto de todos los repositorios externos definidos.
Ten en cuenta que, históricamente, los conceptos de "repositorio" y "espacio de trabajo" se han combinado; el término "espacio de trabajo" se usó con frecuencia para hacer referencia al repositorio principal y, a veces, incluso como sinónimo de "repositorio".
Nombre canónico del repositorio
El nombre con el que siempre se puede direccionar un repositorio. En el contexto de un
espacio de trabajo, cada repositorio tiene un solo nombre canónico. Se puede direccionar un destino dentro de un repositorio
cuyo nombre canónico es canonical_name con la etiqueta
@@canonical_name//package:target (observa 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 direccionar un repositorio en el contexto de otro
repositorio determinado. Esto se puede considerar como el "apodo" de un repositorio: el repositorio con el
nombre canónico michael podría tener el nombre aparente mike en el contexto del repositorio
alice, pero podría tener el nombre aparente mickey en el contexto del repositorio
bob. En este caso, se puede direccionar un destino dentro de michael con la etiqueta
@mike//package:target en el contexto de alice (observa el único @).
Por el contrario, esto se puede entender como una asignación de repositorio: cada repositorio mantiene una asignación de "nombre de repositorio aparente" a un "nombre de repositorio canónico".
Regla de repositorio
Un esquema para las definiciones de repositorio que le indica a Bazel cómo materializar un
repositorio. Por ejemplo, podría ser "descargar un archivo ZIP desde una URL determinada
y extraerlo", o "recuperar un artefacto de Maven determinado y ponerlo a disposición como un
java_import destino", o simplemente "crear un symlink de un directorio local". Cada repositorio se
define llamando a una regla de repositorio con una cantidad adecuada de argumentos.
Consulta Reglas de repositorio para obtener más información sobre cómo escribir tus propias reglas de 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 crea un symlink de un
directorio local que ya es un repositorio de Bazel.
Recuperar un repositorio
La acción de poner un repositorio a disposición en el disco local mediante la ejecución de su regla de repositorio asociada. Los repositorios definidos en un espacio de trabajo no están disponibles en el disco local antes de que se recuperen.
Por lo general, Bazel solo recupera un repositorio cuando necesita algo de él, y 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 recuperación previa de un repositorio,
un destino o todos los repositorios necesarios para realizar cualquier compilación. Esta capacidad
permite 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 establece en falso (--nofetch), el comando utilizará cualquier versión almacenada en caché
de la dependencia y, si no existe ninguna, el comando resultará en
fallo.
Consulta las opciones de recuperación para obtener más información sobre el control de la recuperación.
Diseño del directorio
Después de recuperarse, el repositorio se puede encontrar en el subdirectorio external en la
base de salida, con 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 servir como archivo de límite de repositorio; sin embargo, también se puede usar
para 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 admiten instrucciones
load. La repo() función toma los mismos argumentos que la package()
función en los archivos BUILD; mientras que package()
especifica atributos comunes para todos los destinos de compilación dentro del paquete, repo()
lo hace de manera 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"],
)
El sistema WORKSPACE heredado
En versiones anteriores de Bazel (antes de la 9.0), las dependencias externas se introducían
definiendo repositorios en el archivo WORKSPACE (o WORKSPACE.bazel). Este archivo tiene una
sintaxis similar a la de los archivos BUILD, ya que emplea reglas de repositorio en lugar de reglas de compilación.
El siguiente fragmento es un ejemplo para usar la regla de repositorio http_archive en el
WORKSPACE archivo:
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 WORKSPACE
sistema, de forma predeterminada, el nombre canónico de un repositorio también es su nombre aparente para
todos los demás repositorios.
Consulta la lista completa de funciones disponibles en
WORKSPACE archivos.
Deficiencias del sistema WORKSPACE
En los años posteriores a la introducción del sistema WORKSPACE, los usuarios informaron muchos
puntos débiles, incluidos los siguientes:
- Bazel no evalúa los archivos
WORKSPACEde ninguna dependencia, por lo que todas las dependencias transitivas deben definirse en el archivoWORKSPACEdel 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
llamen a esta macro en sus archivos
WORKSPACE.- Esto tiene sus propios problemas: las macros no pueden
loadotros.bzlarchivos, por lo que estos proyectos deben definir sus dependencias transitivas en esta macro "deps" o solucionar este problema haciendo que el usuario llame a varias macros "deps" en capas. - Bazel evalúa el archivo
WORKSPACEde forma secuencial. Además, las dependencias se especifican conhttp_archivecon URLs, sin información de versión. Esto significa que no hay una forma confiable de realizar la resolución de versiones en el caso de dependencias de diamantes (Adepende deByC;ByCdependen de diferentes versiones deD).
- Esto tiene sus propios problemas: las macros no pueden
Debido a las deficiencias de WORKSPACE, el nuevo sistema basado en módulos (con nombre en clave "Bzlmod") reemplazó gradualmente el sistema WORKSPACE heredado entre Bazel 6 y 9. Lee la guía de migración de Bzlmod para obtener información sobre cómo migrar a Bzlmod.
Vínculos externos en Bzlmod
- Ejemplos de uso de Bzlmod en bazelbuild/examples
- Bazel External Dependencies Overhaul (documento de diseño original de Bzlmod)
- Charla de BazelCon 2021 sobre Bzlmod
- Charla del Día de la comunidad de Bazel sobre Bzlmod