En este instructivo, se abordan 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 de Java simple que ilustre los 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:
- Cómo crear un destino
- Visualiza las dependencias del proyecto
- Divide el proyecto en varios destinos y paquetes
- Cómo controlar la visibilidad del objetivo en todos los paquetes
- Destinos de referencia mediante etiquetas
- Cómo implementar un destino
Antes de comenzar
Instala Bazel
Para prepararte para el instructivo, primero instala Bazel si aún no lo tienes instalado.
Instala el JDK
Instala Java JDK (la versión preferida es la 11; sin embargo, se admiten las versiones entre la 8 y la 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" > “Avanzado” pestaña > "Variables de entorno..." .
- En la lista "Variables del usuario" (la que está en la parte superior), haz clic en "Nueva…".
- En "Nombre de la variable", , ingresa
JAVA_HOME
. - Haz clic en "Explorar directorio…".
- Navega al directorio de 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 ejemplo
Recupera el proyecto de ejemplo del repositorio de GitHub de Bazel:
git clone https://github.com/bazelbuild/examples
El proyecto de ejemplo de este instructivo se encuentra en el directorio examples/java-tutorial
y tiene la siguiente estructura:
java-tutorial
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── cmdline
│ │ ├── BUILD
│ │ └── Runner.java
│ ├── Greeting.java
│ └── ProjectRunner.java
└── WORKSPACE
Compila con Bazel
Configura el lugar de trabajo
Antes de compilar un proyecto, debes configurar su lugar 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
WORKSPACE
, que identifica el directorio y su contenido como un lugar 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 el proyecto. (Un directorio dentro del lugar de trabajo que contiene un archivoBUILD
es un paquete). Más adelante en este instructivo, aprenderás sobre los paquetes).
Para designar un directorio como lugar de trabajo de Bazel, crea un archivo vacío llamado WORKSPACE
en ese directorio.
Cuando Bazel compila el proyecto, todas las entradas y dependencias deben estar en el mismo Workspace. 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.
Comprende el archivo BUILD
Un archivo BUILD
contiene varios tipos de instrucciones diferentes para Bazel.
El tipo más importante es la regla de compilación, que le indica a Bazel cómo compilar la
los resultados deseados, como bibliotecas o objetos binarios ejecutables. Cada instancia
de una regla de compilación en el archivo BUILD
se llama destino y apunta a un
conjunto específico de archivos
de origen y dependencias. Un objetivo también puede apuntar a otros objetivos.
Echa un vistazo al archivo java-tutorial/BUILD
:
java_binary(
name = "ProjectRunner",
srcs = glob(["src/main/java/com/example/*.java"]),
)
En nuestro ejemplo, el objetivo ProjectRunner
crea una instancia de la regla java_binary
integrada de Bazel. La regla le indica a Bazel
compilar un archivo .jar
y una secuencia de comandos de shell wrapper (ambos con el nombre del destino)
Los atributos del destino establecen explícitamente sus dependencias y opciones.
Si bien el atributo name
es obligatorio, muchos son opcionales. Por ejemplo, en la
El destino de la regla ProjectRunner
, name
es el nombre del destino y srcs
especifica
los archivos de origen que usa Bazel para compilar el destino y main_class
especifica
la clase que contiene el método principal. (Habrás notado que nuestro ejemplo
Usa glob para pasar un conjunto de archivos de origen a Bazel.
en lugar de enumerarlos uno por uno).
Compila el proyecto
Para compilar tu proyecto de ejemplo, navega al directorio java-tutorial
y ejecuta lo siguiente:
bazel build //:ProjectRunner
En la etiqueta de destino, la parte //
es la ubicación del archivo BUILD
.
en relación con la raíz del lugar de trabajo (en este caso, la raíz en sí),
y ProjectRunner
es el nombre del destino en el archivo BUILD
. (Deberás
obtendrás más información sobre las etiquetas de destino al final de este instructivo).
Bazel produce 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. Compilación de lugares de Bazel
Resultados en el directorio bazel-bin
, en la raíz del lugar de trabajo. Explora
su contenido para obtener una idea de la estructura de salida de Bazel.
Ahora, prueba tu objeto binario recién compilado:
bazel-bin/ProjectRunner
Revisa el gráfico de dependencias
Bazel requiere que se declaren explícitamente las dependencias de compilación en los archivos BUILD. Bazel usa esas sentencias para crear el gráfico de dependencias del proyecto, que permite compilaciones incrementales precisas.
Para visualizar las dependencias del proyecto de ejemplo, puedes generar una representación de texto del gráfico de dependencias. Para ello, ejecuta este comando en la raíz del espacio de trabajo:
bazel query --notool_deps --noimplicit_deps "deps(//:ProjectRunner)" --output graph
El comando anterior le indica a Bazel que busque todas las dependencias del destino.
//:ProjectRunner
(sin incluir las dependencias implícitas ni de host) y formatea el
la salida como un grafo.
Luego, pega el texto en GraphViz.
Como puedes ver, el proyecto tiene un único destino que compila dos archivos de origen con dependencias adicionales:
Después de configurar tu espacio de trabajo, compilar tu proyecto y examinar sus dependencias, puedes agregarle complejidad.
Cómo definir mejor tu compilación de Bazel
Si bien un solo objetivo es suficiente para los proyectos pequeños, proyectos más grandes en múltiples objetivos y paquetes para permitir compilaciones (es decir, solo reconstruir lo que ha cambiado) y acelerar tus compilaciones al construir varias partes de un proyecto a la vez.
Cómo especificar varios destinos de compilación
Puedes dividir la compilación del proyecto de ejemplo en dos destinos. Reemplaza el contenido de
el archivo java-tutorial/BUILD
con 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 //:ProjectRunner
Bazel produce 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 recién compilado:
bazel-bin/ProjectRunner
Si ahora modificas ProjectRunner.java
y vuelves a compilar el proyecto, solo Bazel
vuelve 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. Las compilaciones de destino ProjectRunner
dos archivos de origen y depende de otro destino (:greeter
), que se compila
un archivo fuente adicional.
Usa varios paquetes
Ahora dividamos el proyecto en varios paquetes. Si observas el directorio src/main/java/com/example/cmdline
, verás que también contiene un archivo BUILD
, 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).
Echa un vistazo al archivo src/main/java/com/example/cmdline/BUILD
:
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 //
(de ahí 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 asignar explícitamente el destino runner
.
en la visibilidad de //src/main/java/com/example/cmdline/BUILD
para los objetivos de
//BUILD
con el atributo visibility
Esto se debe a que, de forma predeterminada, los objetivos solo son visibles para otros objetivos en el mismo archivo BUILD
. (Bazel usa aplicaciones de destino
visibilidad para evitar problemas, como bibliotecas con detalles de implementación
filtrando en 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 paquete nuevo ejecutando el siguiente comando en la raíz del espacio de trabajo:
bazel build //src/main/java/com/example/cmdline:runner
Bazel produce 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 tu objeto binario recién compilado:
./bazel-bin/src/main/java/com/example/cmdline/runner
Modificaste el proyecto para que se compile como dos paquetes, cada uno con uno los objetivos y comprender las dependencias entre ellos.
Usa etiquetas para hacer referencia a destinos
En los archivos BUILD
y en la línea de comandos, Bazel usa etiquetas de destino para hacer referencia a objetivos, por ejemplo, //:ProjectRunner
o //src/main/java/com/example/cmdline:runner
. Su sintaxis es la siguiente:
//path/to/package:target-name
Si el objetivo es un objetivo de regla, path/to/package
es la ruta a la
que contiene el archivo BUILD
, y target-name
es el nombre que le asignaste
el destino en el archivo BUILD
(el atributo name
). Si el destino es un archivo, 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 hagas referencia a destinos en la raíz del repositorio, la ruta del paquete estará vacía, así que solo usa //:target-name
. Cuando se hace referencia a destinos dentro del mismo BUILD
incluso puedes omitir el identificador raíz del espacio de trabajo //
y solo usar
:target-name
Por ejemplo, para los destinos del archivo java-tutorial/BUILD
, no tenías que especificar una ruta de acceso del paquete, ya que la raíz del lugar 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
,
debías 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, empaquetemos un destino 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 de desarrollo de software.
Como recordarás, la regla de compilación java_binary
produce un .jar
y una secuencia de comandos de shell wrapper. Consulta el contenido de runner.jar
con el siguiente comando:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar
El 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, así que si la dejas así, se ejecutará de manera local, pero
no se ejecutará de forma independiente
en otra máquina. Por suerte, la regla java_binary
te permite compilar un binario independiente y ejecutable. Para compilarlo, agrega
_deploy.jar
para el nombre del destino:
bazel build //src/main/java/com/example/cmdline:runner_deploy.jar
Bazel produce 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. Echa un vistazo al contenido de este JAR independiente con el
el mismo comando que antes:
jar tf bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar
El 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 las reglas que administran 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 instructivo de la aplicación para iOS) para comenzar compilando aplicaciones móviles para iOS y Android con Bazel.
¡Feliz compilación!