1. Overview

The Gluon Client plugin leverages GraalVM, OpenJDK and JavaFX 11+, by compiling into native code the Java Client application and all its required dependencies, so it can directly be executed as a native application on the target platform.

One of the advantages is a much faster startup time, as we don’t need the JVM to start anymore. The resulting application will be entirely integrated with the native operating system.

The Gluon Client plugin comes in two flavors, depending if you are a Maven or a Gradle user.

2. The Gluon Client plugin for Maven

If you have a Java or JavaFX project and you are using Maven as a build tool, you can easily include the plugin to start creating native applications.

The plugin can be found here: https://github.com/gluonhq/client-maven-plugin

2.1. Requirements

At this moment the plugin is in beta, and supports Linux, Mac OS X and iOS platforms for now.

To use the plugin to develop and deploy native applications on Mac or iOS platforms, you need a Mac with MacOS X 10.13.2 or superior, and Xcode 9.2 or superior, available from the Mac App Store. Once Xcode is downloaded and installed, open it and accept the license terms.

For now, only JDK 11 is supported. Any JDK 11 distribution that doesn’t bundle JavaFX is valid, like:

Once downloaded and installed, don’t forget to set JAVA_HOME pointing to that JDK.

To build for iOS devices, you also need to have llvm in your path. You can download llvm 6.0.0 for Mac OS X here.

Once downloaded, add it to your path, editing .bash_profile and adding:

export PATH="/Users/<user>/Downloads/clang+llvm-6.0.0-x86_64-apple-darwin/bin:$PATH"

2.2. Getting started

To use the plugin, apply the following steps:

2.2.1. Apply the plugin

Edit your pom file and add the plugin:

<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>client-maven-plugin</artifactId>
    <version>0.0.11</version>
    <configuration>
        <!-- Uncomment to run on iOS: -->
        <!-- <target>ios</target>-->
        <mainClass>your.mainClass</mainClass>
    </configuration>
</plugin>

<pluginRepositories>
    <pluginRepository>
        <id>gluon-releases</id>
        <url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
    </pluginRepository>
</pluginRepositories>

The plugin allows some options that can be set in configuration, to modify the default settings, and several goals, to build and run the native application.

2.2.2. Goals

You can run the regular goals to build and run your project as a regular Java project, if you use the javafx-maven-plugin plugin:

mvn clean javafx:run

Once the project is ready, the plugin has these main goals:

client:compile

This goal does the AOT compilation. It is a very intensive and lengthy task (several minutes, depending on your project and CPU), so it should be called only when the project is ready and runs fine on a VM.

Run:

mvn client:compile

The results will be available at target/client/$targetOS-$arch/gvm.

When the object is created, this goal will generate the native executable for the target platform.

Run:

mvn client:link

The results will be available at target/client/$targetOS-$arch/$AppName.app.

client:build

This goal simply combines client:compile and client:link.

client:run

Runs the executable in the target platform.

Run:

mvn client:run

Or run directly the application from command line:

target/client/$targetOS-$arch/$AppName.app/$AppName

On Mac OS X it will create a distributable application, on iOS the app could be deployed to an iOS device or to the iOS simulator.

2.2.3. Run the samples

There are several samples available here to run with the Gluon Client plugin for Maven.

Before we proceed, set JAVA_HOME to Java 11.

HelloWorld

Let’s run HelloWorld with the Gluon Client plugin for Maven.

The main class is:

package hello;

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World!!");
        System.exit(0);
    }
}

and the pom file is:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>hello</groupId>
    <artifactId>helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>helloWorld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.gluonhq</groupId>
                <artifactId>client-maven-plugin</artifactId>
                <version>0.0.11</version>
                <configuration>
                    <!-- Uncomment to run on iOS: -->
                    <!-- <target>ios</target>-->
                    <mainClass>hello.HelloWorld</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>gluon-releases</id>
            <url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
        </pluginRepository>
    </pluginRepositories>
</project>
Target: Linux

Run the client:compile goal, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ helloworld ---
...
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [helloWorld:10801]        (cap):   1,379.32 ms
[SUB] [helloWorld:10801]        setup:   3,246.26 ms
...
[SUB] [helloWorld:10801]   (typeflow):  35,378.54 ms
[SUB] [helloWorld:10801]    (objects):  34,572.46 ms
[SUB] [helloWorld:10801]   (features):   2,757.69 ms
[SUB] [helloWorld:10801]     analysis:  73,734.82 ms
[SUB] [helloWorld:10801]     (clinit):     667.75 ms
[SUB] [helloWorld:10801]     universe:   2,830.90 ms
[SUB] [helloWorld:10801]      (parse):   8,167.43 ms
[SUB] [helloWorld:10801]     (inline):  10,770.02 ms
[SUB] [helloWorld:10801]    (compile):  67,391.16 ms
[SUB] [helloWorld:10801]      compile:  88,810.64 ms
[SUB] [helloWorld:10801]        image:   6,914.08 ms
[SUB] [helloWorld:10801]        write:     480.62 ms
[SUB] [helloWorld:10801]      [total]: 178,337.87 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:01 min

And as a result, HelloWorld.o, with 42.4 MB, can be found under target/client/linux-x86_64/gvm/tmp/SVM-15***/HelloWorld.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ helloworld ---
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.082 s

produces target/client/linux-x86_64/HelloWorld (46.7 MB), that can be executed directly or with client:run.

Target output

The output of mvn client:run task is as follows:

[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ helloworld ---
[SUB] Hello World!!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.937 s
Target: Mac OS X

Run the client:compile goal, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ helloworld ---
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [helloWorld:6930]        (cap):   1,262.71 ms
[SUB] [helloWorld:6930]        setup:   2,662.47 ms
[SUB] [helloWorld:6930]   (typeflow):  20,849.14 ms
[SUB] [helloWorld:6930]    (objects):  19,014.41 ms
[SUB] [helloWorld:6930]   (features):   2,831.15 ms
[SUB] [helloWorld:6930]     analysis:  43,647.57 ms
[SUB] [helloWorld:6930]     (clinit):     587.53 ms
[SUB] [helloWorld:6930]     universe:   1,707.76 ms
[SUB] [helloWorld:6930]      (parse):   3,695.30 ms
[SUB] [helloWorld:6930]     (inline):   8,424.84 ms
[SUB] [helloWorld:6930]    (compile):  34,429.28 ms
[SUB] [helloWorld:6930]      compile:  49,084.08 ms
[SUB] [helloWorld:6930]        image:   6,129.88 ms
[SUB] [helloWorld:6930]        write:   1,831.17 ms
[SUB] [helloWorld:6930]      [total]: 108,191.48 ms
result of compile = 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:52 min

And as a result, HelloWorld.o, with 39.7 MB, can be found under target/client/macos-x86_64/gvm/tmp/SVM-15***/HelloWorld.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ helloworld ---
result of linking = 0
...
BUNDLE ID = hello.HelloWorld
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.228 s

produces target/client/macos-x86_64/HelloWorld.app (40.1 MB), that can be executed directly or with client:run.

Target output

The output of mvn client:run task is as follows:

[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ helloworld ---
[SUB] 2019-05-27 18:03:40.251 helloWorld[7029:199633] Starting Gluon VM...
[SUB] Starting on thread: 0x7000017ac000
[SUB] Hello World!!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.937 s
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Uncomment <target>ios</target> from the pom, and run the client:compile goal, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ helloworld ---
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [helloWorld:7311]        (cap):   4,363.25 ms
[SUB] [helloWorld:7311]        setup:   5,754.89 ms
[SUB] [helloWorld:7311]   (typeflow):  19,433.06 ms
[SUB] [helloWorld:7311]    (objects):  22,537.46 ms
[SUB] [helloWorld:7311]   (features):   2,748.67 ms
[SUB] [helloWorld:7311]     analysis:  45,640.35 ms
[SUB] [helloWorld:7311]     (clinit):     634.58 ms
[SUB] [helloWorld:7311]     universe:   2,124.83 ms
[SUB] [helloWorld:7311]      (parse):   4,606.49 ms
[SUB] [helloWorld:7311]    (compile):  45,577.68 ms
[SUB] [helloWorld:7311]    (bitcode):   2,737.40 ms
[SUB] [helloWorld:7311]       (link):  20,962.06 ms
[SUB] [helloWorld:7311]         (gc):  16,719.34 ms
[SUB] [helloWorld:7311]       (llvm): 153,929.10 ms
[SUB] [helloWorld:7311]   (stackmap):   4,638.59 ms
[SUB] [helloWorld:7311]      compile: 250,409.77 ms
[SUB] [helloWorld:7311]        image:   7,407.73 ms
[SUB] [helloWorld:7311]        write:   7,551.65 ms
[SUB] [helloWorld:7311]      [total]: 320,941.75 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 05:26 min

And as a result, HelloWorld.o, with 35.1 MB, can be found under target/client/ios-arm64/gvm/tmp/SVM-15***/helloWorld.o.

Note that the process takes some time. There will be performance improvements, but either way, it is convenient to test first on desktop (and with HotSpot) as much as possible (i.e. with mvn javafx:run), so client:compile doesn’t have to be repeated due to avoidable errors.

Now running mvn client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ helloworld ---
...
BUNDLE ID = hello.HelloWorld
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 18.716 s

produces target/client/ios-arm64/helloWorld.app (62.1 MB).

Target output

Now it can be deployed to a plugged iOS device with mvn client:run.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ helloworld ---
...
Starting vm...
Starting GVM for ios
Hello World!!
App is installed on the device
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.000 s

Even if there is no UI for this application, we’ll get the message printed to the terminal.

Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set <target>ios-sim</target> at the plugin’s configuration in the pom file, and run the client:compile goal, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ helloworld ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [helloWorld:13629]        (cap):   1,478.68 ms
[SUB] [helloWorld:13629]        setup:   2,855.63 ms
[SUB] [helloWorld:13629]   (typeflow):  20,595.76 ms
[SUB] [helloWorld:13629]    (objects):  21,356.76 ms
[SUB] [helloWorld:13629]   (features):   3,130.13 ms
[SUB] [helloWorld:13629]     analysis:  45,980.78 ms
[SUB] [helloWorld:13629]     (clinit):     538.39 ms
[SUB] [helloWorld:13629]     universe:   2,091.10 ms
[SUB] [helloWorld:13629]      (parse):   4,865.22 ms
[SUB] [helloWorld:13629]     (inline):   8,673.21 ms
[SUB] [helloWorld:13629]    (compile):  33,827.08 ms
[SUB] [helloWorld:13629]      compile:  50,316.72 ms
[SUB] [helloWorld:13629]        image:   5,689.51 ms
[SUB] [helloWorld:13629]        write:   1,938.24 ms
[SUB] [helloWorld:13629]      [total]: 110,223.30 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:55 min

And as a result, HelloWorld.o, with 41 MB, can be found under target/client/ios-x86_64/gvm/tmp/SVM-15***/helloWorld.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ helloworld ---
...
BUNDLE ID = hello.HelloWorld
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.980 s

produces target/client/ios-x86_64/helloWorld.app (41.6 MB).

Finally, running client:run, will open the simulator, install the application and launch it.

App deployed

Since there is no UI for this application, we’ll get the message printed to the terminal.

[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ helloworld ---
[SUB] UIApplication launched!
[SUB] UIApplication started GVM in a separate thread
[SUB] Starting vm...
[SUB] Starting GVM
[SUB] [UIAPP] applicationDidBecomeActive
[SUB] Hello World!!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.085 s
HelloFX

Let’s run now a JavaFX application.

public class HelloFX extends Application {

    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");
        Label label = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");

        ImageView imageView = new ImageView(new Image(HelloFX.class.getResourceAsStream("/hellofx/openduke.png")));
        imageView.setFitHeight(200);
        imageView.setPreserveRatio(true);

        VBox root = new VBox(30, imageView, label);
        root.setAlignment(Pos.CENTER);

        Scene scene = new Scene(root, 640, 480);
        scene.getStylesheets().add(HelloFX.class.getResource("styles.css").toExternalForm());
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

and pom file:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>hello</groupId>
    <artifactId>hellofx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hellofx</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <javafx.version>12.0.1</javafx.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.2</version>
                <configuration>
                    <mainClass>hellofx.HelloFX</mainClass>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.gluonhq</groupId>
                <artifactId>client-maven-plugin</artifactId>
                <version>0.0.11</version>
                <configuration>
                    <!-- Uncomment to run on iOS: -->
                    <!-- <target>ios</target>-->
                    <mainClass>hellofx.HelloFX</mainClass>
                </configuration>
            </plugin>

        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>gluon-releases</id>
            <url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
        </pluginRepository>
    </pluginRepositories>
</project>
Target: Linux

We run the application to make sure everything works with Java 11:

mvn clean javafx:run

Running client:compile goal produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofx ---
...
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [hellofx:28625]        (cap):   1,512.23 ms
[SUB] [hellofx:28625]        setup:   4,019.44 ms
...
[SUB] [hellofx:28625]   (typeflow): 111,549.85 ms
[SUB] [hellofx:28625]    (objects):  88,773.89 ms
[SUB] [hellofx:28625]   (features):   6,896.03 ms
[SUB] [hellofx:28625]     analysis: 212,251.63 ms
[SUB] [hellofx:28625]     (clinit):   2,929.08 ms
[SUB] [hellofx:28625]     universe:   9,557.51 ms
[SUB] [hellofx:28625]      (parse):  16,202.72 ms
[SUB] [hellofx:28625]     (inline):  27,791.56 ms
[SUB] [hellofx:28625]    (compile):  96,688.32 ms
[SUB] [hellofx:28625]      compile: 144,230.06 ms
[SUB] [hellofx:28625]        image:  11,715.25 ms
[SUB] [hellofx:28625]        write:     811.31 ms
[SUB] [hellofx:28625]      [total]: 387,139.51 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 06:46 min

And as a result, HelloFX.o, with 88.2 MB, can be found under target/client/linux-x86_64/gvm/tmp/SVM-15***/hellofx.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofx ---
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.031 s

produces target/client/linux-x86_64/HelloFX (98.5 MB), that can be executed directly or with nativeRun.

HelloFX build output

Finally, we can run the application:

HelloFX application
Target: Mac OS X

We run the application to make sure everything works with Java 11:

mvn clean javafx:run

Running mvn client:compile goal produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofx ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [hellofx:14136]        (cap):   1,730.18 ms
[SUB] [hellofx:14136]        setup:   3,382.49 ms
[SUB] [hellofx:14136]   (typeflow):  80,638.46 ms
[SUB] [hellofx:14136]    (objects):  63,333.14 ms
[SUB] [hellofx:14136]   (features):   5,847.48 ms
[SUB] [hellofx:14136]     analysis: 155,460.00 ms
[SUB] [hellofx:14136]     (clinit):   6,293.02 ms
[SUB] [hellofx:14136]     universe:  15,496.54 ms
[SUB] [hellofx:14136]      (parse):   8,398.09 ms
[SUB] [hellofx:14136]     (inline):  15,441.53 ms
[SUB] [hellofx:14136]    (compile):  71,389.13 ms
[SUB] [hellofx:14136]      compile: 101,241.25 ms
[SUB] [hellofx:14136]        image:  14,197.14 ms
[SUB] [hellofx:14136]        write:   4,016.45 ms
[SUB] [hellofx:14136]      [total]: 297,521.18 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 05:06 min

And as a result, HelloFX.o, with 86.7 MB, can be found under target/client/macos-x86_64/gvm/tmp/SVM-15***/hellofx.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofx ---
result of linking = 0
...
BUNDLE ID = hellofx.HelloFX
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.100 s

produces target/client/macos-x86_64/HelloFX.app (86.9 MB), that can be executed directly or with nativeRun.

HelloFX build output

Finally, we can run the application:

HelloFX application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Set target ios at the plugin’s configuration in the pom file:

<configuration>
    <target>ios</target>
    <mainClass>hellofx.HelloFX</mainClass>
</configuration>

We can now run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofx ---
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [hellofx:14379]        (cap):   1,535.49 ms
[SUB] [hellofx:14379]        setup:   3,117.25 ms
[SUB] [hellofx:14379]   (typeflow):  96,799.89 ms
[SUB] [hellofx:14379]    (objects):  90,909.41 ms
[SUB] [hellofx:14379]   (features):   6,952.39 ms
[SUB] [hellofx:14379]     analysis: 199,936.12 ms
[SUB] [hellofx:14379]     (clinit):   2,794.81 ms
[SUB] [hellofx:14379]     universe:  13,354.26 ms
[SUB] [hellofx:14379]      (parse):  14,009.20 ms
[SUB] [hellofx:14379]    (compile): 101,178.33 ms
[SUB] [hellofx:14379]    (bitcode):   7,371.21 ms
[SUB] [hellofx:14379]       (link):  53,393.09 ms
[SUB] [hellofx:14379]         (gc):  81,721.29 ms
[SUB] [hellofx:14379]       (llvm): 343,375.52 ms
[SUB] [hellofx:14379]   (stackmap):  24,513.52 ms
[SUB] [hellofx:14379]      compile: 634,467.14 ms
[SUB] [hellofx:14379]        image:  19,559.72 ms
[SUB] [hellofx:14379]        write:  34,263.98 ms
[SUB] [hellofx:14379]      [total]: 909,888.96 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15:17 min

And as a result, hellofx.o, with 74.7 MB, can be found under target/client/ios-arm64/gvm/tmp/SVM-15***/hellofx.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofx ---
...
BUNDLE ID = hellofx.HelloFX
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 34.315 s

produces target/client/ios-arm64/hellofx.app (136.6 MB).

Target output

Now it can be deployed to a plugged iOS device with mvn client:run.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ hellofx ---
...
UIApplication launched!
UIApplication started GVM in a separate thread
Starting vm...
Starting GVM for ios
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target ios-sim at the plugin’s configuration in the pom file:

<configuration>
    <target>ios-sim</target>
    <mainClass>hellofx.HelloFX</mainClass>
</configuration>

We run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofx ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [hellofx:1685]        (cap):   4,562.32 ms
[SUB] [hellofx:1685]        setup:   6,110.04 ms
[SUB] [hellofx:1685]   (typeflow):  71,143.43 ms
[SUB] [hellofx:1685]    (objects):  54,356.13 ms
[SUB] [hellofx:1685]   (features):   5,811.98 ms
[SUB] [hellofx:1685]     analysis: 135,088.39 ms
[SUB] [hellofx:1685]     (clinit):   2,172.74 ms
[SUB] [hellofx:1685]     universe:   6,270.40 ms
[SUB] [hellofx:1685]      (parse):   8,552.27 ms
[SUB] [hellofx:1685]     (inline):  11,480.83 ms
[SUB] [hellofx:1685]    (compile):  60,598.69 ms
[SUB] [hellofx:1685]      compile:  85,710.89 ms
[SUB] [hellofx:1685]        image:  12,773.94 ms
[SUB] [hellofx:1685]        write:   3,454.84 ms
[SUB] [hellofx:1685]      [total]: 252,748.83 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:20 min

And as a result, hellofx.o, with 86.1 MB, can be found under target/client/ios-x86_64/gvm/tmp/SVM-15***/hellofx.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofx ---
...
BUNDLE ID = hellofx.HelloFX
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.143 s

produces target/client/ios-x86_64/hellofx.app (86.8 MB).

Finally, running client:run, will open the simulator, install the application and launch it.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ hellofx ---
[SUB] UIApplication launched!
[SUB] UIApplication started GVM in a separate thread
[SUB] Starting vm...
[SUB] Starting GVM
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

2.2.4. GluonClient configuration

Note

This is for advanced users.

The plugin allows some customization to modify the default settings, which are:

<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>client-maven-plugin</artifactId>
    <version>0.0.11</version>
    <configuration>
        <target>host</target>
        <graalLibsVersion>20.0.0-ea+12</graalLibsVersion>
        <graalLibsPath></graalLibsPath>
        <javaStaticSdkVersion>11-ea+6</javaStaticSdkVersion>
        <javafxStaticSdkVersion>11-ea+6</javafxStaticSdkVersion>
        <bundlesList></bundlesList>
        <resourcesList></resourcesList>
        <reflectionList></reflectionList>
        <jniList></jniList>
        <delayInitList></delayInitList>
        <releaseSymbolsList></releaseSymbolsList>
        <mainClass>your.mainClass</mainClass>
        <verbose>false</verbose>
        <attachVersion>4.0.2</attachVersion>
        <attachList></attachList>
    </configuration>
</plugin>
target

A string that defines the target platform. The default is host, which refers to the platform that currently hosts the process (Mac OS X for now). It can be set also to ios to create native images for iOS devices (Aarch64) or to ios-sim, to run on iOS simulator (x86_64).

Default: host

graalLibsVersion

The Graal library version. For more information, see Graal.

Default is 20.0.0-ea+12

graalLibsPath

The local path to the Graal libraries. If it is not set, the default path will be used, under: ~/.gluon/omega/graalLibs/$graalLibsVersion/bundle/lib.

Default is empty.

javaStaticSdkVersion

The version of the Java static libraries. These will be located under: ~/.gluon/omega/javaStaticSdk/$javaStaticSdkVersion/$target-libs-$javaStaticSdkVersion.

Default is 11-ea+6

javafxStaticSdkVersion

The version of the JavaFX SDK and its static libraries. These will be located under: ~/.gluon/omega/javafxStaticSdk/$javaStaticSdkVersion/$target-sdk/lib.

Default is 13-ea+7

bundlesList

List of additional full qualified bundle resources that will be added to the default bundles list, that already includes:

  • com/sun/javafx/scene/control/skin/resources/controls

  • com.sun.javafx.tk.quantum.QuantumMessagesBundle

resourcesList

List of additional resource patterns or extensions that will be added to the default resource list that already includes:

  • png, gif, jpg, jpeg, bmp,

  • ttf, css, fxml, json,

  • frag, gls, license

reflectionList

List of additional full qualified classes that will be added to the default reflection list, that already includes most of the JavaFX classes.

Note: The current list is added to file under target/client/gvm/reflectionconfig-$target.json.

jniList

List of additional full qualified classes that will be added to the default jni list, that already includes most of the JavaFX classes.

Note: The current list is added to file under target/client/gvm/jniconfig-$target.json.

delayInitList

List of additional full qualified classes that will be added to the default delayed initialization list.

releaseSymbolsList

List of additional JNI functions that will be added to the default release symbols list, that already includes most of the JNI methods

Note: The current list is added to file under target/client/gvm/release.symbols.

verbose

Set to true will generate a more verbose output, useful when having errors, to identify possible issues.

Default is false

Also, you can get a more verbose output for these goals running with -X:

mvn -X client:compile
Attach Configuration

If you want to include Gluon Attach services to your project, you can use attachList to including the name of the services, like:

<!-- dependencies -->
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>display</artifactId>
    <version>4.0.2</version>
</dependency>

<!-- plugin -->
<configuration>
    <attachList>
        <list>display</list>
    </attachList>
</configuration>

By default the attachVersion is 4.0.2.

After saving the changes, the dependencies for the defined services will be resolved to include those for defined target, and when the native compile goal is executed, the native services implementations will be added to the reflection and JNI lists.

Note: the Attach platform implementations will be added only to the goals of the Client plugin, but not to the JavaFX plugin.

2.2.5. Native tasks from command line

To help debugging the native tasks, two scripts are generated:

  • The goal client:compile generates target/client/gvm/compile.sh.

  • The goal client:link generates target/client/gvm/link.sh

If any of these goals fails to run successfully, running the script from a terminal could help identifying the cause of the error.

2.2.6. HelloFXML Sample

Let’s run now a JavaFX application with FXML to illustrate how to use the extended options and customize the plugin configuration.

In this case, we need to add the resource bundle of the project, and since the FXMLLoader will use reflection to inspect the FXML file, we need to add a few classes from this file to the reflection list: mainly the FXMLLoader itself, the controller and the controls, since the containers are already part of this list.

public class HelloFXML extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        AnchorPane root = FXMLLoader.load(HelloFXML.class.getResource("hello.fxml"),
                ResourceBundle.getBundle("hellofx.hello"));
        Scene scene = new Scene(root, 640, 480);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

and pom file:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>hello</groupId>
    <artifactId>hellofxml</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hellofxml</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <javafx.version>12.0.1</javafx.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.2</version>
                <configuration>
                    <mainClass>hellofxml/hellofx.HelloFXML</mainClass>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.gluonhq</groupId>
                <artifactId>client-maven-plugin</artifactId>
                <version>0.0.11</version>
                <configuration>
                    <!-- Uncomment to run on iOS: -->
                    <!-- <target>ios</target>-->
                    <bundlesList>
                        <list>hellofx.hello</list>
                    </bundlesList>
                    <reflectionList>
                        <list>javafx.fxml.FXMLLoader</list>
                        <list>hellofx.HelloController</list>
                        <list>javafx.scene.control.Button</list>
                        <list>javafx.scene.control.Label</list>
                    </reflectionList>
                    <mainClass>hellofxml/hellofx.HelloFXML</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>gluon-releases</id>
            <url>http://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
        </pluginRepository>
    </pluginRepositories>
</project>

We run it first to make sure everything works, with Java 11 and mvn clean javafx:run.

Target: Linux

Running mvn client:compile produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofxml ---
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [hellofxml:30515]        (cap):   1,234.70 ms
[SUB] [hellofxml:30515]        setup:   3,105.20 ms
...
[SUB] [hellofxml:30515]   (typeflow): 101,557.73 ms
[SUB] [hellofxml:30515]    (objects):  81,457.99 ms
[SUB] [hellofxml:30515]   (features):   5,450.71 ms
[SUB] [hellofxml:30515]     analysis: 192,087.02 ms
[SUB] [hellofxml:30515]     (clinit):   2,549.60 ms
[SUB] [hellofxml:30515]     universe:   6,947.91 ms
[SUB] [hellofxml:30515]      (parse):  13,650.26 ms
[SUB] [hellofxml:30515]     (inline):  21,077.94 ms
[SUB] [hellofxml:30515]    (compile): 102,970.23 ms
[SUB] [hellofxml:30515]      compile: 141,499.07 ms
[SUB] [hellofxml:30515]        image:  12,222.85 ms
[SUB] [hellofxml:30515]        write:     865.76 ms
[SUB] [hellofxml:30515]      [total]: 360,441.62 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 06:04 min

And as a result, hellofxml.o, with 91.4 MB, can be found under target/client/linux-x86_64/gvm/tmp/SVM-15***/hellofxml.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofxml ---
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.902 s

produces target/client/linux-x86_64/hellofxml (101.9 MB).

Target output

It can be executed directly or with mvn client:run.

HelloFXML application
Target: Mac OS X

Running mvn client:compile produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofxml ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [hellofxml:2191]        (cap):   1,913.76 ms
[SUB] [hellofxml:2191]        setup:   3,539.64 ms
[SUB] [hellofxml:2191]   (typeflow):  95,477.67 ms
[SUB] [hellofxml:2191]    (objects):  74,631.86 ms
[SUB] [hellofxml:2191]   (features):   7,241.69 ms
[SUB] [hellofxml:2191]     analysis: 183,636.41 ms
[SUB] [hellofxml:2191]     (clinit):  10,581.14 ms
[SUB] [hellofxml:2191]     universe:  17,382.84 ms
[SUB] [hellofxml:2191]      (parse):   8,291.19 ms
[SUB] [hellofxml:2191]     (inline):  12,765.37 ms
[SUB] [hellofxml:2191]    (compile):  59,033.67 ms
[SUB] [hellofxml:2191]      compile:  85,489.15 ms
[SUB] [hellofxml:2191]        image:  13,567.52 ms
[SUB] [hellofxml:2191]        write:   4,444.06 ms
[SUB] [hellofxml:2191]      [total]: 311,393.13 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 05:18 min

And as a result, hellofxml.o, with 89.8 MB, can be found under target/client/macos-x86_64/gvm/tmp/SVM-15***/hellofxml.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofxml ---
result of linking = 0
...
BUNDLE ID = hellofx.HelloFXML
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.931 s

produces target/client/macos-x86_64/hellofxml.app (90.1 MB).

Target output

It can be executed directly or with client:run.

HelloFXML application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Set target ios at the plugin’s configuration in the pom file:

<configuration>
    <target>ios</target>
    ...
    <mainClass>hellofxml/hellofx.HelloFXML</mainClass>
</configuration>

We can now run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofxml ---
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [hellofxml:2572]        (cap):   2,783.99 ms
[SUB] [hellofxml:2572]        setup:   4,144.19 ms
[SUB] [hellofxml:2572]   (typeflow):  81,749.81 ms
[SUB] [hellofxml:2572]    (objects):  76,670.25 ms
[SUB] [hellofxml:2572]   (features):   5,268.01 ms
[SUB] [hellofxml:2572]     analysis: 169,485.01 ms
[SUB] [hellofxml:2572]     (clinit):   2,485.75 ms
[SUB] [hellofxml:2572]     universe:  15,096.05 ms
[SUB] [hellofxml:2572]      (parse):  11,568.10 ms
[SUB] [hellofxml:2572]    (compile): 102,455.94 ms
[SUB] [hellofxml:2572]    (bitcode):  10,184.40 ms
[SUB] [hellofxml:2572]       (link):  51,906.72 ms
[SUB] [hellofxml:2572]         (gc):  37,234.46 ms
[SUB] [hellofxml:2572]       (llvm): 356,039.94 ms
[SUB] [hellofxml:2572]   (stackmap):  16,016.09 ms
[SUB] [hellofxml:2572]      compile: 589,350.72 ms
[SUB] [hellofxml:2572]        image:  18,826.24 ms
[SUB] [hellofxml:2572]        write:  34,072.91 ms
[SUB] [hellofxml:2572]      [total]: 834,560.65 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14:00 min

And as a result, hellofxml.o, with 78.1 MB, can be found under target/client/ios-arm64/gvm/tmp/SVM-15***/hellofxml.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofx ---
...
BUNDLE ID = hellofx.HelloFX
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 34.315 s

produces target/client/ios-arm64/hellofxml.app (141.1 MB).

Target output

Now it can be deployed to a plugged iOS device with mvn client:run.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ hellofxml ---
...
UIApplication launched!
UIApplication started GVM in a separate thread
Starting vm...
Starting GVM for ios
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target ios-sim at the plugin’s configuration in the pom file:

<configuration>
    <target>ios-sim</target>
    ...
    <mainClass>hellofxml/hellofx.HelloFXML</mainClass>
</configuration>

We run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellofxml ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [hellofxml:3152]        (cap):   1,634.47 ms
[SUB] [hellofxml:3152]        setup:   3,137.70 ms
[SUB] [hellofxml:3152]   (typeflow):  70,101.39 ms
[SUB] [hellofxml:3152]    (objects):  62,403.87 ms
[SUB] [hellofxml:3152]   (features):   6,792.13 ms
[SUB] [hellofxml:3152]     analysis: 143,828.01 ms
[SUB] [hellofxml:3152]     (clinit):   2,700.84 ms
[SUB] [hellofxml:3152]     universe:   6,560.65 ms
[SUB] [hellofxml:3152]      (parse):   8,931.42 ms
[SUB] [hellofxml:3152]     (inline):  15,640.60 ms
[SUB] [hellofxml:3152]    (compile):  59,281.64 ms
[SUB] [hellofxml:3152]      compile:  88,968.11 ms
[SUB] [hellofxml:3152]        image:  13,972.84 ms
[SUB] [hellofxml:3152]        write:   4,127.82 ms
[SUB] [hellofxml:3152]      [total]: 263,843.30 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:32 min

And as a result, hellofxml.o, with 88.9 MB, can be found under target/client/ios-x86_64/gvm/tmp/SVM-15***/hellofxml.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellofxml ---
...
BUNDLE ID = hellofx.HelloFXML
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.748 s

produces target/client/ios-x86_64/helloWorld.app (89.7 MB).

Target output

Finally, running client:run, will open the simulator, install the application and launch it.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ hellofxml ---
[SUB] UIApplication launched!
[SUB] UIApplication started GVM in a separate thread
[SUB] Starting vm...
[SUB] Starting GVM
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

2.2.7. HelloGluon Sample

Time now to run a Gluon Mobile application to illustrate how to use the extended options and customize the plugin extension gluonClient.

In this case, we need to add some Gluon Attach services like Storage or Display, and for that we’ll use attachList.

public class HelloGluon extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            FloatingActionButton fab = new FloatingActionButton(MaterialDesignIcon.SEARCH.text,
                    e -> System.out.println("Search"));

            ImageView imageView = new ImageView(new Image(HelloGluon.class.getResourceAsStream("openduke.png")));

            imageView.setFitHeight(200);
            imageView.setPreserveRatio(true);

            Label label = new Label("Hello, Gluon Mobile!");
            VBox root = new VBox(20, imageView, label);
            root.setAlignment(Pos.CENTER);

            View view = new View(root) {
                @Override
                protected void updateAppBar(AppBar appBar) {
                    appBar.setTitleText("Gluon Mobile");
                }
            };

            fab.showOn(view);

            return view;
        });
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.LIGHT_GREEN.assignTo(scene);
        scene.getStylesheets().add(HelloGluon.class.getResource("styles.css").toExternalForm());

        if (Platform.isDesktop()) {
            Dimension2D dimension2D = DisplayService.create()
                    .map(DisplayService::getDefaultDimensions)
                    .orElse(new Dimension2D(640, 480));
            scene.getWindow().setWidth(dimension2D.getWidth());
            scene.getWindow().setHeight(dimension2D.getHeight());
        }
    }

    public static void main(String[] args) {
        launch();
    }

}

and pom file:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gluonhq.hello</groupId>
    <artifactId>hellogluon</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hellogluon</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <javafx.version>12.0.1</javafx.version>
        <attach.version>4.0.2</attach.version>
        <mainClassName>com.gluonhq.hello.HelloGluon</mainClassName>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq</groupId>
            <artifactId>charm-glisten</artifactId>
            <version>6.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>display</artifactId>
            <version>${attach.version}</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>lifecycle</artifactId>
            <version>${attach.version}</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>statusbar</artifactId>
            <version>${attach.version}</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>storage</artifactId>
            <version>${attach.version}</version>
        </dependency>
        <dependency>
            <groupId>com.gluonhq.attach</groupId>
            <artifactId>util</artifactId>
            <version>${attach.version}</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>Gluon</id>
            <url>https://nexus.gluonhq.com/nexus/content/repositories/releases</url>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>11</release>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.2</version>
                <configuration>
                    <mainClass>${mainClassName}</mainClass>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.gluonhq</groupId>
                <artifactId>client-maven-plugin</artifactId>
                <version>0.0.11</version>
                <configuration>
                    <!-- Uncomment to run on iOS: -->
                    <!-- <target>ios</target>-->
                    <attachList>
                        <list>display</list>
                        <list>lifecycle</list>
                        <list>statusbar</list>
                        <list>storage</list>
                    </attachList>
                    <reflectionList>
                        <list>com.gluonhq.impl.charm.glisten.control.skin.GlistenButtonSkin</list>
                    </reflectionList>
                    <mainClass>${mainClassName}</mainClass>
                </configuration>
            </plugin>
        </plugins>

    </build>

    <pluginRepositories>
        <pluginRepository>
            <id>gluon-releases</id>
            <url>https://nexus.gluonhq.com/nexus/content/repositories/releases/</url>
        </pluginRepository>
    </pluginRepositories>
</project>
Target: Linux

We run the application to make sure everything works with Java 11:

mvn clean javafx:run

Note that there are no Attach services implementations added at this point (this could be solved by adding the required Desktop dependencies).

Running mvn client:compile produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellogluon ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] Nothing to compile - all classes are up to date
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
[SUB] [hellogluon:13852]    classlist:   3,645.12 ms
...
[SUB] [hellogluon:13852]        (cap):   1,326.75 ms
[SUB] [hellogluon:13852]        setup:   3,455.03 ms
...
[SUB] [hellogluon:13852]   (typeflow): 104,615.62 ms
[SUB] [hellogluon:13852]    (objects):  72,848.39 ms
[SUB] [hellogluon:13852]   (features):   5,668.11 ms
[SUB] [hellogluon:13852]     analysis: 187,766.97 ms
[SUB] [hellogluon:13852]     (clinit):   2,535.21 ms
[SUB] [hellogluon:13852]     universe:   8,587.79 ms
[SUB] [hellogluon:13852]      (parse):  13,792.32 ms
[SUB] [hellogluon:13852]     (inline):  21,433.43 ms
[SUB] [hellogluon:13852]    (compile): 126,237.12 ms
[SUB] [hellogluon:13852]      compile: 165,628.68 ms
[SUB] [hellogluon:13852]        image:  12,238.32 ms
[SUB] [hellogluon:13852]        write:   1,457.07 ms
[SUB] [hellogluon:13852]      [total]: 383,573.54 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 06:26 min

And as a result, hellogluon.o, with 94.0 MB, can be found under target/client/linux-x86_64/gvm/tmp/SVM-15***/hellogluon.o.

Running client:link now:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellogluon ---
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.088 s

produces target/client/linux-x86_64/hellogluon (104.6 MB).

Target output

It can be executed directly or via mvn client:run.

HelloFXML application
Target: Mac OS X

We run the application to make sure everything works with Java 11:

mvn clean javafx:run

Note that there are no Attach services implementations added at this point (this could be solved by adding the required Desktop dependencies).

Running mvn client:compile produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellogluon ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [hellogluon:12613]        (cap):   1,856.29 ms
[SUB] [hellogluon:12613]        setup:   3,270.98 ms
[SUB] [hellogluon:12613]   (typeflow):  89,120.52 ms
[SUB] [hellogluon:12613]    (objects):  58,477.52 ms
[SUB] [hellogluon:12613]   (features):   5,850.47 ms
[SUB] [hellogluon:12613]     analysis: 160,829.79 ms
[SUB] [hellogluon:12613]     (clinit):   4,264.76 ms
[SUB] [hellogluon:12613]     universe:  10,439.26 ms
[SUB] [hellogluon:12613]      (parse):  12,414.80 ms
[SUB] [hellogluon:12613]     (inline):  14,159.47 ms
[SUB] [hellogluon:12613]    (compile):  80,053.29 ms
[SUB] [hellogluon:12613]      compile: 112,293.86 ms
[SUB] [hellogluon:12613]        image:  13,318.50 ms
[SUB] [hellogluon:12613]        write:   4,069.33 ms
[SUB] [hellogluon:12613]      [total]: 307,402.39 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 05:14 min
[

And as a result, hellogluon.o, with 92.7 MB, can be found under target/client/macos-x86_64/gvm/tmp/SVM-15***/hellogluon.o.

Running client:link now:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellogluon ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.394 s

produces target/client/macos-x86_64/hellogluon.app (92.9 MB), that can be executed directly or with mvn client:run.

Target output

Finally, we can run the application:

HelloFXML application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Set target ios at the plugin’s configuration in the pom file:

<configuration>
    <target>ios</target>
    ...
    <mainClass>hellogluon/com.gluonhq.hello.HelloGluon</mainClass>
</configuration>

We can now run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellogluon ---
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [hellogluon:13237]        (cap):   1,530.92 ms
[SUB] [hellogluon:13237]        setup:   2,789.03 ms
[SUB] [hellogluon:13237]   (typeflow):  79,704.32 ms
[SUB] [hellogluon:13237]    (objects):  68,049.90 ms
[SUB] [hellogluon:13237]   (features):   6,750.74 ms
[SUB] [hellogluon:13237]     analysis: 160,862.57 ms
[SUB] [hellogluon:13237]     (clinit):   3,173.82 ms
[SUB] [hellogluon:13237]     universe:  13,060.50 ms
[SUB] [hellogluon:13237]      (parse):  10,600.57 ms
[SUB] [hellogluon:13237]    (compile): 100,812.23 ms
[SUB] [hellogluon:13237]    (bitcode):   9,685.39 ms
[SUB] [hellogluon:13237]       (link):  56,574.09 ms
[SUB] [hellogluon:13237]         (gc):  38,831.32 ms
[SUB] [hellogluon:13237]       (llvm): 356,604.46 ms
[SUB] [hellogluon:13237]   (stackmap):  20,596.55 ms
[SUB] [hellogluon:13237]      compile: 597,913.97 ms
[SUB] [hellogluon:13237]        image:  19,327.04 ms
[SUB] [hellogluon:13237]        write:   5,610.54 ms
[SUB] [hellogluon:13237]      [total]: 802,621.03 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13:27 min

And as a result, hellogluon.o, with 79.6 MB, can be found under target/client/ios-arm64/gvm/tmp/SVM-15***/hellogluon.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellogluon ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.795 s

produces target/client/ios-arm64/hellogluon.app (144.2 MB).

Target output

Now it can be deployed to a plugged iOS device with mvn client:run.

App deployed
[INFO] --- client-maven-plugin:0.0.11:run (default-cli) @ hellogluon ---
...
UIApplication launched!
UIApplication started GVM in a separate thread
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target ios-sim at the plugin’s configuration in the pom file:

<configuration>
    <target>ios-sim</target>
    ...
    <mainClass>hellogluon/com.gluonhq.hello.HelloGluon</mainClass>
</configuration>

We run mvn client:compile, that produces the following output:

[INFO] --- client-maven-plugin:0.0.11:compile (default-cli) @ hellogluon ---
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [hellogluon:13844]        (cap):   2,037.06 ms
[SUB] [hellogluon:13844]        setup:   3,605.17 ms
[SUB] [hellogluon:13844]   (typeflow):  64,785.27 ms
[SUB] [hellogluon:13844]    (objects):  53,660.56 ms
[SUB] [hellogluon:13844]   (features):   5,039.73 ms
[SUB] [hellogluon:13844]     analysis: 127,426.99 ms
[SUB] [hellogluon:13844]     (clinit):   1,912.69 ms
[SUB] [hellogluon:13844]     universe:   9,017.71 ms
[SUB] [hellogluon:13844]      (parse):   9,725.01 ms
[SUB] [hellogluon:13844]     (inline):  14,960.54 ms
[SUB] [hellogluon:13844]    (compile):  85,166.45 ms
[SUB] [hellogluon:13844]      compile: 115,495.34 ms
[SUB] [hellogluon:13844]        image:  13,649.70 ms
[SUB] [hellogluon:13844]        write:   2,433.07 ms
[SUB] [hellogluon:13844]      [total]: 274,735.85 ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 04:40 min

And as a result, hellogluon.o, with 92 MB, can be found under target/client/ios-x86_64/gvm/tmp/SVM-15***/hellogluon.o.

Running client:link:

[INFO] --- client-maven-plugin:0.0.11:link (default-cli) @ hellogluon ---
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.133 s

produces build/client/ios-x86_64/hellogluon.app (92.7 MB).

Target output

Finally, running client:run, will open the simulator, install the application and launch it.

App deployed
> Task :nativeRun
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

2.3. Feedback and issues

Please submit your feedback or any possible issue to the plugin’s issue tracker.

3. The Gluon Client plugin for Gradle

If you have a Java or JavaFX project and you are using Gradle as a build tool, you can easily include the plugin to start creating native applications.

The plugin can be found here: https://github.com/gluonhq/client-gradle-plugin

3.1. Requirements

At this moment the plugin is in beta, and supports Linux, Mac OS X and iOS platforms for now.

To use the plugin to develop and deploy native applications on Mac or iOS platforms, you need a Mac with MacOS X 10.13.2 or superior, and Xcode 9.2 or superior, available from the Mac App Store. Once Xcode is downloaded and installed, open it and accept the license terms.

For now, only JDK 11 is supported. Any JDK 11 distribution that doesn’t bundle JavaFX is valid, like:

Once downloaded and installed, don’t forget to set JAVA_HOME pointing to that JDK.

To build for iOS devices, you also need to have llvm in your path. You can download llvm 6.0.0 for Mac OS X here.

Once downloaded, add it to your path, editing .bash_profile and adding:

export PATH="/Users/<user>/Downloads/clang+llvm-6.0.0-x86_64-apple-darwin/bin:$PATH"

3.2. Getting started

To use the plugin, apply the following steps:

3.2.1. Apply the plugin

Using the plugins DSL, add to your build.gradle file:

plugins {
    id 'com.gluonhq.client-gradle-plugin' version '0.0.11'
}

This requires adding the plugin repository to the settings.gradle file:

pluginManagement {
    repositories {
        maven {
            url "https://nexus.gluonhq.com/nexus/content/repositories/releases"
        }
        gradlePluginPortal()
    }
}

rootProject.name = 'HelloFX'

Alternatively, you can use the buildscript DSL directly in the build.gradle file:

buildscript {
    repositories {
        maven {
            url "https://nexus.gluonhq.com/nexus/content/repositories/releases"
        }
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath 'com.gluonhq:client-gradle-plugin:0.0.11'
    }
}
apply plugin: 'com.gluonhq.client-gradle-plugin'

The plugin adds the gluonClient extension, to modify the default settings, and a few tasks, to build and run the native application.

3.2.2. Tasks

You can run the regular tasks to build and run your project as a regular Java project:

./gradlew clean build run

Once the project is ready, the plugin has these three main tasks:

nativeCompile

This tasks does the AOT compilation. It is a very intensive and lengthy task (several minutes, depending on your project and CPU), so it should be called only when the project is ready and runs fine on a VM.

Run:

./gradlew nativeCompile

The results, for the selected target, will be available at $buildDir/client/$targetOS-$arch/gvm.

When the object is created, this task will generate the native executable for the target platform.

Run:

./gradlew nativeLink

The results will be available at $buildDir/client/$targetOS-$arch/$AppName.app.

nativeBuild

This task simply combines nativeCompile and nativeLink.

nativeRun

Runs the executable in the target platform.

Run:

./gradlew nativeRun

Or run directly the application from command line:

build/client/$targetOS-$arch/$AppName.app/$AppName

On Mac OS X it will create a distributable application, on iOS the app could be deployed to an iOS device or to the iOS simulator.

3.2.3. Run the samples

There are several samples available here to run with the Gluon Client plugin for gradle.

Before we proceed, set JAVA_HOME to Java 11.

HelloWorld

Let’s run HelloWorld with the Gluon Client plugin for Gradle.

The main class is:

package hello;

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World!!");
        System.exit(0);
    }
}

and the build file is:

plugins {
    id 'application'
    id 'com.gluonhq.client-gradle-plugin' version '0.0.11'
}

repositories {
    mavenCentral()
}

gluonClient {
    // target = "ios" // uncomment this to run on iOS device
}

mainClassName = 'hello.HelloWorld'
Target: Linux

Run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [HelloWorld:21451]    classlist:   2,141.57 ms
...
[SUB] [HelloWorld:21451]        (cap):   1,408.78 ms
[SUB] [HelloWorld:21451]        setup:   3,410.19 ms
...
[SUB] [HelloWorld:21451]   (typeflow):  30,142.97 ms
[SUB] [HelloWorld:21451]    (objects):  31,365.97 ms
[SUB] [HelloWorld:21451]   (features):   2,908.54 ms
[SUB] [HelloWorld:21451]     analysis:  65,884.42 ms
[SUB] [HelloWorld:21451]     (clinit):   1,126.44 ms
[SUB] [HelloWorld:21451]     universe:   4,915.20 ms
[SUB] [HelloWorld:21451]      (parse):   9,661.30 ms
[SUB] [HelloWorld:21451]     (inline):  12,633.83 ms
[SUB] [HelloWorld:21451]    (compile):  73,335.44 ms
[SUB] [HelloWorld:21451]      compile:  98,439.23 ms
[SUB] [HelloWorld:21451]        image:   7,344.77 ms
[SUB] [HelloWorld:21451]        write:     458.76 ms
[SUB] [HelloWorld:21451]      [total]: 183,009.80 ms

BUILD SUCCESSFUL in 3m 03s

And as a result, HelloWorld.o, with 42.4 MB, can be found under build/client/linux-x86_64/gvm/tmp/SVM-15***/HelloWorld.o.

Running nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

BUILD SUCCESSFUL in 1s

produces build/client/linux-x86_64/HelloWorld (46.7 MB), that can be executed directly or with nativeRun.

Build output

The output of nativeRun task is as follows:

> Task :nativeRun
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

[SUB] Hello World!!
Target: Mac OS X

Run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [HelloWorld:3489]        (cap):   3,476.81 ms
[SUB] [HelloWorld:3489]        setup:   5,717.04 ms
[SUB] [HelloWorld:3489]   (typeflow):  16,521.70 ms
[SUB] [HelloWorld:3489]    (objects):  19,209.70 ms
[SUB] [HelloWorld:3489]   (features):   2,014.54 ms
[SUB] [HelloWorld:3489]     analysis:  38,453.90 ms
[SUB] [HelloWorld:3489]     (clinit):     514.33 ms
[SUB] [HelloWorld:3489]     universe:   1,903.85 ms
[SUB] [HelloWorld:3489]      (parse):   4,219.47 ms
[SUB] [HelloWorld:3489]     (inline):   7,154.73 ms
[SUB] [HelloWorld:3489]    (compile):  32,308.68 ms
[SUB] [HelloWorld:3489]      compile:  46,525.76 ms
[SUB] [HelloWorld:3489]        image:   5,377.92 ms
[SUB] [HelloWorld:3489]        write:   1,708.16 ms
[SUB] [HelloWorld:3489]      [total]: 100,998.35 ms

BUILD SUCCESSFUL in 1m 50s

And as a result, HelloWorld.o, with 39.7 MB, can be found under build/client/macos-x86_64/gvm/tmp/SVM-15***/HelloWorld.o.

Running nativeLink:

> Task :nativeLink
...
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos

BUILD SUCCESSFUL in 9s

produces build/client/macos-x86_64/HelloWorld.app (40.1 MB), that can be executed directly or with nativeRun.

Build output

Since there is no UI for this application, it makes sense to run it from a terminal to see the output:

[SUB] Hello, JAVA main, argc = 1, argv = 0x7ffee25fe5b8
[SUB] 2019-06-13 19:17:45.248 HelloWorld[3631:107111] Starting Gluon VM...
[SUB] Hello World!!
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Uncomment target = "ios" at the build, and run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [HelloWorld:3817]        (cap):   1,315.90 ms
[SUB] [HelloWorld:3817]        setup:   2,799.82 ms
[SUB] [HelloWorld:3817]   (typeflow):  20,591.70 ms
[SUB] [HelloWorld:3817]    (objects):  24,583.29 ms
[SUB] [HelloWorld:3817]   (features):   3,261.04 ms
[SUB] [HelloWorld:3817]     analysis:  49,291.58 ms
[SUB] [HelloWorld:3817]     (clinit):     538.09 ms
[SUB] [HelloWorld:3817]     universe:   1,867.47 ms
[SUB] [HelloWorld:3817]      (parse):   3,494.96 ms
[SUB] [HelloWorld:3817]    (compile):  43,220.52 ms
[SUB] [HelloWorld:3817]    (bitcode):   2,832.98 ms
[SUB] [HelloWorld:3817]       (link):  18,093.20 ms
[SUB] [HelloWorld:3817]         (gc):  14,612.64 ms
[SUB] [HelloWorld:3817]       (llvm): 139,887.92 ms
[SUB] [HelloWorld:3817]   (stackmap):   4,067.58 ms
[SUB] [HelloWorld:3817]      compile: 226,916.73 ms
[SUB] [HelloWorld:3817]        image:   7,368.38 ms
[SUB] [HelloWorld:3817]        write:   8,897.11 ms
[SUB] [HelloWorld:3817]      [total]: 298,974.61 ms

BUILD SUCCESSFUL in 5m 7s

And as a result, HelloWorld.o, with 35.1 MB, can be found under build/client/ios-arm64/gvm/tmp/SVM-15***/helloWorld.o.

Note that the process takes some time. There will be performance improvements, but either way, it is convenient to test first on desktop (and with HotSpot) as much as possible (i.e. with ./gradlew run), so nativeCompile doesn’t have to be repeated due to avoidable errors.

Now running nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
BUILD SUCCESSFUL in 18s

produces build/client/ios-arm64/HelloWorld.app (62.1 MB).

Target output

Now it can be deployed to a plugged iOS device with ./gradlew nativeRun.

App deployed
Starting vm...
Starting GVM for ios
Hello World!!

> Task :nativeRun
App is installed on the device

Even if there is no UI for this application, we’ll get the message printed to the terminal.

Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target = "ios-sim" at the build, and run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [HelloWorld:4092]        (cap):   1,629.44 ms
[SUB] [HelloWorld:4092]        setup:   3,145.77 ms
[SUB] [HelloWorld:4092]   (typeflow):  17,232.20 ms
[SUB] [HelloWorld:4092]    (objects):  20,878.30 ms
[SUB] [HelloWorld:4092]   (features):   2,551.07 ms
[SUB] [HelloWorld:4092]     analysis:  41,752.45 ms
[SUB] [HelloWorld:4092]     (clinit):     720.15 ms
[SUB] [HelloWorld:4092]     universe:   1,983.27 ms
[SUB] [HelloWorld:4092]      (parse):   3,328.60 ms
[SUB] [HelloWorld:4092]     (inline):   6,960.57 ms
[SUB] [HelloWorld:4092]    (compile):  33,788.82 ms
[SUB] [HelloWorld:4092]      compile:  47,023.51 ms
[SUB] [HelloWorld:4092]        image:   5,773.90 ms
[SUB] [HelloWorld:4092]        write:   1,878.33 ms
[SUB] [HelloWorld:4092]      [total]: 103,188.96 ms

BUILD SUCCESSFUL in 1m 51s

And as a result, HelloWorld.o, with 41 MB, can be found under build/client/ios-x86_64/gvm/tmp/SVM-15***/helloWorld.o.

Running nativeLink:

> Task :nativeLink
SVMBridge, osname = Mac OS X
...
BUNDLE ID = hello.HelloWorld

BUILD SUCCESSFUL in 7s

produces build/client/ios-x86_64/helloWorld.app (41.6 MB).

Finally, running nativeRun, will open the simulator, install the application and launch it.

App deployed

Since there is no UI for this application, we’ll get the message printed to the terminal:

[SUB] Starting vm...
[SUB] Starting GVM
[SUB] [UIAPP] applicationDidBecomeActive
[SUB] Hello World!!

BUILD SUCCESSFUL in 8s
HelloFX

Let’s run now a JavaFX application, with main class:

public class HelloFX extends Application {

    public void start(Stage stage) {
        String javaVersion = System.getProperty("java.version");
        String javafxVersion = System.getProperty("javafx.version");
        Label label = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + ".");

        ImageView imageView = new ImageView(new Image(HelloFX.class.getResourceAsStream("/hellofx/openduke.png")));
        imageView.setFitHeight(200);
        imageView.setPreserveRatio(true);

        VBox root = new VBox(30, imageView, label);
        root.setAlignment(Pos.CENTER);

        Scene scene = new Scene(root, 640, 480);
        scene.getStylesheets().add(HelloFX.class.getResource("styles.css").toExternalForm());
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

and build file:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.7'
    id 'com.gluonhq.client-gradle-plugin' version '0.0.11'
}

repositories {
    mavenCentral()
}

gluonClient {
    // target = "ios" // uncomment to deploy on iOS
}

javafx {
    version = "12.0.1"
    modules = [ "javafx.controls" ]
}

mainClassName = 'hellofx.HelloFX'
Target: Linux

We run the application to make sure everything works with Java 11:

./gradlew clean run

Once everything runs successfully, we can go ahead and native compile the app using the nativeCompile task. It produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [HelloFX:27626]        (cap):   3,242.79 ms
[SUB] [HelloFX:27626]        setup:   7,786.80 ms
...
[SUB] [HelloFX:27626]   (typeflow): 349,134.12 ms
[SUB] [HelloFX:27626]    (objects): 149,127.62 ms
[SUB] [HelloFX:27626]   (features):  14,968.42 ms
[SUB] [HelloFX:27626]     analysis: 518,273.06 ms
[SUB] [HelloFX:27626]     (clinit):   3,465.51 ms
[SUB] [HelloFX:27626]     universe:   9,470.82 ms
[SUB] [HelloFX:27626]      (parse):  17,991.31 ms
[SUB] [HelloFX:27626]     (inline):  27,394.37 ms
[SUB] [HelloFX:27626]    (compile): 159,031.10 ms
[SUB] [HelloFX:27626]      compile: 210,645.83 ms
[SUB] [HelloFX:27626]        image:  15,363.52 ms
[SUB] [HelloFX:27626]        write:   2,827.83 ms
[SUB] [HelloFX:27626]      [total]: 772,984.17 ms

BUILD SUCCESSFUL in 13m 16s

And as a result, HelloFX.o, with 88.2 MB, can be found under build/client/linux-x86_64/gvm/tmp/SVM-15***/HelloFX.o.

Running nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

BUILD SUCCESSFUL in 3s

produces build/client/linux-x86_64/HelloFX (98.5 MB), that can be executed directly or with nativeRun.

HelloFX build output

Finally, we can run the application to see the following window:

HelloFX application
Target: Mac OS X

We run the application to make sure everything works with Java 11:

./gradlew clean run

The nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [HelloFX:4281]        (cap):   1,321.24 ms
[SUB] [HelloFX:4281]        setup:   3,051.65 ms
[SUB] [HelloFX:4281]   (typeflow):  63,253.17 ms
[SUB] [HelloFX:4281]    (objects):  48,348.30 ms
[SUB] [HelloFX:4281]   (features):   7,441.98 ms
[SUB] [HelloFX:4281]     analysis: 122,221.92 ms
[SUB] [HelloFX:4281]     (clinit):   2,710.24 ms
[SUB] [HelloFX:4281]     universe:   7,039.62 ms
[SUB] [HelloFX:4281]      (parse):   8,027.64 ms
[SUB] [HelloFX:4281]     (inline):  12,303.28 ms
[SUB] [HelloFX:4281]    (compile):  60,476.05 ms
[SUB] [HelloFX:4281]      compile:  86,296.71 ms
[SUB] [HelloFX:4281]        image:  13,426.13 ms
[SUB] [HelloFX:4281]        write:   4,655.63 ms
[SUB] [HelloFX:4281]      [total]: 240,104.69 ms

BUILD SUCCESSFUL in 4m 11s

And as a result, HelloFX.o, with 86.7 MB, can be found under build/client/macos-x86_64/gvm/tmp/SVM-15***/HelloFX.o.

Running nativeLink:

> Task :nativeLink

BUNDLE ID = hellofx.HelloFX
BUILD SUCCESSFUL in 9s

produces build/client/macos-x86_64/HelloFX.app (86.9 MB), that can be executed directly or with nativeRun.

HelloFX build output

Finally, we can run the application:

HelloFX application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Uncomment target = "ios" at the build file:

gluonClient {
    target = "ios"
}

We can now run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [HelloFX:4407]        (cap):   1,579.93 ms
[SUB] [HelloFX:4407]        setup:   3,208.00 ms
[SUB] [HelloFX:4407]   (typeflow):  64,047.69 ms
[SUB] [HelloFX:4407]    (objects):  51,110.04 ms
[SUB] [HelloFX:4407]   (features):   5,944.12 ms
[SUB] [HelloFX:4407]     analysis: 124,486.39 ms
[SUB] [HelloFX:4407]     (clinit):   1,714.05 ms
[SUB] [HelloFX:4407]     universe:   6,711.85 ms
[SUB] [HelloFX:4407]      (parse):   8,319.86 ms
[SUB] [HelloFX:4407]    (compile):  88,166.16 ms
[SUB] [HelloFX:4407]    (bitcode):   7,262.72 ms
[SUB] [HelloFX:4407]       (link):  53,229.43 ms
[SUB] [HelloFX:4407]         (gc):  40,510.46 ms
[SUB] [HelloFX:4407]       (llvm): 334,081.88 ms
[SUB] [HelloFX:4407]   (stackmap):  20,373.89 ms
[SUB] [HelloFX:4407]      compile: 555,012.93 ms
[SUB] [HelloFX:4407]        image:  17,508.66 ms
[SUB] [HelloFX:4407]        write:  32,804.77 ms
[SUB] [HelloFX:4407]      [total]: 743,535.62 ms

BUILD SUCCESSFUL in 12m 32s

And as a result, HelloFX.o, with 74.7 MB, can be found under build/client/ios-arm64/gvm/tmp/SVM-15***/HelloFX.o.

Running nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
BUNDLE ID = hellofx.HelloFX
Looking for /Entitlements.plist

BUILD SUCCESSFUL in 35s

produces build/client/ios-arm64/hellofx.app (136.6 MB).

Target output

Now it can be deployed to a plugged iOS device with ./gradlew nativeRun.

App deployed
> Task :nativeRun
...
UIApplication launched!
UIApplication started GVM in a separate thread
Starting vm...
Starting GVM for ios
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target="ios-sim" at the build file:

gluonClient {
    target = "ios-sim"
}

We run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [HelloFX:6284]        (cap):   1,557.18 ms
[SUB] [HelloFX:6284]        setup:   3,111.15 ms
[SUB] [HelloFX:6284]   (typeflow):  72,026.23 ms
[SUB] [HelloFX:6284]    (objects):  58,938.67 ms
[SUB] [HelloFX:6284]   (features):   5,474.95 ms
[SUB] [HelloFX:6284]     analysis: 140,110.07 ms
[SUB] [HelloFX:6284]     (clinit):   5,178.91 ms
[SUB] [HelloFX:6284]     universe:  12,192.93 ms
[SUB] [HelloFX:6284]      (parse):  10,695.68 ms
[SUB] [HelloFX:6284]     (inline):  15,844.24 ms
[SUB] [HelloFX:6284]    (compile):  70,111.21 ms
[SUB] [HelloFX:6284]      compile: 102,085.16 ms
[SUB] [HelloFX:6284]        image:  12,910.21 ms
[SUB] [HelloFX:6284]        write:   3,695.59 ms
[SUB] [HelloFX:6284]      [total]: 277,666.96 ms

BUILD SUCCESSFUL in 4m 47s

And as a result, HelloFX.o, with 86.1 MB, can be found under build/client/ios-x86_64/gvm/tmp/SVM-15***/HelloFX.o.

Running ./gradlew nativeLink:

> Task :nativeLink
...
BUNDLE ID = hellofx.HelloFX
BUILD SUCCESSFUL in 8s

produces build/client/ios-x86_64/HelloFX.app (86.8 MB).

Finally, running ./gradlew nativeRun, will open the simulator, install the application and launch it.

App deployed
> Task :nativeRun
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] UIApplication launched!
[SUB] UIApplication started GVM in a separate thread
[SUB] Starting vm...
[SUB] Starting GVM
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

3.2.4. GluonClient extension

Note: This is for advanced users.

The plugin allows some customization to modify the default settings, which are:

gluonClient {
    target = "host"
    graalLibsVersion = "20.0.0-ea+12"
    graalLibsPath = ""
    javaStaticSdkVersion = "11-ea+6"
    javafxStaticSdkVersion = "11-ea+6"
    bundlesList = []
    resourcesList = []
    reflectionList = []
    jniList = []
    delayInitList = []
    releaseSymbolsList = []
    verbose = false
    attachConfig {
        version = "4.0.2"
        services ''
        configuration = "implementation"
    }
}
target

A string that defines the target platform. The default is host, which refers to the platform that currently hosts the process (Mac OS X for now). It can be set also to ios to create native images for iOS devices (arm64) or to ios-sim, to run on iOS simulator (x86_64).

Default: host

graalLibsVersion

The Graal library version. For more information, see Graal.

Default is 20.0.0-ea+12

graalLibsPath

The local path to the Graal libraries. If it is not set, the default path will be used, under: ~/.gluon/omega/graalLibs/$graalLibsVersion/bundle/lib.

Default is empty.

javaStaticSdkVersion

The version of the Java static libraries. These will be located under: ~/.gluon/omega/javaStaticSdk/$javaStaticSdkVersion/$target-libs-$javaStaticSdkVersion.

Default is 11-ea+6

javafxStaticSdkVersion

The version of the JavaFX SDK and its static libraries. These will be located under: ~/.gluon/omega/javafxStaticSdk/$javaStaticSdkVersion/$target-sdk/lib.

Default is 11-ea+6

bundlesList

List of additional full qualified bundle resources that will be added to the default bundles list, that already includes:

  • com/sun/javafx/scene/control/skin/resources/controls

  • com.sun.javafx.tk.quantum.QuantumMessagesBundle

resourcesList

List of additional resource patterns or extensions that will be added to the default resource list that already includes:

  • png, gif, jpg, jpeg, bmp,

  • ttf, css, fxml, json,

  • frag, gls, license

reflectionList

List of additional full qualified classes that will be added to the default reflection list, that already includes most of the JavaFX classes.

Note: The current list is added to file under build/client/gvm/reflectionconfig-$target.json.

jniList

List of additional full qualified classes that will be added to the default jni list, that already includes most of the JavaFX classes.

Note: The current list is added to file under build/client/gvm/jniconfig-$target.json.

delayInitList

List of additional full qualified classes that will be added to the default delayed initialization list.

releaseSymbolsList

List of additional JNI functions that will be added to the default release symbols list, that already includes most of the JNI methods

Note: The current list is added to file under build/client/gvm/release.symbols.

verbose

Set to true will generate a more verbose output, useful when having errors, to identify possible issues.

Default is false

Also, you can get a more verbose output for these goals running with --info:

./gradlew --info nativeCompile
Attach Configuration

If you want to include Gluon Attach services to your project, you can use attachConfig, including the name of the services, like:

gluonClient {
    attachConfig {
        services 'display', 'lifecycle', 'statusbar', 'storage'
    }
}

By default the version is 4.0.2, and the default configuration used is implementation but both can be changed if required.

After saving the changes, when Gradle runs the configuration phase, the dependencies for the defined services will be included (based on the defined target), and when the native compile task is executed, the native services implementations will be added to the reflection and JNI lists.

Note: since the Attach platform implementations are added to the dependencies, these will be available to the run task too.

3.2.5. Running native tasks from scripts

To help debugging the native tasks, two scripts are generated:

  • The task nativeCompile generates build/client/gvm/compile.sh.

  • The task nativeLink generates build/client/gvm/link.sh

If any of these task fails to run successfully, running the script from a terminal could help identifying the cause of the error.

3.2.6. HelloFXML Sample

Let’s run now a JavaFX application with FXML to illustrate how to use the extended options and customize the plugin extension gluonClient. In this case, we need to add the resource bundle of the project, and since the FXMLLoader will use reflection to inspect the FXML file, we need to add a few classes from this file to the reflection list: mainly the FXMLLoader itself, the controller and the controls, since the containers are already part of this list.

public class HelloFXML extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        AnchorPane root = FXMLLoader.load(HelloFXML.class.getResource("hello.fxml"),
                ResourceBundle.getBundle("hellofx.hello"));
        Scene scene = new Scene(root, 640, 480);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

and build file:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.7'
    id 'com.gluonhq.client-gradle-plugin' version '0.0.11'
}

repositories {
    mavenCentral()
}

gluonClient {
    // target = "ios" // uncomment to deploy on iOS

    bundlesList = ["hellofx.hello"]
    reflectionList = ["javafx.fxml.FXMLLoader", "hellofx.HelloController", "javafx.scene.control.Button", "javafx.scene.control.Label"]
}

javafx {
    version = "12.0.1"
    modules = [ "javafx.controls", "javafx.fxml" ]
}

mainClassName = 'hellofxml/hellofx.HelloFXML'
Target: Linux

We run the application to make sure everything works with Java 11:

./gradlew clean run

The nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux
...
[SUB] [HelloFXML:31183]        (cap):   1,439.70 ms
[SUB] [HelloFXML:31183]        setup:   3,399.55 ms
...
[SUB] [HelloFXML:31183]   (typeflow): 140,469.96 ms
[SUB] [HelloFXML:31183]    (objects): 104,948.18 ms
[SUB] [HelloFXML:31183]   (features):   6,415.64 ms
[SUB] [HelloFXML:31183]     analysis: 259,685.79 ms
[SUB] [HelloFXML:31183]     (clinit):   3,498.66 ms
[SUB] [HelloFXML:31183]     universe:   9,277.06 ms
[SUB] [HelloFXML:31183]      (parse):  21,848.33 ms
[SUB] [HelloFXML:31183]     (inline):  29,953.15 ms
[SUB] [HelloFXML:31183]    (compile): 153,374.97 ms
[SUB] [HelloFXML:31183]      compile: 209,183.02 ms
[SUB] [HelloFXML:31183]        image:  15,505.02 ms
[SUB] [HelloFXML:31183]        write:   1,525.00 ms
[SUB] [HelloFXML:31183]      [total]: 503,575.20 ms

BUILD SUCCESSFUL in 8m 26s

And as a result, HelloFX.o, with 91.4 MB, can be found under build/client/linux-x86_64/gvm/tmp/SVM-15***/HelloFXML.o.

Running nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

BUILD SUCCESSFUL in 3s

produces build/client/linux-x86_64/HelloFX (101.9 MB), that can be executed directly or with nativeRun.

Target output

Finally, we can run the application:

HelloFXML application
Target: Mac OS X

We run the application to make sure everything works with Java 11:

./gradlew clean run

The nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [HelloFXML:4554]        (cap):   1,746.00 ms
[SUB] [HelloFXML:4554]        setup:   3,233.15 ms
[SUB] [HelloFXML:4554]   (typeflow): 105,738.79 ms
[SUB] [HelloFXML:4554]    (objects): 107,173.94 ms
[SUB] [HelloFXML:4554]   (features):   5,917.60 ms
[SUB] [HelloFXML:4554]     analysis: 224,476.46 ms
[SUB] [HelloFXML:4554]     (clinit):   5,736.49 ms
[SUB] [HelloFXML:4554]     universe:  15,473.69 ms
[SUB] [HelloFXML:4554]      (parse):  17,257.25 ms
[SUB] [HelloFXML:4554]     (inline):  30,180.54 ms
[SUB] [HelloFXML:4554]    (compile):  80,559.90 ms
[SUB] [HelloFXML:4554]      compile: 135,024.79 ms
[SUB] [HelloFXML:4554]        image:  16,311.57 ms
[SUB] [HelloFXML:4554]        write:   2,569.05 ms
[SUB] [HelloFXML:4554]      [total]: 400,588.17 ms

BUILD SUCCESSFUL in 6m 50s

And as a result, HelloFX.o, with 89.8 MB, can be found under build/client/macos-x86_64/gvm/tmp/SVM-15***/HelloFXML.o.

Running nativeLink:

> Task :nativeLink
...
BUNDLE ID = hellofx.HelloFXML
BUILD SUCCESSFUL in 9s

produces build/client/macos-x86_64/HelloFX.app (90.1 MB), that can be executed directly or with nativeRun.

Target output

Finally, we can run the application:

HelloFXML application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Uncomment target="ios" at the build file:

gluonClient {
    target = "ios"
}

We can now run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [HelloFXML:4934]        (cap):   1,635.93 ms
[SUB] [HelloFXML:4934]        setup:   3,158.97 ms
[SUB] [HelloFXML:4934]   (typeflow):  84,886.99 ms
[SUB] [HelloFXML:4934]    (objects):  86,018.25 ms
[SUB] [HelloFXML:4934]   (features):   7,406.12 ms
[SUB] [HelloFXML:4934]     analysis: 186,331.82 ms
[SUB] [HelloFXML:4934]     (clinit):   3,046.42 ms
[SUB] [HelloFXML:4934]     universe:  19,742.59 ms
[SUB] [HelloFXML:4934]      (parse):  10,085.40 ms
[SUB] [HelloFXML:4934]    (compile): 120,111.04 ms
[SUB] [HelloFXML:4934]    (bitcode):  13,331.32 ms
[SUB] [HelloFXML:4934]       (link):  60,474.99 ms
[SUB] [HelloFXML:4934]         (gc):  54,469.09 ms
[SUB] [HelloFXML:4934]       (llvm): 364,444.24 ms
[SUB] [HelloFXML:4934]   (stackmap):  33,878.87 ms
[SUB] [HelloFXML:4934]      compile: 661,992.13 ms
[SUB] [HelloFXML:4934]        image:  18,785.54 ms
[SUB] [HelloFXML:4934]        write:   4,148.82 ms
[SUB] [HelloFXML:4934]      [total]: 897,274.94 ms

BUILD SUCCESSFUL in 15m 5s

And as a result, HelloFXML.o, with 78.1 MB, can be found under build/client/ios-arm64/gvm/tmp/SVM-15***/HelloFXML.o.

Running ./gradlew nativeLink:

> Task :nativeLink
...
BUNDLE ID = hellofx.HelloFXML
Looking for /Entitlements.plist

BUILD SUCCESSFUL in 37s

produces build/client/ios-arm64/HelloFXML.app (141.1 MB).

Target output

Now it can be deployed to a plugged iOS device with ./gradlew nativeRun.

App deployed
> Task :nativeRun
...
UIApplication launched!
UIApplication started GVM in a separate thread
Starting vm...
Starting GVM for ios
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target="ios-sim" at build file:

gluonClient {
    target = "ios-sim"
}

We run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [HelloFXML:7489]        (cap):   1,594.44 ms
[SUB] [HelloFXML:7489]        setup:   2,895.24 ms
[SUB] [HelloFXML:7489]   (typeflow):  84,688.02 ms
[SUB] [HelloFXML:7489]    (objects):  60,965.97 ms
[SUB] [HelloFXML:7489]   (features):   6,250.53 ms
[SUB] [HelloFXML:7489]     analysis: 158,357.25 ms
[SUB] [HelloFXML:7489]     (clinit):   3,742.47 ms
[SUB] [HelloFXML:7489]     universe:   8,617.77 ms
[SUB] [HelloFXML:7489]      (parse):   9,598.54 ms
[SUB] [HelloFXML:7489]     (inline):  16,041.28 ms
[SUB] [HelloFXML:7489]    (compile):  62,640.95 ms
[SUB] [HelloFXML:7489]      compile:  93,574.01 ms
[SUB] [HelloFXML:7489]        image:  15,343.23 ms
[SUB] [HelloFXML:7489]        write:   5,130.51 ms
[SUB] [HelloFXML:7489]      [total]: 287,128.97 ms

BUILD SUCCESSFUL in 4m 57s

And as a result, HelloFXML.o, with 88.9 MB, can be found under build/client/ios-x86_64/gvm/tmp/SVM-15***/hellofxml.o.

Running ./gradlew nativeLink:

> Task :nativeLink
...
BUNDLE ID = hellofx.HelloFXML

BUILD SUCCESSFUL in 8s

produces build/client/ios-x86_64/helloWorld.app (89.7 MB).

Target output

Finally, running ./gradlew nativeRun, will open the simulator, install the application and launch it.

App deployed
> Task :nativeRun
...
[SUB] UIApplication launched!
[SUB] UIApplication started GVM in a separate thread
[SUB] Starting vm...
[SUB] Starting GVM
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

3.2.7. HelloGluon Sample

Time now to run a Gluon Mobile application to illustrate how to use the extended options and customize the plugin extension gluonClient.

In this case, we need to add some Gluon Attach services like Storage or Display, and for that we’ll use attachConfig.

public class HelloGluon extends MobileApplication {

    @Override
    public void init() {
        addViewFactory(HOME_VIEW, () -> {
            FloatingActionButton fab = new FloatingActionButton(MaterialDesignIcon.SEARCH.text,
                    e -> System.out.println("Search"));

            ImageView imageView = new ImageView(new Image(HelloGluon.class.getResourceAsStream("openduke.png")));

            imageView.setFitHeight(200);
            imageView.setPreserveRatio(true);

            Label label = new Label("Hello, Gluon Mobile!");
            VBox root = new VBox(20, imageView, label);
            root.setAlignment(Pos.CENTER);

            View view = new View(root) {
                @Override
                protected void updateAppBar(AppBar appBar) {
                    appBar.setTitleText("Gluon Mobile");
                }
            };

            fab.showOn(view);

            return view;
        });
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.LIGHT_GREEN.assignTo(scene);
        scene.getStylesheets().add(HelloGluon.class.getResource("styles.css").toExternalForm());

        if (Platform.isDesktop()) {
            Dimension2D dimension2D = DisplayService.create()
                    .map(DisplayService::getDefaultDimensions)
                    .orElse(new Dimension2D(640, 480));
            scene.getWindow().setWidth(dimension2D.getWidth());
            scene.getWindow().setHeight(dimension2D.getHeight());
        }
    }

    public static void main(String[] args) {
        launch();
    }

}

and build file:

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.7'
    id 'com.gluonhq.client-gradle-plugin' version '0.0.11'
}

repositories {
    mavenCentral()
    maven {
        url 'https://nexus.gluonhq.com/nexus/content/repositories/releases/'
    }
}

dependencies {
    implementation "com.gluonhq:charm-glisten:6.0.1"
}

gluonClient {
//    target = "ios"  // uncomment to deploy on iOS

    attachConfig {
        services 'display', 'lifecycle', 'statusbar', 'storage'
    }

    reflectionList = ["com.gluonhq.impl.charm.glisten.control.skin.GlistenButtonSkin"]
}

javafx {
    version = "12.0.1"
    modules = [ "javafx.controls" ]
}

mainClassName = "$moduleName/com.gluonhq.hello.HelloGluon"
Target: Linux

We run the application to make sure everything works with Java 11:

./gradlew clean run

Then we run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

[SUB] [HelloGluon:10257]    classlist:   4,057.39 ms
...
[SUB] [HelloGluon:10257]        (cap):   1,613.84 ms
[SUB] [HelloGluon:10257]        setup:   3,770.71 ms
...
[SUB] [HelloGluon:10257]   (typeflow): 127,265.64 ms
[SUB] [HelloGluon:10257]    (objects):  86,496.34 ms
[SUB] [HelloGluon:10257]   (features):   5,895.26 ms
[SUB] [HelloGluon:10257]     analysis: 225,133.81 ms
[SUB] [HelloGluon:10257]     (clinit):   3,118.91 ms
[SUB] [HelloGluon:10257]     universe:   9,076.35 ms
[SUB] [HelloGluon:10257]      (parse):  16,318.77 ms
[SUB] [HelloGluon:10257]     (inline):  26,051.64 ms
[SUB] [HelloGluon:10257]    (compile): 138,184.50 ms
[SUB] [HelloGluon:10257]      compile: 187,079.39 ms
[SUB] [HelloGluon:10257]        image:  16,477.65 ms
[SUB] [HelloGluon:10257]        write:   1,644.85 ms
[SUB] [HelloGluon:10257]      [total]: 447,750.97 ms

BUILD SUCCESSFUL in 7m 30s

And as a result, HelloGluon.o, with 94.0 MB, can be found under build/client/linux-x86_64/gvm/tmp/SVM-15***/HelloGluon.o.

Running nativeLink now gives:

> Task :nativeLink
Omega :: host triplet = x86_64-linux-linux
Omega :: target triplet = x86_64-linux-linux

BUILD SUCCESSFUL in 3s

produces build/client/linux-x86_64/HelloGluon (104.6 MB), that can be executed directly or with nativeRun.

Target output

Finally, we can run the application:

HelloFXML application
Target: Mac OS X

We run the application to make sure everything works with Java 11:

./gradlew clean run

Then we run the nativeCompile task, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
[SUB] [HelloGluon:9541]        (cap):   2,124.34 ms
[SUB] [HelloGluon:9541]        setup:   3,706.26 ms
[SUB] [HelloGluon:9541]   (typeflow): 106,209.12 ms
[SUB] [HelloGluon:9541]    (objects):  77,480.00 ms
[SUB] [HelloGluon:9541]   (features):   5,566.23 ms
[SUB] [HelloGluon:9541]     analysis: 193,795.24 ms
[SUB] [HelloGluon:9541]     (clinit):   7,761.62 ms
[SUB] [HelloGluon:9541]     universe:  17,877.38 ms
[SUB] [HelloGluon:9541]      (parse):   8,530.13 ms
[SUB] [HelloGluon:9541]     (inline):  14,178.24 ms
[SUB] [HelloGluon:9541]    (compile):  76,125.96 ms
[SUB] [HelloGluon:9541]      compile: 103,819.71 ms
[SUB] [HelloGluon:9541]        image:  12,953.83 ms
[SUB] [HelloGluon:9541]        write:   2,946.23 ms
[SUB] [HelloGluon:9541]      [total]: 338,567.89 ms

BUILD SUCCESSFUL in 5m 50s

And as a result, HelloGluon.o, with 92.7 MB, can be found under build/client/macos-x86_64/gvm/tmp/SVM-15***/HelloGluon.o.

Running nativeLink now gives:

> Task :nativeLink
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-macos
...
BUILD SUCCESSFUL in 9s

produces build/client/macos-x86_64/HelloGluon.app (92.9 MB), that can be executed directly or with nativeRun.

Target output

Finally, we can run the application:

HelloFXML application
Target: iOS
Note

This requires an iOS device that has to be plugged in at the run phase. A valid provisioning profile is required as well, either by enrolling to the Apple Developer program or by using free provisioning. See iOS Deployment.

Uncomment target="ios" at the build file:

gluonClient {
    target = "ios"
}

We can now run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...
[SUB] [HelloGluon:9885]        (cap):   1,541.61 ms
[SUB] [HelloGluon:9885]        setup:   3,186.98 ms
[SUB] [HelloGluon:9885]   (typeflow):  86,109.12 ms
[SUB] [HelloGluon:9885]    (objects):  75,800.09 ms
[SUB] [HelloGluon:9885]   (features):   8,539.73 ms
[SUB] [HelloGluon:9885]     analysis: 182,393.09 ms
[SUB] [HelloGluon:9885]     (clinit):   5,458.42 ms
[SUB] [HelloGluon:9885]     universe:  16,788.57 ms
[SUB] [HelloGluon:9885]      (parse):   9,897.31 ms
[SUB] [HelloGluon:9885]    (compile): 113,354.02 ms
[SUB] [HelloGluon:9885]    (bitcode):   9,489.21 ms
[SUB] [HelloGluon:9885]       (link):  54,496.10 ms
[SUB] [HelloGluon:9885]         (gc):  40,755.21 ms
[SUB] [HelloGluon:9885]       (llvm): 366,547.98 ms
[SUB] [HelloGluon:9885]   (stackmap):  18,408.70 ms
[SUB] [HelloGluon:9885]      compile: 615,070.09 ms
[SUB] [HelloGluon:9885]        image:  17,377.75 ms
[SUB] [HelloGluon:9885]        write:   8,085.74 ms
[SUB] [HelloGluon:9885]      [total]: 846,313.12 ms

BUILD SUCCESSFUL in 14m 16s

And as a result, HelloGluon.o, with 79.6 MB, can be found under build/client/ios-arm64/gvm/tmp/SVM-15***/HelloGluon.o.

Running ./gradlew nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = arm64-macos-ios
...

BUILD SUCCESSFUL in 40s

produces build/client/ios-arm64/HelloGluon.app (144.2 MB).

Target output

Now it can be deployed to a plugged iOS device with ./gradlew nativeRun.

App deployed
> Task :nativeRun
...
UIApplication launched!
UIApplication started GVM in a separate thread
Starting vm...
Starting GVM for ios
[UIAPP] applicationDidBecomeActive
GL_VERSION string = OpenGL ES 2.0 Metal - 61.1
GL_VERSION (major.minor) = 0.0
...
Target: iOS Simulator
Note

This step doesn’t require a provisioning profile.

Set target="ios-sim" at build file:

gluonClient {
    target = "ios-sim"
}

We run ./gradlew nativeCompile, that produces the following output:

> Task :nativeCompile
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] [HelloGluon:10489]        (cap):   2,129.13 ms
[SUB] [HelloGluon:10489]        setup:   3,602.35 ms
[SUB] [HelloGluon:10489]   (typeflow):  80,780.39 ms
[SUB] [HelloGluon:10489]    (objects):  70,229.51 ms
[SUB] [HelloGluon:10489]   (features):   5,489.90 ms
[SUB] [HelloGluon:10489]     analysis: 164,692.24 ms
[SUB] [HelloGluon:10489]     (clinit):   4,077.52 ms
[SUB] [HelloGluon:10489]     universe:  17,442.15 ms
[SUB] [HelloGluon:10489]      (parse):   8,954.81 ms
[SUB] [HelloGluon:10489]     (inline):  16,090.15 ms
[SUB] [HelloGluon:10489]    (compile):  75,762.91 ms
[SUB] [HelloGluon:10489]      compile: 106,629.37 ms
[SUB] [HelloGluon:10489]        image:  14,504.06 ms
[SUB] [HelloGluon:10489]        write:   3,225.46 ms
[SUB] [HelloGluon:10489]      [total]: 314,091.92 ms

BUILD SUCCESSFUL in 5m 23s

And as a result, HelloGluon.o, with 92 MB, can be found under build/client/ios-x86_64/gvm/tmp/SVM-15***/helloGluon.o.

Running ./gradlew nativeLink:

> Task :nativeLink
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
BUILD SUCCESSFUL in 10s

produces build/client/ios-x86_64/helloGluon.app (92.7 MB).

Target output

Finally, running ./gradlew nativeRun, will open the simulator, install the application and launch it.

App deployed
> Task :nativeRun
Omega :: host triplet = x86_64-macos-macos
Omega :: target triplet = x86_64-macos-ios
...
[SUB] GL_VERSION string = OpenGL ES 2.0 APPLE-17.0.77
[SUB] GL_VERSION (major.minor) = 0.0

3.3. Feedback and issues

Please submit your feedback or any possible issue to the plugin’s issue tracker.

4. iOS Deployment

If you want to test and distribute your app via the App Store, you’ll need to enroll in the Apple Developer Program. However, if you only want to test on your iOS device, you can use free provisioning.

Either way, first of all you will need a unique Apple ID.

4.1. Creating an Apple ID

In case you don’t have it yet, create an Apple ID.

Create Apple ID

You will need a valid email account, and you will receive and email to validate and activate the Apple ID.

In case you are going to use free provisioning, this Apple ID must not be connected to the Apple Developer Program.

Once you have your Apple ID verified and activated, open Xcode, go to Preferences → Accounts. Press the + button at the bottom left to add an account, select `Apple ID, and include the email address used to create the Apple ID.

Create account

4.2. Xcode project

The first time you start deploying to an iOS device, you need to follow these steps, so Xcode configures properly your device for future iOS deployment.

Note: If you are using free provisioning, you will have to follow this procedure every time you create a new project.

4.2.1. Create an empty Xcode project

Open Xcode and go to File → New → Project…​, and select a simple template for iOS, like Single View App.

Single View App
Name and bundle

Provide a name and a bundle ID to the project.

Note: if you are using free provisioning, the bundle ID must match the one defined in your Java project (or the other way around).

Project data

Press next, provide a location and create the project.

Signing the project

Once the project is created, it will be opened and you will have access to the General tab with the identity and signing information.

At this point you should plug your iOS device, and select it from the top drop-down list of build schemes that list your iOS device and iOS simulators.

Project general

Make sure you have selected Automatically manage signing, and select a valid Team from the drop-down list.

If you get the error like: The app ID "hellofx.HelloFX" cannot be registered to your develoment team, this means that the proposed app ID is already in use or has already been registered by the Apple servers, and cannot longer be used. The only solution is to change the app ID until you get a valid one:

Valid app ID

Note that the provisioning profile generated by Xcode for that app ID can be found in your ~/Library/MobileDevice/Provisioning Profiles folder.

For free provisioning, this profile will expire after one week.

Deploying the project

Once everything is in place (there are no visible errors in the General screen), press the Play button that will build and run the current scheme.

Note: Using free provisioning it is possible that the run fails with a message like: Could not launch "HelloFX", that prompts you to verify the Developer App certificate is trusted on your device, as by default the certificate is untrusted. Go to your iPhone, Settings → General → Device Management, and select the certificate to trust it. Then go back to Xcode and run again. This time, the app will launch and you will get a white screen.

Press stop to finish and quit Xcode.

4.3. Developer Program

If you (as individual or as a company) have enrolled to the Apple Developer Program, to deploy to iOS you need to generate:

  • A valid signing identity

  • A valid provisioning profile

The following steps describe how these can be generated.

4.3.1. iOS Development Certificate

Go to the Developer portal, and access Certificates, Identifiers & Profiles. Select Certificates from the left, and press +, to create a Development certificate (for testing). Later on you will need a Distribution certificate for distribution through the App Store.

New Certificate

To generate a certificate, you need a Certificate Signing Request (CSR) file from your Mac.

Certificate Signing Request

According to Apple Help, these are the steps to generate the CSR:

  • Launch the Keychain Access application, located in /Applications/Utilities.

  • Choose Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority.

  • Enter an email address in the User Email Address field.

  • Enter a name for the key.

  • Leave the CA Email Address field empty.

  • Choose Saved to disk, and click Continue.

  • Save the CSR file.

Upload CSR

Return to the Developer portal and upload the CSR:

Upload CSR
Download and install the certificate

Once the CSR is uploaded, a certificate is generated. Download this certificate to your Mac.

Download Certificate

Then double click the .cer file to install in Keychain Access.

4.3.2. Identifiers

Go to the Developer portal, and access Certificates, Identifiers & Profiles. Select Identifiers from the left, and press +, to create a new app identifier.

You can enable app services when you create the App ID or modify these settings later on.

New App Identifier

Provide a name and make sure you use the exact Bundle ID from your app, the one listed in your Default-Info.plist file under the CFBundleIdentifier key.

App ID

Alternatively, you can use a wildcard that will let you use a single App ID to match multiple apps. It is recommended to use your domain name, to filter out other apps.

Wildcard

Select any of the required services (if you are using an explicit Bundle ID), and click continue to create the App ID.

4.3.3. Devices

To create a provisioning profile for development, the devices that are allowed to run the application have to be identified.

To add your testing devices, you need to provide their UDID.It can be found from Xcode → Window → Devices and Simulators. Plug your device, select it from Connected devices, and then select its identifier and copy it.

Device identifier

Go to the Developer portal, and access Certificates, Identifiers & Profiles. Select Devices from the left, and press +, to add a new device.

Add a name and paste the devices’s identifier.

Add Device

4.3.4. Profiles

Go to the Developer portal, and access Certificates, Identifiers & Profiles. Select Profiles from the left, and press +, to add a new provisioning profile.

To add a Development profile (later on you will need a Distribution one), select iOS App Development, and press continue.

iOS App Development

From the drop down list, select a valid App ID:

  • If you plan to use services such as Game Center, In-App Purchase, and Push Notifications, or want a Bundle ID unique to a single app, use an explicit App ID.

  • If you want to create one provisioning profile for multiple apps or don’t need a specific Bundle ID, select a wildcard App ID.

iOS App ID

Press continue and select one or more certificates that should be included in this provisioning profile.

Press continue and select one or more devices that should be include in this provisioning profile.

Press continue and finally, give a name to the provisioning profile, it will be used to identify the profile in the portal. Then press Generate.

Provisioning profile

When finished, download it and install it by double clicking on the file.

4.4. Java Project

Back to your Java project, and assuming you have already done the AOT compilation phase, you need to run ./gradlew nativeLink (Gradle plugin) or mvn client:link (Maven plugin). This will generate the src/ios/Default-Info.plist file and the src/ios/assets folder with a default iconset.

You can modify the default values and iconset.

If you are using free provisioning or an explicit provisioning profile, edit the src/ios/Default-Info.plist file and set the exact same bundle ID as the one in the profile (note it is case sensitive):

<dict>
	<key>CFBundleIdentifier</key>
	<string>hello.fx.HelloFX</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
...

If you are using a wildcard, make sure the domain (if any) matches.

Note: the bundle identifier key doesn’t need to match your main class.

If you have modified the plist file, save it, and run again ./gradlew nativeLink (Gradle plugin) or mvn client:link (Maven plugin).

Using verbose = true (Gradle) or <verbose>true</verbose> (Maven) in the client plugin can be of help to trace which provisioning profile is used, and to check that a valid one was found:

CONSIDER provprofile ProvisioningProfile [type=Development, file=/Users/<user>/Library/MobileDevice/Provisioning Profiles/caae4d7d-b5d2-4************.mobileprovision, uuid=caae4d7d-b5d2-4************, name=iOS Team Provisioning Profile: hello.fx.HelloFX, appIdName=XC hello fx HelloFX, appIdPrefix=D6QST****, appId=D6QST****.hello.fx.HelloFX, creationDate=2019-06-18, expirationDate=2019-06-25, certFingerprints=[DA3D9B8C7640*******]]

YES, we have a MATCH!!

If the link task finishes successfully, you can deploy. With your iOS device plugged in, run: ./gradlew nativeRun (Gradle plugin) or mvn client:run (Maven plugin).

Note: If you are using free provisioning, this will deploy your Java app replacing the Xcode app.

At the end of the process you should get your app running on your iOS device.