Módulos de Bazel

Denuncia un problema Ver fuente Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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. Este es análogo a los conceptos conocidos en otros sistemas de gestión de dependencias, como un Artefacto de Maven, un paquete de npm, un módulo de Go o un contenedor de Cargo

Un módulo debe tener un archivo MODULE.bazel en la raíz del repositorio. Este archivo es el el manifiesto del módulo, la declaración de su nombre, versión, lista de dependencias directas y otra información. Para ver 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")

Consulta la lista completa de directivas disponibles en MODULE.bazel.

Para realizar la resolución del módulo, Bazel comienza por leer el archivo MODULE.bazel y, luego, solicita de forma reiterada el nombre de cualquier dependencia MODULE.bazel de un registro de Bazel hasta que descubre todo el gráfico de la dependencia.

De forma predeterminada, Bazel selecciona una versión de cada módulo. usar. Bazel representa cada módulo con un repositorio y consulta el registro. de nuevo para aprender a definir cada repositorio.

Formato de la versión

Bazel tiene un ecosistema diverso y los proyectos usan varios esquemas de control de versiones. El el más popular por mucho es SemVer, pero hay también destaca proyectos con diferentes esquemas, Abseil, cuyo se basan en fechas, por ejemplo, 20210324.2).

Por este motivo, Bzlmod adopta una versión más relajada de la especificación de SemVer. Las diferencias incluyen las siguientes:

  • SemVer prescribe que la "liberación" parte de la versión debe constar de 3 segmentos: MAJOR.MINOR.PATCH. En Bazel, este requisito se flexibiliza, por lo que que se permite cualquier cantidad de segmentos.
  • En SemVer, cada uno de los segmentos de la parte "release" debe ser solo dígitos. En Bazel, esto se relaja para permitir letras, y la semántica de comparación coincide con los “identificadores” en la parte “prelanzamiento”.
  • Además, no se aplican las semánticas de los aumentos de versiones principales, secundarias y de parches. Sin embargo, consulta el nivel de compatibilidad para obtener detalles sobre cómo denotamos la retrocompatibilidad.

Cualquier versión válida de SemVer es una versión válida del módulo de Bazel. Además, dos Las versiones a y b de SemVer comparan a < b si y solo si se cumple lo mismo cuando se comparan con versiones de módulos de Bazel.

Selección de versión

Considera el problema de la dependencia de diamante, un elemento básico en la dependencia con control de versiones de una organización. Supongamos que tienes el gráfico de dependencias:

       A 1.0
      /     \
   B 1.0    C 1.1
     |        |
   D 1.0    D 1.1

¿Qué versión de D se debería usar? Para resolver esta pregunta, Bzlmod usa la Selección de versión mínima (MVS) en el sistema de módulos de Go. MVS asume que todos los datos de un módulo son retrocompatibles, por lo que elige la versión más alta especificado por cualquier dependiente (D 1.1 en nuestro ejemplo). Se llama “mínima” porque D 1.1 es la versión más antigua que podría satisfacer nuestros requisitos. Incluso si existe D 1.2 o una versión posterior, no las seleccionamos. El uso de MVS crea un proceso de selección de versiones que es de alta fidelidad y reproducible.

Versiones retiradas

El registro puede declarar ciertas versiones como eliminadas si se deben evitar (por ejemplo, en el caso de vulnerabilidades de seguridad). Bazel muestra un error cuando se selecciona un de un módulo. Para corregir este error, actualiza a una versión más reciente que no se haya quitado o usa la marca --allow_yanked_versions para permitir explícitamente la versión quitada.

Nivel de compatibilidad

En Go, la suposición de MVS sobre la retrocompatibilidad funciona porque trata versiones incompatibles con versiones anteriores de un módulo como un módulo independiente. En términos de SemVer, lo que significa que A 1.x y A 2.x se consideran módulos distintos y pueden coexistir en el gráfico de dependencias resuelto. A su vez, esto es posible gracias a a la versión principal en la ruta del paquete en Go, por lo que conflictos de tiempo de compilación o de vinculación.

Sin embargo, Bazel no puede proporcionar esas garantías, por lo que necesita la "versión principal". para detectar versiones incompatibles con versiones anteriores. Este número se denomina nivel de compatibilidad y cada versión del módulo lo especifica en su directiva module(). Con esta información, Bazel puede arrojar un error cuando detecta las versiones del mismo módulo con diferentes niveles de compatibilidad existen en el gráfico de dependencias resuelto.

Anula

Especifica anulaciones en el archivo MODULE.bazel para alterar el comportamiento de la resolución de módulos de Bazel. Solo se aplican las anulaciones del módulo raíz. Si un módulo se usa como dependencia, se ignoran sus anulaciones.

Cada anulación se especifica para un nombre de módulo determinado, lo que afecta a en el gráfico de dependencias. Aunque solo las anulaciones del módulo raíz toman pueden ser para dependencias transitivas que el módulo raíz no dependen directamente.

Anulación de una sola versión

La single_version_override tiene varios propósitos:

  • Con el atributo version, puedes fijar una dependencia a una versión específica, independientemente de las versiones de la dependencia que se soliciten en el gráfico de dependencias.
  • Con el atributo registry, puedes forzar esta dependencia para que provenga de un en lugar de seguir el registro normal de selección.
  • Con los atributos patch*, puedes especificar un conjunto de parches para aplicar a el módulo descargado.

Todos estos atributos son opcionales y se pueden combinar entre sí.

Anulación de varias versiones

Se puede especificar un multiple_version_override para permitir que coexistan varias versiones del mismo módulo en el grafo de dependencias resuelto.

Puedes especificar una lista explícita de versiones permitidas para el módulo, que deben deben estar presentes en el gráfico de la dependencia antes de la resolución; debe existir alguna dependencia transitiva según cada versión permitida. Después del del módulo, solo quedan las versiones permitidas, mientras que Bazel actualiza otras versiones del módulo a la versión más cercana permitida y con nivel de compatibilidad. Si no existe una versión permitida más alta con el mismo nivel de compatibilidad, Bazel arroja un error.

Por ejemplo, si las versiones 1.1, 1.3, 1.5, 1.7 y 2.0 existen en el gráfico de dependencias antes de la resolución y la versión principal es la de compatibilidad nivel:

  • Una anulación de varias versiones que permite 1.3, 1.7 y 2.0 genera que 1.1 se actualice a 1.3, 1.5 se actualice a 1.7 y las demás versiones permanezcan iguales.
  • Una anulación de varias versiones que permite 1.5 y 2.0 genera un error, ya que 1.7 no tiene una versión superior con el mismo nivel de compatibilidad para actualizar.
  • Una anulación de varias versiones que permite 1.9 y 2.0 genera un error, ya que 1.9 no está presente en el gráfico de dependencias antes de la resolución.

Además, los usuarios también pueden anular el registro con el atributo registry, de manera similar a las anulaciones de una sola versión.

Anulaciones que no son del registro

Las anulaciones que no son de registro quitan un módulo por completo de la resolución de la versión. Bazel no solicita estos archivos MODULE.bazel desde un registro, sino desde el repositorio en sí.

Bazel admite las siguientes anulaciones que no son de registro:

Define repositorios que no representan módulos de Bazel

Con bazel_dep, puedes definir repositorios que representen otros módulos de Bazel. A veces, es necesario definir un repositorio que no represente un Bazel. module; por ejemplo, uno que contenga un archivo JSON sin formato para que se lea como datos.

En este caso, puedes usar la directiva use_repo_rule para definir directamente un repositorio invocando una regla de repositorio. Este repo solo será visible para el módulo al que definidos.

En términos generales, esto se implementa con el mismo mecanismo que las extensiones de módulo, lo que te permite definir repositorios con más flexibilidad.

Nombres de repositorios y dependencias estrictas

El nombre aparente de un repositorio que respalda un módulo a sus dependientes directos es el nombre predeterminado del nombre de módulo, a menos que el Atributo repo_name de bazel_dep directiva dice lo contrario. Ten en cuenta que esto significa que un módulo solo puede encontrar sus dependencias directas. Esto ayuda a evitar fallas accidentales debido a cambios en las dependencias transitivas.

El nombre canónico de un repositorio que respalda un módulo es module_name+version (por ejemplo, bazel_skylib+1.0.3) o module_name+ (por ejemplo, bazel_features+), dependiendo de si hay varias versiones del módulo en todo el gráfico de la dependencia (consulta multiple_version_override). Ten en cuenta que el formato de nombre canónico no es una API en la que debas confiar. está sujeto a cambios en cualquier momento. En lugar de codificar el nombre canónico, usa una forma compatible para obtenerlo directamente desde Bazel: * En los archivos BUILD y .bzl, usa Label.repo_name en una instancia de Label creada a partir de una cadena de etiqueta que proporciona el nombre aparente del repositorio, p. ej., Label("@bazel_skylib").repo_name. * Cuando busques archivos runfiles, usa $(rlocationpath ...) o una de las bibliotecas de archivos de ejecución @bazel_tools//tools/{bash,cpp,java}/runfiles o, para un conjunto de reglas rules_foo, en @rules_foo//foo/runfiles. * Cuando interactúes con Bazel desde una herramienta externa, como un IDE o un servidor de lenguaje, usa el comando bazel mod dump_repo_mapping para obtener la asignación de nombres aparentes a nombres canónicos para un conjunto determinado de repositorios.

Las extensiones de módulos también pueden agregar repositorios adicionales al alcance visible de un módulo.