Cross compiling Java application with MicroDoc GraalVM Embedded
We illustrate how to debug the native image of a Java application, produced by MicroDoc GraalVM Embedded, and running on a remote embedded device. The debugging experience is illustrated both inside the IntelliJ IDEA and VSCode IDE. The embedded device runs Linux on an ARMv8-A processor (running in AArch64 mode), like a Raspberry Pi 4 or 5.
Building Java native images using MicroDoc GraalVM Embedded with Maven and Gradle
⏩ TL;DR (too long; didn’t read)
- Read documentation of Native Image on https://www.graalvm.org/latest/reference-manual/native-image/#build-a-native-executable
- Configure
native-imageoptions to enable cross compilation.
🏗️ Build a native image on the build computer
In order to build a native image for a target device, we need to specify some options to native-image. For example:
$ <somewhere>/graalvm/bin/native-image \
-O0 -g -cp <somewhere>/bin -o <somewhere>/hello-world --target=linux-aarch64 \
--native-compiler-path=<somewhere>/aarch64-none-linux-gnu-gcc \
--native-linker-options=-L<somewhere>/lib \
-H:+UnlockExperimentalVMOptions \
-H:+UseCAPCache -H:CAPCacheDir=<somewhere>/cap-cache/aarch64-linux-gnu \
-H:-UnlockExperimentalVMOptions \
com.example.HelloWorld
This builds the executable <somewhere>/hello-world that runs on the target device.
⚠️ When building debug native images of your application, consider using the following options:
-H:-StripDebugInfoand-H:DebugInfoSourceSearchPath=<somewhere>/src
Building with Maven
We recommend using the official Maven plugin for GraalVM Native Image building. Please follow the instructions on the official documentation and specify the following options in the profile section:
<profile>
<id>native-aarch64</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>build</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<fallback>false</fallback>
<agent>
<enabled>true</enabled>
</agent>
<buildArgs>
<buildArg>--target=linux-aarch64</buildArg>
<buildArg>--native-compiler-path=$somewhere/aarch64-none-linux-gnu-gcc</buildArg>
<buildArg>--native-linker-options=-L$somewhere/lib</buildArg>
<buildArg>-H:+UnlockExperimentalVMOptions</buildArg>
<buildArg>-H:+UseCAPCache</buildArg>
<buildArg>-H:CAPCacheDir=$somewhere/cap-cache/aarch64-linux-gnu</buildArg>
<buildArg>-H:-UnlockExperimentalVMOptions</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
In orde to build the target image, you need to specify the profile when ubuilding with Maven:
mvn -Pnative-aarch64 package
Building with gradle
We recommend using the official Gradle plugin for GraalVM Native Image building. Please follow the instructions on the official documentation and specify the following options in the graalvmNative section:
graalvmNative {
buildArgs.add('--target=linux-aarch64')
buildArgs.add("--native-compiler-path=$somewhere/aarch64-none-linux-gnu-gcc")
buildArgs.add("--native-linker-options=-L$somewhere/lib")
buildArgs.add('-H:+UnlockExperimentalVMOptions')
buildArgs.add('-H:+UseCAPCache')
buildArgs.add('-H:CAPCacheDir=$somewhere/cap-cache/aarch64-linux-gnu')
buildArgs.add('-H:-UnlockExperimentalVMOptions')
buildArgs.add("-Dllvm.bin.dir=$System.env.BUNDLE_D0EPS/llvm/bin")
}
In order to build the target image, you need to specify the nativeBuild when building with Gradle:
gradle nativeBuild