En esta página, se responden algunas preguntas frecuentes sobre las dependencias externas en Bazel.
MODULE.bazel
¿Por qué MODULE.bazel no admite load
?
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 fuente de las dependencias, por lo que, si el archivo MODULE.bazel load
es otro archivo, Bazel no puede recuperar ese archivo sin recuperar todo el archivo fuente. Ten en cuenta que el archivo MODULE.bazel es especial, ya que se aloja directamente en el registro.
Por lo general, a las personas que solicitan load
en MODULE.bazel les interesan algunos casos de uso, y se pueden resolver sin load
:
- Asegúrate 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_version
en un archivo .bzl cargado desde un archivo BUILD. - Dividir un archivo MODULE.bazel muy grande en secciones fáciles de administrar, principalmente para monorepos: el módulo raíz puede usar la directiva
include
para dividir su archivo MODULE.bazel en varios segmentos Por el mismo motivo por el que no permitimosload
en los archivos MODULE.bazel,include
no se puede usar en módulos que no sean raíz. - Los usuarios del antiguo sistema de WORKSPACE pueden recordar declarar un repositorio y, luego,
load
desde ese repositorio de inmediato para realizar una lógica compleja. Esta función se reemplazó por las extensiones de módulos.
¿Puedo especificar un rango de SemVer para un bazel_dep
?
No. Algunos otros administradores de paquetes, como npm y Cargo, admiten rangos de versiones (de forma implícita o explícita), 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 se pueda reproducir sin un archivo de bloqueo.
En cambio, Bazel usa la selección de versión mínima como Go, que, en cambio, 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 estricto de SemVer 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 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 en realidad buscan una manera de iterar rápidamente durante el desarrollo local. Esto se puede lograr con un objeto local_path_override
.
¿Por qué hay tantos use_repo
?
Los usos de extensiones de módulo en los archivos MODULE.bazel a veces incluyen una directiva use_repo
grande. Por ejemplo, un uso típico de la extensión go_deps
desde 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 directiva use_repo
es que ejecuta extensiones de módulo de forma diferida. Es decir, una extensión de módulo solo se ejecuta si se observa su resultado. Dado que el "resultado" de una extensión de módulo son definiciones de repositorios, esto significa que solo ejecutamos una extensión de módulo si se solicita un repositorio que define (por ejemplo, si se compila el @org_golang_x_net//:foo
de destino, 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 la directiva use_repo
. El usuario puede decirle 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 objeto extension_metadata
desde su función de implementación. El usuario puede ejecutar el comando bazel mod tidy
para actualizar las directivas use_repo
de estas extensiones de módulo.
Migración de Bzlmod
¿Cuál se evalúa primero, MODULE.bazel o WORKSPACE?
Cuando se configuran --enable_bzlmod
y --enable_workspace
, es natural preguntarnos cuál es el sistema que se consulta primero. La respuesta corta es que MODULE.bazel (Bzlmod) se evalúa primero.
La respuesta larga es que “cuál se evalúa primero” no es la pregunta correcta. En cambio, la pregunta correcta es: en el contexto del repo con el nombre canónico @@foo
, ¿a qué se resuelve el nombre aparente del repo @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 en realidad, 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” de la guía de migración para averiguar a qué repositorio se resuelve realmente un nombre aparente.
- Si el repositorio de contexto es el principal (
@@
):- Si
bar
es 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
ouse_repo_rule
),@bar
resuelve lo que afirma ese archivo MODULE.bazel. - De lo contrario, si
bar
es un repositorio definido en WORKSPACE (lo que significa que su nombre canónico es@@bar
),@bar
se resuelve en@@bar
. - De lo contrario,
@bar
se 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 de Bzlmod-world (es decir, corresponde a un módulo de Bazel que no es raíz o es generado por una extensión de módulo), solo verá otros repositorios de Bzlmod-world y no repositorios de WORKSPACE-world.
- En particular, esto incluye cualquier repositorio que se presente en una extensión de módulo similar a
non_module_deps
en el módulo raíz o instancias deuse_repo_rule
en el módulo raíz.
- En particular, esto incluye cualquier repositorio que se presente en una extensión de módulo similar a
- Si el repositorio de contexto se define en WORKSPACE, haz lo siguiente:
- Primero, verifica si la definición del repositorio de contexto tiene el atributo mágico
repo_mapping
. Si es así, primero revisa la asignación (por lo que, para un repo definido conrepo_mapping = {"@bar": "@baz"}
, estaríamos viendo@baz
a continuación). - Si
bar
es un nombre de repo aparente que introduce el archivo MODULE.bazel del módulo raíz,@bar
se resuelve en lo que afirma ese archivo MODULE.bazel. (Esto es lo mismo que el artículo 1 en el caso del repositorio principal). - De lo contrario,
@bar
se resuelve como@@bar
. Es probable que apunte a unbar
de repo definido en WORKSPACE. Si no se define ese repo, Bazel arrojará 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 de Bzlmod-world (sin incluir el repositorio principal) solo verán los repositorios de Bzlmod-world.
- Los repositorios de WORKSPACE-world (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 de WORKSPACE-world.
Es importante tener en cuenta que las etiquetas en la línea de comandos de Bazel (incluidas las marcas de Starlark, los valores de marcas con formato de etiqueta y los patrones de destino de compilación o prueba) se consideran como el repositorio principal como el repositorio de contexto.
Otro
¿Cómo preparo y ejecuto una compilación sin conexión?
Usa el comando bazel fetch
para obtener información de los repositorios de antemano. Puedes usar la marca --repo
(como bazel fetch --repo @foo
) para recuperar solo el repo @foo
(resuelto en el contexto del repo 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 quieres recuperar repositorios y modificarlos para probarlos de forma local, considera usar el comando bazel vendor
.
¿Cómo uso los proxies HTTP?
Bazel respeta las variables de entorno http_proxy
y HTTPS_PROXY
que suelen aceptar otros programas, como curl.
¿Cómo hago para que Bazel prefiera IPv6 en configuraciones de pila doble IPv4/IPv6?
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 generar excepciones de 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
.
En particular, haz lo siguiente:
Usa la opción de inicio
--host_jvm_args=-Djava.net.preferIPv6Addresses=true
. Por ejemplo, agrega la siguiente línea a tu archivo.bazelrc
:startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true
Cuando ejecutes destinos de compilación de Java que deban conectarse a Internet (como en las pruebas de integración), usa la marca de herramienta
--jvmopt=-Djava.net.preferIPv6Addresses=true
. Por ejemplo, incluye lo siguiente en tu archivo.bazelrc
:build --jvmopt=-Djava.net.preferIPv6Addresses
Si usas
rules_jvm_external
para la resolución de versiones de dependencias, también agrega-Djava.net.preferIPv6Addresses=true
a la variable de entornoCOURSIER_OPTS
para proporcionar opciones de JVM para Coursier.
¿Se pueden ejecutar reglas de repo de forma remota con la ejecución remota?
No, o al menos, aún no. Es posible que los usuarios que empleen servicios de ejecución remota para acelerar
sus compilaciones noten que las reglas del 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ía y, luego, cada archivo de origen se subiría al servicio de ejecución remota como archivo de entrada. Es natural preguntar
por qué el servicio de ejecución remota no solo descarga y extrae ese archivo,
lo que ahorra un recorrido inútil.
Parte de la razón es que las reglas de repositorio (y las extensiones de módulo) son similares a las "secuencias de comandos" que ejecuta Bazel. Un ejecutor remoto ni siquiera tiene que tener 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.
Hay ideas preliminares para resolver este problema reimaginando las reglas del repositorio como reglas de compilación, lo que, por supuesto, les permitiría ejecutarse de forma remota, pero, por el contrario, generaría nuevas inquietudes arquitectónicas (por ejemplo, los comandos query
podrían necesitar ejecutar acciones, lo que complica su diseño).
Para obtener más información sobre este tema, consulta Una forma de admitir repositorios que necesitan Bazel para su recuperación.