En esta página, se responden algunas preguntas frecuentes sobre las dependencias externas en Bazel.
MODULE.bazel
¿Cómo debería versionar un módulo de Bazel?
Configurar version con la directiva module en el archivo de origen
MODULE.bazel puede tener varias desventajas y efectos secundarios no deseados si no se
administra con cuidado:
Duplicación: Por lo general, lanzar una versión nueva de un módulo implica tanto aumentar la versión en
MODULE.bazelcomo etiquetar el lanzamiento, dos pasos separados que pueden desincronizarse. Si bien la automatización puede reducir este riesgo, es más sencillo y seguro evitarlo por completo.Inconsistencia: Los usuarios que anulan un módulo con una confirmación específica mediante una anulación que no es de registro verán una versión incorrecta. Por ejemplo, si el
MODULE.bazelen el archivo de origen estableceversion = "0.3.0", pero se realizaron confirmaciones adicionales desde ese lanzamiento, un usuario que anule con una de esas confirmaciones aún vería0.3.0. En realidad, la versión debería reflejar que está por delante del lanzamiento, por ejemplo,0.3.1-rc1.Problemas de anulación que no son de registro: El uso de valores de marcador de posición puede causar problemas cuando los usuarios anulan un módulo con una anulación que no es de registro. Por ejemplo,
0.0.0no se ordena como la versión más alta, que suele ser el comportamiento esperado que desean los usuarios cuando realizan una anulación que no es de registro.
Por lo tanto, es mejor evitar configurar la versión en el archivo de origen
MODULE.bazel. En su lugar, configúrala en el MODULE.bazel almacenado en el registro
(p.ej., el Registro central de Bazel), que es la fuente de información real para
la versión del módulo durante la resolución de dependencias externas de Bazel (consulta Registros de Bazel).
Por lo general, esto se automatiza. Por ejemplo, el rules-template repositorio de reglas de ejemplo
usa una acción de GitHub bazel-contrib/publish-to-bcr publish.yaml para
publicar el lanzamiento en el BCR. La acción genera un parche para el archivo de origen
MODULE.bazel con la versión de lanzamiento. Este parche se almacena en el
registro y se aplica cuando se recupera el módulo durante la resolución de dependencias externas
de Bazel.
De esta manera, la versión de los lanzamientos en el registro se establecerá correctamente en
la versión lanzada y, por lo tanto, bazel_dep, single_version_override y
multiple_version_override funcionarán como se espera, al tiempo que se evitan posibles
problemas cuando se realiza una anulación que no es de registro, ya que la versión del archivo de origen
será el valor predeterminado (''), que siempre se manejará
correctamente (después de todo, es el valor predeterminado de la versión) y se comportará como se
espera cuando se ordene (la cadena vacía se trata como la versión más alta).
¿Qué es un nivel de compatibilidad?
Debes dejar de usar compatibility_level.
Aumentar compatibility_level genera conflictos de versiones que son difíciles
de resolver para los usuarios finales. Por lo tanto, a partir de Bazel 8.6.0 y 9.1.0, ambos
compatibility_level y max_compatibility_level son operaciones no op.
Los mantenedores de módulos que introducen cambios rotundos importantes deben asegurarse de que las fallas de compilación proporcionen mensajes de error claros y rutas de migración prácticas.
Documentación heredada:
El compatibility_level de un módulo de Bazel
debe aumentarse en la misma confirmación que introduce un cambio incompatible con versiones anteriores ("rotundo").
Sin embargo, Bazel puede mostrar un error si detecta que existen versiones del mismo módulo con diferentes niveles de compatibilidad en el gráfico de dependencias resuelto. Esto puede suceder cuando, por ejemplo, dos módulos dependen de versiones de un tercer módulo con diferentes niveles de compatibilidad.
Por lo tanto, aumentar compatibility_level con demasiada frecuencia puede ser muy perjudicial
y no se recomienda. Para evitar esta situación, compatibility_level se debe
aumentar solo cuando el cambio rotundo afecte a la mayoría de los casos de uso y no sea
fácil de migrar o solucionar.
¿Por qué MODULE.bazel no admite loads?
Durante la resolución de dependencias, el archivo MODULE.bazel de todas las dependencias externas a las que se hace referencia
se recupera de los registros. En esta etapa, aún no se recuperan los archivos de origen de
las dependencias. Por lo tanto, si el archivo MODULE.bazel loads
otro archivo, Bazel no podrá recuperar ese archivo sin
recuperar todo el archivo de origen. Ten en cuenta que el archivo MODULE.bazel en sí es
especial, ya que se aloja directamente en el registro.
Existen algunos casos de uso en los que las personas que solicitan loads en MODULE.bazel suelen estar
interesadas, y se pueden resolver sin loads:
- Asegurarse de que la versión que aparece en MODULE.bazel sea coherente con los metadatos de compilación almacenados en otro lugar, por ejemplo, en un archivo .bzl: Esto se puede lograr con el método
native.module_versionen un archivo .bzl cargado desde un archivo BUILD. - Dividir un archivo MODULE.bazel muy grande en secciones administrables,
en especial para monorepos: El módulo raíz puede usar la
includedirectiva para dividir su archivo MODULE.bazel en varios segmentos. Por el mismo motivo por el que no permitimosloads en archivos MODULE.bazel,includeno se puede usar en módulos que no sean raíz. - Es posible que los usuarios del sistema WORKSPACE anterior recuerden declarar un repositorio y, luego,
inmediatamente
loading desde ese repositorio para realizar una lógica compleja. Esta capacidad se reemplazó por extensiones de módulo.
¿Puedo especificar un rango de SemVer para un bazel_dep?
No. Algunos otros administradores de paquetes, como npm y Cargo admiten rangos de versiones (implícita o explícitamente), y esto suele requerir un solucionador de restricciones (lo que dificulta la predicción del resultado para los usuarios) y hace que la resolución de versiones no sea reproducible sin un archivo de bloqueo.
En su lugar, Bazel usa la selección de versión mínima como Go, que, por el contrario, facilita la predicción del resultado y garantiza la reproducibilidad. Esta es una compensación que coincide con los objetivos de diseño de Bazel.
Además, las versiones de los módulos de Bazel son un superconjunto de SemVer, por lo que lo que tiene sentido en un entorno SemVer estricto no siempre se transfiere a las versiones de los módulos de Bazel.
¿Puedo obtener automáticamente la versión más reciente de un bazel_dep?
En ocasiones, algunos usuarios solicitan la capacidad de especificar bazel_dep(name = "foo",
version = "latest") para obtener automáticamente la versión más reciente de una dependencia. Esto es
similar a la pregunta sobre los rangos
de SemVer, y la respuesta también es
no.
La solución recomendada aquí es que la automatización se encargue de esto. Por ejemplo, Renovate admite módulos de Bazel.
A veces, los usuarios que hacen esta pregunta realmente buscan una forma de iterar rápidamente durante el desarrollo local. Esto se puede lograr con un
local_path_override.
¿Por qué todos estos use_repos?
Los usos de la extensión del módulo en los archivos MODULE.bazel a veces vienen con una directiva grande
use_repo. Por ejemplo, un uso típico de la
go_deps extensión de gazelle podría verse de la siguiente manera:
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(
go_deps,
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
"org_golang_x_net",
... # potentially dozens of lines...
)
La directiva use_repo larga puede parecer redundante, ya que la información ya está
en el archivo go.mod al que se hace referencia.
El motivo por el que Bazel necesita esta use_repo directiva es que ejecuta las extensiones de módulo
de forma diferida. Es decir, una extensión de módulo solo se ejecuta si se observa su resultado. Como el "resultado" de una extensión de módulo son las definiciones de repositorio, esto significa
que solo ejecutamos una extensión de módulo si se solicita un repositorio que define (por
ejemplo, si se compila el destino @org_golang_x_net//:foo en el ejemplo
anterior). Sin embargo, no sabemos qué repositorios definiría una extensión de módulo hasta
después de ejecutarla. Aquí es donde entra en juego la directiva use_repo. El usuario puede
indicarle a Bazel qué repositorios espera que genere la extensión, y Bazel solo
ejecutará la extensión cuando se usen estos repositorios específicos.
Para ayudar a mantener esta directiva use_repo, una extensión de módulo puede mostrar
un extension_metadata
objeto desde su función de implementación. El usuario puede ejecutar el bazel mod tidy
comando para actualizar las use_repo directivas de estas extensiones de módulo.
Migración de Bzlmod
¿Qué se evalúa primero, MODULE.bazel o WORKSPACE?
Cuando se configuran --enable_bzlmod y --enable_workspace, es natural
preguntarse qué sistema se consulta primero. La respuesta breve es que MODULE.bazel
(Bzlmod) se evalúa primero.
La respuesta larga es que "qué se evalúa primero" no es la pregunta correcta.
En cambio, la pregunta correcta es: en el contexto del repositorio con
el nombre canónico @@foo, ¿a qué se resuelve el nombre de repositorio
aparente @bar? Como alternativa, ¿cuál
es la asignación de repositorio de @@base?
Las etiquetas con nombres de repositorio aparentes (un solo @ inicial) pueden hacer referencia a diferentes
elementos según el contexto desde el que se resuelven. Cuando ves una etiqueta
@bar//:baz y te preguntas a qué apunta, primero debes averiguar
cuál es el repositorio de contexto. Por ejemplo, si la etiqueta está en un archivo BUILD ubicado
en el repositorio @@foo, el repositorio de contexto es @@foo.
Luego, según cuál sea el repositorio de contexto, se puede usar la tabla "Visibilidad del repositorio" en la guía de migración para averiguar a qué repositorio se resuelve realmente un nombre aparente.
- Si el repositorio de contexto es el repositorio principal (
@@):- Si
bares un nombre de repositorio aparente que introduce el archivo MODULE.bazel del módulo raíz (a través de cualquiera debazel_dep,use_repo,module,use_repo_rule), entonces@barse resuelve en lo que afirma ese archivo MODULE.bazel. - De lo contrario, si
bares un repositorio definido en WORKSPACE (lo que significa que su nombre canónico es@@bar), entonces@barse resuelve en@@bar. - De lo contrario,
@barse resuelve en algo como@@[unknown repo 'bar' requested from @@], y esto, en última instancia, generará un error.
- Si
- Si el repositorio de contexto es un repositorio del mundo de Bzlmod (es decir, corresponde a un
módulo de Bazel que no es raíz o se genera mediante una extensión de módulo), entonces
solo verá otros repositorios del mundo de Bzlmod y no repositorios del mundo de WORKSPACE.
- En particular, esto incluye cualquier repositorio que se introduzca en una extensión de módulo similar a
non_module_depsen el módulo raíz o instanciasuse_repo_ruleen el módulo raíz.
- En particular, esto incluye cualquier repositorio que se introduzca en una extensión de módulo similar a
- Si el repositorio de contexto se define en WORKSPACE:
- Primero, verifica si la definición del repositorio de contexto tiene el atributo mágico
repo_mappingSi es así, primero revisa la asignación (por lo que, para un repositorio definido conrepo_mapping = {"@bar": "@baz"}, veríamos a@baza continuación). - Si
bares un nombre de repositorio aparente que introduce el archivo MODULE.bazel del módulo raíz,@barse resuelve en lo que afirma ese archivo MODULE.bazel. (Esto es lo mismo que el elemento 1 en el caso del repositorio principal). - De lo contrario,
@barse resuelve en@@bar. Lo más probable es que apunte a un repositoriobardefinido en WORKSPACE. Si no se define ese repositorio, Bazel mostrará un error.
- Primero, verifica si la definición del repositorio de contexto tiene el atributo mágico
Para obtener una versión más concisa, haz lo siguiente:
- Los repositorios del mundo de Bzlmod (excepto el repositorio principal) solo verán los repositorios del mundo de Bzlmod
- Los repositorios del mundo de WORKSPACE (incluido el repositorio principal) primero verán lo que define el módulo raíz en el mundo de Bzlmod y, luego, volverán a ver los repositorios del mundo de WORKSPACE.
Cabe destacar que las etiquetas en la línea de comandos de Bazel (incluidas las marcas de Starlark, los valores de marca con tipo de etiqueta y los patrones de destino de compilación o prueba) se tratan como si tuvieran el repositorio principal como repositorio de contexto.
Otro
¿Cómo preparo y ejecuto una compilación sin conexión?
Usa el comando bazel fetch para recuperar repositorios. Puedes usar la marca --repo
(como bazel fetch --repo @foo) para recuperar solo el repositorio @foo (resuelto en el
contexto del repositorio principal, consulta la pregunta
anterior) o usar un
patrón de destino (como bazel fetch @foo//:bar) para recuperar todas las dependencias transitivas de
@foo//:bar (esto equivale a bazel build --nobuild @foo//:bar).
Para asegurarte de que no se realicen recuperaciones durante una compilación, usa --nofetch. Más precisamente,
esto hace que falle cualquier intento de ejecutar una regla de repositorio no local.
Si deseas recuperar repositorios y modificarlos para realizar pruebas de forma local, considera usar
el bazel vendor comando.
¿Cómo aíslo mis compilaciones de Internet?
Estos son algunos sitios web en Internet de acceso público en los que se basa una compilación típica de Bazel y lo que puedes hacer para aislarte de posibles interrupciones, en especial como usuario empresarial de Bazel.
releases.bazel.build: Bazelisk descarga los objetos binarios de lanzamiento de Bazel desde este sitio web. Puedes configurar Bazelisk para que se descargue desde el duplicado interno de tu empresa.bcr.bazel.build: Bazel consulta el BCR para obtener metadatos del módulo durante la resolución del módulo. Puedes duplicar el BCR y configurar la marca--registrypara quitar tu dependencia de la infraestructura de servicio de BCR (consulta el aviso legal para obtener más información). Como alternativa, puedes asegurarte de que tu archivo MODULE.bazel.lock esté actualizado y configurar tus máquinas de CI o de desarrollador con una caché de descarga rellenada previamente (--repository_cache). Si se configura correctamente, la caché de descarga contendrá todos los archivos de registro necesarios para una compilación, y el archivo de bloqueo contendrá sus sumas de verificación. Luego, Bazel usará los resultados almacenados en caché y evitará el acceso a Internet durante la resolución del módulo.mirror.bazel.buildygithub.com: Muchos módulos tienen archivos de origen alojados en estos dos sitios web. Considera configurar un duplicado interno de la empresa para los archivos de origen y usa--downloader_configo--module_mirrorspara dirigir Bazel a ellos. Como alternativa, una caché de descarga rellenada previamente , como se mencionó en el punto anterior, también ayudará a Bazel a evitar por completo el acceso a Internet para los archivos de origen.
¿Cómo uso proxies HTTP?
Bazel respeta las variables de entorno http_proxy y HTTPS_PROXY que suelen
aceptar otros programas, como
curl.
¿Cómo hago que Bazel prefiera IPv6 en configuraciones de IPv4/IPv6 de pila doble?
En máquinas solo IPv6, Bazel puede descargar dependencias sin cambios. Sin embargo,
en máquinas IPv4/IPv6 de pila doble, Bazel sigue la misma convención que Java,
y prefiere IPv4 si está habilitado. En algunas situaciones, por ejemplo, cuando la red IPv4
no puede resolver o alcanzar direcciones externas, esto puede causar excepciones Network
unreachable y fallas de compilación. En estos casos, puedes anular el comportamiento de Bazel para preferir IPv6 con la propiedad del sistema java.net.preferIPv6Addresses=true
Específicamente, son las siguientes:
Usa la opción de inicio, por ejemplo, agregando la siguiente línea en tu archivo
.bazelrc:--host_jvm_args=-Djava.net.preferIPv6Addresses=truestartup --host_jvm_args=-Djava.net.preferIPv6Addresses=trueCuando ejecutes destinos de compilación de Java que necesiten conectarse a Internet (como para pruebas de integración), usa la
--jvmopt=-Djava.net.preferIPv6Addresses=truemarca de herramienta. Por ejemplo, incluye en tu.bazelrcarchivo:build --jvmopt=-Djava.net.preferIPv6AddressesSi usas
rules_jvm_externalpara la resolución de la versión de dependencia, también agrega-Djava.net.preferIPv6Addresses=truea laCOURSIER_OPTSvariable de entorno para proporcionar opciones de JVM para Coursier.
¿Se pueden ejecutar reglas de repositorio de forma remota con la ejecución remota?
No, o al menos, todavía no. Es posible que los usuarios que emplean servicios de ejecución remota para acelerar
sus compilaciones noten que las reglas de repositorio aún se ejecutan de forma local. Por ejemplo, primero se descargaría un
http_archive en la máquina local (con cualquier
caché de descarga local, si corresponde), se extraerá y, luego, cada archivo de origen se
subiría al servicio de ejecución remota como un archivo de entrada. Es natural preguntarse
por qué el servicio de ejecución remota no solo descarga y extrae ese archivo,
lo que ahorra un viaje de ida y vuelta inútil.
Parte del motivo es que las reglas de repositorio (y las extensiones de módulo) son similares a "secuencias de comandos" que ejecuta Bazel. Un ejecutor remoto ni siquiera tiene instalado Bazel.
Otro motivo es que Bazel suele necesitar los archivos BUILD en los archivos descargados y extraídos para realizar la carga y el análisis, que se realizan de forma local.
Existen ideas preliminares para resolver este problema mediante la reimaginación de las reglas de repositorio como
reglas de compilación, lo que, naturalmente, les permitiría ejecutarse de forma remota, pero, a la inversa,
plantearía nuevas inquietudes arquitectónicas (por ejemplo, los query comandos podrían
necesitar ejecutar acciones, lo que complicaría su diseño).
Para obtener más información sobre este tema, consulta Una forma de admitir repositorios que necesitan Bazel para recuperarse.