Preguntas frecuentes

Informar un problema Ver fuente Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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?

Establecer version con la directiva module en el archivo fuente MODULE.bazel puede tener varios inconvenientes y efectos secundarios no deseados si no se administra con cuidado:

  • Duplicación: Por lo general, lanzar una nueva versión de un módulo implica incrementar la versión en MODULE.bazel y 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 reemplacen un módulo con una confirmación específica usando un reemplazo no registrado verán una versión incorrecta. Por ejemplo, si el MODULE.bazel en el archivo fuente establece version = "0.3.0", pero se realizaron confirmaciones adicionales desde ese lanzamiento, un usuario que realice un reemplazo con una de esas confirmaciones seguirá viendo 0.3.0. En realidad, la versión debería reflejar que está adelantada a la versión de lanzamiento, por ejemplo, 0.3.1-rc1.

  • Problemas de anulación no registrados: El uso de valores de marcador de posición puede causar problemas cuando los usuarios anulan un módulo con una anulación no registrada. Por ejemplo, 0.0.0 no se ordena como la versión más alta, que suele ser el comportamiento esperado que los usuarios desean cuando realizan una anulación no relacionada con el registro.

Por lo tanto, es mejor evitar establecer la versión en el archivo fuente 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 verdad 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 repositorio de reglas de ejemplo rules-template usa una acción de GitHub de bazel-contrib/publish-to-bcr publish.yaml para publicar la versión en el BCR. La acción genera un parche para el archivo fuente 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 las versiones del registro se establecerá correctamente en la versión lanzada y, por lo tanto, bazel_dep, single_version_override y multiple_version_override funcionarán según lo previsto, a la vez que se evitarán posibles problemas cuando se realice una anulación que no sea del registro, ya que la versión del archivo fuente será el valor predeterminado (''), que siempre se controlará correctamente (después de todo, es el valor de la versión predeterminada) y se comportará según lo previsto al ordenar (la cadena vacía se trata como la versión más alta).

¿Cuándo debo incrementar el nivel de compatibilidad?

El compatibility_level de un módulo de Bazel se debe incrementar en la misma confirmación que introduce un cambio incompatible con versiones anteriores ("rotundo").

Sin embargo, Bazel puede arrojar 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 ocurrir, por ejemplo, cuando dos módulos dependen de versiones de un tercer módulo con diferentes niveles de compatibilidad.

Por lo tanto, incrementar compatibility_level con demasiada frecuencia puede ser muy perjudicial y no se recomienda. Para evitar esta situación, el compatibility_level solo debe incrementarse cuando el cambio interruptivo 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 fuente de las dependencias, por lo que, si el archivo MODULE.bazel loads otro archivo, Bazel no puede recuperar ese archivo sin recuperar todo el archivo fuente. 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 coincida con los metadatos de compilación almacenados en otro lugar, por ejemplo, en un archivo .bzl: Esto se puede lograr usando el método native.module_version en un archivo .bzl cargado desde un archivo BUILD.
  • Dividir un archivo MODULE.bazel muy grande en secciones administrables, en especial para monorepositorios: 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 permitimos loads en los archivos MODULE.bazel, no se puede usar include en módulos que no sean raíz.
  • Es posible que los usuarios del sistema WORKSPACE anterior recuerden haber declarado un repo y, luego, haber realizado inmediatamente un load desde ese repo para ejecutar una lógica compleja. Esta capacidad se reemplazó por las extensiones de módulos.

¿Puedo especificar un rango de SemVer para un bazel_dep?

No. Otros administradores de paquetes, como npm y Cargo, admiten rangos de versiones (de forma implícita o explícita), lo que a menudo requiere un solucionador de restricciones (lo que hace que el resultado sea más difícil de predecir para los usuarios) y hace que la resolución de versiones no sea reproducible sin un archivo de bloqueo.

En cambio, Bazel usa la selección de versión mínima, como Go, que, en contraste, hace que el resultado sea fácil de predecir 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 aplica 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 objeto local_path_override.

¿Por qué hay tantas use_repo?

A veces, los usos de extensiones de módulos en archivos MODULE.bazel incluyen una directiva use_repo grande. Por ejemplo, un uso típico de la extensión go_deps 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 directiva use_repo es que ejecuta las extensiones de módulos 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 destino @org_golang_x_net//:foo, en el ejemplo anterior). Sin embargo, no sabemos qué repositorios definiría una extensión del módulo hasta que la ejecutemos. 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 del módulo puede devolver 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ódulos.

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 "cuál se evalúa primero" no es la pregunta correcta, sino que 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 del repositorio de @@base?

Las etiquetas con nombres de repo evidentes (un solo @ inicial) pueden hacer referencia a diferentes cosas 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 repo de contexto. Por ejemplo, si la etiqueta está en un archivo BUILD ubicado en el repo @@foo, el repo de contexto es @@foo.

Luego, según 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 (@@):
    1. Si bar es un nombre de repo aparente que se introdujo a través del archivo MODULE.bazel del módulo raíz (a través de cualquiera de bazel_dep, use_repo, module o use_repo_rule), entonces @bar se resuelve en lo que declara ese archivo MODULE.bazel.
    2. De lo contrario, si bar es un repo definido en WORKSPACE (lo que significa que su nombre canónico es @@bar), entonces @bar se resuelve como @@bar.
    3. De lo contrario, @bar se resuelve en algo como @@[unknown repo 'bar' requested from @@], lo que, en última instancia, generará un error.
  • Si el repo de contexto es un repo del mundo de Bzlmod (es decir, corresponde a un módulo de Bazel no raíz o se genera a partir de una extensión de módulo), solo verá otros repos del mundo de Bzlmod y ningún repo del mundo de WORKSPACE.
    • En particular, esto incluye cualquier repo que se introduzca en una extensión de módulo similar a non_module_deps en el módulo raíz o las instancias de use_repo_rule en el módulo raíz.
  • Si el repo de contexto se define en WORKSPACE, haz lo siguiente:
    1. Primero, verifica si la definición del repo de contexto tiene el atributo mágico repo_mapping. Si es así, primero revisa la asignación (por lo que, para un repositorio definido con repo_mapping = {"@bar": "@baz"}, veríamos @baz a continuación).
    2. Si bar es un nombre de repo aparente introducido por el archivo MODULE.bazel del módulo raíz, @bar se resuelve en lo que declara ese archivo MODULE.bazel. (Es lo mismo que el elemento 1 en el caso del repositorio principal).
    3. De lo contrario, @bar se resuelve como @@bar. Lo más probable es que esto apunte a un repo bar definido en WORKSPACE. Si no se define tal repo, Bazel arrojará un error.

Para obtener una versión más concisa, haz lo siguiente:

  • Los repositorios de Bzlmod-world (excepto el 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, recurrirán a los repositorios de WORKSPACE-world.

Cabe destacar que las etiquetas en la línea de comandos de Bazel (incluidas las marcas de Starlark, los valores de marcas con tipo de etiqueta y los patrones de destino de compilación o prueba) se tratan como si tuvieran el repo principal como repo de contexto.

Otro

¿Cómo preparo y ejecuto una compilación sin conexión?

Usa el comando bazel fetch para realizar la recuperación previa de repositorios. 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 realizar pruebas 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 las máquinas solo IPv6, Bazel puede descargar dependencias sin cambios. Sin embargo, en las máquinas de pila doble IPv4/IPv6, 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 de Network unreachable y fallas en la compilación. En estos casos, puedes anular el comportamiento de Bazel para que prefiera IPv6 con la propiedad del sistema java.net.preferIPv6Addresses=true. En particular, haz lo siguiente:

  • Usa la --host_jvm_args=-Djava.net.preferIPv6Addresses=true opción de inicio, por ejemplo, agregando la siguiente línea en tu archivo .bazelrc:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • Cuando ejecutes destinos de compilación de Java que necesiten conectarse a Internet (como para 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 entorno COURSIER_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 no todavía. Es posible que los usuarios que emplean servicios de ejecución remota para acelerar sus compilaciones noten que las reglas del repositorio aún se ejecutan de forma local. Por ejemplo, un http_archive se descargaría primero en la máquina local (con cualquier caché de descarga local, si corresponde), se extraerá y, luego, cada archivo fuente 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 descarga y extrae ese archivo, lo que ahorraría un viaje de ida y vuelta inútil.

Parte del motivo es que las reglas del repositorio (y las extensiones de módulos) son similares a "scripts" que ejecuta Bazel. Un ejecutor remoto ni siquiera tiene necesariamente instalado Bazel.

Otro motivo es que Bazel a menudo necesita 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, como reimaginar las reglas del repositorio como reglas de compilación, lo que permitiría que se ejecuten de forma remota, pero, a la vez, generaría nuevas inquietudes arquitectónicas (por ejemplo, los comandos query 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 que se recupere Bazel.