I couldn’t figure this out myself, nor even find the appropriate Stack Overflow article on my own, so I’m writing this. I hope you find it and it alleviates your annoyed state.
This is a companion discussion topic for the original entry at https://blog.thecodewhisperer.com/permalink/stdin-gradle-kotlin-dsl
Funny how the Gradle Kotlin DSL makes some things more consistent, but at the same time others less so.
Another alternative that I use and that is equivalent (as far as I know) is this:
tasks.run<JavaExec> {
standardInput = System.`in`
}
This works because run
comes pre-defined as an extension property on TaskContainer
in the kotlin-dsl
. If you jump to to the implementation you will find this:
val TaskContainer.`run`: TaskProvider<org.gradle.api.tasks.JavaExec>
get() = named<org.gradle.api.tasks.JavaExec>("run")
We need the explicit <JavaExec>
to get the invoke
extension from Gradle to take precedence over the kotlin run
scope-function. However since invoke
operators are just syntactic sugar for just typing out invoke
as a method call, we can also do this:
tasks.run.invoke {
standardInput = System.`in`
}
And now it becomes interesting again, because how is invoke
implemented? Let’s have a look:
operator fun <T> NamedDomainObjectProvider<T>.invoke(action: T.() -> Unit) =
configure(action)
So the invoke
method simply forwards to configure
, which means we can also do this:
tasks.run.configure {
standardInput = System.`in`
}
I am sticking with the first option I mentioned, but having many ways to do the same thing sure is confusing.
1 Like
Thank you! The last version seems to express the intent most directly, so I prefer it. What makes you prefer the first version?
It’s a close call for sure.
a.) this is closer to how I configure other parts, for example tests
tasks.test {
useJUnitPlatform()
}
b.) I sometimes find myself helping the IDE/Kotlin compiler to figure out the right generic type. This happens for example when using some AssertJ matchers in Kotlin code. It’s logical for me, that this helps to clarify the type of this
within the block.
But again, just personal preference.
Btw. I think tasks.getByName
should be avoided in larger Gradle configurations, as I think it doesn’t allow configuration avoidance.
Thanks for the link. Evidently it suffices to replace getByName()
with named()
, so I’ll do that:
tasks.named("run", JavaExec::class) {
standardInput = System.`in`
}
If there’s a more-idiomatic way to write that, I’ll do that.