https://kotlinlang.org logo
#ksp
Title
# ksp
r

Robert Jaros

01/26/2023, 12:53 PM
After upgrading to 1.0.9 I see these errors when runnning any gradle task in a project using my ksp plugin:
Copy code
> Cannot change attributes of dependency configuration ':generatedByKspKotlinFrontendApiDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':generatedByKspKotlinFrontendImplementationDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':generatedByKspKotlinFrontendCompileOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':generatedByKspKotlinFrontendRuntimeOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':generatedByKspKotlinFrontendIntransitiveDependenciesMetadata' after it has been resolved
Any ideas what could be the cause?
I've located a source of the problem and I have no idea how does it relate to KSP at all. I have a configuration of the
bootJar
Spring Boot task:
Copy code
getByName("bootJar", BootJar::class) {
            dependsOn("frontendArchive", "backendMainClasses")
            classpath = files(
                kotlin.targets["backend"].compilations["main"].output.allOutputs +
                        project.configurations["backendRuntimeClasspath"] +
                        (project.tasks["frontendArchive"] as Jar).archiveFile
            )
        }
When I change it to this form:
Copy code
getByName("bootJar", BootJar::class) {
            dependsOn("frontendArchive", "backendMainClasses")
            classpath = files(
                kotlin.targets["backend"].compilations["main"].output.allOutputs,
                        project.configurations["backendRuntimeClasspath"],
                        (project.tasks["frontendArchive"] as Jar).archiveFile
            )
        }
it works the same but the problem is gone.
Magic ! 🙂
t

Ting-Yuan Huang

01/26/2023, 5:19 PM
It's magic (that I don't understand) 🙂 Here are some more context: those configurations are used to specify generated sources from each ksp task, and they are added as source sets to kotlin compilation. The configuration names never leave ksp's implementation although they are observable from outside. I should have used detached configurations 🤦
r

Robert Jaros

02/05/2023, 11:29 AM
After hours of struggling with this issue I have some additional comments.
1. The problem can be easily reproduced by adding this code:
Copy code
afterEvaluate {
    configurations["jvmRuntimeClasspath"].files
}
at the end of https://github.com/google/ksp/blob/main/examples/multiplatform/workload/build.gradle.kts
After that gradle can't even load the example project with this error:
Copy code
A problem occurred configuring project ':workload'.
> Failed to notify project evaluation listener.
   > Cannot change attributes of dependency configuration ':workload:generatedByKspKotlinJsApiDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspKotlinJsImplementationDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspKotlinJsCompileOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspKotlinJsRuntimeOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspKotlinJsIntransitiveDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspTestKotlinJsApiDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspTestKotlinJsImplementationDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspTestKotlinJsCompileOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspTestKotlinJsRuntimeOnlyDependenciesMetadata' after it has been resolved
   > Cannot change attributes of dependency configuration ':workload:generatedByKspTestKotlinJsIntransitiveDependenciesMetadata' after it has been resolved
2. This affect KSP 1.0.9 only, there is no problem with 1.0.8.
3. Only KMP projects are affected.
4. This construct
configurations["jvmRuntimeClasspath"].files
is a typical way to find jvm dependencies when building "fat jar". I've googled, but couldn't find another working way to do this.
5. The Eclipse Vert.x gradle plugin, which internally uses shadow gradle plugin ("com.github.johnrengelman.shadow") seems to be affected by this issue as well. The project doesn't work as well with this message:
Copy code
Cannot change dependencies of dependency configuration ':implementation' after it has been included in dependency resolution.
6. I've came up with a "horrifying" workaround for custom fat jar configuration. I've added code like this to my own gradle plugin:
Copy code
afterEvaluate {
                configurations.filter { it.name.startsWith("js") && it.name.endsWith("DependenciesMetadata") }
                    .forEach {
                        configurations.remove(it)
                    }
                configurations["jvmRuntimeClasspath"].files
            }
I have no idea what it breaks (probably a lot 😉), but my project seems to work fine with this.
7. Still no idea how to fix shadow plugin (update: managed to fix by changing gradle plugins order)
@Ting-Yuan Huang any comments would be appreciated 🙂
t

Ting-Yuan Huang

02/08/2023, 9:57 PM
Thanks for the investigation and test case. I'm investigating
b

Big Chungus

02/10/2023, 12:13 PM
Is there an issue for this I could subscribe to? For now I have things going with tweaked Robert's workaround
Copy code
afterEvaluate {
  configurations.filter { it.name.startsWith("generatedByKspKotlinJs") && it.name.endsWith("DependenciesMetadata") }
    .forEach {
      configurations.remove(it)
    }
}
r

Robert Jaros

02/10/2023, 12:24 PM
a

Anton Lakotka [JB]

03/14/2023, 7:49 AM
@Robert Jaros So issue happened because of 3 elements: 1. configuration resolution (it was implicit in your case, through plus operator. read below for the details) 2. applied Kotlin plugin, which is happening in
afterEvaluate
block. 3. changing attributes of a configuration that contribute to jvmRuntimeClasspath through
Configuration::extendsFrom
api. When you changed in your code from:
Copy code
files(
/*...*/
project.configurations["backendRuntimeClasspath"] +
(project.tasks["frontendArchive"] as Jar).archiveFile
)
to this:
Copy code
files(
/*...*/
project.configurations["backendRuntimeClasspath"],
(project.tasks["frontendArchive"] as Jar).archiveFile
)
You actually told gradle to resolve this configuration lazily. And that fixed the issue. Why it got resolved eagerly before that change? Because there is no
operator fun FileCollection.plus(file: File)
but there is
operator fun Iterable<T>.plus(element: T): List<T>
and
Configuration
implements
FileCollection
which also implements
Iterable<File>
. And iterating over
Configuration
or
FileCollection
triggers its resolution. So general recommendation is to try to avoid configuration resolution as much as possible. For such thing you can use protection mechanism implemented by Android Gradle Plugin: DependencyResolutionChecks.kt Nevertheless issue is still valid for Kotlin Gradle Plugin.
165 Views