Instructivo de Bazel: Compila un proyecto de Java

Informar un problema Ver fuente Por la noche · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

Este instructivo abarca los conceptos 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 Se ilustran 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 hacer 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 los paquetes
  • Haz referencia a destinos mediante etiquetas
  • Implementa un destino

Antes de comenzar

Instala Bazel

Para prepararte para el instructivo, primero instala Bazel si sigues estos pasos: si aún no lo tienes instalado.

Cómo instalar el JDK

  1. Instala Java JDK (la versión preferida es la 11; sin embargo, se admiten las versiones entre la 8 y la 15).

  2. Configura la variable de entorno JAVA_HOME para que apunte al JDK.

    • En Linux/macOS:

      export JAVA_HOME="$(dirname $(dirname $(realpath $(which javac))))"
      
    • En Windows:

      1. Abre el Panel de control.
      2. Ve a "Sistema y seguridad". > "Sistema" > "Configuración avanzada del sistema" > “Avanzado” pestaña > "Variables de entorno..." .
      3. En "Variables de usuario" (la de la parte superior), haz clic en "Nueva...".
      4. En "Nombre de la variable", , ingresa JAVA_HOME.
      5. Haz clic en "Explorar directorio...".
      6. Navega al directorio de JDK (por ejemplo, C:\Program Files\Java\jdk1.8.0_152).
      7. 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/examples

El proyecto de muestra para este instructivo se encuentra en examples/java-tutorial y está estructurado de la siguiente manera:

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 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. Integra 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 del directorio del proyecto.

  • Uno o más archivos BUILD, que le indican a Bazel cómo compilar diferentes partes de el proyecto. (un directorio dentro del espacio de trabajo que contiene un archivo BUILD es un paquete. Aprenderás sobre paquetes más adelante en este instructivo).

Para designar un directorio como 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 Workspace. Los archivos que residen en diferentes espacios de trabajo son independientes de uno a menos que esté vinculado, lo que está fuera del alcance de este instructivo.

Comprende el archivo BUILD

Un archivo BUILD 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 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 destino también puede apuntar a otras objetivos.

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 interfaz Regla java_binary. Esta 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 muestra, navega hasta el directorio java-tutorial y ejecuta el siguiente comando:

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. Explorar a través de su contenido para tener 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 muestra, puedes generar un texto del gráfico de dependencias ejecutando este comando en el raíz del lugar 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 formatear 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:

Gráfico de dependencia del objetivo "ProjectRunner"

Después de configurar tu espacio de trabajo, crea tu proyecto y examina las dependencias, puedes agregar algo de 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 tu 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 la entradas anteriores, pero la estructura de la compilación es diferente:

Gráfico de dependencia del objetivo "ProjectRunner" Después de agregar una dependencia

Ahora, compilaste el proyecto con dos destinos. Las compilaciones de destino ProjectRunner un archivo de origen y depende de otro destino (:greeter), que se compila un archivo fuente adicional.

Cómo usar varios paquetes

Ahora dividamos el proyecto en varios paquetes. Si observas src/main/java/com/example/cmdline, puedes ver que también contiene un archivo BUILD y algunos archivos fuente. Para Bazel, el espacio de trabajo 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 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 // (por lo tanto, la etiqueta de destino //:greeter). Bazel lo sabe a través del atributo deps. Observa el gráfico de dependencia:

Gráfico de dependencia del "ejecutor" de destino

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 destinos 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 target, entonces path/to/package es la ruta a la raíz del paquete. target-name es el nombre del archivo de destino, incluida su ruta de acceso completa.

Cuando se hace referencia a destinos en la raíz del repositorio, la ruta del paquete está vacía, 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 tuviste que especificar una ruta de acceso del paquete, ya que la raíz del lugar de trabajo es un paquete (//) tus dos etiquetas de destino eran solo //:ProjectRunner y //:greeter.

Sin embargo, para los destinos del archivo //src/main/java/com/example/cmdline/BUILD, sigue estos pasos: debió especificar la ruta de acceso del paquete completa de //src/main/java/com/example/cmdline y la etiqueta de destino era //src/main/java/com/example/cmdline:runner.

Empaqueta un destino de Java para la implementación

Ahora empaquetemos un destino de Java para la implementación compilando el objeto binario con todos de sus dependencias del entorno 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. Echa un vistazo al contenido de runner.jar con este 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 objeto binario implementable y autónomo. 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 desde tu entorno de desarrollo, ya que contiene el entorno de ejecución dependencias. 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:

¡Feliz compilación!