This project shows how to set up a Mill project that uses OpenJFX (JavaFx) library. More specifically it shows how one needs to configure the use of the module system for JDK1.9+. Unlike other modules, the JavaFX cannot be used as regular JAR libraries. This means the JVM parameters for the module path and module names must be used.
The project contains two examples of a very simple JavaFX application that shows a button. In the first application, when pressed the message:
Hello World!
is written to console. One can exit the application when desired. The second application starts up a very small dialogue box. Pressing the button terminates the application. Each of these demonstrates alternate ways of starting up a JavaFX/OpenFX application from Scala.
The project has three modules: javafx
, managed
and unmanaged
that you can
find in the build script build.sc
. Both the javafx
and managed
module
shows how to set up the modules using Mill's support for managed libraries. In
these cases, you need only add the library to use, the dependent JavaFX modules
will be downloaded for you.
The javafx
module is an example of the use of pure Java code. To execute the
application use any of these command:
./mill -i javafx.run
./mill -i javafx.runMain helloworld.HelloWorld
./mill -i --watch javafx.run
./mill -i javafx.runMain button.Main
./mill -i --watch javafx.runMain button.Main
The managed
module has two applications, each exemplifying 2 different ways to
start Scala JavaFX (OpenFX) applications (one cannot use the class instance
directly). To execute the application use any of these command:
./mill -i managed.run
./mill -i managed.runMain helloworld.HelloWorld
./mill -i --watch managed.run
./mill -i managed.runMain button.Main
./mill -i --watch managed.runMain button.Main
To use the IDE:
./mill mill.scalalib.GenIdea/idea
The unmanaged
module requires that you list all the dependent JavaFX
libraries that are required. You can use both managed and unmanaged libraries
simultaneously, so you need list those libraries you wish to use. This module
has the exact same applications, so the command are the same save for the
module name.
To execute the application use any of these command:
./mill -i unmanaged.run
./mill -i unmanaged.runMain helloworld.HelloWorld
./mill -i --watch unmanaged.run
./mill -i unmanaged.runMain button.Main
./mill -i --watch unmanaged.runMain button.Main
The build.sc
tries to automate as much of the JVM module parameters as
possible. More concretely, it uses Mill's runClasspath()
method to obtain the
list of libraries (contains bot managed and unmanaged archives). It then filters
these to obtain the JavaFX libraries.
override def forkArgs: Target[Seq[String]] = T {
// get the unmanaged libraries
val unmanaged: Loose.Agg[PathRef] = runClasspath() // unmanagedClasspath()
// get the OpenJFX unmanaged libraries
val s: Loose.Agg[String] = unmanaged.map(_.path.toString())
.filter{
s =>
val t= s.toLowerCase()
t.contains("javafx") || t.contains("controlsfx")
}
However, it may be necessary to tweak those parameters according
to you use case. More concretely you may need to add module exports or
patches (a patch allows you to substitute a boot module with another drop-in
replacement, for example using TestFX/Monocle).
So tale a look at the override def forkArgs: Target[Seq[String]]
method and
change that accordingly.
To use this project in IntelliJ execute the following command:
./mill mill.scalalib.GenIdea/idea
Note that Mill has support for Bloop. However, this needs to be set up in IntelliJ first.
In VSCode one need only open and import the Mill build script. Because Mill supports Bloop, this should automatically work for you provided you have installed the Metals extension for Visual Studio Code. See also metals-vscode in Github.
After version 0.5.3
attempting to run a JavaFX application breaks.
See:
Discussion on a workaround is here.
Unfortunately, this also did not work. It seems like it is not only the artifact downloads
that may be causing problems. More specifically in version 0.5.3
we cannot run a
class that is not marked as a main class.
These execute correctly:
./mill -i javafx.run
./mill -i javafx.runMain helloworld.HelloWorld
./mill -i --watch javafx.run
This fails: ``
with the error:
Error: JavaFX runtime components are missing, and are required to run this application
Previous versions also work.
###Version 0.9.4
Mill fails to resolve the dependencies:
./mill -i javafx.run
[17/31] javafx.resolvedIvyDeps
1 targets failed
javafx.resolvedIvyDeps Failed to load source dependencies
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-controls/13.0.2/javafx-controls-13.0.2-${javafx.platform}.jar
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/13.0.2/javafx-graphics-13.0.2-${javafx.platform}.jar
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-base/13.0.2/javafx-base-13.0.2-${javafx.platform}.jar
Mill fails to resolve the dependencies:
./mill -i javafx.runMain helloworld.HelloWorld
[16/28] javafx.resolvedIvyDeps
1 targets failed
javafx.resolvedIvyDeps Failed to load source dependencies
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-controls/13.0.2/javafx-controls-13.0.2-${javafx.platform}.jar
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-graphics/13.0.2/javafx-graphics-13.0.2-${javafx.platform}.jar
not found: https://repo1.maven.org/maven2/org/openjfx/javafx-base/13.0.2/javafx-base-13.0.2-${javafx.platform}.jar
We added a classifier automatically. In the working Mill versions we see that it makes no difference to the downloaded artifacts.
Tested on JDK 11. because the JDK does not include the JavaFX run-time, this has to be installed. For Ubuntu, we need to:
- sudo apt install openjfx
- dpkg-query -L openjfx
- java --module-path /usr/share/openjfx/lib --add-modules=javafx.controls,javafx.fxml,javafx.base,javafx.media,javafx.web,javafx.swing -jar '/home/lotfi/Documents/MyAppfolder/my_application.jar'
The dpkg-query
provides us with the path to the OpenJFX run-time. This
path and the required modules must be added to the java
comma nd line
arguments.
You can install and use the system wide JavaFX. But we need to use and indicate the module path.
In Ubuntu linux
user@machine:~$ update-alternatives --list java
/usr/lib/jvm/java-11-openjdk-amd64/bin/java
/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
/usr/lib/jvm/java-8-oracle/jre/bin/java
The following gets us the current active path:
user@machine:~$readlink -f /usr/bin/java
/usr/lib/jvm/java-11-openjdk-amd64/bin/java
Look at Gradle JavaFX plugin It provides the module paths