1

I am building a Spring Native application on a Linux machine. Building an executable that runs on Linux works.

How can I build an executable on the same machine that runs natively on Windows? One option would be to create a Docker image, but is there also a way to build a Windows executable that would run without Docker while using the Linux machine as build environment?

My setup:

  • GraalVM CE 21.1.0 (Java 11) is installed, PATH and JAVA_HOME are set accordingly

  • pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Native</description>
    <properties>
        <java.version>11</java.version>
        <repackage.classifier>exec</repackage.classifier>
        <native-buildtools.version>0.9.0</native-buildtools.version>
        <spring-native.version>0.10.0</spring-native.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-native</artifactId>
            <version>${spring-native.version}</version>
        </dependency>
        <dependency>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>junit-platform-native</artifactId>
            <version>${native-buildtools.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>${repackage.classifier}</classifier>
                    <image>
                        <builder>paketobuildpacks/builder:tiny</builder>
                        <env>
                            <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
                        </env>
                    </image>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.experimental</groupId>
                <artifactId>spring-aot-maven-plugin</artifactId>
                <version>${spring-native.version}</version>
                <executions>
                    <execution>
                        <id>generate</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <version>${native-buildtools.version}</version>
                <executions>
                    <execution>
                        <id>build-native</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>
  • A single application class in placed in the subfolder src/main/java/demo:
package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
werner
  • 13,518
  • 6
  • 30
  • 45

1 Answers1

2

There's currently no option in GraalVM for cross-compiling the application for different OSes. The technical reason for that is that during the build process native code from JDK is linked into the executable and that's obviously platform specific, and also the code paths that are OS specific are recorded in the output binary and the code for the other OSes might not, because it was not executed or looked at during the analysis.

So there's no way currently to produce a Windows executable on Linux. The options you can explore are:

  • Building a docker image with the Linux executable
  • Building a linux executable and running it with Windows Linux Subsystem (WLS)
  • Building the native image on a Windows machine, for example using a CI service that offers Windows machines. Here's an example how to do that with GitHub actions: https://blogs.oracle.com/developers/building-cross-platform-native-images-with-graalvm (see the chapter "Creating The Windows Image" for Windows)

I think the last option of building on a cloud machine is the most end user-friendly.

Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25