Is anyone using Gradle to build JavaFX 2.0 projects?


(Ryan J) #1

Edit: Removed my link to the javafx plugin; it appears to be for JavaFX 1.0. Here’s what I’m looking to do with Gradle / JavaFX 2.0:

I want to be able to run my code from Gradle (like the ‘java’ plugin) and I want to be able to build distributions using the ‘application’ plugin. JavaFX is bundled with some native DLLs that make things difficult (at least for me). JavaFX relies on the following directory structure AFAIK:

+-- bin
|
 |-- native system libraries
+-- lib
|
 |-- jfxrt.jar

It depends on having those native system libraries at ‘…/bin/*’. It’s a bit of a kludge, but I can get the application plugin to package things so they work by doing this (I haven’t tested this):

def binDir = file("${buildDir}/javafx-native-libs")
  configurations {
    javafxBin
}
  dependencies {
    // all native DLLs, zipped into a single archive, created and uploaded by hand
    javafxBin "ca.jptech.javafx:javafx-bin-i586:2.0.2:windows@zip"
}
  task javafxBin {
    inputs.file configurations.javafxBin.singleFile
    outputs.dir binDir
      doLast {
        binDir.mkdirs()
          copy {
            from zipTree(configurations.javafxBin.singleFile)
            into binDir
        }
    }
}
  applicationDistribution.from(javafxBin) {
    into "bin"
}

However, I’m not quite sure how to get the ‘run’ target of the ‘java’ plugin set up so those system libraries get found at ‘…/bin/*’. I’m wondering if anyone has any suggestions. I’m going to play around with using ‘-Djava.library.path=${buildDir}/javafx-native-libs’ soon, but I figured I’d ask first just in case someone else has already struggled through it.

TIA, Ryan


(Ryan J) #2

This is what I’ve come up with. It’s not perfect, but it should be enough to help others get started. Comments are inline.

I created ‘javafx.gradle’ and put it alongside ‘build.gradle’:

// JavaFX system libs from the JAVAFX_HOME/bin directory will get unpacked here.
def binDir = file("${buildDir}/javafx-native-libs")
  configurations {
    // A custom configuration so we can pull the JavaFX system libs from an
    // artifact in our local repository.
    javafxBin
}
  dependencies {
    // WARNING: These dependencies are NOT included in any public repositories.
    // They MUST be created by hand and made available via a local repository.
      // The jfxrt.jar file, taken from the SDK rt/lib directory, uploaded by hand
    compile "ca.jptech.javafx:javafx-lib-i586:2.0.2:windows"
      // All JavaFX system libs, zipped into a single archive, created and uploaded
    // by hand.
All files are in the root of the archive (no sub-dirs).
    javafxBin "ca.jptech.javafx:javafx-bin-i586:2.0.2:windows@zip"
}
  task javafxBin {
    // If the input file is changed (unlikely), the task will get re-run.
    inputs.file configurations.javafxBin.singleFile
      // If the ${buildDir}/javafx-native-libs directory changes in any way,
    // the task will get re-run.
This will typically be caused by running
    // the 'clean' task.
This is also used as the source for the
    //
    applicationDistribution.from(javafxBin)
    // ..configuration below.
    outputs.dir binDir
      doLast {
        binDir.mkdirs()
          // Unzip JavaFX system libs into ${buildDir}/javafx-native-libs
        copy {
            from zipTree(configurations.javafxBin.singleFile)
            into binDir
        }
    }
}
  applicationDistribution.from(javafxBin) {
    // Make sure the 'installApp' task of the 'application' plugin copies the
    // JavaFX system libs from ${buildDir}/javafx-native-libs into the ~/bin/
    // directory.
The system libs will live alongside the launcher scripts
    // created by the 'application' plugin.
    //
    // WARNING: The layout of the distribution directory is important.
The
    // JavaFX runtime expects the JavaFX system libs to be accessible, from
    // the perspective of the javafx-lib-i586-2.0.2.jar, at ../bin/*.
The
    // distribution layout MUST look like this or things will break:
    //
    // +-- bin
    // | |-- launcher scripts
    // | |-- JavaFX system libs (a bunch of DLLs)
    // +-- lib
    // | |-- javafx-lib-i586-2.0.2.jar
    // | |-- other dependencies (jars)
    //
    // This setup is NOT IDEAL.
It forces you to distribute the
    // javafx-lib-i586-2.0.2.jar file with your application which is the
    // bulk of the JavaFX runtime.
Ideally, the entire JavaFX runtime
    // could be bundled in a way that would make including it in a
    // distribution optional.
Something like:
    //
    // +-- bin
    // | |-- launcher scripts
    // +-- javafx-redist
    // | +-- bin
    // | | |-- JavaFX system libs (a bunch of DLLs)
    // | +-- lib
    // | | |-- javafx-lib-i586-2.0.2.jar
    // +-- lib
    // | |-- other dependencies (jars)
    //
    // ..would make it much easier to include / exclude the JavaFX runtime when
    // using install builders such as Install4J.
      into "bin"
}
  // Make sure the 'javafxBin' task runs before the 'application' plugin's
// 'run' task.
run.dependsOn('javafxBin')
  // When running via the 'application' plugin's 'run' task, pass a JVM arg
// that allows the use of a different path for JavaFX system libs.
//
// WARNING: I don't know if this will replace a single system library path
// or if gets appended to a list of library paths.
I have not tested it
// beyond making sure it works with a 'HelloWorld' JavaFX application.
run.jvmArgs += "-Djava.library.path=${buildDir}/javafx-native-libs"

Then, for any project / module I want to use JavaFX with, I use a configuration like this in ‘build.gradle’:

project('javafx-hello-world') {
    apply plugin: 'java'
    apply plugin: 'application'
    mainClassName = "HelloWorld"
      apply from: "${rootProject.projectDir}/javafx.gradle"
}

So far it seems to work ok. I would prefer to uses the ‘java.library.path’ JVM arg with the launcher scripts created by the application plugin. I think GRADLE-1456 is related. I also find myself struggling to create a ‘provided’ style scope that works well everywhere (IDE, distribution bundles, etc.). GRADLE-784 is an RFE that would help this.