I use `kotlin-dsl` gradle plugin in an included bu...
# kotlin-dsl
m
I use
kotlin-dsl
gradle plugin in an included build that contains convention plugins. I use latest Gradle 8.14.3 It broke when I wanted to update Kotlin to
2.2.0
. My convention plugins are depending on another gradle plugins that the convention plugins are configuring. Dagger and Hilt are the ones that are now failing saying that
Copy code
Class 'dagger.hilt.android.plugin.HiltExtension' was compiled with an incompatible version of Kotlin. The actual metadata version is 2.2.0, but the compiler version 2.0.0 can read versions up to 2.1.0
Does it mean that gradle kotlin-dsl plugin always uses the kotlin compiler bundled in Gradle and there is a Gradle update required to fix this compatibility issue? Or is there any way to force gradle using newer kotlin for building gradle convention plugins?
m
You can also use
org.jetbrains.kotlin.jvm
instead of
kotlin-dsl
and compile against 2.2 libs
v
The resolution is to ask dagger hilt to add apiVersion=1.8, languageVersion=apiVersion, and coreLibrariesVesion=1.8, so it supports backward compatibility
➕ 2
m
That works too! So many solutions!
m
Updating
kotlin-dsl
to 6.2.0 instead of 5.2.0 that is bundled with gradle 8.14.3 helped as well. Although Gradle now warns about that.
Copy code
This version of Gradle expects version '5.2.0' of the `kotlin-dsl` plugin but version '6.2.0' has been applied to project ':gradle:conventions'. Let Gradle control the version of `kotlin-dsl` by removing any explicit `kotlin-dsl` version constraints from your build logic.
WARNING: Unsupported Kotlin plugin version.
The `embedded-kotlin` and `kotlin-dsl` plugins rely on features of Kotlin `2.0.21` that might work differently than in the requested version `2.2.0`.
m
Ah yes, including by id. That may work
Copy code
id("org.gradle.kotlin.kotlin-dsl") version "6.2.0"
Not sure why Gradle is complaining and what the exact risks of doing so are
There may be though 🤷
m
I am mostly interested though, how to switch from kotlin-dsl to kotlin-jvm. This looks like the most robust solution to not fall into this issue agin in the future. Since the
gradlePlugin
block for defining plugins comes from the
kotlin-dsl
. And additionally I rely on some legacy groovy code and had to change the compilations dependency by doing
Copy code
fun setupLanguageDependencies() {
    tasks.compileGroovy.configure {
        classpath = sourceSets.main.get().compileClasspath
    }

    tasks.compileKotlin.configure {
        libraries.from(sourceSets.main.get().groovy.classesDirectory)
    }
}
💯 1
m
+1 to using `org.jetbrains.kotlin.jvm`
❤️ 1
v
Since the
gradlePlugin
block for defining plugins comes from the
kotlin-dsl
.
It is not
Not directly
It comes from one of the plugins that plugin is applying for you
m
yes, I figured it out it comes from the java-gradle-plugin
👌 1
☝️ 1
Article from @Martin opened my 👀
Tried to switch to kotlin("jvm") + the sam-with-receiver plugin, but still the AGP seems not to allow mi using sam with receiver. The documentation of the plugin sam-with-receiver plugin says I have to configure the annotations, but i didn't have to using 'kotlin-dsl'.
m
kotlin-dsl
configures the annotation for you
Dit you try this?
Copy code
plugins {
  id("org.jetbrains.kotlin.jvm").version("2.2.0")
  id("org.jetbrains.kotlin.plugin.sam.with.receiver").version("2.2.0")
}

samWithReceiver {
  annotation(HasImplicitReceiver::class.qualifiedName!!)
}
?
m
ah, no, haven't seen this annotation being on the AGP interfaces
m
It's on Gradle
Action<>
(which is used inside AGP)
m
right, makes sene!
m
All the
kotlin-dsl
code is here if you want to look into what it's doing/take the parts that you are interested in
m
perfect, managed to migrate my plugins modules to
kotlin.jvm
. Thanks. Fantastic piece of work this article of yours @Martin
v
Apparently, Dagger did try using
apiVersion=1.9
and
languageVersion=1.9
, however they did not configure it consistently. I’ve explained the details in https://github.com/google/dagger/issues/4848#issuecomment-3108369445
👍 1
It is fun how mr @Martin says “oh, sam-with-receiver and assignment-plugin causes fragmentation of Gradle Kotlin DSL users as it results in multiple ways of doing the same thing” in https://mbonnin.net/2025-07-10_the_case_against_kotlin_dsl/, and finally they suggest:
If you’re looking for a sample plugin that does’t use
kotlin-dsl
, nmcp is a good example.
Oh, dear. nmcp uses gratatouille which is, well, quite a different way of writing Gradle plugins.
m
Yes, just write all your plugins with Gratatouille!
v
I’m sorry, yet it is not that documented and it is not that used as the regular Gradle Kotlin DSL is.
I wonder if precompiled script plugins still have significant overhead taking up-to-date and build cache into account.
➕ 1
m
touché on the documentation
Gratatouille is quite orthogonal to
kotlin-dsl
though. It removes the need for worker boilerplate and provides classloader isolation. For all the rest, it uses plain regular Kotlin..
You can use Gratatouille with
kotlin-dsl
if you want to (although I won't encourage it)
v
Well, you blame kotlin-dsl for: 1. bad naming. Well, naming is hard.
kotlin-dsl
started in 2017 or earlier (it is 8+years already?), so let’s account for that as well. 2. fragmenting the ecosystem by adding sam conversion for Action. Frankly, I would say it converges Groovy and Kotlin scripts. It is great we can have a single method with
Action
parameter and have both Groovy and Kotlin support lambdas there. Otherwise you’d have to duplicate logic:
Clojure
for Groovy and
..->..
for Kotlin 3. fragmenting the ecosystem by adding assignment plugin. Once again, I think it converges Groovy and Kotlin, and it reduces the noise in the build scripts 4. it is slow (reference to 3-year-old benchmarks from 2022) 5. it’s stuck on older tools <-- this is very valid In any case your points 2 and 3 blame kotlin-dsl for “fragmenting the ecosystem by adding the second option to write the same thing”. What I said above was exactly the same with Gratatouille: it adds yet another language to create Gradle plugins, and it adds a framework to execute Gradle plugins. If “kotlin-dsl is bad for fragmenting the ecosystem”, then the same applies to Gratatouille. If Gratatouille is not bad for “fragmenting the ecosystem by adding yet another language for Gradle plugins”, then “kotlin-dsl” is not bad either.
m
Looks like we agree on 1. and 5., that's already something! For 4. we need more benchmarks but there is definitely more work involved with precompiled scripts so it can't be faster.
For 2. and 3., I'm not really advocating for Gratatouille just yet, sorry if it sounded like that. I was just too lazy to maintain a plugin that uses
org.jetbrains.kotlin.jvm
but not Gratatouille. If you have another simpler example, I'll definitely update the post as it's true there is some cognitive load to gratatouille
And Gratatouille isn't really ready for showtime yet. I'm personnally very happy with it but wouldn't really recommend it for the neophyte
tldr; I should replace that example in the post. If anyone has a better example, I'll take it!
v
For 4. we need more benchmarks but there is definitely more work involved with precompiled scripts so it can't be faster.
Depends on whether you use precompiled script plugins actually. 🙂 Kotlin DSL precompiled script plugins are just one part of what
kotlin-dsl
plugin does. The added
kotlinDsl()
dependency and the configured Kotlin Compiler plugins can be used in normal Kotlin files without ever using any precompiled script plugin, and I'd guess that the performance drain for seeing no precompiled Kotlin DSL script plugins are used is neglectible hopefully. 🙂 Besides the ecosystem-split you two are a bit talking about different things I'd say. I think Martin is more focussed on Gradle plugin code, Vladimir on build scripts. And actually the point is vaguely moot, because in build scripts you will anyway have these compiler plugins enabled to converge Groovy DSL and Kotlin DSL. So even if no plugin in the world would use
kotlin-dsl
, or rather those two compiler plugins, you would still have that "split". tAnd actually, by not usingMoreover, without using those two compiler plugins you cannot use the syntax that you can use in build scripts within the plugin code, so imho it is better to use those compiler plugins because then you can use the same syntax you use in Kotlin DSL build scripts also in Kotlin written plugins. 🙂
👍 1
💯 1
m
> you would still have that "split". Agree. But without
kotlin-dsl
you would have this invariant: •
.kt
=> regular Kotlin •
.gradle.kts
=> Gradle flavored Kotlin I'd be completely mostly fine with this.
Maybe
kotlin-dsl
should enforce that everyone uses precompiled scripts plugins
e
groovy-gradle-plugin also has the same syntax in .gradle and .groovy files
👆 1
v
I'd be completely mostly fine with this
You might be, I would hate it by heart. 😄
> Maybe
kotlin-dsl
should enforce that everyone uses precompiled scripts plugins Why by whoevers sake should such a major restriction be implemented? o_O
m
🤷 conventions I guess
And again, even if we wanted this, we would need
kotlin-dsl
but without the precompiled scripts plugins and with a configurable Kotlinc version
v
That would be bad conventions though. Conventions are to improve things and make things easier. That change would be the opposite. Unless you split it into two plugins, one for normal users and one for precompiled script plugins that could also be used together
Oh wait, that's
kotlin-dsl
😄
m
I'm lost. Can you configure your kotlinc version with
kotlin-dsl
?
v
Vampire managed to build a meta-joke with kotlin-dsl
v
I'm lost. Can you configure your kotlinc version with
I don't think so, messages are out of order, that was intended before your kotlinc message
m
Damn, I missed the joke!
BTW, there should have been a
kotlin-gradle-plugin
plugin
v
Yes
We all agree on the bad naming, that's not questionable 😄
😄 1
e
there is a
kotlin-dsl.precompiled-script-plugins
👀 1
v
Btw. the
kotlin-dsl
without precompiled script plugins can already be done. Just apply
java-gradle-plugin
and
kotlin-dsl-base
plugins. Applying these two and additionally the
kotlin-dsl-precompiled-script-plugin
is all that the
kotlin-dsl
plugin does, besides checking whether the version is the one expected by current Gradle version.
e
also with the introduction of https://kotlinlang.org/docs/whatsnew22.html#new-experimental-build-tools-api, there is the hope that perhaps a future version of Gradle could use that for a more stable interface to the Kotlin compiler, allowing it to be swapped out more easily
v
And if you also do not want what the
kotlin-dsl-base
plugin is doing (which includes applying the embedded kotlin plugin version), you could also instead apply the
org.gradle.kotlin.kotlin-dsl.compiler-settings
plugin which adds and configures the compiler plugins and add the
gradleKotlinDsl()
dependency manually to
compileOnly
and
testImplementation
. That is, what the base plugin is doing in addition to applying the embedded kotlin plugin version. You probably would have to first add the
KotlinDslPluginOptions
extension though I think. Iirc the base plugin would do that and the compiler settings plugin requires it to be there.