Skip to content

Commit 3fc773f

Browse files
ansmanKSP Auto Pick
authored and
KSP Auto Pick
committed
Delete the ksp caches when the task is restored from cache
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 (cherry picked from commit 3f31c10)
1 parent a43ab02 commit 3fc773f

File tree

12 files changed

+203
-11
lines changed

12 files changed

+203
-11
lines changed

gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspAATask.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.google.devtools.ksp.processing.*
2222
import org.gradle.api.DefaultTask
2323
import org.gradle.api.artifacts.Configuration
2424
import org.gradle.api.file.ConfigurableFileCollection
25+
import org.gradle.api.file.DirectoryProperty
2526
import org.gradle.api.logging.LogLevel
2627
import org.gradle.api.provider.MapProperty
2728
import org.gradle.api.provider.Property
@@ -92,7 +93,7 @@ abstract class KspAATask @Inject constructor(
9293
kspConfig.commonSourceRoots,
9394
kspConfig.libraries
9495
),
95-
kspConfig.cachesDir.get(),
96+
kspConfig.cachesDir.asFile.get(),
9697
kspConfig.classpathStructure,
9798
kspConfig.libraries,
9899
kspConfig.processorClasspath,
@@ -102,11 +103,11 @@ abstract class KspAATask @Inject constructor(
102103
!inputChanges.isIncremental ||
103104
inputChanges.getFileChanges(kspConfig.libraries).iterator().hasNext()
104105
)
105-
kspConfig.cachesDir.get().deleteRecursively()
106+
kspConfig.cachesDir.get().asFile.deleteRecursively()
106107
emptyList()
107108
}
108109
} else {
109-
kspConfig.cachesDir.get().deleteRecursively()
110+
kspConfig.cachesDir.get().asFile.deleteRecursively()
110111
emptyList()
111112
}
112113

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

322-
@get:Internal
323-
abstract val cachesDir: Property<File>
323+
@get:LocalState
324+
abstract val cachesDir: DirectoryProperty
324325

325326
@get:OutputDirectory
326327
abstract val kotlinOutputDir: Property<File>
@@ -444,7 +445,7 @@ abstract class KspAAWorkerAction : WorkAction<KspAAWorkParameter> {
444445
libraries = gradleCfg.libraries.files.toList()
445446
projectBaseDir = gradleCfg.projectBaseDir.get()
446447
outputBaseDir = gradleCfg.outputBaseDir.get()
447-
cachesDir = gradleCfg.cachesDir.get()
448+
cachesDir = gradleCfg.cachesDir.get().asFile
448449
kotlinOutputDir = gradleCfg.kotlinOutputDir.get()
449450
classOutputDir = gradleCfg.classOutputDir.get()
450451
resourceOutputDir = gradleCfg.resourceOutputDir.get()

gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.gradle.api.UnknownTaskException
2424
import org.gradle.api.artifacts.Configuration
2525
import org.gradle.api.attributes.Attribute
2626
import org.gradle.api.file.ConfigurableFileCollection
27+
import org.gradle.api.file.Directory
2728
import org.gradle.api.file.FileCollection
2829
import org.gradle.api.provider.ListProperty
2930
import org.gradle.api.provider.Provider
@@ -97,7 +98,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
9798

9899
@JvmStatic
99100
fun getKspCachesDir(project: Project, sourceSetName: String, target: String) =
100-
File(project.project.buildDir, "kspCaches/$target/$sourceSetName")
101+
project.layout.buildDirectory.dir("kspCaches/$target/$sourceSetName")
101102

102103
@JvmStatic
103104
private fun getSubpluginOptions(
@@ -109,6 +110,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
109110
allWarningsAsErrors: Provider<Boolean>,
110111
commandLineArgumentProviders: ListProperty<CommandLineArgumentProvider>,
111112
commonSources: Provider<List<File>>,
113+
cachesDir: Provider<Directory>
112114
): Provider<List<SubpluginOption>> {
113115
val options = project.objects.listProperty(SubpluginOption::class.java)
114116
options.add(
@@ -127,7 +129,9 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
127129
)
128130
)
129131
options.add(
130-
InternalSubpluginOption("cachesDir", getKspCachesDir(project, sourceSetName, target).path)
132+
cachesDir.map {
133+
InternalSubpluginOption("cachesDir", it.asFile.path)
134+
}
131135
)
132136
options.add(
133137
InternalSubpluginOption("kspOutputDir", getKspOutputDir(project, sourceSetName, target).path)
@@ -292,10 +296,12 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
292296
val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
293297
.extendsFrom(*nonEmptyKspConfigurations.toTypedArray()).markResolvable()
294298

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

300306
kspTask.options.addAll(
301307
getSubpluginOptions(
@@ -307,6 +313,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
307313
allWarningsAsErrors = project.provider { kspExtension.allWarningsAsErrors },
308314
commandLineArgumentProviders = kspTask.commandLineArgumentProviders,
309315
commonSources = project.provider { emptyList() },
316+
cachesDir = kspCachesDir
310317
)
311318
)
312319
kspTask.inputs.property("apOptions", kspExtension.apOptions)
@@ -468,7 +475,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
468475
createIncrementalChangesTransformer(
469476
isIncremental,
470477
isIntermoduleIncremental,
471-
getKspCachesDir(project, sourceSetName, target),
478+
kspCachesDir.get().asFile,
472479
project.provider { classStructureFiles },
473480
project.provider { kspTask.libraries },
474481
project.provider { processorClasspath }
@@ -495,7 +502,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
495502
createIncrementalChangesTransformer(
496503
isIncremental,
497504
false,
498-
getKspCachesDir(project, sourceSetName, target),
505+
kspCachesDir.get().asFile,
499506
project.provider { project.files() },
500507
project.provider { project.files() },
501508
project.provider { processorClasspath }
@@ -518,7 +525,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
518525
createIncrementalChangesTransformer(
519526
isIncremental,
520527
false,
521-
getKspCachesDir(project, sourceSetName, target),
528+
kspCachesDir.get().asFile,
522529
project.provider { project.files() },
523530
project.provider { project.files() },
524531
project.provider { processorClasspath }
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.google.devtools.ksp.test
2+
3+
import org.gradle.testkit.runner.GradleRunner
4+
import org.junit.Rule
5+
import org.junit.Test
6+
import org.junit.runner.RunWith
7+
import org.junit.runners.Parameterized
8+
import java.io.File
9+
10+
@RunWith(Parameterized::class)
11+
class BuildCacheIncrementalIT(useKSP2: Boolean) {
12+
@Rule
13+
@JvmField
14+
val project: TemporaryTestProject = TemporaryTestProject("buildcache-incremental", useKSP2 = useKSP2)
15+
16+
// See https://github.com/google/ksp/issues/2042 for details
17+
@Test
18+
fun testIncrementalBuildCache() {
19+
val buildCacheDir = File(project.root, "build-cache").absolutePath.replace(File.separatorChar, '/')
20+
File(project.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir")
21+
22+
val gradleRunner = GradleRunner.create().withProjectDir(project.root)
23+
val k1 = "workload/src/main/kotlin/p1/K1.kt"
24+
val k2 = "workload/src/main/kotlin/p1/K2.kt"
25+
26+
gradleRunner.withArguments("assemble").build()
27+
28+
File(project.root, k2).writeText(
29+
"package p1\n\n@MyAnnotation\nclass K2\n"
30+
)
31+
gradleRunner.withArguments("assemble").build()
32+
33+
File(project.root, k2).delete()
34+
gradleRunner.withArguments("assemble").build()
35+
36+
File(project.root, k1).writeText(
37+
"package p1\n\nclass K1(val foo: String)\n"
38+
)
39+
gradleRunner.withArguments("assemble").build()
40+
}
41+
42+
companion object {
43+
@JvmStatic
44+
@Parameterized.Parameters(name = "KSP2={0}")
45+
fun params() = listOf(arrayOf(true), arrayOf(false))
46+
}
47+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
kotlin("jvm")
3+
}
4+
5+
repositories {
6+
mavenCentral()
7+
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.gradle.caching=true
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
pluginManagement {
2+
val kotlinVersion: String by settings
3+
val kspVersion: String by settings
4+
val testRepo: String by settings
5+
plugins {
6+
id("com.google.devtools.ksp") version kspVersion
7+
kotlin("jvm") version kotlinVersion
8+
}
9+
repositories {
10+
maven(testRepo)
11+
gradlePluginPortal()
12+
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
13+
}
14+
}
15+
16+
buildCache {
17+
val buildCacheDir: String by settings
18+
local {
19+
directory = File(buildCacheDir)
20+
removeUnusedEntriesAfterDays = 30
21+
}
22+
}
23+
24+
rootProject.name = "playground"
25+
26+
include(":workload")
27+
include(":test-processor")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
val kspVersion: String by project
2+
val testRepo: String by project
3+
4+
plugins {
5+
kotlin("jvm")
6+
}
7+
8+
group = "com.example"
9+
version = "1.0-SNAPSHOT"
10+
11+
repositories {
12+
maven(testRepo)
13+
mavenCentral()
14+
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
15+
}
16+
17+
dependencies {
18+
implementation(kotlin("stdlib"))
19+
implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
20+
}
21+
22+
sourceSets.main {
23+
java.srcDirs("src/main/kotlin")
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import com.google.devtools.ksp.processing.*
2+
import com.google.devtools.ksp.symbol.*
3+
import com.google.devtools.ksp.validate
4+
import java.io.OutputStreamWriter
5+
6+
class TestBuildCacheProcessor : SymbolProcessor {
7+
lateinit var codeGenerator: CodeGenerator
8+
lateinit var logger: KSPLogger
9+
10+
fun init(
11+
options: Map<String, String>,
12+
kotlinVersion: KotlinVersion,
13+
codeGenerator: CodeGenerator,
14+
logger: KSPLogger,
15+
) {
16+
this.codeGenerator = codeGenerator
17+
this.logger = logger
18+
}
19+
20+
override fun process(resolver: Resolver): List<KSAnnotated> {
21+
resolver.getSymbolsWithAnnotation("p1.MyAnnotation").forEach { decl ->
22+
decl as KSClassDeclaration
23+
24+
val pkg = decl.packageName.asString()
25+
val name = decl.simpleName.asString()
26+
val generated = name + "Generated"
27+
val output = codeGenerator.createNewFile(
28+
Dependencies(false, decl.containingFile!!),
29+
pkg, generated
30+
)
31+
OutputStreamWriter(output).use {
32+
it.write("package $pkg\n\nclass $generated(val className: String = $name::class.java.simpleName)\n")
33+
}
34+
}
35+
resolver.getNewFiles().forEach {
36+
it.validate()
37+
}
38+
return emptyList()
39+
}
40+
}
41+
42+
class TestBuildCacheProcessorProvider : SymbolProcessorProvider {
43+
override fun create(
44+
env: SymbolProcessorEnvironment,
45+
): SymbolProcessor {
46+
return TestBuildCacheProcessor().apply {
47+
init(env.options, env.kotlinVersion, env.codeGenerator, env.logger)
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TestBuildCacheProcessorProvider
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
val testRepo: String by project
2+
3+
plugins {
4+
id("com.google.devtools.ksp")
5+
kotlin("jvm")
6+
}
7+
8+
version = "1.0-SNAPSHOT"
9+
10+
repositories {
11+
maven(testRepo)
12+
mavenCentral()
13+
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
14+
}
15+
16+
dependencies {
17+
implementation(kotlin("stdlib"))
18+
ksp(project(":test-processor"))
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package p1
2+
3+
@MyAnnotation
4+
class K1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package p1
2+
3+
annotation class MyAnnotation

0 commit comments

Comments
 (0)