What would be the recommended option for maximum G...
# plugin-development
t
What would be the recommended option for maximum Gradle version compatibility when using Kotlin in a plugin? (at a minimum to expose Kotlin extension functions) For now I've been using Gradle 7.6 whose
kotlin-dsl
produces Kotlin 1.4-compatible bytecode, compatible all the way down to Gradle 6.8. IIUC (I'm not well versed into Kotlin compatibility), using Gradle 8.0–8.2 would only possibly allow for Kotlin 1.5 (3 versions down from 1.8), i.e. Gradle 7.2; and bumping to >=8.3 cuts that to Gradle 7.5 (Kotlin 1.6) Do I understand properly? and what would you recommend? (I'm thinking about rewriting from Kotlin to Java, but still need Kotlin for extension functions, but could then possibly just use
kotlin("jvm")
rather than `embedded-kotlin`/`kotlin-dsl`; also, I don't think shipping variants is worse the hassle, this is for a small plugin, not AGP or KGP)
m
Use
kotlin("jvm").version("2.0.0")
with
apiVersion
/`languageVersion`
The hard part is ensuring none of your dependencies use an unsupported Kotlin version
Gradle 8.0–8.2 would only possibly allow for Kotlin 1.5
I like to think of it the other way around: the version of Kotlin you use determines the Gradle compatibility. You can use Gradle 8.8 and compile with Kotlin 1.5 (or any other version) using
kotlin("jvm")
If you want Gradle 6.8 compatibility, I would do something like this:
Copy code
# gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip

# build.gradle.kts
plugins {
  id(kotlin("jvm")).version("2.0.0")
}

dependencies {
  compileOnly("dev.gradleplugins:gradle-api:6.8)
}

kotlin {
  compilerOptions {
    // this is deprecated but looks like it's still working
    apiVersion.set(KotlinVersion.KOTLIN_1_4)
    languageVersion.set(KotlinVersion.KOTLIN_1_4)
  }
}
And then run integration tests with both Gradle 6.8 and Gradle 8.8
BTW, I’m curious about that part: “need Kotlin for extension functions”. Does that mean
build.gradle.kts
users get better APIs? I’ve been trying to avoid any Kotlin constructs in plugin public API (extensions, function types, etc...) because it’s so easy to forget about Groovy caller doing so
t
BTW, I’m curious about that part: “need Kotlin for extension functions”. Does that mean
build.gradle.kts
users get better APIs?
No, that without them Groovy DSL users get a better experience, because Kotlin DSL doesn't generate type-safe accessors for everything
👍 1
j
I agree with Martin's approach. you can also set the compatibility on the task itself, like
Copy code
tasks.withType<KotlinCompile> {
            kotlinOptions {
                languageVersion = "1.4"
                apiVersion = "1.4"
            }
        }
i have done this in concert with
kotlin-dsl
plugin as well, and it works
t
Note for those following along: this only works since Gradle 8.1 though, as previous versions forced those in afterEvaluate: https://github.com/gradle/gradle/commit/0d3bc03f2f6d72b7c31ad43c4eb337976a5c82ce https://docs.gradle.org/8.1/release-notes.html#kotlin-dsl-improvements#easier-customization-of-kotlin-options Gradle 8.1 comes with Kotlin 1.8 though (https://docs.gradle.org/current/userguide/compatibility.html#kotlin), and IIUC Kotlin 1.8 only guarantees compatibility down to 1.5 (https://kotlinlang.org/docs/kotlin-evolution.html#compatibility-options), which means Gradle 7.2.
That being said, https://kotlinlang.org/docs/compatibility-modes.html talks about "at least three previous language and API versions in addition to the latest stable one", and the commit history for this page gives more information (https://github.com/JetBrains/kotlin-web-site/commit/d75a00ce972d155c5d8f100ed532d25cf45e495c): > For example, Kotlin 1.8.0 supports language versions 1.3...1.9, and Kotlin 1.9.0 will likely support 1.4...2.0. It is thus a bit misleading to claim that we support exactly three previous versions. So I guess this means I could use Gradle 8.9 with
kotlin-dsl
(or possibly just
embedded-kotlin
) and reconfiguring the
languageVersion
and
apiVersion
to
"1.4"
to keep compatibility down to Gradle 6.8 !
(not sure how it'll work with Gradle 9 though, which will both update to Kotlin 2.x and break API compatibility by moving everything to lazy properties; hopefully by that time Gradle will provide more tooling to create plugins with Gradle version-specific variants)
m
> this only works since Gradle 8.1 though, as previous versions forced those in afterEvaluate There are ways but they’re not pretty 😅 ... In general, I try to stay clear of
kotlin-dsl
though and use the plain
org.jetbrains.kotlin.jvm
cause it gives more control (and also forced me to understand the sam-with-receiver magic) (and is not bound to the Gradle version)
> at least three previous language and API versions in addition to the latest stable one I think the docs are erring on the cautious side. In practice looks like you can target 1.4 with Kotlin 2.0.0. If that’s not the case, they should really remove that enum instead of deprecating it.
I like building with the latest tools always. Gives me bugfixes and also makes it easier to follow along/send feedback, cf conservative libraries with liberal tooling.
t
Fwiw, just did a
gradle init
for a new project and noticed that generating a plugin project using Kotlin as implementation language uses the Kotlin JVM plugin (and not `kotlin-dsl`; which I suppose is more tailored for "private to the build" convention plugins)
👍 1
j
The lazy properties thing is improved by the recent change that you can now use = to call setters in kts
m
My hot take is
kotlin-dsl
should die, replaced by declarative Gradle.
kotlin-dsl
tries too much to look declarative while under the hood everything is imperative.
👎 1