2

I am trying to build a simple JavaFX 11 program with Kotlin and Java 11, using Gradle, following the instructions here. However, this page uses Gradle's Groovy DSL, and I am trying to use the Kotlin DSL. Surprisingly, my Google searches have not turned up a document that maps each Groovy construct to its equivalent Kotlin construct or explains in general how to convert Groovy DSL code to equivalent Kotlin DSL code. (This seems like a big oversight in the Gradle documentation!).

In particular, this document contains the following Groovy code:

compileJava {
    doFirst {
        options.compilerArgs = [
            '--module-path', classpath.asPath,
            '--add-modules', 'javafx.controls'
        ]
    }
}

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

As far as I can tell, the Kotlin equivalent to the first part appears to be:

tasks.withType<JavaCompile> {
    options.compilerArgs.addAll(arrayOf(
        "--module-path", classpath.asPath,
        "--add-modules", "javafx.controls"
    ))
}

However, I have not been able to figure out what the Kotlin DSL equivalent to the second part is. Note that 'run' is a standard function extension in Kotlin's standard library, so it does not appear that the Kotlin version of this code can use the name 'run' for the same purpose in the Kotlin DSL.

(Note: I considered trying to use a plugin for the JavaFX support (as described by this page, for instance), but the plugin seems quite complicated to use, and I already am having enough problems with the number of complications in this project that I am hesitant to introduce a very-lightly-documented open-source plugin into the mix. I really am trying to produce the simplest possible "Hello, World" program in JavaFX/Gradle at the moment, and this has so far seemed surprisingly difficult.).

Any help would be appreciated.

Naman
  • 27,789
  • 26
  • 218
  • 353
Some Guy
  • 405
  • 8
  • 15
  • Note: To avoid confusing anybody, the "Kotlin equivalent to the first part" is actually more correctly `tasks.named('compileJava'){ ... }` which will affect only one specific JavaCompile task with the given name, rather than all of them (for instance, the 'compileTestJava' task also may exist and have the type JavaCompile). – Some Guy Nov 07 '18 at 02:46

3 Answers3

4

Using the configuration avoidance APIs, the equivalent to the second block is:

tasks.named<JavaExec>("run") {
    doFirst {
        jvmArgs = listOf("--module-path", classpath.asPath,"--add-modules", "javafx.controls")
    }
}

The key is that run has the JavaExec type, which like any task's type can be discovered by creating a task to print the class of the task that you then run:

tasks.register("getName") {
    doFirst {
        print("Class name: ${tasks["run"].javaClass}")
    }
}

Note that as your JavaFX application grows, you will need to specify additional modules like this:

tasks.named<JavaExec>("run") {
    doFirst {
        jvmArgs = listOf("--module-path", classpath.asPath,
            "--add-modules", "javafx.base,javafx.controls,javafx.graphics")
    }
}
lwestby
  • 1,209
  • 6
  • 10
  • Thanks. I was able to successfully run my program using this syntax. Marking this answer as "Accepted". – Some Guy Nov 07 '18 at 02:38
3

Surprisingly, my Google searches have not turned up a document that maps each Groovy construct to its equivalent Kotlin construct or explains in general how to convert Groovy DSL code to equivalent Kotlin DSL code.

Please have a look at https://guides.gradle.org/migrating-build-logic-from-groovy-to-kotlin/ and esp. the Configuring tasks section. According to that, I'd say the Kotlin DSL equivalent is

tasks.named<JavaExec>("run").doFirst {
    jvmArgs = listOf('--module-path', classpath.asPath, '--add-modules', 'javafx.controls')
}
sschuberth
  • 28,386
  • 6
  • 101
  • 146
  • Thanks for the pointer to the document. Unfortunately, it doesn't seem to provide a complete Groovy-to-Kotlin mapping, and doesn't answer my specific question. Specifically, there does not appear to be any property named "jvmArgs" available in the scope of a "doFirst" call. I tried supplying an explicit type to the getByName call ('tasks.getByName("run")') and that didn't work either. – Some Guy Nov 03 '18 at 01:29
  • Does [this comment](https://github.com/gradle/kotlin-dsl/issues/439#issuecomment-317679164) to a Gradle issue solve your problem? If so, I'll update my answer accordingly. – sschuberth Nov 03 '18 at 06:50
  • Unfortunately, that comment is solving a different problem (namely how to find certain tasks which implement an interface which is not their task superclass).@lwestby's answer is correct in that the correct type to be using in this case is JavaExec, not Run as I had assumed. – Some Guy Nov 07 '18 at 02:41
  • Thanks, I've edited my answer accordingly to avoid confusion. – sschuberth Nov 07 '18 at 07:50
3

With Gradle 5.0 and kotlin-dsl 1.0, tasks that are registered or created by plugins can be statically accessed through the tasks container (TaskContainer. There is this example provided in the release notes:

plugins {
    java
}

tasks {
    named<Test>("test") {
        testLogging.showStacktraces = true
    }
}

you can now write:

plugins {
    java
}

tasks {
    test {
        testLogging.showStacktraces = true
    }
}

For your example, you are most likely using the application plugin, which registers the run task so you can configure it in a similar matter. One issue to be aware of is that run clashes with the Kotlin stdlib run method so you need to apply some workaround to make sure it gets invoked (see gradle/kotlin-dsl/issues/1175)

tasks {
  compileJava {
    doFirst {
      jvmArgs = listOf("--module-path", classpath.asPath,
          "--add-modules", "javafx.base,javafx.controls,javafx.graphics")
    }
  }

  (run) {
    doFirst {
      jvmArgs = listOf(
        "--module-path", classpath.asPath,
        "--add-modules", "javafx.controls"
      )
    }
  }
}

The other answers show how you can use the name, type, or combination to query the container for specific tasks.

mkobit
  • 43,979
  • 12
  • 156
  • 150
  • I updated to Gradle 5, but was not able to get this code to work as written here, because 'run' is also the name of a standard Kotlin extension function available for all classes (kotlin.run in the standard library), and Gradle insisted on using that one instead of org.gradle.kotlin.dsl.run. I had to use "import org.gradle.kotlin.dsl.run as run1" and then use "run1" in my code. Is there a cleaner way to do this? – Some Guy Dec 02 '18 at 13:17
  • 1
    Good catch @SomeGuy - I've updated the answer and added a link to an open issue that is similar – mkobit Dec 04 '18 at 16:06