Skip to content

Commit

Permalink
Delete the ksp caches when the task is restored from cache
Browse files Browse the repository at this point in the history
Without this change, the task's caches will be in an incorrect state and
the next incremental run can lead to compile errors.

Now the caches have been marked as local state and Gradle will remove
them when the task is restored from the build cache.

This fixes #2042
  • Loading branch information
ansman authored and ting-yuan committed Sep 5, 2024
1 parent 1630cf9 commit 3f31c10
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.google.devtools.ksp.processing.*
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
Expand Down Expand Up @@ -92,7 +93,7 @@ abstract class KspAATask @Inject constructor(
kspConfig.commonSourceRoots,
kspConfig.libraries
),
kspConfig.cachesDir.get(),
kspConfig.cachesDir.asFile.get(),
kspConfig.classpathStructure,
kspConfig.libraries,
kspConfig.processorClasspath,
Expand All @@ -102,11 +103,11 @@ abstract class KspAATask @Inject constructor(
!inputChanges.isIncremental ||
inputChanges.getFileChanges(kspConfig.libraries).iterator().hasNext()
)
kspConfig.cachesDir.get().deleteRecursively()
kspConfig.cachesDir.get().asFile.deleteRecursively()
emptyList()
}
} else {
kspConfig.cachesDir.get().deleteRecursively()
kspConfig.cachesDir.get().asFile.deleteRecursively()
emptyList()
}

Expand Down Expand Up @@ -319,8 +320,8 @@ abstract class KspGradleConfig @Inject constructor() {
@get:Internal
abstract val outputBaseDir: Property<File>

@get:Internal
abstract val cachesDir: Property<File>
@get:LocalState
abstract val cachesDir: DirectoryProperty

@get:OutputDirectory
abstract val kotlinOutputDir: Property<File>
Expand Down Expand Up @@ -444,7 +445,7 @@ abstract class KspAAWorkerAction : WorkAction<KspAAWorkParameter> {
libraries = gradleCfg.libraries.files.toList()
projectBaseDir = gradleCfg.projectBaseDir.get()
outputBaseDir = gradleCfg.outputBaseDir.get()
cachesDir = gradleCfg.cachesDir.get()
cachesDir = gradleCfg.cachesDir.get().asFile
kotlinOutputDir = gradleCfg.kotlinOutputDir.get()
classOutputDir = gradleCfg.classOutputDir.get()
resourceOutputDir = gradleCfg.resourceOutputDir.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.gradle.api.UnknownTaskException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
Expand Down Expand Up @@ -97,7 +98,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool

@JvmStatic
fun getKspCachesDir(project: Project, sourceSetName: String, target: String) =
File(project.project.buildDir, "kspCaches/$target/$sourceSetName")
project.layout.buildDirectory.dir("kspCaches/$target/$sourceSetName")

@JvmStatic
private fun getSubpluginOptions(
Expand All @@ -109,6 +110,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
allWarningsAsErrors: Provider<Boolean>,
commandLineArgumentProviders: ListProperty<CommandLineArgumentProvider>,
commonSources: Provider<List<File>>,
cachesDir: Provider<Directory>
): Provider<List<SubpluginOption>> {
val options = project.objects.listProperty(SubpluginOption::class.java)
options.add(
Expand All @@ -127,7 +129,9 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
)
)
options.add(
InternalSubpluginOption("cachesDir", getKspCachesDir(project, sourceSetName, target).path)
cachesDir.map {
InternalSubpluginOption("cachesDir", it.asFile.path)
}
)
options.add(
InternalSubpluginOption("kspOutputDir", getKspOutputDir(project, sourceSetName, target).path)
Expand Down Expand Up @@ -292,10 +296,12 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
.extendsFrom(*nonEmptyKspConfigurations.toTypedArray()).markResolvable()

val kspCachesDir = getKspCachesDir(project, sourceSetName, target)
fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
// depends on the processor; if the processor changes, it needs to be reprocessed.
kspTask.dependsOn(processorClasspath.buildDependencies)
kspTask.commandLineArgumentProviders.addAll(kspExtension.commandLineArgumentProviders)
kspTask.localState.register(kspCachesDir)

kspTask.options.addAll(
getSubpluginOptions(
Expand All @@ -307,6 +313,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
allWarningsAsErrors = project.provider { kspExtension.allWarningsAsErrors },
commandLineArgumentProviders = kspTask.commandLineArgumentProviders,
commonSources = project.provider { emptyList() },
cachesDir = kspCachesDir
)
)
kspTask.inputs.property("apOptions", kspExtension.apOptions)
Expand Down Expand Up @@ -467,7 +474,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
createIncrementalChangesTransformer(
isIncremental,
isIntermoduleIncremental,
getKspCachesDir(project, sourceSetName, target),
kspCachesDir.get().asFile,
project.provider { classStructureFiles },
project.provider { kspTask.libraries },
project.provider { processorClasspath }
Expand All @@ -494,7 +501,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
createIncrementalChangesTransformer(
isIncremental,
false,
getKspCachesDir(project, sourceSetName, target),
kspCachesDir.get().asFile,
project.provider { project.files() },
project.provider { project.files() },
project.provider { processorClasspath }
Expand All @@ -517,7 +524,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
createIncrementalChangesTransformer(
isIncremental,
false,
getKspCachesDir(project, sourceSetName, target),
kspCachesDir.get().asFile,
project.provider { project.files() },
project.provider { project.files() },
project.provider { processorClasspath }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.google.devtools.ksp.test

import org.gradle.testkit.runner.GradleRunner
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 BuildCacheIncrementalIT(useKSP2: Boolean) {
@Rule
@JvmField
val project: TemporaryTestProject = TemporaryTestProject("buildcache-incremental", useKSP2 = useKSP2)

// See https://github.com/google/ksp/issues/2042 for details
@Test
fun testIncrementalBuildCache() {
val buildCacheDir = File(project.root, "build-cache").absolutePath.replace(File.separatorChar, '/')
File(project.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir")

val gradleRunner = GradleRunner.create().withProjectDir(project.root)
val k1 = "workload/src/main/kotlin/p1/K1.kt"
val k2 = "workload/src/main/kotlin/p1/K2.kt"

gradleRunner.withArguments("assemble").build()

File(project.root, k2).writeText(
"package p1\n\n@MyAnnotation\nclass K2\n"
)
gradleRunner.withArguments("assemble").build()

File(project.root, k2).delete()
gradleRunner.withArguments("assemble").build()

File(project.root, k1).writeText(
"package p1\n\nclass K1(val foo: String)\n"
)
gradleRunner.withArguments("assemble").build()
}

companion object {
@JvmStatic
@Parameterized.Parameters(name = "KSP2={0}")
fun params() = listOf(arrayOf(true), arrayOf(false))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
kotlin("jvm")
}

repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.caching=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
pluginManagement {
val kotlinVersion: String by settings
val kspVersion: String by settings
val testRepo: String by settings
plugins {
id("com.google.devtools.ksp") version kspVersion
kotlin("jvm") version kotlinVersion
}
repositories {
maven(testRepo)
gradlePluginPortal()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}
}

buildCache {
val buildCacheDir: String by settings
local {
directory = File(buildCacheDir)
removeUnusedEntriesAfterDays = 30
}
}

rootProject.name = "playground"

include(":workload")
include(":test-processor")
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
val kspVersion: String by project
val testRepo: String by project

plugins {
kotlin("jvm")
}

group = "com.example"
version = "1.0-SNAPSHOT"

repositories {
maven(testRepo)
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}

dependencies {
implementation(kotlin("stdlib"))
implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
}

sourceSets.main {
java.srcDirs("src/main/kotlin")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import java.io.OutputStreamWriter

class TestBuildCacheProcessor : SymbolProcessor {
lateinit var codeGenerator: CodeGenerator
lateinit var logger: KSPLogger

fun init(
options: Map<String, String>,
kotlinVersion: KotlinVersion,
codeGenerator: CodeGenerator,
logger: KSPLogger,
) {
this.codeGenerator = codeGenerator
this.logger = logger
}

override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getSymbolsWithAnnotation("p1.MyAnnotation").forEach { decl ->
decl as KSClassDeclaration

val pkg = decl.packageName.asString()
val name = decl.simpleName.asString()
val generated = name + "Generated"
val output = codeGenerator.createNewFile(
Dependencies(false, decl.containingFile!!),
pkg, generated
)
OutputStreamWriter(output).use {
it.write("package $pkg\n\nclass $generated(val className: String = $name::class.java.simpleName)\n")
}
}
resolver.getNewFiles().forEach {
it.validate()
}
return emptyList()
}
}

class TestBuildCacheProcessorProvider : SymbolProcessorProvider {
override fun create(
env: SymbolProcessorEnvironment,
): SymbolProcessor {
return TestBuildCacheProcessor().apply {
init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TestBuildCacheProcessorProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
val testRepo: String by project

plugins {
id("com.google.devtools.ksp")
kotlin("jvm")
}

version = "1.0-SNAPSHOT"

repositories {
maven(testRepo)
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}

dependencies {
implementation(kotlin("stdlib"))
ksp(project(":test-processor"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package p1

@MyAnnotation
class K1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package p1

annotation class MyAnnotation

0 comments on commit 3f31c10

Please sign in to comment.