GraalVM Native Image & bld

<1 min read

Someone asked us if bld could be used to generate native application with GraalVM. Our first reaction was, sure, you can create an extension to do just that. But after a bit of reflection, it dawned on me that it could easily be done using the bld-exec extension. The extension allows for executing applications from the command line.

The Maven plugin configuration given to us, as an example, looked something like:

<profile>
  <id>native-image</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.graalvm.nativeimage</groupId>
        <artifactId>native-image-maven-plugin</artifactId>
        <version>21.2.0</version>
        <configuration>
          <imageName>ExampleApp</imageName>
          <mainClass>com.example.ExampleApp</mainClass>
          <buildArgs>
            <!-- Reduces the image size - Ensures the native image doesn't include the
            JVM as a fallback option -->
            <buildArg>--no-fallback</buildArg>
            <!-- Disables the use of the GraalVM compilation server -->
            <buildArg>--no-server</buildArg>
            <!-- Improve startup time - Initialize classes at build time rather than at
            runtime -->
            <buildArg>--initialize-at-build-time</buildArg>
            <!-- Include all files under /resources -->
          </buildArgs>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>native-image</goal>
            </goals>
            <phase>package</phase>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

That's a handful at first glance. But once you understand how the native-image utility options work, it is pretty easy to convert:

@BuildCommand(value = "native-exec", summary = "Builds a native executable")
public void nativeExec() throws Exception {
  new ExecOperation()
    .fromProject(this)
    .timeout(120)
    .workDir(buildMainDirectory().getAbsolutePath())
    .command("native-image",
        "--no-fallback",
        "--no-server",
        "--initialize-at-build-time",
        "com.example.ExampleApp",
        new File(workDirectory(), "ExampleApp").getAbsolutePath())
    .execute();
}

Just use ./bld compile native-exec, and you're off to the races.

I've created a full example to demonstrate how to build a native executable using a class or JAR.

After converting over 60 projects, I'm still impressed with how flexible (yet, simple) bld truly is.