En este instructivo, se explican los aspectos básicos de la compilación de aplicaciones de Java con
Bazel. Configurarás tu espacio de trabajo y compilarás un proyecto simple de Java que
ilustra conceptos clave de Bazel, como los destinos y los archivos BUILD.
Tiempo estimado de finalización: 30 minutos.
Qué aprenderás
En este instructivo, aprenderás a realizar lo siguiente:
- Compilar un destino
- Visualizar las dependencias del proyecto
- Dividir el proyecto en varios destinos y paquetes
- Controlar la visibilidad de los destinos en los paquetes
- Hacer referencia a los destinos a través de etiquetas
- Implementar un destino
Antes de comenzar
Instala Bazel
Para prepararte para el instructivo, primero instala Bazel si aún no lo hiciste.
Instala el JDK
Instala Java JDK (la versión preferida es 11, pero se admiten las versiones entre 8 y 15).
Establece la variable de entorno JAVA_HOME para que apunte al JDK.
En Linux/macOS:
export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"En Windows:
- Abre el Panel de control.
- Ve a "Sistema y seguridad" > "Sistema" > "Configuración avanzada del sistema" > pestaña "Opciones avanzadas" > "Variables de entorno...". .
- En la lista "Variables de usuario" (la que está en la parte superior), haz clic en "Nueva...".
- En el campo "Nombre de la variable", ingresa
JAVA_HOME. - Haz clic en "Buscar directorio...".
- Navega al directorio del JDK (por ejemplo
C:\Program Files\Java\jdk1.8.0_152). - Haz clic en "Aceptar" en todas las ventanas de diálogo.
Obtén el proyecto de muestra
Recupera el proyecto de muestra del repositorio de GitHub de Bazel:
git clone https://github.com/bazelbuild/examplesEl proyecto de muestra para este instructivo se encuentra en el examples/java-tutorial
directorio y tiene la siguiente estructura:
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── MODULE.bazel
Compila con Bazel
Configura el espacio de trabajo
Antes de compilar un proyecto, debes configurar su espacio de trabajo. Un espacio de trabajo es un directorio que contiene los archivos fuente de tu proyecto y los resultados de la compilación de Bazel. También contiene archivos que Bazel reconoce como especiales:
El archivo
MODULE.bazel, que identifica el directorio y su contenido como un espacio de trabajo de Bazel y se encuentra en la raíz de la estructura de directorios del proyectoUno o más archivos
BUILD, que le indican a Bazel cómo compilar diferentes partes de l proyecto. (un directorio dentro del espacio de trabajo que contiene un archivoBUILDes un paquete. obtendrás más información sobre los paquetes más adelante en este instructivo)
Para designar un directorio como un espacio de trabajo de Bazel, crea un archivo vacío llamado
MODULE.bazel en ese directorio.
Cuando Bazel compila el proyecto, todas las entradas y dependencias deben estar en el mismo espacio de trabajo. Los archivos que residen en diferentes espacios de trabajo son independientes entre sí, a menos que estén vinculados, lo que está fuera del alcance de este instructivo.
Conceptos básicos sobre el archivo BUILD
Un BUILD archivo contiene varios tipos diferentes de instrucciones para Bazel.
El tipo más importante es la regla de compilación, que le indica a Bazel cómo compilar los
resultados deseados, como objetos binarios o bibliotecas ejecutables. Cada instancia
de una regla de compilación en el archivo BUILD se denomina destino y apunta a un
conjunto específico de archivos fuente y dependencias. Un destino también puede apuntar a otros
destinos.
Observa el archivo java-tutorial/BUILD:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
En nuestro ejemplo, el destino ProjectRunner crea una instancia de la regla
java_binary integrada de Bazel. La regla le indica a Bazel que
compile un archivo .jar y una secuencia de comandos de shell de wrapper (ambos con el nombre del destino).
Los atributos del destino indican explícitamente sus dependencias y opciones.
Si bien el atributo name es obligatorio, muchos son opcionales. Por ejemplo, en el
ProjectRunner destino de la regla, name es el nombre del destino, srcs especifica
los archivos fuente que Bazel usa para compilar el destino y main_class especifica
la clase que contiene el método principal. (Es posible que hayas notado que nuestro ejemplo
usa glob para pasar un conjunto de archivos fuente a Bazel
en lugar de enumerarlos uno por uno).
Compila el proyecto
Para compilar tu proyecto de muestra, navega al directorio java-tutorial
y ejecuta lo siguiente:
bazel build //:ProjectRunnerEn la etiqueta de destino, la parte // es la ubicación del archivo BUILD
en relación con la raíz del espacio de trabajo (en este caso, la raíz en sí)
y ProjectRunner es el nombre del destino en el archivo BUILD. (Obtendrás más información sobre las etiquetas de destino al final de este instructivo).
Bazel genera un resultado similar al siguiente:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 1.021s, Critical Path: 0.83s
Felicitaciones, acabas de compilar tu primer destino de Bazel. Bazel coloca los resultados de la compilación
en el directorio bazel-bin en la raíz del espacio de trabajo. Explora
su contenido para tener una idea de la estructura de salida de Bazel.
Ahora, prueba el objeto binario que acabas de compilar:
bazel-bin/ProjectRunnerRevisa el gráfico de dependencias
Bazel requiere que las dependencias de compilación se declaren explícitamente en los archivos BUILD. Bazel usa esas instrucciones para crear el gráfico de dependencias del proyecto, lo que permite compilaciones incrementales precisas.
Para visualizar las dependencias del proyecto de muestra, puedes generar una representación de texto del gráfico de dependencias ejecutando este comando en la raíz del espacio de trabajo:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graphEl comando anterior le indica a Bazel que busque todas las dependencias del destino
//:ProjectRunner (excepto las dependencias implícitas y del host) y que formatee el
resultado como un gráfico.
Luego, pega el texto en GraphViz.
Como puedes ver, el proyecto tiene un solo destino que compila dos archivos fuente sin dependencias adicionales:
Después de configurar tu espacio de trabajo, compilar tu proyecto y examinar sus dependencias, puedes agregar algo de complejidad.
Define mejor tu compilación de Bazel
Si bien un solo destino es suficiente para proyectos pequeños, es posible que desees dividir proyectos más grandes en varios destinos y paquetes para permitir compilaciones incrementales rápidas (es decir, solo volver a compilar lo que cambió) y acelerar tus compilaciones mediante la compilación de varias partes de un proyecto a la vez.
Especifica varios destinos de compilación
Puedes dividir la compilación del proyecto de muestra en dos destinos. Reemplaza el contenido de
el archivo java-tutorial/BUILD por lo siguiente:
java_binary(
name = "ProjectRunner",
srcs = ["src/main/java/com/example/ProjectRunner.java"],
main_class = "com.example.ProjectRunner",
deps = [":greeter"],
)
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
)
Con esta configuración, Bazel primero compila la biblioteca greeter y, luego, el objeto binario
ProjectRunner. El atributo deps en java_binary le indica a Bazel que
se requiere la biblioteca greeter para compilar el objeto binario ProjectRunner.
Para compilar esta nueva versión del proyecto, ejecuta el siguiente comando:
bazel build //:ProjectRunnerBazel genera un resultado similar al siguiente:
INFO: Found 1 target...
Target //:ProjectRunner up-to-date:
bazel-bin/ProjectRunner.jar
bazel-bin/ProjectRunner
INFO: Elapsed time: 2.454s, Critical Path: 1.58s
Ahora, prueba el objeto binario que acabas de compilar:
bazel-bin/ProjectRunnerSi ahora modificas ProjectRunner.java y vuelves a compilar el proyecto, Bazel solo
volverá a compilar ese archivo.
Si observas el gráfico de dependencias, puedes ver que ProjectRunner depende de las
mismas entradas que antes, pero la estructura de la compilación es diferente:
Ahora compilaste el proyecto con dos destinos. El destino ProjectRunner compila
un archivo fuente y depende de otro destino (:greeter), que compila
un archivo fuente adicional.
Usa varios paquetes
Ahora dividamos el proyecto en varios paquetes. Si observas el
src/main/java/com/example/cmdline directorio, puedes ver que también contiene
un BUILD archivo, además de algunos archivos fuente. Por lo tanto, para Bazel, el espacio de trabajo ahora
contiene dos paquetes, //src/main/java/com/example/cmdline y // (ya que
hay un archivo BUILD en la raíz del espacio de trabajo).
Observa el src/main/java/com/example/cmdline/BUILD archivo:
java_binary(
name = "runner",
srcs = ["Runner.java"],
main_class = "com.example.cmdline.Runner",
deps = ["//:greeter"],
)
El destino runner depende del destino greeter en el paquete // (por lo tanto
la etiqueta de destino //:greeter). Bazel lo sabe a través del atributo deps.
Observa el gráfico de dependencias:
Sin embargo, para que la compilación se realice correctamente, debes otorgar explícitamente al destino runner
en //src/main/java/com/example/cmdline/BUILD visibilidad a los destinos en
//BUILD con el atributo visibility. Esto se debe a que, de forma predeterminada, los destinos
solo son visibles para otros destinos en el mismo BUILD archivo. (Bazel usa la visibilidad del destino
para evitar problemas, como que las bibliotecas que contienen detalles de implementación
se filtren en las APIs públicas).
Para ello, agrega el atributo visibility al destino greeter en
java-tutorial/BUILD como se muestra a continuación:
java_library(
name = "greeter",
srcs = ["src/main/java/com/example/Greeting.java"],
visibility = ["//src/main/java/com/example/cmdline:__pkg__"],
)
Ahora puedes compilar el nuevo paquete ejecutando el siguiente comando en la raíz del espacio de trabajo:
bazel build //src/main/java/com/example/cmdline:runnerBazel genera un resultado similar al siguiente:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner.jar
bazel-bin/src/main/java/com/example/cmdline/runner
INFO: Elapsed time: 1.576s, Critical Path: 0.81s
Ahora, prueba el objeto binario que acabas de compilar:
./bazel-bin/src/main/java/com/example/cmdline/runnerAhora modificaste el proyecto para que se compile como dos paquetes, cada uno con un destino, y comprendes las dependencias entre ellos.
Usa etiquetas para hacer referencia a los destinos
En los archivos BUILD y en la línea de comandos, Bazel usa etiquetas de destino para hacer referencia a
los destinos, por ejemplo, //:ProjectRunner o
//src/main/java/com/example/cmdline:runner. Su sintaxis es la siguiente:
//path/to/package:target-name
Si el destino es un destino de regla, path/to/package es la ruta de acceso al
directorio que contiene el archivo BUILD, y target-name es el nombre que le asignaste al
destino en el archivo BUILD (el atributo name). Si el destino es un archivo
destino, entonces path/to/package es la ruta de acceso a la raíz del paquete, y
target-name es el nombre del archivo de destino, incluida su ruta de acceso completa.
Cuando haces referencia a destinos en la raíz del repositorio, la ruta de acceso del paquete está vacía,
solo usa //:target-name. Cuando haces referencia a destinos dentro del mismo BUILD
archivo, incluso puedes omitir el identificador de raíz del espacio de trabajo // y solo usar
:target-name.
Por ejemplo, para los destinos en el archivo java-tutorial/BUILD, no tuviste que
especificar una ruta de acceso del paquete, ya que la raíz del espacio de trabajo es un paquete (//) y
tus dos etiquetas de destino eran simplemente //:ProjectRunner y //:greeter.
Sin embargo, para los destinos en el archivo //src/main/java/com/example/cmdline/BUILD, tuviste que especificar la ruta de acceso completa del paquete de //src/main/java/com/example/cmdline
y tu etiqueta de destino era //src/main/java/com/example/cmdline:runner.
Empaqueta un destino de Java para la implementación
Ahora empaquetaremos un destino de Java para la implementación compilando el objeto binario con todas sus dependencias de tiempo de ejecución. Esto te permite ejecutar el objeto binario fuera de tu entorno de desarrollo.
Como recordarás, la regla de compilación java_binary
produce un .jar y una secuencia de comandos de shell de wrapper. Observa el contenido de
runner.jar con este comando:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jarEl contenido es el siguiente:
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
Como puedes ver, runner.jar contiene Runner.class, pero no su dependencia,
Greeting.class. La secuencia de comandos runner que genera Bazel agrega greeter.jar
a la ruta de clase, por lo que, si la dejas así, se ejecutará de forma local, pero no
se ejecutará de forma independiente en otra máquina. Afortunadamente, la regla java_binary
te permite compilar un objeto binario independiente y apto para la implementación. Para compilarlo, agrega
_deploy.jar al nombre del destino:
bazel build //src/main/java/com/example/cmdline:runner_deploy.jarBazel genera un resultado similar al siguiente:
INFO: Found 1 target...
Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date:
bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
INFO: Elapsed time: 1.700s, Critical Path: 0.23s
Acabas de compilar runner_deploy.jar, que puedes ejecutar de forma independiente fuera de
tu entorno de desarrollo, ya que contiene las dependencias de tiempo de ejecución
requeridas. Observa el contenido de este JAR independiente con el
mismo comando que antes:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jarEl contenido incluye todas las clases necesarias para ejecutar:
META-INF/
META-INF/MANIFEST.MF
build-data.properties
com/
com/example/
com/example/cmdline/
com/example/cmdline/Runner.class
com/example/Greeting.class
Lecturas adicionales
Para obtener más información, consulta los siguientes recursos:
rules_jvm_external para obtener reglas para administrar dependencias transitivas de Maven
Dependencias externas para obtener más información sobre cómo trabajar con repositorios locales y remotos.
Las otras reglas para obtener más información sobre Bazel
El instructivo de compilación de C++ para comenzar a compilar proyectos de C++ con Bazel.
El instructivo de la aplicación para Android y el instructivo de la aplicación para iOS para comenzar a compilar aplicaciones para dispositivos móviles para Android y iOS con Bazel.
¡Suerte en el proceso de compilación!