Gradle Kotlin DSL equivalent for Groovy DSL 'run'? - gradle

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.

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")
}
}

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')
}

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.

Related

Gradle 6 dependencies with strict version and because keyword

The problem
I'm currently experimenting with Gradle 6.0 and ran into the problem that I would like to combine the because statement with the syntax for e.g. strict and rejected versions.
My buildscript:
dependencies {
testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-api') {
version {
strictly '[5.0, 6.0]'
prefer '5.5.2'
reject '5.5.1' // for testing purpose only
}
}
testRuntimeOnly(group: 'org.junit.jupiter', name: 'junit-jupiter-engine') {
version {
strictly '[5.0, 6.0]'
prefer '5.5.2'
reject '5.5.1' // for testing purpose only
}
}
// Force Gradle to load the JUnit Platform Launcher from the module-path
testRuntimeOnly(group: 'org.junit.platform', name: 'junit-platform-launcher') {
version {
strictly '[1.5, 2.0]'
prefer '1.5.2'
}
}
}
What I've tried so far
I've currently tried to add the because statement below or above the versionstatement and adding curly brackets around them, but none of these things seemed to work out.
The question
Is it possible to add the because statement to the last dependency and if yes, how?
It would be interesting too, to know whether I can combine both testRuntimeOnly into one.
Using the Kotlin DSL, you can easily see exactly what is available to you. So converting your sample to use the Kotlin DSL, we have
dependencies {
testImplementation("org.junit.jupiter", "junit-jupiter-api") {
version {
strictly("[5.0, 6.0]")
prefer("5.5.2")
reject("5.5.1") // for testing purpose only
}
}
testRuntimeOnly("org.junit.jupiter", "junit-jupiter-engine") {
version {
strictly("[5.0, 6.0]")
prefer("5.5.2")
reject("5.5.1") // for testing purpose only
}
}
// Force Gradle to load the JUnit Platform Launcher from the module-path
testRuntimeOnly("org.junit.platform", "junit-platform-launcher") {
version {
strictly("[1.5, 2.0]")
prefer("1.5.2")
}
}
}
Is it possible to add the because statement to the last dependency and if yes, how?
Yes it is. Since I'm using the Kotlin DSL now, I can easily bring up intelli-sense:
You can see here because is available outside the version block, so:
// Force Gradle to load the JUnit Platform Launcher from the module-path
testRuntimeOnly("org.junit.platform", "junit-platform-launcher") {
version {
strictly("[1.5, 2.0]")
prefer("1.5.2")
}
because("my reason here.")
}

Not able to extend a Gradle task

I'm new with Gradle (we're switching from SBT) and using it to build apps made with Play Framework.
I need to add some filtering on the resources before Gradle processes them (I would like to inject some build properties into the configuration to make them available from the code).
I've managed to "extend" the java processResources task, but, for some reason, I cannot do the same with play processPlayBinaryPlayResources.
processPlayBinaryPlayResources {
filter ReplaceTokens, tokens: [
"applicationVersion": version
]
}
Even this doesn't work :
def playVersion = "2.6.20"
def scalaVersion = "2.12"
def javaVersion = "1.8"
apply plugin: "java"
apply plugin: "idea"
apply plugin: "play"
model {
components {
play {
platform play: playVersion, scala: scalaVersion, java: javaVersion
injectedRoutesGenerator = true
}
}
}
processPlayBinaryPlayResources {
doLast {
println("ok")
}
}
dependencies {
compile "io.vavr:vavr:0.9.2"
compile "org.imgscalr:imgscalr-lib:4.2"
compile "com.typesafe.play:play-guice_${scalaVersion}:2.6.20"
compile "com.typesafe.akka:akka-http_${scalaVersion}:10.1.5"
compile "com.typesafe.play:filters-helpers_${scalaVersion}:2.6.20"
compile "ch.qos.logback:logback-classic:1.2.3"
}
It yields :
> Could not find method processPlayBinaryPlayResources() for arguments [build_6grwx7eowye82rdqpu4qlinur$_run_closure2#582d9dbd] on root project 'myproject' of type org.gradle.api.Project.
Any idea why ?
Your assumption of finding a task processPlayBinaryPlayResources is based on that, that the java plugin automatically adds a processResources task for all source set as process<sourceset_name>Resources . This happens only when a source set is added using java pugins sourceSets method which, in this case PlayBinaryPlay is not. The play plugin uses its own DSL to configure source sets.
Therefore when you try extending processPlayBinaryPlayResources it does not happen as no such tasks by that name exists in the first place and hence while delegating it to Project, you end up with this
Could not find method processPlayBinaryPlayResources() for arguments [build_6grwx7eowye82rdqpu4qlinur$_run_closure2#582d9dbd] on root project 'myproject' of type org.gradle.api.Project.
Lastly, I would like to add that the processPlayBinaryPlayResources task is not added by the play plugin.

JUnit 5, Java 9 and Gradle: How to pass --add-modules?

I want to migrate from Java 8 to Java 9. When running my tests I get a CNFE regarding javax.xml.bind.JAXBContext. Therefore, "--add-modules java.xml.bind" seems to be required. I tried to extend my GRADLE_OPTS env variable, but the error remains. Any hint is appreciated.
You can follow the five basic steps while migrating as stated in the gradle-building java9 modules which are:-
When converting a java-library project to produce a Java 9 module,
there are five changes you should to make to your project.
Add a module-info.java describing the module.
Modify the compileJava task to produce a module.
Modify the compileTestJava task to locally alter the module.
Modify the test task to consume the locally altered module.
(Optional) Add Automatic-Module-Name manifest entries for all other projects.
In your use case, you need to ensure that the
compileTestJava {
inputs.property("moduleName", moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'org.junit.jupiter.api', // junit5 automatic module specific
'--add-modules', 'java.xml.bind', // jaxb specific
'--add-reads', "$moduleName=org.junit.jupiter.api", // allow junit to read your module
'--patch-module', "$moduleName=" + files(sourceSets.test.java.srcDirs).asPath, // add test source files to your module
]
classpath = files()
}
}
and then for executing the test, you would need not to udpate the test task as
test {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'ALL-MODULE-PATH', // to resolve all module in the module path to be accessed by gradle test runner
'--add-reads', "$moduleName=org.junit.jupiter.api",
'--patch-module', "$moduleName=" + files(sourceSets.test.java.outputDir).asPath,
]
classpath = files()
}
}
Note: For a long-term solution though I would also suggest you follow the important point mentioned in this answer as well.
According to Alan Bateman, I added the following lines to build.gradle so that gradle bootRun also works:
runtime('org.glassfish.jaxb:jaxb-runtime:2.3.0', 'javax.activation:activation:1.1.1')

Kotlin and Gradle - Reading from stdio

I am trying to execute my Kotlin class using the command:
./gradlew -q run < src/main/kotlin/samples/input.txt
Here is my HelloWorld.kt class:
package samples
fun main(args: Array<String>) {
println("Hello, world!")
val lineRead = readLine()
println(lineRead)
}
Here is my build.gradle.kts:
plugins {
kotlin("jvm")
application
}
application {
mainClassName = "samples.HelloWorldKt"
}
dependencies {
compile(kotlin("stdlib"))
}
repositories {
jcenter()
}
The code executes, but the data contained inside the input.txt file is not displayed. Here is the output I get:
Hello, world!
null
I want to be able to execute the gradlew command above and the input.txt stream be redirected to stdio. I can easily do that in C++. Once I compile my .cpp file, I can run:
./my_code < input.txt
and it executes as expected.
How can I achieve the same thing with Kotlin and Gradle?
Update: Based on this answer, I've tried adding this to build.gradle.kts but it is not a valid syntax:
AjahnCharles suggestion about run { standardInput = System.in } is correct, but to port it to kotlin-dsl you need a different syntax.
run in this case is the task name and you configure existing task of application plugin.
To configure existing task in kotlin-dsl you should use one of this ways:
val run by tasks.getting(JavaExec::class) {
standardInput = System.`in`
}
or
val run: JavaExec by tasks
run.standardInput = System.`in`
The upcoming version of Gradle 4.3 should provide API for plugin writers to read user input.
The reason of difference between of Groovy and Kotlin in this case because Groovy uses dynamic types, but in Kotlin you must specify task type to have autocompletion and just to compile config script
I finally settled on this (Gradle 7.1.1):
plugins {
application
}
tasks.getByName("run", JavaExec::class) {
standardInput = System.`in`
}
I don't know enough Kotlin yet to judge whether this is equivalent to https://stackoverflow.com/a/46662535/253921.
Almost, but this doesn't work :'(
In Theory
My understanding: < input.txt sets the standard input for the gradlew process, but by default this is not forwarded to your program.
You want to add this to your build.gradle.kts:
run {
standardInput = System.`in`
}
Sources:
https://discuss.gradle.org/t/why-doesnt-system-in-read-block-when-im-using-gradle/3308/2
https://discuss.gradle.org/t/how-can-i-execute-a-java-application-that-asks-for-user-input/3264
In Practice
These build configs look about the same to me, yet Groovy works and Kotlin doesn't. I'm starting to think that the Gradle Kotlin DSL doesn't support the standardInput term yet :/
Here's my working Groovy version if that's any help:
apply plugin: 'kotlin'
apply plugin: 'application'
buildscript {
ext.kotlin_version = '1.1.4'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
repositories {
jcenter()
}
dependencies {
// api => exported to consumers (found on their compile classpath)
// implementation => used internally (not exposed to consumers)
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
mainClassName = "samples.HelloWorldKt"
run {
standardInput = System.in
}

Why my dependency is printed twice?

I have the following fairly simple build.gradle build script:
repositories {
mavenCentral()
}
configurations { libs }
dependencies {
libs 'org.hibernate:hibernate-core:4.3.5.Final'
}
configurations.libs.files { println it }
When I run it with gradlew build (I'm using Gradle 1.12, latest at the moment) I get the following:
DefaultExternalModuleDependency{group='org.hibernate', name='hibernate-core', version='4.3.5.Final', configuration='default'}
DefaultExternalModuleDependency{group='org.hibernate', name='hibernate-core', version='4.3.5.Final', configuration='default'}
These seem to be the same dependencies, but I don't get it why there are 2 of them when I have added just a single one.
Does anyone know why? Did I do something wrong? Or is there something I don't understand?
The Configuration#files method expects a predicate. (For API details, see Configuration in the Gradle Build Language Reference.) If you instead pass it a closure containing a println statement, (seemingly) strange things will happen. You probably want something like the following instead:
// configurations should only be resolved in
// the execution phase, so let's declare a task
task printLibs {
doLast {
configurations.libs.each { println it }
}
}

Resources