dependency-analysis-gradle-plugin
dependency-analysis-gradle-plugin copied to clipboard
Gradle8 + KSP compatibility
Build scan link https://scans.gradle.com/s/5q4mdauav5xri
Plugin version 1.20.0
Gradle version 8.1.1
(Optional) Android Gradle Plugin (AGP) version 8.0.2
reason
output for bugs relating to incorrect advice
N/A
Describe the bug
We use KSP in our Android project. During migration to Gradle 8+ we faced failures caused by buildHealth
task.
The issue is that Gradle8 requires dependencies between tasks to be defined explicitly.
It seems that dependency analysis plugin uses KSP output directories but does not define any relation to KSP tasks.
To Reproduce
- Open sample project in Android Studio
- Run
./gradlew buildHealth
from the root of the project
Expected behavior
buildHealth
task finished successfully (with or without advice)
Additional context
FAILURE: Build failed with an exception.
* What went wrong:
Some problems were found with the configuration of task ':app:kspDebugKotlin' (type 'KspTaskJvm').
- Gradle detected a problem with the following location: '/Users/a.erdman/proj/build_health_gradle8_compatibility_2/app/build/generated/ksp/debug'.
Reason: Task ':app:explodeCodeSourceDebug' uses this output of task ':app:kspDebugKotlin' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':app:kspDebugKotlin' as an input of ':app:explodeCodeSourceDebug'.
2. Declare an explicit dependency on ':app:kspDebugKotlin' from ':app:explodeCodeSourceDebug' using Task#dependsOn.
3. Declare an explicit dependency on ':app:kspDebugKotlin' from ':app:explodeCodeSourceDebug' using Task#mustRunAfter.
Please refer to https://docs.gradle.org/8.1.1/userguide/validation_problems.html#implicit_dependency for more details about this problem.
- Gradle detected a problem with the following location: '/Users/a.erdman/proj/build_health_gradle8_compatibility_2/app/build/generated/ksp/debug/kotlin'.
Reason: Task ':app:explodeCodeSourceDebug' uses this output of task ':app:kspDebugKotlin' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed.
Possible solutions:
1. Declare task ':app:kspDebugKotlin' as an input of ':app:explodeCodeSourceDebug'.
2. Declare an explicit dependency on ':app:kspDebugKotlin' from ':app:explodeCodeSourceDebug' using Task#dependsOn.
3. Declare an explicit dependency on ':app:kspDebugKotlin' from ':app:explodeCodeSourceDebug' using Task#mustRunAfter.
Please refer to https://docs.gradle.org/8.1.1/userguide/validation_problems.html#implicit_dependency for more details about this problem.
This affects more than just builds that use KSP. I have a simple task which produces generated source code that JavaCompile
steps depend on, and see the same failure.
Thanks for the report.
I think this is a bug in Gradle. See https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/685#issuecomment-1650309499. tl;dr you can manually wire the tasks together.
I tried to link tasks before publishing the issue but it didn't work. I have just checked the same implementation you provided in the mentioned issue. It does not work too. You can check it in the sample project. Even after adding the code below
tasks.withType(com.autonomousapps.tasks.CodeSourceExploderTask) {
dependsOn('kspKotlin', "kspDebugKotlin")
}
running ./gradlew buildHealth
twice guarantees the failure.
https://github.com/gradle/gradle/issues/25885
Here is a more fine-grained workaround for android projects. At least this works for us. It doesn't seem to affect JVM modules in our case.
allprojects {
pluginManager.withPlugin('com.google.devtools.ksp') {
pluginManager.withPlugin('com.android.library') {
androidComponents {
beforeVariants(selector().all()) { variant ->
String variantName = variant.name.capitalize()
tasks.configureEach { task ->
if (task.name == "explodeCodeSource$variantName") {
task.dependsOn("ksp${variantName}Kotlin")
}
}
}
}
}
pluginManager.withPlugin('com.android.application') {
androidComponents {
beforeVariants(selector().all()) { variant ->
String variantName = variant.name.capitalize()
tasks.configureEach { task ->
if (task.name == "explodeCodeSource$variantName") {
task.dependsOn("ksp${variantName}Kotlin")
}
}
}
}
}
}
}
I've explored this a bit more recently, and while the linked antlr plugin issue is a gradle bug (because antlr is a core gradle plugin), this is, more generally, a bug in plugin implementation. For plugins to correctly contribute generated source, such that task dependency information is carried and end users don't need to think about these workarounds, they should do this:
Correct
// pseudocode
sourceSets.main.<java|kotlin|etc>.srcDir(myGeneratingTask.map { it.outputDirectory })
I haven't explored the ksp code, but I assume it's not doing this, and is instead doing something like
Incorrect
// pseudocode
val outputDirectory = project.layout.buildDirectory.dir("my-dir")
myGeneratingTask.configure { t ->
t.outputDirectory.set(outputDirectory)
}
sourceSets.main.<java|kotlin|etc>.srcDir(outputDirectory)
That isn't enough information for Gradle to know which task generates code into that directory, unfortunately.