Simplifying native-image generation with Maven plugin and embeddable configuration
In this blog post we show two features recently added to GraalVM to simplify the generation of native images. One is a Maven plugin so you can include the native image generation in your build without calling the command line utilities manually. Then we look at native-image.properties
as a way to include the configuration for your library in the jar file to avoid manual configuration.
Building netty-plot with native-image-maven-plugin
In a previous blog post we have shown how to make use of isolates and compressed references in GraalVM native images. Maven was used to build a uber-jar (fat-jar) that was then compiled into a native image with the GraalVM native-image tool. As of recent GraalVM releases we are now able to build native images right out of maven without running the native-image tool as a separate step after building the uber-jar.
This blog post shows you how to use Maven to easily build native images. If you are using GraalVM as your JAVA_HOME
it is sufficient to just add:
into the <plugins>
section of your pom-file. For the previously published native-netty-plot example the above is sufficient to allow maven to build the native-image for us. Running mvn package
will now give us:
The output shows that the plugin figured out what jar-files it needs to pass to native-image and what the executable main-class should be (com.oracle.svm.nettyplot.PlotServer
). Once mvn package
completes, you can find the executable netty-plot
in the target directory of the maven project.
Since the Maven integration is available in almost any IDE you having a maven plugin means you can generate native images without leaving it:
Plugin customization
It is possible to customize the image build with a <configuration>
node in the <plugin>
node. Currently the following options are available.
Configuration parameter <mainClass>
By default the plugin consults several locations in your pom file in the following order to determine what the main class of the image should be:
<maven-shade-plugin> <transformers> <transformer> <mainClass>
<maven-assembly-plugin> <archive> <manifest> <mainClass>
<maven-jar-plugin> <archive> <manifest> <mainClass>
If it cannot find a proper definition of a main class in any of these locations you can add a <mainClass>
in the <configuration>
node of the plugin to specify a custom main class.
Configuration parameter <imageName>
If an image filename is not set explicitly native-image will choose one for you (e.g. netty-plot
in netty-plot example above). Use parameter <imageName>
if you want to provide a custom filename for your image.
Configuration parameter <buildArgs>
If you want to pass additional native-image options for image building you can use the <buildArgs>
parameter.
Example configuration
Suppose you want to build an image with assertions enabled that uses com.acme.MeepMeep
as main class. You would simply add:
to the definition of the native-image-maven-plugin in your pom-file.
Using maven-plugin with GraalVM Enterprise Edition:
If you use GraalVM Enterprise Edition as your JAVA_HOME
the plugin builds images with enterprise feature enabled. E.g. the executable will automatically be built with compressed references and other optimizations enabled.
Using maven-plugin without GraalVM:
With the plugin it is also possible to build images even without having a GraalVM release installed on your system. The only requirement is to have a JVMCI enabled JDK on your machine. Currently image building is only supported for Java 8 (we are actively working on adding support for Java 11) therefore — as of now — you have to download a JVMCI enabled OpenJDK8. Once you have such a JDK set as your JAVA_HOME
you can use the above native-image-maven-plugin snippet in your maven projects and image building will work. The reason this works is because the plugin will cause maven to download all the necessary components for image building directly from maven-central. In this scheme most of the time updating to a later version is simply a matter of changing the <version>
tag in your <plugin>
definition. For example, once we have 1.0-RC15
released you just need to update your plugin definition to:
No further steps are required. Maven will take care of the rest!
⚠ Please note that in this scenario using macro options like--language:<name>
or --tool:<name>
will not work as they require a full GraalVM release to be used as your JAVA_HOME
. Also be aware that if a newer GraalVM release has changes that depend on a newer JVMCI enabled JDK you will have to update the JVMCI enabled JDK as well … caveat emptor.
Composable native-image.properties
Since the recent releases of GraalVM we also have another important native-image building improvement. It is now possible to embed native-image.properties
files in META-INF/native-image
in jar-files. The native-image tool will automatically process all files it finds in this resource location and use them to construct native-image command line arguments. The native-netty-plot example from the previous blog post already makes use of this feature. By having a native-image.properties
file located at src/main/resources/META-INF/native-image/com.oracle.substratevm/netty-plot
building the image now does not require any additional arguments. Running the following command:
native-image -jar target/netty-plot-0.1-jar-with-dependencies.jar
is sufficient to build the image. Before, building an image that contains Netty always required the use of additional command line options. Usually it was something like:
-H:ReflectionConfigurationResources=reflection-config.json \
--delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder
The reason this works now is that these extra flags can now be embedded in the jar-file itself. Looking into the native-image.properties
file from above shows what extra flags are passed to native-image when netty-plot-0.1-jar-with-dependencies.jar
is on the image classpath.
Here again we find among other things the familiar options needed for building images that contain Netty. Notice the use of ${.}
in the above file. This is a convenient feature of native-image.properties
files that allows to refer to the location the native-image.properties
file resides in. This way it is possible to conveniently specify resource locations relative to the location of a native-image.properties
file. In the above case ${.}
gets expanded to META-INF/native-image/com.oracle.substratevm/netty-plot
.
Making Java libraries native-image compatible by default
If correctly applied, the mechanism described above allow Java library writers to equip their libraries with out-of-the-box native-image compatibility.
In the example of Netty, if we had an artifact io.netty:netty-all
that contains:
META-INF/native-image/io.netty/netty-all/reflection-config.json
META-INF/native-image/io.netty/netty-all/native-image.properties
in addition to the substitutions from com.oracle.svm.nettyplot.NettySubstitutions
then Netty could be used with native-image out of the box. The other native-image.properties
file for our netty-plot example would then only contain options that netty-plot itself needs:
In addition to that, we could get rid of the com.oracle.substratevm:svm
dependency in our pom file
which is currently only needed because NettySubstitutions depend on it.
💡 About <scope>provided</scope>
The
<scope>provided</scope>
used above is important because the substitutions are only needed during image building wherecom.oracle.substratevm:svm
is provided by the image builder itself. If this scope definition is omitted Maven would putcom.oracle.substratevm:svm
and all its transitive dependencies into the jar file when it creates an uber-jar. Clearly we wouldn't want that to happen!
If io.netty:netty-all
would contain all the specifics needed to allow using it in native-images the above dependency in netty-plot could be reduced to
because netty-plot itself only depends on our official org.graalvm.nativeimage API.
Conclusion
In this article we looked at how one can use the GraalVM native image Maven plugin to simplify the creation of native images from your projects. We also looked at a way for libraries to embed the necessary native-image configuration bits in the META-INF/native-image
directory in the jar files.
These two are a quality of life improvements that allow the ecosystem to take advantage of the GraalVM native images and ahead of time compilation. Download GraalVM and give it a try: https://www.graalvm.org/downloads.
See a good feature missing or have any other feedback? Please reach out to us, leave an issue at the GraalVM GitHub repo or contact us any other way!