Reserva la fecha: BazelCon 2023 se celebrará el 24 y 25 de octubre en Google Múnich. ¡Ya comenzó el registro! Más información

Compila programas con Bazel

Informa un problema Ver código fuente

En esta página, se explica cómo compilar un programa con Bazel, la sintaxis de los comandos de compilación y la sintaxis del patrón de destino.

Guía de inicio rápido

Para ejecutar Bazel, ve al directorio workspace base o a cualquiera de sus subdirectorios y escribe bazel. Consulta compilación si necesitas crear un lugar de trabajo nuevo.

bazel help
                             [Bazel release bazel version]
Usage: bazel command options ...

Comandos disponibles:

  • analyze-profile: Analiza los datos del perfil de compilación.
  • aquery: Ejecuta una consulta en el gráfico de acción posanálisis.
  • build: Compila los destinos especificados.
  • canonicalize-flags: Canonicaliza las marcas de Bazel.
  • clean: Quita los archivos de salida y detiene el servidor de manera opcional.
  • cquery: Ejecuta una consulta de gráfico de dependencia posanálisis.
  • dump: Vuelca el estado interno del proceso del servidor de Bazel.
  • help: Imprime ayuda para los comandos o el índice.
  • info: Muestra información del entorno de ejecución sobre el servidor de Bazel.
  • fetch: Recupera todas las dependencias externas de un destino.
  • mobile-install: Instala apps en dispositivos móviles.
  • query: Ejecuta una consulta de gráfico de dependencia.
  • run: Ejecuta el destino especificado.
  • shutdown: Detiene el servidor de Bazel.
  • test: Compila y ejecuta los destinos de prueba especificados.
  • version: Imprime información de la versión de Bazel.

Cómo obtener ayuda

  • bazel help command: imprime ayuda y opciones para command.
  • bazel helpstartup_options: Opciones para la JVM que aloja Bazel.
  • bazel helptarget-syntax: Explica la sintaxis para especificar objetivos.
  • bazel help info-keys: Muestra una lista de claves que usó el comando de información.

La herramienta bazel realiza muchas funciones, llamadas comandos. Los más usados son bazel build y bazel test. Puedes explorar los mensajes de ayuda en línea con bazel help.

Cómo crear un destino

Para poder iniciar una compilación, necesitas un lugar de trabajo. Un lugar de trabajo es un árbol de directorios que contiene todos los archivos de origen necesarios para compilar tu aplicación. Bazel te permite realizar una compilación a partir de un volumen completamente de solo lectura.

Para compilar un programa con Bazel, escribe bazel build seguido del destino que deseas compilar.

bazel build //foo

Después de ejecutar el comando para compilar //foo, verás un resultado similar al siguiente:

INFO: Analyzed target //foo:foo (14 packages loaded, 48 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 9.905s, Critical Path: 3.25s
INFO: Build completed successfully, 6 total actions

Primero, Bazel carga todos los paquetes en el gráfico de dependencias de tu destino. Esto incluye dependencias declaradas, los archivos que se enumeran directamente en el archivo BUILD del destino y las dependencias transitivas, los archivos que figuran en los archivos BUILD de las dependencias de tu destino. Después de identificar todas las dependencias, Bazel las analiza para verificar que sean correctas y crea las acciones de compilación. Por último, Bazel ejecuta los compiladores y otras herramientas de la compilación.

Durante la fase de ejecución de la compilación, Bazel imprime mensajes de progreso. Los mensajes de progreso incluyen el paso de compilación actual (como el compilador o el vinculador) cuando se inicia y la cantidad completada de acciones de compilación. A medida que comienza la compilación, la cantidad total de acciones suele aumentar a medida que Bazel descubre el gráfico de acción completo, pero el número se estabiliza en unos segundos.

Al final de la compilación, Bazel imprime qué destinos se solicitaron, si se compilaron o no y, de ser así, dónde se pueden encontrar los archivos de salida. Las secuencias de comandos que ejecutan compilaciones pueden analizar este resultado de manera confiable. Consulta --show_result para obtener más detalles.

Si vuelves a escribir el mismo comando, la compilación terminará mucho más rápido.

bazel build //foo
INFO: Analyzed target //foo:foo (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //foo:foo up-to-date:
  bazel-bin/foo/foo
INFO: Elapsed time: 0.144s, Critical Path: 0.00s
INFO: Build completed successfully, 1 total action

Esta es una compilación nula. Debido a que no cambió nada, no hay paquetes para volver a cargar ni pasos de compilación. Si algo cambiara en “foo” o sus dependencias, Bazel volvería a ejecutar algunas acciones de compilación o completar una compilación incremental.

Cómo crear varios destinos

Bazel ofrece varias formas de especificar los destinos que se compilarán. En conjunto, estos se conocen como patrones de destino. Esta sintaxis se usa en comandos como build, test o query.

Mientras que las etiquetas se usan para especificar destinos individuales, como la declaración de dependencias en archivos BUILD, los patrones de destino de Bazel especifican varios objetivos. Los patrones de destino son una generalización de la sintaxis de etiquetas para conjuntos de objetivos, mediante comodines. En el caso más simple, cualquier etiqueta válida también es un patrón de destino válido, ya que identifica un conjunto de un solo objetivo.

Todos los patrones de destino que comienzan con // se resuelven en relación con el lugar de trabajo actual.

//foo/bar:wiz Solo el objetivo //foo/bar:wiz.
//foo/bar Equivale a //foo/bar:bar.
//foo/bar:all Todos los objetivos de la regla en el paquete foo/bar.
//foo/... Todos los objetivos de regla en todos los paquetes debajo del directorio foo.
//foo/...:all Todos los objetivos de regla en todos los paquetes debajo del directorio foo.
//foo/...:* Todos los destinos (reglas y archivos) en todos los paquetes en el directorio foo.
//foo/...:all-targets Todos los destinos (reglas y archivos) en todos los paquetes en el directorio foo.
//... Todos los destinos incluidos en los paquetes del lugar de trabajo. Esto no incluye los destinos de los repositorios externos.
//:all Todos los objetivos del paquete de nivel superior, si hay un archivo "BUILD" en la raíz del lugar de trabajo.

Los patrones de destino que no comienzan con // se resuelven en relación con el directorio de trabajo actual. En estos ejemplos, se supone que el directorio de trabajo es foo:

:foo Equivale a //foo:foo.
bar:wiz Equivale a //foo/bar:wiz.
bar/wiz Equivale a lo siguiente:
  • //foo/bar/wiz:wiz si foo/bar/wiz es un paquete
  • //foo/bar:wiz si foo/bar es un paquete
  • De lo contrario, //foo:bar/wiz
bar:all Equivale a //foo/bar:all.
:all Equivale a //foo:all.
...:all Equivale a //foo/...:all.
... Equivale a //foo/...:all.
bar/...:all Equivale a //foo/bar/...:all.

De forma predeterminada, los symlinks de directorio se siguen para patrones de destino recursivos, excepto los que apuntan a la base de salida, como los symlinks de conveniencia que se crean en el directorio raíz del lugar de trabajo.

Además, Bazel no sigue los symlinks cuando evalúa patrones de destino recursivos en ningún directorio que contenga un archivo llamado de la siguiente manera: DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN

foo/... es un comodín sobre los paquetes, que indica todos los paquetes debajo del directorio foo (para todas las raíces de la ruta del paquete). :all es un comodín sobre los objetivos, que coincide con todas las reglas dentro de un paquete. Estos dos se pueden combinar, como en foo/...:all, y cuando se usan ambos comodines, esto se puede abreviar como foo/....

Además, :* (o :all-targets) es un comodín que coincide con todos los destinos en los paquetes coincidentes, incluidos los archivos que normalmente no compila una regla, como los archivos _deploy.jar asociados con las reglas java_binary.

Esto implica que :* denota un superconjunto de :all. Si bien puede resultar confusa, esta sintaxis permite usar el comodín :all conocido para compilaciones típicas, en las que no se desea la compilación de destinos como _deploy.jar.

Además, Bazel permite usar una barra diagonal en lugar de los dos puntos que requiere la sintaxis de la etiqueta. Esto suele ser conveniente cuando se usa la expansión del nombre de archivo de Bash. Por ejemplo, foo/bar/wiz es equivalente a //foo/bar:wiz (si hay un paquete foo/bar) o a //foo:bar/wiz (si hay un paquete foo).

Muchos comandos de Bazel aceptan una lista de patrones de destino como argumentos, y todos respetan el operador de negación de prefijo -. Esto se puede usar para restar un conjunto de objetivos del conjunto especificado en los argumentos anteriores. Ten en cuenta que esto significa que el orden es importante. Por ejemplo:

bazel build foo/... bar/...

significa "compilar todos los objetivos por debajo de foo y todos los objetivos por debajo de bar", mientras

bazel build -- foo/... -foo/bar/...

significa "compilar todos los destinos debajo de foo, excepto aquellos en foo/bar". (El argumento -- es necesario para evitar que los argumentos posteriores que comienzan con - se interpreten como opciones adicionales).

Es importante señalar que restar destinos de esta manera no garantiza que no se compilen, ya que pueden ser dependencias de objetivos que no se restaron. Por ejemplo, si hay un //foo:all-apis de destino que, entre otros, depende de //foo/bar:api, este último se compilará como parte de la compilación del primero.

Los objetivos con tags = ["manual"] no se incluyen en los patrones comodín de objetivos (..., :*, :all, etc.) cuando se especifican en comandos como bazel build y bazel test (pero se incluyen en patrones de destino comodín comodín, es decir, se restarán). Debes especificar estos destinos de prueba con patrones de destino explícitos en la línea de comandos si deseas que Bazel los compile o pruebe. Por el contrario, bazel query no realiza este tipo de filtrado de forma automática (esto podría vencer el propósito de bazel query).

Recupera dependencias externas

De forma predeterminada, Bazel descargará y incluirá symlinks a dependencias externas durante la compilación. Sin embargo, esto puede ser no deseado, ya sea porque deseas saber cuándo se agregan nuevas dependencias externas o porque prefieres "recargar" las dependencias (por ejemplo, antes de un vuelo en el que te quedarás sin conexión). Si deseas evitar que se agreguen dependencias nuevas durante las compilaciones, puedes especificar la marca --fetch=false. Ten en cuenta que esta marca solo se aplica a las reglas de repositorio que no apuntan a un directorio en el sistema de archivos local. Los cambios que se realicen, por ejemplo, en local_repository, new_local_repository y las reglas del repositorio del SDK de Android y el NDK siempre se aplicarán, independientemente del valor que posea --fetch .

Si no permites la recuperación durante las compilaciones y Bazel encuentra nuevas dependencias externas, la compilación fallará.

Para recuperar las dependencias de forma manual, ejecuta bazel fetch. Si no permites la recuperación durante la compilación, deberás ejecutar bazel fetch:

  • Antes de compilar por primera vez
  • Después de agregar una dependencia externa nueva.

Una vez que se haya ejecutado, no deberías volver a ejecutarlo hasta que el archivo WORKSPACE cambie.

fetch toma una lista de destinos para los que se desean recuperar las dependencias. Por ejemplo, esto recuperaría las dependencias necesarias para compilar //foo:bar y //bar:baz:

bazel fetch //foo:bar //bar:baz

Para recuperar todas las dependencias externas de un lugar de trabajo, ejecuta el siguiente comando:

bazel fetch //...

No es necesario que ejecutes la recuperación de Bazel si tienes todas las herramientas que usas (desde jars de biblioteca hasta JDK) en la raíz de tu lugar de trabajo. Sin embargo, si usas algo fuera del directorio del lugar de trabajo, Bazel ejecutará automáticamente bazel fetch antes de ejecutar bazel build.

La caché del repositorio

Bazel intenta evitar recuperar el mismo archivo varias veces, incluso si se necesita el mismo archivo en lugares de trabajo diferentes o si la definición de un repositorio externo cambia, pero aún necesita el mismo archivo para descargar. Para ello, bazel almacena en caché todos los archivos descargados en la caché del repositorio, que, de forma predeterminada, se encuentra en ~/.cache/bazel/_bazel_$USER/cache/repos/v1/. La ubicación se puede cambiar con la opción --repository_cache. La caché se comparte entre todos los lugares de trabajo y las versiones instaladas de Bazel. Se toma una entrada de la caché si Bazel sabe con seguridad que tiene una copia del archivo correcto, es decir, si la solicitud de descarga tiene una suma SHA256 del archivo especificado y un archivo con ese hash se encuentra en la caché. Por lo tanto, especificar un hash para cada archivo externo no solo es una buena idea desde la perspectiva de la seguridad, sino que también ayuda a evitar descargas innecesarias.

Después de cada acierto de caché, se actualiza la hora de modificación del archivo en la caché. De esta manera, se puede determinar con facilidad el último uso de un archivo en el directorio de caché, por ejemplo, para limpiar la caché de forma manual. La caché nunca se limpia de manera automática, ya que puede contener una copia de un archivo que ya no está disponible en sentido ascendente.

Directorios de archivos de distribución

El directorio de distribución es otro mecanismo de Bazel para evitar descargas innecesarias. Bazel busca directorios de distribución antes que la caché del repositorio. La diferencia principal es que el directorio de distribución requiere preparación manual.

Con la opción --distdir=/path/to-directory, puedes especificar directorios de solo lectura adicionales para buscar archivos en lugar de recuperarlos. Se toma un archivo de ese directorio si el nombre del archivo es igual al nombre base de la URL y, además, el hash del archivo es igual al que se especifica en la solicitud de descarga. Esto solo funciona si se especifica el hash de archivos en la declaración WORKSPACE.

Si bien la condición en el nombre del archivo no es necesaria para la corrección, reduce la cantidad de archivos candidatos a uno por directorio especificado. De esta manera, la especificación de los directorios de archivos de distribución sigue siendo eficiente, incluso si aumenta la cantidad de archivos en dicho directorio.

Ejecuta Bazel en un entorno aislado

Para mantener el tamaño binario de Bazel pequeño, las dependencias implícitas de Bazel se recuperan a través de la red mientras se ejecutan por primera vez. Estas dependencias implícitas contienen cadenas de herramientas y reglas que pueden no ser necesarias para todos. Por ejemplo, las herramientas de Android se desagrupan y se recuperan solo cuando se compilan proyectos de Android.

Sin embargo, estas dependencias implícitas pueden causar problemas cuando se ejecuta Bazel en un entorno aislado, incluso si ya suministraste todas tus dependencias de WORKSPACE. Para resolver esto, puedes preparar un directorio de distribución que contenga estas dependencias en una máquina con acceso a la red y, luego, transferirlas al entorno aislado con un enfoque sin conexión.

Para preparar el directorio de distribución, usa la marca --distdir. Deberás hacer esto una vez para cada versión binaria nueva de Bazel, ya que las dependencias implícitas pueden ser diferentes para cada versión.

Para compilar estas dependencias fuera del entorno aislado, primero consulta el árbol de fuentes de Bazel en la versión correcta:

git clone https://github.com/bazelbuild/bazel "$BAZEL_DIR"
cd "$BAZEL_DIR"
git checkout "$BAZEL_VERSION"

Luego, compila el archivo comprimido que contenga las dependencias implícitas del entorno de ejecución para esa versión específica de Bazel:

bazel build @additional_distfiles//:archives.tar

Exporta este archivo comprimido a un directorio que se pueda copiar en tu entorno aislado. Ten en cuenta la marca --strip-components, porque --distdir puede ser bastante complejo con el nivel de anidación del directorio:

tar xvf bazel-bin/external/additional_distfiles/archives.tar \
  -C "$NEW_DIRECTORY" --strip-components=3

Por último, cuando uses Bazel en tu entorno aislado, pasa la marca --distdir que apunta al directorio. Para mayor comodidad, puedes agregarla como una entrada .bazelrc:

build --distdir=path/to/directory

Configuraciones de compilación y compilación cruzada

Todas las entradas que especifican el comportamiento y el resultado de una compilación determinada se pueden dividir en dos categorías distintas. El primer tipo corresponde a la información intrínseca que se almacena en los archivos BUILD de tu proyecto: la regla de compilación, los valores de sus atributos y el conjunto completo de sus dependencias transitivas. El segundo tipo corresponde a los datos externos o ambientales, proporcionados por el usuario o por la herramienta de compilación: la elección de la arquitectura de destino, las opciones de compilación y vinculación, y otras opciones de configuración de la cadena de herramientas. Nos referimos a un conjunto completo de datos del entorno como una configuración.

En una compilación determinada, puede haber más de una configuración. Considera una compilación cruzada, en la que compilas un archivo ejecutable //foo:bin para una arquitectura de 64 bits, pero la estación de trabajo es una máquina de 32 bits. Claramente, la compilación requerirá la compilación de //foo:bin mediante una cadena de herramientas capaz de ejecutar ejecutables de 64 bits. Sin embargo, el sistema de compilación también debe compilar varias herramientas que se usen durante la compilación en sí (por ejemplo, herramientas que se compilan a partir del código fuente y, luego, se usan en, por ejemplo, una genrule), y estas deben compilarse para ejecutarse en tu estación de trabajo. Por lo tanto, podemos identificar dos configuraciones: la configuración exec, que se usa para compilar herramientas que se ejecutan durante la compilación, y la configuración objetivo (o solicitar configuración, pero decimos “configuración de destino” con más frecuencia, aunque esa palabra ya tenga muchos significados), que se usa para compilar el objeto binario que solicitaste en última instancia.

Por lo general, hay muchas bibliotecas que son requisitos previos del destino de compilación solicitado (//foo:bin) y una o más de las herramientas ejecutivas, por ejemplo, algunas bibliotecas base. Estas bibliotecas se deben compilar dos veces, una para la configuración ejecutiva y una para la configuración objetivo. Bazel se encarga de garantizar que ambas variantes se compilen y que los archivos derivados se mantengan separados para evitar interferencias, que, por lo general, se pueden compilar de forma simultánea, ya que son independientes entre sí. Si ves mensajes de progreso que indican que un destino determinado se compila dos veces, es muy probable que esta sea la explicación.

La configuración ejecutiva se deriva de la configuración de destino de la siguiente manera:

  • Usa la misma versión de Crosstool (--crosstool_top) que se especifica en la configuración de la solicitud, a menos que se especifique --host_crosstool_top.
  • Usa el valor de --host_cpu para --cpu (valor predeterminado: k8).
  • Usa los mismos valores de estas opciones que se especifican en la configuración de la solicitud: --compiler, --use_ijars y, si se usa --host_crosstool_top, se usa el valor de --host_cpu a fin de buscar un default_toolchain en Crosstool (ignorando --compiler) para la configuración ejecutiva.
  • Usa el valor de --host_javabase para --javabase
  • Usa el valor de --host_java_toolchain para --java_toolchain
  • Usa compilaciones optimizadas para código C++ (-c opt).
  • No se genera información de depuración (--copt=-g0).
  • Quita la información de depuración de los ejecutables y las bibliotecas compartidas (--strip=always).
  • Coloca todos los archivos derivados en una ubicación especial, diferente de la que usa cualquier configuración de solicitud posible.
  • Elimina el sello de objetos binarios con datos de compilación (consulta las opciones de --embed_*).
  • Todos los demás valores se mantienen en sus valores predeterminados.

Hay muchas razones por las que puede ser preferible seleccionar una configuración de ejecución diferente de la configuración de solicitud. Lo más importante:

En primer lugar, el uso de objetos binarios optimizados y reducidos reduce el tiempo dedicado a vincular y ejecutar las herramientas, el espacio en disco que ocupan las herramientas y el tiempo de E/S de la red en compilaciones distribuidas.

En segundo lugar, al desacoplar las configuraciones de ejecución y solicitud en todas las compilaciones, evitas recompilaciones muy costosas que resultarían de cambios menores en la configuración de solicitud (como cambiar las opciones de vinculador), como se describió antes.

Corregir recompilaciones incrementales

Uno de los objetivos principales del proyecto de Bazel es garantizar recompilaciones incrementales correctas. Las herramientas de compilación anteriores, en especial aquellas basadas en Make, realizan varias suposiciones distintas en su implementación de compilaciones incrementales.

En primer lugar, las marcas de tiempo de los archivos aumentan de forma monotónica. Si bien este es el caso típico, es muy fácil olvidar esta suposición. La sincronización con una revisión anterior de un archivo hace que el tiempo de modificación de ese archivo disminuya; los sistemas basados en Make no se volverán a compilar.

Por lo general, si bien Make detecta cambios en los archivos, no detecta cambios en los comandos. Si alteras las opciones que se pasan al compilador en un paso de compilación determinado, Make no volverá a ejecutar el compilador, y es necesario descartar de forma manual los resultados no válidos de la compilación anterior usando make clean.

Además, Make no es sólido contra la finalización incorrecta de uno de sus subprocesos después de que ese subproceso comienza a escribir en el archivo de salida. Si bien la ejecución actual de Make fallará, la invocación posterior de Make supondrá que el archivo de salida truncado es válido (porque es más reciente que sus entradas) y no se volverá a compilar. De manera similar, si se finaliza el proceso de Make, puede ocurrir una situación similar.

Bazel evita estas suposiciones y otras. Bazel mantiene una base de datos de todo el trabajo realizado antes y solo omitirá un paso de compilación si encuentra que el conjunto de archivos de entrada (y sus marcas de tiempo) en ese paso de compilación y el comando de compilación para ese paso de compilación coinciden de forma exacta con uno en la base de datos, y que el conjunto de archivos de salida (y sus marcas de tiempo) para la entrada de la base de datos coincide exactamente con las marcas de tiempo de los archivos en el disco. Cualquier cambio en los archivos de entrada o de salida, o en el comando en sí, hará que se vuelva a ejecutar el paso de compilación.

El beneficio para los usuarios de compilaciones incrementales correctas es que se pierde menos tiempo debido a la confusión. (Además, se requiere menos tiempo para esperar recompilaciones causadas por el uso de make clean, ya sea necesario o preventivo).

Consistencia de compilación y compilaciones incrementales

De manera formal, se define el estado de una compilación como coherente cuando existen todos los archivos de salida esperados y sus contenidos son correctos, según lo especifican los pasos o reglas necesarios para crearlos. Cuando editas un archivo de origen, se dice que el estado de la compilación es incoherente, y permanece inconsistente hasta que ejecutes la herramienta de compilación de forma correcta para completar el proceso. Describemos esta situación como incoherencia inestable, ya que solo es temporal y se restablece la coherencia mediante la ejecución de la herramienta de compilación.

Existe otro tipo de inconsistencia que es pernicioso: incoherencia estable. Si la compilación alcanza un estado incoherente estable, la invocación correcta que se hizo de la herramienta de compilación no restaura la coherencia: se atasca la compilación y los resultados permanecen incorrectos. Los estados estables incoherentes son la razón principal por la que los usuarios de Make (y otras herramientas de compilación) escriben make clean. Descubrir que la herramienta de compilación falló de esta manera (y luego recuperarla) puede llevar mucho tiempo y ser muy frustrante.

En teoría, la forma más sencilla de lograr una compilación coherente es descartar todas las salidas de compilación anteriores y comenzar de nuevo: haz que cada compilación sea una compilación limpia. Sin duda, este enfoque lleva mucho tiempo para ser práctico (excepto para los ingenieros de lanzamiento) y, por lo tanto, para ser útil, la herramienta de compilación debe poder realizar compilaciones incrementales sin comprometer la coherencia.

El análisis correcto de dependencias incrementales es difícil y, como se describió anteriormente, muchas otras herramientas de compilación hacen un mal trabajo para evitar estados incoherentes y estables durante las compilaciones incrementales. Por el contrario, Bazel ofrece la siguiente garantía: después de una invocación exitosa de la herramienta de compilación durante la cual no realizaste modificaciones, la compilación estará en un estado coherente. (Si editas tus archivos fuente durante una compilación, Bazel no garantiza la coherencia del resultado de la compilación actual. Sin embargo, sí garantiza que los resultados de la próxima compilación restablecerá la coherencia.

Al igual que con todas las garantías, se incluye una letra pequeña: hay algunas formas conocidas de alcanzar un estado estable y inconsistente con Bazel. No garantizamos que investigemos tales problemas que surjan de intentos deliberados para encontrar errores en el análisis de dependencia incremental, pero investigaremos y haremos todo lo posible para corregir todos los estados incoherentes y estables que surjan del uso normal o "razonable" de la herramienta de compilación.

Si en algún momento detectas un estado incoherente y estable con Bazel, informa el error.

Ejecución en zona de pruebas

Bazel usa zonas de pruebas para garantizar que las acciones se ejecuten de forma hermética y correcta. Bazel ejecuta generaciones (en voz baja): acciones en zonas de pruebas que solo contienen el conjunto mínimo de archivos que la herramienta necesita para realizar su trabajo. Actualmente, la zona de pruebas funciona en Linux 3.12 o versiones posteriores con la opción CONFIG_USER_NS habilitada, y también en macOS 10.11 o versiones posteriores.

Bazel imprimirá una advertencia si tu sistema no admite la zona de pruebas para alertarte sobre el hecho de que no se garantiza que las compilaciones sean herméticas y que puedan afectar al sistema host de formas desconocidas. Para inhabilitar esta advertencia, puedes pasar la marca --ignore_unsupported_sandboxing a Bazel.

En algunas plataformas, como los nodos del clúster de Google Kubernetes Engine o Debian, los espacios de nombres de usuario se desactivan de forma predeterminada debido a problemas de seguridad. Para comprobarlo, mira el archivo /proc/sys/kernel/unprivileged_userns_clone: si existe, y contiene un 0, los espacios de nombres de usuario se pueden activar con sudo sysctl kernel.unprivileged_userns_clone=1.

En algunos casos, la zona de pruebas de Bazel no puede ejecutar reglas debido a la configuración del sistema. El síntoma suele ser un error que genera un mensaje similar a namespace-sandbox.c:633: execvp(argv[0], argv): No such file or directory. En ese caso, intenta desactivar la zona de pruebas para genrules con --strategy=Genrule=standalone y para otras reglas con --spawn_strategy=standalone. Además, informa un error en nuestro seguimiento de problemas y menciona qué distribución de Linux usas para que podamos investigar y proporcionar una solución en una versión posterior.

Fases de una compilación

En Bazel, una compilación se produce en tres fases distintas: como usuario, comprender la diferencia entre ellas proporciona estadísticas sobre las opciones que controlan una compilación (consulta a continuación).

Cargando fase

El primero es la carga, durante la cual se cargan, analizan, evalúan y almacenan en caché todos los archivos BUILD necesarios para los destinos iniciales y el cierre transitivo de las dependencias.

Para la primera compilación después de que se inicia un servidor de Bazel, la fase de carga suele tomar muchos segundos tantos archivos de COMPILACIÓN se cargan desde el sistema de archivos. En las compilaciones posteriores, especialmente si no se cambiaron los archivos BUILD, la carga se produce muy rápido.

Los errores informados durante esta fase incluyen lo siguiente: paquetes no encontrados, destinos no encontrados, errores léxicos y gramaticales en un archivo BUILD y errores de evaluación.

Fase de análisis

La segunda fase, análisis, involucra el análisis semántico y la validación de cada regla de compilación, la construcción de un grafo de dependencia de compilación y la determinación exacta del trabajo que se debe realizar en cada paso de la compilación.

Al igual que la carga, el análisis también lleva varios segundos cuando se calcula en su totalidad. Sin embargo, Bazel almacena en caché el grafo de dependencia de una compilación a la siguiente y solo vuelve a analizar lo que debe hacer, lo que puede hacer que las compilaciones incrementales sean extremadamente rápidas en el caso en que los paquetes no hayan cambiado desde la compilación anterior.

Los errores informados en esta etapa incluyen: dependencias inapropiadas, entradas no válidas a una regla y todos los mensajes de error específicos de las reglas.

Las fases de carga y análisis son rápidas, ya que Bazel evita la E/S de archivos innecesaria en esta etapa y solo lee los archivos BUILD para determinar el trabajo que se debe realizar. Esto tiene un diseño exclusivo y hace que Bazel sea una buena base para las herramientas de análisis, como el comando query de Bazel, que se implementa sobre la fase de carga.

Fase de ejecución

La tercera y última fase de la compilación es la ejecución. Esta fase garantiza que los resultados de cada paso en la compilación sean coherentes con sus entradas, volver a ejecutar las herramientas de compilación, vinculación, etc., según sea necesario. En este paso, la compilación pasa la mayor parte del tiempo, que varía de unos segundos a más de una hora para una compilación grande. Los errores informados durante esta fase incluyen los siguientes: archivos faltantes, errores en una herramienta que ejecuta alguna acción de compilación o errores de una herramienta para producir el conjunto esperado de resultados.