Search code examples
javagradlejavafxjavafx-11

How to include plugin dependencies in JavaExec task classpath?


I am using JavaExec tasks to run different classes, but whenever I try to run one of the tasks using gradle <task>, I get an error saying Error: JavaFX runtime components are missing, and are required to run this application.

If I just set mainClassName='exercise1.Cards' or whatever other className, running gradle run works completely fine. I'm guessing that the JavaFX classes are not found when running classes with JavaExec and I'm wondering how I can include them.

build.gradle:

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.7'
}

version '1.0-SNAPSHOT'

sourceCompatibility = 11

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

javafx {
    modules = [ 'javafx.controls' ]
}

task runExercise1(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'exercise1.Cards'
}

task runExercise2(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'exercise2.InvestmentCalculator'
}

task runExercise3(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'exercise3.PointCircle'
}

task runExercise4(type: JavaExec) {
    classpath = sourceSets.main.runtimeClasspath
    main = 'exercise4.OccurrenceHistogram'
}

Solution

  • The org.openjfx.javafxplugin plugin manages for you a few things.

    When you add to your build file:

    javafx {
        modules = [ 'javafx.controls' ]
    }
    

    the plugin translates that into something like:

    run {
        doFirst {
            jvmArgs = ['--module-path', classpath.asPath,
                       '--add-modules', 'javafx.controls']
        }
    }
    

    However, if you create a new JavaExec task, it seems the plugin doesn't process it.

    Given the error you have posted:

    Error: JavaFX runtime components are missing

    it is clear that a possible fix is to do exactly what the plugin does and add the expected jvm args when using modular dependencies.

    So this should work:

    task runExercise1(type: JavaExec) {
        classpath = sourceSets.main.runtimeClasspath
        jvmArgs = ['--module-path', classpath.asPath, 
                   '--add-modules', 'javafx.controls' ]
        main = 'exercise1.Cards'
    }
    

    Alternatively you could create a launcher class that doesn't extend from Application, as that will bypass the modular check (as explained here).

    public class Launcher {
    
        public static void main(String[] args) {
            // optionally process args to select class to run
            Cards.main(args);
        }
    }
    

    Then you could add your task, and even uses runtime arguments to select the main class to run from the launcher.

    task runExercise1(type: JavaExec) {
        classpath = sourceSets.main.runtimeClasspath
        main = 'exercise1.Launcher'
        args 'exercise1' // <-- optionally select class to run
    }