Are we supposed to be able to declare Kotlin multi...
# gradle
c
Are we supposed to be able to declare Kotlin multiplatform targets in convention plugins? It seems to work for js, wasmJs, and native targets. But fails for jvm and android targets.
h
What error do you get? I am able to setup JVM conventions plugins 🤔
c
Copy code
* What went wrong:
Cannot change dependencies of dependency configuration ':my-lib:commonMainApi' after it has been included in dependency resolution.
This is with a really simple convention like this
Copy code
pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
    extensions.findByType<KotlinMultiplatformExtension>()?.apply {
        jvm()
    }
}
v
That is a sign that something somewhere is resolving a configuration too early. Namely during configuration phase which seldomly is a good idea. And after a configuration was part of dependency resolution you cannot modify it anymore. So what you do is most probably just triggering the error but is not the source of the problem. You need to find out who / what / where is resolving a configuration prematurely and fix that.
t
@Carter with which Gradle and Kotlin version do you see it?
c
Kotlin 2.1.20 and Gradle 8.14
I can try to set up a minimal repro. In my own project I just kept commenting out build script stuff and never saw it go away.
t
try with Kotlin 2.1.21-RC - there was a related issue fixed in this release:
c
OK, let me try that. I do have JS targets alongside my JVM and Android targets. I’ll report back what I learn.
j
Agree with @Vampire as I have created a convention plugin today which includes KMP with JVM and Android defaults
c
I was able to track down the problem to another part of my build. I was setting a resolution strategy to force upgrade vulnerable transitive dependencies. Reworking that allowed me to create the targets in a convention plugin. Thanks everyone for the help and for confirming that this should work in the first place, which was really helpful.
👌 1
v
To upgrade transitive dependencies, you should just set version constraints for them like
Copy code
dependencies {
    constraints {
        implementation("the:vulnerableInV1:v2")
    }
}
c
I’ve yet to find a good way to make that work. I’m trying to set a floor (non-vulnerable) value for certain dependencies so that I don’t have to remember to keep updating the dependency over time assuming the transitive reference gets fixed in the library that uses it. And I need to update groups of related dependencies, rather than simple individual dependencies. And some dependencies have nonsense version numbers. So I end up with something like
Copy code
configurations.configureEach {
    incoming.beforeResolve {
        resolutionStrategy.eachDependency {
            val libs = versionCatalogs.named("libs")
            if (requested.group == "io.netty") {
                val requestedSemanticVersion =
                    requested.version!!.removeSuffixIfPresent(".Final").let { SemanticVersion.new(it) }
                val minimumVersion = SemanticVersion.new("4.1.118")
                if (requestedSemanticVersion < minimumVersion) {
                    useVersion("${minimumVersion.serialize()}.Final")
                    because("CVE-2025-24970")
                }
            } else if (requested.group == "com.google.guava" && requested.name == "guava") {
                val requestedSemanticVersion =
                    requested.version!!.removeSuffixIfPresent("-jre").removeSuffixIfPresent("-android")
                        .let { SemanticVersion.new(it) }
                val minimumVersion = SemanticVersion.new("32.0.1")
                if (requestedSemanticVersion < minimumVersion) {
                    useVersion("${minimumVersion.serialize()}-jre")
                    because("CVE-2023-2976")
                }
            }
...
v
Without trying a wild guess of mine would be
Copy code
dependencies {
    constraints {
        implementation("com.google.guava:guava:32.0.1-jre")
    }
    implementation(platform("io.netty:netty-bom:4.1.118.Final"))
}
c
But doesn’t that hard pin the transitive dependency to the exact version, irrespective of whether the dependency asks for a newer version?
v
No
Not unless you use a strict version for example by suffixing it with
!!
Like that it just takes part in normal conflict resolution logic
c
Will dependencies
{ constraints {} }
work in a convention plugin though? It seems to need to know the name of the configuration ahead of time.
v
It works the same as dependencies.
So yes, you can
c
But doesn’t it need to know the name of the configurations?
Its a constraint on a particular configuration, as best I can tell
v
Just like for declaring dependencies, yes. But you can use Strings, or getting the information where you need it from or whatever other logic.