Skip to content

Commit

Permalink
Fix circular dependency with KAPT
Browse files Browse the repository at this point in the history
(cherry picked from commit a74893b)
  • Loading branch information
agolubevgo authored and KSP Auto Pick committed Mar 7, 2024
1 parent 28cb8ff commit f250d02
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.SourceKind
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import java.io.File

/**
Expand Down Expand Up @@ -68,28 +70,48 @@ object AndroidPluginIntegration {
*/
@Suppress("DEPRECATION")
private fun tryUpdateKspWithAndroidSourceSets(
project: Project,
kotlinCompilation: KotlinJvmAndroidCompilation,
kspTaskProvider: TaskProvider<*>
) {
val kaptProvider: TaskProvider<Task>? =
project.locateTask(kotlinCompilation.compileTaskProvider.kaptTaskName)

kotlinCompilation.androidVariant.getSourceFolders(SourceKind.JAVA).forEach { source ->
kspTaskProvider.configure { task ->
when (task) {
is KspTaskJvm -> {
task.setSource(source)
task.dependsOn(source)
}
// this is workaround for KAPT generator that prevents circular dependency
val isKaptOutput =
kaptProvider?.let { it.get().outputs.files.toList().any { file -> source.dir == file } } ?: false
if (!isKaptOutput) {
when (task) {
is KspTaskJvm -> {
task.setSource(source)
task.dependsOn(source)
}

is KspAATask -> {
task.kspConfig.javaSourceRoots.from(source)
task.dependsOn(source)
}
is KspAATask -> {
task.kspConfig.javaSourceRoots.from(source)
task.dependsOn(source)
}

else -> Unit
else -> Unit
}
}
}
}
}

// same logic as in Kapt name generation method
private val TaskProvider<*>.kaptTaskName: String
get() {
val prefix = "kapt"
return if (name.startsWith("compile")) {
name.replaceFirst("compile", prefix)
} else {
"$prefix${name.capitalizeAsciiOnly()}"
}
}

private fun registerGeneratedSources(
project: Project,
kotlinCompilation: KotlinJvmAndroidCompilation,
Expand Down Expand Up @@ -123,7 +145,7 @@ object AndroidPluginIntegration {
// Order is important here as we update task with AGP generated sources and
// then update AGP with source that KSP will generate.
// Mixing this up will cause circular dependency in Gradle
tryUpdateKspWithAndroidSourceSets(kotlinCompilation, kspTaskProvider)
tryUpdateKspWithAndroidSourceSets(project, kotlinCompilation, kspTaskProvider)

registerGeneratedSources(
project,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.google.devtools.ksp.test

import org.gradle.testkit.runner.GradleRunner
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.io.File

@RunWith(Parameterized::class)
class KaptKspTest(useKSP2: Boolean) {
@Rule
@JvmField
val project: TemporaryTestProject = TemporaryTestProject("android-view-binding", "playground", useKSP2)

@Test
fun testPlaygroundAndroid() {
val buildFile = File(project.root, "app/build.gradle.kts")
val content = buildFile.readText()
val newContent = content.replace("kotlin(\"android\")", "kotlin(\"android\")\n kotlin(\"kapt\")\n")
buildFile.writeText(newContent)
val gradleRunner = GradleRunner.create().withProjectDir(project.root)

gradleRunner.withArguments(
"clean",
":app:testDebugUnitTest",
"--configuration-cache-problems=warn",
"--info",
"--stacktrace"
).build().let { result ->
val output = result.output.lines()
val kspTask = output.filter { it.contains(":app:kspDebugKotlin") }
val kaptTask = output.filter { it.contains(":app:kaptDebugKotlin") }
Assert.assertTrue(kspTask.isNotEmpty())
Assert.assertTrue(kaptTask.isNotEmpty())
}
}

companion object {
@JvmStatic
@Parameterized.Parameters(name = "KSP2={0}")
fun params() = listOf(arrayOf(true), arrayOf(false))
}
}

0 comments on commit f250d02

Please sign in to comment.