6

I can't seem to generate a cross-platform build out of a javaFX application.

When I run it from IntelliJ it works fine, and if package it through maven, I can also run the generated jar on Linux.

The problem occurs when I try to run the jar on Mac, where it seems that the JavaFX libraries are not included, then I get this stacktrace:

Graphics Device initialization failed for : es2, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
    at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
    at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:244)
    at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:261)
    at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
    at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
    at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
    at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
    ... 1 more
Exception in thread “main” java.lang.RuntimeException: No toolkit found
    at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:273)
    at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
    at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
    at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:832)

I am using Java 15 compiling to 11 and JavaFX11.

I'm also using maven profiles to generate a specific version every time, and the shade plugin to pack all the dependencies in one fat jar.

There's also a launcher, that's where the manifest points to, before the actual FX Application. This is how the pom looks like.

<profiles>
    <profile>
        <id>linux</id>
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>11</version>
                <classifier>linux</classifier>
            </dependency>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-fxml</artifactId>
                <version>11</version>
                <classifier>linux</classifier>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>mac</id>
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>11</version>
                <classifier>mac</classifier>
            </dependency>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-fxml</artifactId>
                <version>11</version>
                <classifier>mac</classifier>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>win</id>
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>11</version>
                <classifier>win</classifier>
            </dependency>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-fxml</artifactId>
                <version>11</version>
                <classifier>win</classifier>
            </dependency>
        </dependencies>
    </profile>
</profiles>

<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.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.0.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <manifestEntries>
                                    <Main-Class>org.log.Launcher</Main-Class>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

I also noticed that no matter what profile I chose, it always generates the linux version, and I can see this maven log:

[WARNING] javafx-controls-11-win.jar, javafx-graphics-11-linux.jar, javafx-fxml-11-linux.jar, LogAnalyzer-1.0-SNAPSHOT.jar, javafx-base-11-linux.jar, javafx-controls-11-linux.jar, javafx-fxml-11-win.jar define 1 overlapping classes: 
[WARNING]   - module-info
[WARNING] javafx-controls-11-win.jar, javafx-controls-11-linux.jar define 1216 overlapping classes: 
[WARNING]   - com.sun.javafx.scene.control.TableColumnBaseHelper$TableColumnBaseAccessor
[WARNING]   - javafx.scene.control.ComboBoxBase
[WARNING]   - javafx.scene.control.skin.ColorPickerSkin$StyleableProperties$2
[WARNING]   - com.sun.javafx.scene.control.inputmap.InputMap$KeyMappingInterceptor
[WARNING]   - javafx.scene.control.cell.ChoiceBoxTreeTableCell
[WARNING]   - javafx.scene.control.TableCell$3
[WARNING]   - javafx.scene.control.skin.ProgressBarSkin$StyleableProperties$1
[WARNING]   - javafx.scene.control.DialogPane$2
[WARNING]   - javafx.scene.control.skin.ToolBarSkin$4
[WARNING]   - javafx.scene.control.skin.ColorPickerSkin$3
[WARNING]   - 1206 more...
[WARNING] javafx-fxml-11-linux.jar, javafx-fxml-11-win.jar define 80 overlapping classes: 
[WARNING]   - com.sun.javafx.fxml.expression.ExpressionValue$KeyPathMonitor
[WARNING]   - javafx.fxml.FXMLLoader$RootElement
[WARNING]   - com.sun.javafx.fxml.expression.Expression$Parser$TokenType
[WARNING]   - javafx.fxml.FXMLLoader$DefineElement
[WARNING]   - javafx.fxml.Initializable
[WARNING]   - javafx.fxml.FXMLLoader$ControllerMethodEventHandler
[WARNING]   - com.sun.javafx.fxml.BeanAdapter
[WARNING]   - javafx.fxml.FXMLLoader$ControllerAccessor$1
[WARNING]   - javafx.fxml.JavaFXBuilderFactory$ObjectBuilderWrapper$ObjectBuilder
[WARNING]   - javafx.fxml.FXML
[WARNING]   - 70 more...
[WARNING] maven-shade-plugin has detected that some class files are
[WARNING] present in two or more JARs. When this happens, only one
[WARNING] single version of the class is copied to the uber jar.
[WARNING] Usually this is not harmful and you can skip these warnings,
[WARNING] otherwise try to manually exclude artifacts based on
[WARNING] mvn dependency:tree -Ddetail=true and the above output.
[WARNING] See http://maven.apache.org/plugins/maven-shade-plugin/

I've been reading other posts and checking a few videos, and this seems to be happening to other people too, but looks a bit strange to me. What's also weird is that when I generate for windows, I don't see any .dll files inside the generated jar.

Does anyone know how to generate a FX cross-platform jar?

-------- EDIT --------

As suggested in other posts, I have a Launcher class, which calls the actual class that extends Application:

public class Launcher {
    public static void main(String[] args) {
        App.main(args);
    }
}

And the JavaFX class:

public class App extends Application {

    public static void main(String[] args) {
        Application.launch();
    }
        
    @Override
    public void start(Stage stage) throws IOException {
        ...
    }
}

I now removed the profiles from the pom, so when compiling, maven should take all the dependencies (mac, linux and win), but still seems like it's only generating the linux dependencies (I'm running on linux, I guess this has something to do with it), because if I open the jar file, I don't see any .dll files and it still doesn't work on mac. Here is my full pom:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.log</groupId>
    <artifactId>LogAnalyzer</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

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

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>11</version>
        <classifier>linux</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>11</version>
        <classifier>linux</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>11</version>
        <classifier>mac</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>11</version>
        <classifier>mac</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>11</version>
        <classifier>win</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>11</version>
        <classifier>win</classifier>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-csv</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest</artifactId>
        <version>2.2</version>
        <scope>test</scope>
    </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.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.0.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <manifestEntries>
                                    <Main-Class>org.log.Launcher</Main-Class>
                                </manifestEntries>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>
muilpp
  • 1,293
  • 4
  • 17
  • 36
  • 1
    I haven't tried it, but I think it's possible. It seems to be documented (to a certain extent) at [openjfx.io | Runtime images | Custom JDK+JFX Images | Cross-platform jar](https://openjfx.io/openjfx-docs/). I think the maven file posted in the question is probably mostly correct, it only needs to get rid of the profiles so that all of the jars for all of the platforms are placed in the resultant Jar. – jewelsea Nov 11 '20 at 18:19
  • I removed the profiles and added each dependency three times (win, linux and mac), but I get the same warning I got before, which basically says there are overlapping classes that will be removed and the resulting jar works only in linux. In the end it doesn't matter if I compile it for linux, mac, win or the three of them, the resulting jar has always the same size and it works only for linux. – muilpp Nov 11 '20 at 20:35
  • The warning on overlapping classes states "Usually this is not harmful and you can skip these warnings", and I think that is the case here. If your maven project is correct, the unique native libraries will not overlap and should be copied, even with the warning. If it is cross-platform then you shouldn't be compiling for linux or mac or windows. Instead you compile once for all three of them generating just one jar which works on all platform. This single jar should include the native libraries for all platforms, so the jar file size doesn't differ by platform. – jewelsea Nov 11 '20 at 21:00
  • If you wish further help, provide an [mcve], which also includes your application code. – jewelsea Nov 11 '20 at 21:02
  • Also note, the documentation on cross-platform jars which I referred to states: "in order to create a runnable jar with all the required JavaFX dependencies, you will need to use a launcher class that doesn't extend from Application." I've never done that, so I don't have advice on it, but that is one of the reasons why I requested the minimal reproducible example, then at least somebody could see what your launcher class is. – jewelsea Nov 11 '20 at 21:04
  • Perhaps this is a duplicate of: [Maven Shade JavaFX runtime components are missing](https://stackoverflow.com/questions/52653836/maven-shade-javafx-runtime-components-are-missing), with supplementary related material here: [JavaFX 11 : Create a jar file with Gradle](https://stackoverflow.com/questions/52569724/javafx-11-create-a-jar-file-with-gradle/52571719#52571719) – jewelsea Nov 11 '20 at 21:08
  • I already have the launcher, all it does is call the class which extends Application. I'll update it in the question. The thing is that when I open the jar file, I guess I should see some .dll files if I compile it for windows too, right? This is not the case, I only see .so files. The size of the jar is always the same and it seems to contain always linux specific libraries (I'm running on linux, so I guess that at some point it decides to compile only for my system). – muilpp Nov 11 '20 at 23:42
  • Can you provide an [mcve] with the code and pom.xml you are actually using? The current pom.xml you have in your question includes profiles which are activated by platform. But, in your question update, you stated "I now removed the profiles from the pom". And the code snippet you provided calls App.main(args), but you don't have an App.main(args) method. Without the actual source you are using, it is hard for somebody to replicate your issue. – jewelsea Nov 12 '20 at 00:59

2 Answers2

7

As suggested in one of the links provided in a comment by @jewelsea, the trick was to add explicitly the graphics module of each platform independently.

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>11</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>11</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-graphics</artifactId>
        <version>11</version>
        <classifier>win</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-graphics</artifactId>
        <version>11</version>
        <classifier>linux</classifier>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-graphics</artifactId>
        <version>11</version>
        <classifier>mac</classifier>
    </dependency>
</dependencies>

It generates a fat jar and I can finally see the .dll files inside. Tried on linux and mac and it works on both platforms.

muilpp
  • 1,293
  • 4
  • 17
  • 36
0

AFAIK it's not possible anymore to create a cross-platform JAR for JavaFX applications.

It should be possible to create platform specific JARs however.

I'm not very familiar with the Shade Plugin, but according to the docs it should be possible to exclude dependencies.

So e.g. for the mac version make sure you exclude all non-mac javafx dependencies.

Puce
  • 37,247
  • 13
  • 80
  • 152
  • I tried to exclude any dependency not related to the compiled OS, but still I get the same result. It only works on linux even if I specify mac or win, and the generated jar has always the same size no matter what profile I use. – muilpp Nov 11 '20 at 20:37