Olaf Gottschalk
02/13/2025, 9:58 AMkotlin {
compilerOptions {
// ...
}
}
vs
tasks.withType<KotlinCompile> {
compilerOptions {
// ...
}
}
Do these two kinds of approaches do the exact same thing or not? Which one is newer, which one preferred? (and why?)
Then I got a real problem understanding the difference or meaning of setting the Java toolchain using
kotlin {
jvmToolchain(17)
}
and setting the jvm target set via
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
I wanted to use a newer toolchain (I thought this meant which Java tools to use building) and set the jvmToolchain to 21...
This leads to this error:
Inconsistent JVM-target compatibility detected for tasks 'compileJava' (21) and 'compileKotlin' (17).
In order to get rid of this, I now also had to add this setting:
java.sourceCompatibility = JavaVersion_17
where I honestly don't even understand why this is needed - I have no Java source files at all and the doc says that this defines which Java byte code is generated from Java source code.
All in all: is there a good source to read about all of these things in one place that I can read and then really do the correct thing? π
Thanks!mbonnin
02/13/2025, 10:08 AMmbonnin
02/13/2025, 10:09 AMmbonnin
02/13/2025, 10:14 AMcompilerOptions.jvmTarget
.
I'm also not 100% clear why Kotlin-only project would need to care about Java but it's been like this for a while now.tapchicoma
02/13/2025, 10:29 AMI'm also not 100% clear why Kotlin-only project would need to care about Java but it's been like this for a while now.Because in Kotlin/Jvm project Gradle Java plugin controls publishing attributes even if there is no Java sources.
tapchicoma
02/13/2025, 10:30 AMmbonnin
02/13/2025, 10:35 AMBecause in Kotlin/Jvm project Gradle Java plugin controls publishing attributes even if there is no Java sources.Ahhh publishing... It could probably be the other way around though? Have Kotlin
jvmTarget
set java.sourceCompatibility
etc...?tapchicoma
02/13/2025, 10:35 AMmbonnin
02/13/2025, 10:36 AMmbonnin
02/13/2025, 10:39 AMjava {}
if you have org.jetbrains.kotlin.jvm
in your build. If something really needs to be set, I'd have to go through kotlin {}
first so that KGP owns what's happening.mbonnin
02/13/2025, 10:40 AMtapchicoma
02/13/2025, 10:41 AMmbonnin
02/13/2025, 10:42 AMOlaf Gottschalk
02/13/2025, 10:55 AMjava.sourceCompatibility = JavaVersion.VERSION_17
tasks.withType<KotlinCompile> {
compilerOptions {
jvmTarget.set(JvmTarget.fromTarget(java.sourceCompatibility.toString()))
}
}
But when I read what java.sourceCompatibility
sets according to the docs:
Sets the source compatibility used for compiling Java sources
I decided to remove that in my Kotlin only projects...
That's when the problems started and I tried to understand what really happens under the hood.
So, can somebody elaborate on the difference between
kotlin {
...
}
and
tasks.withType<KotlinCompile> {
...
}
Does one do more or less than the other?
And what exactly is controlled by the toolchain setting?
As I build in a GitHub action, I also install the JDK and specifically select e.g. JDK 17. So what would the toolchain setting even do then?Vampire
02/13/2025, 11:04 AMtasks.withType<...> { ... }
anywhere!
You completely break task-configuration avoidance for all tasks of that type.
If so, then use tasks.withType<...>().configureEach { ... }
which is task-configuration avoidance-safe.
The difference between your approach 1 and the fixed 2 is, that 1 configures an extension and thus all tasks that happen to be wired to that extension, while 2 configures all tasks of the given type no matter where they are registered.Olaf Gottschalk
02/13/2025, 11:07 AMconfigureEach
and some without! As the lambda you attach after tasks.withType<...>
has the same receiver (KotlinCompile), I was under the impression they do the exact same thing! How on earth would I find this out - and why don't I have any problems?Vampire
02/13/2025, 11:08 AMVampire
02/13/2025, 11:08 AMVampire
02/13/2025, 11:08 AMOlaf Gottschalk
02/13/2025, 11:09 AMconfigureEach
does not give ANY hint that this is the "best" way to configure task. It says:
Configures each element in this collection using the given action, as each element is required. Actions are run in the order added.
But it does not say: USE me. If you don't, something bad happens.
Why does withType
have an overload method to accept the same kind of code that configureEach
has? This is bad by design...Olaf Gottschalk
02/13/2025, 11:10 AMVampire
02/13/2025, 11:10 AMEven the documentation ondoes not give ANY hint that this is the "best" way to configure task.configureEach
configureEach
does not know what a "task" is and also it is not said that it is always the best thing to do, depends on the situation like any API, but usually you want configureEach
to configure a collection of domain objects.Vampire
02/13/2025, 11:11 AMWhy doesBackwards compatibilityhave an overload method to accept the same kind of code thatwithType
has? This is bad by design...configureEach
Olaf Gottschalk
02/13/2025, 11:11 AMVampire
02/13/2025, 11:11 AMBut in my case, it's a Kotlin project so I guess all Kotlin compile tasks are actually neededVery unlikely
Olaf Gottschalk
02/13/2025, 11:12 AMwithType
has this overload for backward compatibility, why don't they deprecate this overload and explain to use configureEach
?Vampire
02/13/2025, 11:12 AM./gradlew help
why should you configure kotlin compile tasks?
If you do ./gradlew run
why should you compile Kotlin test classes?
If you do ./gradlew whateverThingThatDoesNotNeedEachAndEveryKotlinFileCompiled
why should you configure all Kotlin Compile tasks?Olaf Gottschalk
02/13/2025, 11:13 AMOlaf Gottschalk
02/13/2025, 11:14 AMVampire
02/13/2025, 11:14 AMwhy don't they deprecate this overloadAsk them. But probably why deprecate a valid method, there are use-cases for it. But actually it should be the same as
tasks.withType<...>().all { ... }
I thinkVampire
02/13/2025, 11:15 AMhow can I ever be sure to make things the recommended way?You cannot π Especially as Gradle is actively developed and evolving, so things change. What was recommended and idiomatic yesterday might be bad-practice tomorrow.
Vampire
02/13/2025, 11:16 AMOlaf Gottschalk
02/13/2025, 11:17 AMOlaf Gottschalk
02/13/2025, 11:18 AMOlaf Gottschalk
02/13/2025, 11:21 AMwithType
and `configureEach`: what does using plain kotlin { ... }
do?
Does it also configure ALL tasks of KotlinCompile? In the "correct" lazy way?
And what does setting the toolchain do, if I put it there vs. inside configureEach
?Olaf Gottschalk
02/13/2025, 11:24 AMconfigureEach
on tasks.withType<...>
.
Does this also apply to tasks.register<...>
? I have this without configure
, but plain
tasks.register<Copy> {
from(....)
}
Is that bad as well? Same problem?Vampire
02/13/2025, 12:51 PMwhat does using plaindo?kotlin { ... }
Does it also configure ALL tasks of KotlinCompile?As I said, it configures a project extension that the Kotlin plugin added. It configures all tasks that are wired to this extension. Whether this means all tasks of type
KotlinCompile
depends on your build scripts and the plugins you apply.Vampire
02/13/2025, 12:52 PMtasks.register<Copy> {
from(....)
}No, that is fine.
tasks.register
is the lazy way to create a task and so tasks.register<Copy> { ... }
and tasks.register<Copy>().configure { ... }
are the same.Vampire
02/13/2025, 12:54 PMtasks.register
were introduced there were already the non-lazy ways.
And for those there were convenience methods like tasks.withType(...) { ... }
that then of course used the only available non-lazy way.
Then the lazy stuff was added and you cannot simply change the meaning of the method to behave the lazy way as that would be potentially breaking.mbonnin
02/13/2025, 12:59 PMKotlinCompile
as an implementation detail altogether. Historically tasks have been part of the (public) Gradle model for most of the plugins but I see them more and more as implementation details.mbonnin
02/13/2025, 12:59 PMkotlin {
compilerOptions {
// ...
}
}
mbonnin
02/13/2025, 1:00 PM