aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTing-Yuan Huang <laszio@google.com>2022-11-28 10:34:01 -0800
committerlaszio <ting-yuan@users.noreply.github.com>2022-11-29 11:01:29 -0800
commit1ed2d3b709adc53450eb1c09fffe33a50459cc88 (patch)
treef8ad2716ffc6946479828c97cc20be1920aeb399
parentcaa3e0843dd169dcc3486d1ac28d16489324d564 (diff)
downloadksp-1ed2d3b709adc53450eb1c09fffe33a50459cc88.tar.gz
Pass changes in incrementalChangesTransformer
-rw-r--r--gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt245
-rw-r--r--gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt240
2 files changed, 256 insertions, 229 deletions
diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt
index ada0e587..6e9e4a7b 100644
--- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt
+++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KotlinFactories.kt
@@ -21,22 +21,16 @@ package com.google.devtools.ksp.gradle
import org.gradle.api.Project
import org.gradle.api.Task
-import org.gradle.api.attributes.Attribute
-import org.gradle.api.file.ConfigurableFileCollection
-import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.CacheableTask
-import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.IgnoreEmptyDirectories
-import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
-import org.gradle.api.tasks.LocalState
import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
@@ -44,8 +38,6 @@ import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskProvider
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.process.ExecOperations
-import org.gradle.util.GradleVersion
-import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -56,11 +48,6 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompilerOptionsDefault
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsDefault
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonCompilerOptionsDefault
-import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE
-import org.jetbrains.kotlin.gradle.internal.kapt.incremental.ClasspathSnapshot
-import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChanges
-import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformAction
-import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformLegacyAction
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
@@ -74,9 +61,6 @@ import org.jetbrains.kotlin.gradle.tasks.configuration.BaseKotlin2JsCompileConfi
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileCommonConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
import org.jetbrains.kotlin.incremental.ChangedFiles
-import org.jetbrains.kotlin.incremental.isJavaFile
-import org.jetbrains.kotlin.incremental.isKotlinFile
-import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
import java.nio.file.Paths
import javax.inject.Inject
@@ -107,6 +91,9 @@ class KotlinFactories {
return project.tasks.register(taskName, KspTaskJS::class.java).also { kspTaskProvider ->
BaseKotlin2JsCompileConfig<Kotlin2JsCompile>(KotlinCompilationInfo(kotlinCompilation))
.execute(kspTaskProvider as TaskProvider<Kotlin2JsCompile>)
+ kspTaskProvider.configure {
+ it.incrementalJsKlib = false
+ }
}
}
@@ -137,8 +124,6 @@ class KotlinFactories {
}
}
-private val artifactType = Attribute.of("artifactType", String::class.java)
-
interface KspTask : Task {
@get:Internal
val options: ListProperty<SubpluginOption>
@@ -146,20 +131,8 @@ interface KspTask : Task {
@get:Nested
val commandLineArgumentProviders: ListProperty<CommandLineArgumentProvider>
- @get:OutputDirectory
- var destination: File
-
- @get:Classpath
- val processorClasspath: ConfigurableFileCollection
-
- /**
- * Output directory that contains caches necessary to support incremental annotation processing.
- */
- @get:LocalState
- val kspCacheDir: DirectoryProperty
-
- @get:Input
- var isKspIncremental: Boolean
+ @get:Internal
+ val incrementalChangesTransformers: ListProperty<(ChangedFiles) -> List<SubpluginOption>>
}
@CacheableTask
@@ -167,114 +140,20 @@ abstract class KspTaskJvm @Inject constructor(
workerExecutor: WorkerExecutor,
objectFactory: ObjectFactory
) : KotlinCompile(
- objectFactory.newInstance(KotlinJvmCompilerOptionsDefault::class.java),
- workerExecutor,
- objectFactory
-),
+ objectFactory.newInstance(KotlinJvmCompilerOptionsDefault::class.java),
+ workerExecutor,
+ objectFactory
+ ),
KspTask {
- @get:PathSensitive(PathSensitivity.NONE)
- @get:Optional
- @get:InputFiles
- @get:Incremental
- abstract val classpathStructure: ConfigurableFileCollection
-
- @get:Input
- var isIntermoduleIncremental: Boolean = false
-
- fun configureClasspathSnapshot() {
- isIntermoduleIncremental =
- (project.findProperty("ksp.incremental.intermodule")?.toString()?.toBoolean() ?: true) &&
- isKspIncremental
- if (isIntermoduleIncremental) {
- val classStructureIfIncremental = project.configurations.detachedConfiguration(
- project.dependencies.create(project.files(project.provider { libraries }))
- )
- maybeRegisterTransform(project)
-
- classpathStructure.from(
- classStructureIfIncremental.incoming.artifactView { viewConfig ->
- viewConfig.attributes.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
- }.files
- ).disallowChanges()
- }
- }
-
- private fun maybeRegisterTransform(project: Project) {
- // Use the same flag with KAPT, so as to share the same transformation in case KAPT and KSP are both enabled.
- if (!project.extensions.extraProperties.has("KaptStructureTransformAdded")) {
- val transformActionClass =
- if (GradleVersion.current() >= GradleVersion.version("5.4"))
- StructureTransformAction::class.java
- else
-
- StructureTransformLegacyAction::class.java
- project.dependencies.registerTransform(transformActionClass) { transformSpec ->
- transformSpec.from.attribute(artifactType, "jar")
- transformSpec.to.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
- }
-
- project.dependencies.registerTransform(transformActionClass) { transformSpec ->
- transformSpec.from.attribute(artifactType, "directory")
- transformSpec.to.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
- }
-
- project.extensions.extraProperties["KaptStructureTransformAdded"] = true
- }
- }
-
- // Reuse Kapt's infrastructure to compute affected names in classpath.
- // This is adapted from KaptTask.findClasspathChanges.
- private fun findClasspathChanges(
- changes: ChangedFiles,
- ): KaptClasspathChanges {
- val cacheDir = kspCacheDir.asFile.get()
- cacheDir.mkdirs()
-
- val allDataFiles = classpathStructure.files
- val changedFiles = (changes as? ChangedFiles.Known)?.let { it.modified + it.removed }?.toSet() ?: allDataFiles
-
- val loadedPrevious = ClasspathSnapshot.ClasspathSnapshotFactory.loadFrom(cacheDir)
- val previousAndCurrentDataFiles = lazy { loadedPrevious.getAllDataFiles() + allDataFiles }
- val allChangesRecognized = changedFiles.all {
- val extension = it.extension
- if (extension.isEmpty() || extension == "kt" || extension == "java" || extension == "jar" ||
- extension == "class"
- ) {
- return@all true
- }
- // if not a directory, Java source file, jar, or class, it has to be a structure file, in order to understand changes
- it in previousAndCurrentDataFiles.value
- }
- val previousSnapshot = if (allChangesRecognized) {
- loadedPrevious
- } else {
- ClasspathSnapshot.ClasspathSnapshotFactory.getEmptySnapshot()
- }
-
- val currentSnapshot =
- ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
- cacheDir,
- libraries.files.toList(),
- processorClasspath.files.toList(),
- allDataFiles
- )
-
- val classpathChanges = currentSnapshot.diff(previousSnapshot, changedFiles)
- if (classpathChanges is KaptClasspathChanges.Unknown || changes is ChangedFiles.Unknown) {
- clearIncCache()
- cacheDir.mkdirs()
- }
- currentSnapshot.writeToCache()
-
- return classpathChanges
- }
-
init {
// Mute a warning from ScriptingGradleSubplugin, which tries to get `sourceSetName` before this task is
// configured.
sourceSetName.set("main")
}
+ @get:OutputDirectory
+ abstract val destination: Property<File>
+
// Overrding an internal function is hacky.
// TODO: Ask upstream to open it.
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE")
@@ -285,43 +164,22 @@ abstract class KspTaskJvm @Inject constructor(
taskOutputsBackup: TaskOutputsBackup?
) {
val changedFiles = getChangedFiles(inputChanges, incrementalProps)
- if (isKspIncremental) {
- if (isIntermoduleIncremental) {
- // findClasspathChanges may clear caches, if there are
- // 1. unknown changes, or
- // 2. changes in annotation processors.
- val classpathChanges = findClasspathChanges(changedFiles)
- args.addChangedClasses(classpathChanges)
- } else {
- if (changedFiles.hasNonSourceChange()) {
- clearIncCache()
- }
- }
- } else {
- clearIncCache()
+ val extraOptions = incrementalChangesTransformers.get().flatMap {
+ it(changedFiles)
}
- args.addChangedFiles(changedFiles)
+ args.addPluginOptions(extraOptions)
args.allowNoSourceFiles = true
super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup)
}
override fun skipCondition(): Boolean = sources.isEmpty && javaSources.isEmpty
- override val incrementalProps: List<FileCollection>
- get() = listOf(
- sources,
- javaSources,
- commonSourceSet,
- classpathSnapshotProperties.classpath,
- classpathSnapshotProperties.classpathSnapshot
- )
-
@get:InputFiles
@get:SkipWhenEmpty
@get:IgnoreEmptyDirectories
@get:PathSensitive(PathSensitivity.RELATIVE)
override val javaSources: FileCollection = super.javaSources.filter {
- !destination.isParentOf(it)
+ !destination.get().isParentOf(it)
}
}
@@ -330,10 +188,10 @@ abstract class KspTaskJS @Inject constructor(
objectFactory: ObjectFactory,
workerExecutor: WorkerExecutor
) : Kotlin2JsCompile(
- objectFactory.newInstance(KotlinJsCompilerOptionsDefault::class.java),
- objectFactory,
- workerExecutor
-),
+ objectFactory.newInstance(KotlinJsCompilerOptionsDefault::class.java),
+ objectFactory,
+ workerExecutor
+ ),
KspTask {
// Overrding an internal function is hacky.
@@ -346,11 +204,10 @@ abstract class KspTaskJS @Inject constructor(
taskOutputsBackup: TaskOutputsBackup?
) {
val changedFiles = getChangedFiles(inputChanges, incrementalProps)
- if (!isKspIncremental || changedFiles.hasNonSourceChange()) {
- clearIncCache()
- } else {
- args.addChangedFiles(changedFiles)
+ val extraOptions = incrementalChangesTransformers.get().flatMap {
+ it(changedFiles)
}
+ args.addPluginOptions(extraOptions)
super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup)
}
}
@@ -360,10 +217,10 @@ abstract class KspTaskMetadata @Inject constructor(
workerExecutor: WorkerExecutor,
objectFactory: ObjectFactory
) : KotlinCompileCommon(
- objectFactory.newInstance(KotlinMultiplatformCommonCompilerOptionsDefault::class.java),
- workerExecutor,
- objectFactory
-),
+ objectFactory.newInstance(KotlinMultiplatformCommonCompilerOptionsDefault::class.java),
+ workerExecutor,
+ objectFactory
+ ),
KspTask {
// Overrding an internal function is hacky.
@@ -376,11 +233,10 @@ abstract class KspTaskMetadata @Inject constructor(
taskOutputsBackup: TaskOutputsBackup?
) {
val changedFiles = getChangedFiles(inputChanges, incrementalProps)
- if (!isKspIncremental || changedFiles.hasNonSourceChange()) {
- clearIncCache()
- } else {
- args.addChangedFiles(changedFiles)
+ val extraOptions = incrementalChangesTransformers.get().flatMap {
+ it(changedFiles)
}
+ args.addPluginOptions(extraOptions)
args.expectActualLinker = true
super.callCompilerAsync(args, kotlinSources, inputChanges, taskOutputsBackup)
}
@@ -398,47 +254,12 @@ abstract class KspTaskNative @Inject internal constructor(
objectFactory.newInstance(KotlinMultiplatformCommonCompilerOptionsDefault::class.java)
}
-// This forces rebuild.
-private fun KspTask.clearIncCache() {
- kspCacheDir.get().asFile.deleteRecursively()
-}
-
-private fun ChangedFiles.hasNonSourceChange(): Boolean {
- if (this !is ChangedFiles.Known)
- return true
+internal fun SubpluginOption.toArg() = "plugin:${KspGradleSubplugin.KSP_PLUGIN_ID}:$key=$value"
- return !(this.modified + this.removed).all {
- it.isKotlinFile(listOf("kt")) || it.isJavaFile()
- }
-}
-
-fun CommonCompilerArguments.addChangedClasses(changed: KaptClasspathChanges) {
- if (changed is KaptClasspathChanges.Known) {
- changed.names.map { it.replace('/', '.').replace('$', '.') }.ifNotEmpty {
- addPluginOptions(listOf(SubpluginOption("changedClasses", joinToString(":"))))
- }
- }
-}
-
-fun SubpluginOption.toArg() = "plugin:${KspGradleSubplugin.KSP_PLUGIN_ID}:$key=$value"
-
-fun CommonCompilerArguments.addPluginOptions(options: List<SubpluginOption>) {
+internal fun CommonCompilerArguments.addPluginOptions(options: List<SubpluginOption>) {
pluginOptions = (options.map { it.toArg() } + pluginOptions!!).toTypedArray()
}
-fun CommonCompilerArguments.addChangedFiles(changedFiles: ChangedFiles) {
- if (changedFiles is ChangedFiles.Known) {
- val options = mutableListOf<SubpluginOption>()
- changedFiles.modified.filter { it.isKotlinFile(listOf("kt")) || it.isJavaFile() }.ifNotEmpty {
- options += SubpluginOption("knownModified", map { it.path }.joinToString(File.pathSeparator))
- }
- changedFiles.removed.filter { it.isKotlinFile(listOf("kt")) || it.isJavaFile() }.ifNotEmpty {
- options += SubpluginOption("knownRemoved", map { it.path }.joinToString(File.pathSeparator))
- }
- options.ifNotEmpty { addPluginOptions(this) }
- }
-}
-
internal fun File.isParentOf(childCandidate: File): Boolean {
val parentPath = Paths.get(this.absolutePath).normalize()
val childCandidatePath = Paths.get(childCandidate.absolutePath).normalize()
diff --git a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
index 68258372..bb3ca97a 100644
--- a/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
+++ b/gradle-plugin/src/main/kotlin/com/google/devtools/ksp/gradle/KspSubplugin.kt
@@ -22,6 +22,9 @@ import org.gradle.api.Project
import org.gradle.api.Task
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.FileCollection
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
@@ -29,7 +32,13 @@ import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
+import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.config.ApiVersion
+import org.jetbrains.kotlin.gradle.internal.kapt.incremental.CLASS_STRUCTURE_ARTIFACT_TYPE
+import org.jetbrains.kotlin.gradle.internal.kapt.incremental.ClasspathSnapshot
+import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChanges
+import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformAction
+import org.jetbrains.kotlin.gradle.internal.kapt.incremental.StructureTransformLegacyAction
import org.jetbrains.kotlin.gradle.plugin.CompilerPluginConfig
import org.jetbrains.kotlin.gradle.plugin.FilesSubpluginOption
import org.jetbrains.kotlin.gradle.plugin.InternalSubpluginOption
@@ -50,6 +59,10 @@ import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
+import org.jetbrains.kotlin.incremental.ChangedFiles
+import org.jetbrains.kotlin.incremental.isJavaFile
+import org.jetbrains.kotlin.incremental.isKotlinFile
+import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
import java.util.concurrent.Callable
import javax.inject.Inject
@@ -218,11 +231,10 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
val kotlinCompileTask = kotlinCompileProvider.get()
+ val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
+ .extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
fun configureAsKspTask(kspTask: KspTask, isIncremental: Boolean) {
// depends on the processor; if the processor changes, it needs to be reprocessed.
- val processorClasspath = project.configurations.maybeCreate("${kspTaskName}ProcessorClasspath")
- .extendsFrom(*nonEmptyKspConfigurations.toTypedArray())
- kspTask.processorClasspath.from(processorClasspath)
kspTask.dependsOn(processorClasspath.buildDependencies)
kspTask.commandLineArgumentProviders.addAll(kspExtension.commandLineArgumentProviders)
@@ -240,11 +252,7 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
)
}
)
- kspTask.destination = kspOutputDir
kspTask.inputs.property("apOptions", kspExtension.arguments)
- kspTask.kspCacheDir.fileValue(getKspCachesDir(project, sourceSetName, target)).disallowChanges()
-
- kspTask.isKspIncremental = isIncremental
}
fun configureAsAbstractKotlinCompileTool(kspTask: AbstractKotlinCompileTool<*>) {
@@ -312,12 +320,29 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
configureAsKspTask(kspTask, isIncremental)
configureAsAbstractKotlinCompileTool(kspTask as AbstractKotlinCompileTool<*>)
configurePluginOptions(kspTask)
- kspTask.configureClasspathSnapshot()
- kspTask.libraries.setFrom(kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries }))
+ kspTask.libraries.setFrom(
+ kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries })
+ )
kspTask.compilerOptions.noJdk.value(kotlinCompileTask.compilerOptions.noJdk)
kspTask.compilerOptions.useK2.value(false)
kspTask.compilerOptions.moduleName.convention(kotlinCompileTask.moduleName.map { "$it-ksp" })
kspTask.moduleName.value(kotlinCompileTask.moduleName.get())
+ kspTask.destination.value(kspOutputDir)
+
+ val isIntermoduleIncremental =
+ (project.findProperty("ksp.incremental.intermodule")?.toString()?.toBoolean() ?: true) &&
+ isIncremental
+ val classStructureFiles = getClassStructureFiles(project, kspTask.libraries)
+ kspTask.incrementalChangesTransformers.add(
+ createIncrementalChangesTransformer(
+ isIncremental,
+ isIntermoduleIncremental,
+ getKspCachesDir(project, sourceSetName, target),
+ project.provider { classStructureFiles },
+ project.provider { kspTask.libraries },
+ project.provider { processorClasspath }
+ )
+ )
}
// Don't support binary generation for non-JVM platforms yet.
// FIXME: figure out how to add user generated libraries.
@@ -331,12 +356,24 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
configureAsKspTask(kspTask, isIncremental)
configureAsAbstractKotlinCompileTool(kspTask as AbstractKotlinCompileTool<*>)
configurePluginOptions(kspTask)
- kspTask.libraries.setFrom(kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries }))
- kspTask.compilerOptions.freeCompilerArgs.value(kotlinCompileTask.compilerOptions.freeCompilerArgs)
+ kspTask.libraries.setFrom(
+ kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries })
+ )
+ kspTask.compilerOptions.freeCompilerArgs
+ .value(kotlinCompileTask.compilerOptions.freeCompilerArgs)
kspTask.compilerOptions.useK2.value(false)
kspTask.compilerOptions.moduleName.convention(kotlinCompileTask.moduleName.map { "$it-ksp" })
- @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
- kspTask.incrementalJsKlib = false
+
+ kspTask.incrementalChangesTransformers.add(
+ createIncrementalChangesTransformer(
+ isIncremental,
+ false,
+ getKspCachesDir(project, sourceSetName, target),
+ project.provider { project.files() },
+ project.provider { project.files() },
+ project.provider { project.files() },
+ )
+ )
}
}
}
@@ -347,8 +384,21 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
configureAsKspTask(kspTask, isIncremental)
configureAsAbstractKotlinCompileTool(kspTask as AbstractKotlinCompileTool<*>)
configurePluginOptions(kspTask)
- kspTask.libraries.setFrom(kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries }))
+ kspTask.libraries.setFrom(
+ kotlinCompileTask.project.files(Callable { kotlinCompileTask.libraries })
+ )
kspTask.compilerOptions.useK2.value(false)
+
+ kspTask.incrementalChangesTransformers.add(
+ createIncrementalChangesTransformer(
+ isIncremental,
+ false,
+ getKspCachesDir(project, sourceSetName, target),
+ project.provider { project.files() },
+ project.provider { project.files() },
+ project.provider { project.files() },
+ )
+ )
}
}
}
@@ -375,7 +425,6 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
kspOutputDir.deleteRecursively()
}
}
-
}
}
else -> return project.provider { emptyList() }
@@ -427,8 +476,6 @@ class KspGradleSubplugin @Inject internal constructor(private val registry: Tool
artifactId = KSP_COMPILER_PLUGIN_ID,
version = KSP_VERSION
)
-
- val apiArtifact = "com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION"
}
// Copied from kotlin-gradle-plugin, because they are internal.
@@ -447,3 +494,162 @@ internal fun findJavaTaskForKotlinCompilation(compilation: KotlinCompilation<*>)
is KotlinJvmCompilation -> compilation.compileJavaTaskProvider // may be null for Kotlin-only JVM target in MPP
else -> null
}
+
+internal val artifactType = Attribute.of("artifactType", String::class.java)
+
+internal fun maybeRegisterTransform(project: Project) {
+ // Use the same flag with KAPT, so as to share the same transformation in case KAPT and KSP are both enabled.
+ if (!project.extensions.extraProperties.has("KaptStructureTransformAdded")) {
+ val transformActionClass =
+ if (GradleVersion.current() >= GradleVersion.version("5.4"))
+ StructureTransformAction::class.java
+ else
+
+ StructureTransformLegacyAction::class.java
+ project.dependencies.registerTransform(transformActionClass) { transformSpec ->
+ transformSpec.from.attribute(artifactType, "jar")
+ transformSpec.to.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
+ }
+
+ project.dependencies.registerTransform(transformActionClass) { transformSpec ->
+ transformSpec.from.attribute(artifactType, "directory")
+ transformSpec.to.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
+ }
+
+ project.extensions.extraProperties["KaptStructureTransformAdded"] = true
+ }
+}
+
+internal fun getClassStructureFiles(
+ project: Project,
+ libraries: ConfigurableFileCollection,
+): FileCollection {
+ maybeRegisterTransform(project)
+
+ val classStructureIfIncremental = project.configurations.detachedConfiguration(
+ project.dependencies.create(project.files(project.provider { libraries }))
+ )
+
+ return classStructureIfIncremental.incoming.artifactView { viewConfig ->
+ viewConfig.attributes.attribute(artifactType, CLASS_STRUCTURE_ARTIFACT_TYPE)
+ }.files
+}
+
+// Reuse Kapt's infrastructure to compute affected names in classpath.
+// This is adapted from KaptTask.findClasspathChanges.
+internal fun findClasspathChanges(
+ changes: ChangedFiles,
+ cacheDir: File,
+ allDataFiles: Set<File>,
+ libs: List<File>,
+ processorCP: List<File>,
+): KaptClasspathChanges {
+ cacheDir.mkdirs()
+
+ val changedFiles = (changes as? ChangedFiles.Known)?.let { it.modified + it.removed }?.toSet() ?: allDataFiles
+
+ val loadedPrevious = ClasspathSnapshot.ClasspathSnapshotFactory.loadFrom(cacheDir)
+ val previousAndCurrentDataFiles = lazy { loadedPrevious.getAllDataFiles() + allDataFiles }
+ val allChangesRecognized = changedFiles.all {
+ val extension = it.extension
+ if (extension.isEmpty() || extension == "kt" || extension == "java" || extension == "jar" ||
+ extension == "class"
+ ) {
+ return@all true
+ }
+ // if not a directory, Java source file, jar, or class, it has to be a structure file, in order to understand changes
+ it in previousAndCurrentDataFiles.value
+ }
+ val previousSnapshot = if (allChangesRecognized) {
+ loadedPrevious
+ } else {
+ ClasspathSnapshot.ClasspathSnapshotFactory.getEmptySnapshot()
+ }
+
+ val currentSnapshot =
+ ClasspathSnapshot.ClasspathSnapshotFactory.createCurrent(
+ cacheDir,
+ libs,
+ processorCP,
+ allDataFiles
+ )
+
+ val classpathChanges = currentSnapshot.diff(previousSnapshot, changedFiles)
+ if (classpathChanges is KaptClasspathChanges.Unknown || changes is ChangedFiles.Unknown) {
+ cacheDir.deleteRecursively()
+ cacheDir.mkdirs()
+ }
+ currentSnapshot.writeToCache()
+
+ return classpathChanges
+}
+
+internal fun ChangedFiles.hasNonSourceChange(): Boolean {
+ if (this !is ChangedFiles.Known)
+ return true
+
+ return !(this.modified + this.removed).all {
+ it.isKotlinFile(listOf("kt")) || it.isJavaFile()
+ }
+}
+
+fun KaptClasspathChanges.toSubpluginOptions(): List<SubpluginOption> {
+ return if (this is KaptClasspathChanges.Known) {
+ this.names.map { it.replace('/', '.').replace('$', '.') }.ifNotEmpty {
+ listOf(SubpluginOption("changedClasses", joinToString(":")))
+ } ?: emptyList()
+ } else {
+ emptyList()
+ }
+}
+
+fun ChangedFiles.toSubpluginOptions(): List<SubpluginOption> {
+ return if (this is ChangedFiles.Known) {
+ val options = mutableListOf<SubpluginOption>()
+ this.modified.filter { it.isKotlinFile(listOf("kt")) || it.isJavaFile() }.ifNotEmpty {
+ options += SubpluginOption("knownModified", map { it.path }.joinToString(File.pathSeparator))
+ }
+ this.removed.filter { it.isKotlinFile(listOf("kt")) || it.isJavaFile() }.ifNotEmpty {
+ options += SubpluginOption("knownRemoved", map { it.path }.joinToString(File.pathSeparator))
+ }
+ options
+ } else {
+ emptyList()
+ }
+}
+
+// Return a closure that captures required arguments only.
+internal fun createIncrementalChangesTransformer(
+ isKspIncremental: Boolean,
+ isIntermoduleIncremental: Boolean,
+ cacheDir: File,
+ classpathStructure: Provider<FileCollection>,
+ libraries: Provider<FileCollection>,
+ processorCP: Provider<FileCollection>,
+): (ChangedFiles) -> List<SubpluginOption> = { changedFiles ->
+ val options = mutableListOf<SubpluginOption>()
+ if (isKspIncremental) {
+ if (isIntermoduleIncremental) {
+ // findClasspathChanges may clear caches, if there are
+ // 1. unknown changes, or
+ // 2. changes in annotation processors.
+ val classpathChanges = findClasspathChanges(
+ changedFiles,
+ cacheDir,
+ classpathStructure.get().files,
+ libraries.get().files.toList(),
+ processorCP.get().files.toList()
+ )
+ options += classpathChanges.toSubpluginOptions()
+ } else {
+ if (changedFiles.hasNonSourceChange()) {
+ cacheDir.deleteRecursively()
+ }
+ }
+ } else {
+ cacheDir.deleteRecursively()
+ }
+ options += changedFiles.toSubpluginOptions()
+
+ options
+}