diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index ebc24f0297d..99a46cb2018 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -13,12 +13,6 @@ import org.gradle.api.attributes.AttributeContainer import org.gradle.api.internal.FeaturePreviews import org.gradle.api.internal.plugins.DslObject import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.api.provider.Provider -import org.gradle.api.publish.PublicationContainer -import org.gradle.api.publish.PublishingExtension -import org.gradle.api.publish.maven.MavenPom -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.publish.maven.internal.publication.MavenPublicationInternal import org.gradle.api.tasks.SourceTask import org.gradle.api.tasks.TaskProvider import org.gradle.jvm.tasks.Jar @@ -33,15 +27,16 @@ import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName import org.jetbrains.kotlin.gradle.plugin.sources.* import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder -import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope -import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset -import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.* +import org.jetbrains.kotlin.gradle.utils.SingleActionPerBuild +import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild +import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.target.presetName @@ -113,8 +108,20 @@ class KotlinMultiplatformPlugin( exportProjectStructureMetadataForOtherBuilds(project) SingleActionPerBuild.run(project.rootProject, "cleanup-processed-metadata") { - project.gradle.buildFinished { - SourceSetMetadataStorageForIde.cleanupStaleEntries(project) + if (isConfigurationCacheAvailable(project.gradle)) { + BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry!!.onTaskCompletion( + project.gradle.sharedServices + .registerIfAbsent( + "cleanup-stale-sourceset-metadata", + CleanupStaleSourceSetMetadataEntriesService::class.java + ) { + CleanupStaleSourceSetMetadataEntriesService.configure(it, project) + } + ) + } else { + project.gradle.buildFinished { + SourceSetMetadataStorageForIde.cleanupStaleEntries(project) + } } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt index a1380cd09c3..67c15bc3f5e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt @@ -6,20 +6,31 @@ package org.jetbrains.kotlin.gradle.plugin.sources import org.gradle.api.Project +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters +import org.gradle.api.services.BuildServiceSpec +import org.gradle.tooling.events.FinishEvent +import org.gradle.tooling.events.OperationCompletionListener import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import java.io.File object SourceSetMetadataStorageForIde { fun cleanupStaleEntries(project: Project) { - val projectStorageDirectories = project.rootProject.allprojects.associateBy { projectStorage(it) } - getStorageRoot(project).listFiles().orEmpty().filter { it.isDirectory }.forEach { directory -> + val projectStorageDirectories = project.rootProject.allprojects + .associate { projectStorage(it) to it.multiplatformExtensionOrNull?.sourceSets.orEmpty().map { it.name } } + cleanupStaleEntries(getStorageRoot(project), projectStorageDirectories) + } + + fun cleanupStaleEntries(projectStorageRoot: File, projectStorageDirectories: Map>) { + projectStorageRoot.listFiles().orEmpty().filter { it.isDirectory }.forEach { directory -> // If no project corresponds to the directory, remove the directory if (directory !in projectStorageDirectories) { directory.deleteRecursively() } else { // Under the project's directory, delete subdirectories that don't correspond to any source set: - val sourceSets = projectStorageDirectories.getValue(directory)?.project?.multiplatformExtensionOrNull?.sourceSets.orEmpty() - val sourceSetNames = sourceSets.map { it.name } + val sourceSetNames = projectStorageDirectories.getValue(directory) directory.listFiles().orEmpty().filter { it.isDirectory }.forEach { subdirectory -> if (subdirectory.name !in sourceSetNames) subdirectory.deleteRecursively() @@ -28,9 +39,9 @@ object SourceSetMetadataStorageForIde { } } - private fun getStorageRoot(project: Project): File = project.rootDir.resolve(".gradle/kotlin/sourceSetMetadata") + internal fun getStorageRoot(project: Project): File = project.rootDir.resolve(".gradle/kotlin/sourceSetMetadata") - private fun projectStorage(project: Project): File { + internal fun projectStorage(project: Project): File { val projectPathSegments = generateSequence(project) { it.parent }.map { it.name } return getStorageRoot(project).resolve( // Escape dots in project names to avoid ambiguous paths. @@ -42,4 +53,28 @@ object SourceSetMetadataStorageForIde { internal fun sourceSetStorageWithScope(project: Project, sourceSetName: String, scope: KotlinDependencyScope) = sourceSetStorage(project, sourceSetName).resolve(scope.scopeName) +} + +abstract class CleanupStaleSourceSetMetadataEntriesService : BuildService, AutoCloseable, OperationCompletionListener { + interface Parameters : BuildServiceParameters { + val projectStorageRoot: Property + val projectStorageDirectories: MapProperty> + } + + override fun onFinish(event: FinishEvent?) { + // noop + } + + override fun close() { + SourceSetMetadataStorageForIde.cleanupStaleEntries(parameters.projectStorageRoot.get(), parameters.projectStorageDirectories.get()) + } + + companion object { + fun configure(spec: BuildServiceSpec, project: Project) { + spec.parameters.projectStorageRoot.set(SourceSetMetadataStorageForIde.getStorageRoot(project)) + spec.parameters.projectStorageDirectories.set(project.rootProject.allprojects.associate { + SourceSetMetadataStorageForIde.projectStorage(it) to it.multiplatformExtensionOrNull?.sourceSets.orEmpty().map { it.name } + }) + } + } } \ No newline at end of file