diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt index d6ee511fc42..c9027e84cfb 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/HierarchicalMppIT.kt @@ -103,6 +103,14 @@ class HierarchicalMppIT : BaseGradleIT() { assertTrue("$it") { it.newVisibleSourceSets == emptySet() } } } + + // ALso check that the files produced by dependency transformations survive a clean build: + val existingFilesFromReports = reports.flatMap { it.useFiles }.filter { it.isFile } + assertTrue { existingFilesFromReports.isNotEmpty() } + build("clean") { + assertSuccessful() + existingFilesFromReports.forEach { assertTrue("Expected that $it exists after clean build.") { it.isFile } } + } } // --- Move the dependency from jvmAndJsMain to commonMain, expect that it is now propagated to commonTest: @@ -582,7 +590,8 @@ class HierarchicalMppIT : BaseGradleIT() { scope, it.groupId + ":" + it.moduleName, it.allVisibleSourceSets.joinToString(","), - it.useFilesForSourceSets.keys.joinToString(",") + it.useFilesForSourceSets.keys.joinToString(","), + it.useFilesForSourceSets.values.flatten().joinToString(",") ) println(" " + line.joinToString(" :: ")) @@ -613,7 +622,8 @@ class HierarchicalMppIT : BaseGradleIT() { val scope: String, val groupAndModule: String, val allVisibleSourceSets: Set, - val newVisibleSourceSets: Set // those which the dependsOn parents don't see + val newVisibleSourceSets: Set, // those which the dependsOn parents don't see + val useFiles: List ) { val isExcluded: Boolean get() = allVisibleSourceSets.isEmpty() @@ -622,14 +632,17 @@ class HierarchicalMppIT : BaseGradleIT() { const val TEST_OUTPUT_COMPONENT_SEPARATOR = " :: " const val TEST_OUTPUT_ITEMS_SEPARATOR = "," + private operator fun List.component6() = this[5] + fun parseTestOutputLine(line: String): DependencyTransformationReport { val tail = line.substringAfter(TEST_OUTPUT_MARKER + TEST_OUTPUT_COMPONENT_SEPARATOR) - val (sourceSetName, scope, groupAndModule, allVisibleSourceSets, newVisibleSourceSets) = + val (sourceSetName, scope, groupAndModule, allVisibleSourceSets, newVisibleSourceSets, useFiles) = tail.split(TEST_OUTPUT_COMPONENT_SEPARATOR) return DependencyTransformationReport( sourceSetName, scope, groupAndModule, allVisibleSourceSets.split(TEST_OUTPUT_ITEMS_SEPARATOR).filter { it.isNotEmpty() }.toSet(), - newVisibleSourceSets.split(TEST_OUTPUT_ITEMS_SEPARATOR).filter { it.isNotEmpty() }.toSet() + newVisibleSourceSets.split(TEST_OUTPUT_ITEMS_SEPARATOR).filter { it.isNotEmpty() }.toSet(), + useFiles.split(TEST_OUTPUT_ITEMS_SEPARATOR).map { File(it) } ) } } 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 5d7ee49a12a..8117bf158fd 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 @@ -29,18 +29,16 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies 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.checkSourceSetVisibilityRequirements 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.locateOrRegisterTask import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask -import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget.* @@ -111,6 +109,12 @@ class KotlinMultiplatformPlugin( project.pluginManager.apply(ScriptingGradleSubplugin::class.java) exportProjectStructureMetadataForOtherBuilds(project) + + SingleActionPerBuild.run(project.rootProject, "cleanup-processed-metadata") { + project.gradle.buildFinished { + SourceSetMetadataStorageForIde.cleanupStaleEntries(project) + } + } } private fun exportProjectStructureMetadataForOtherBuilds( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt index 91196ed071d..734555a765d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt @@ -155,7 +155,8 @@ class DefaultKotlinSourceSet( ?.associateBy { ModuleIds.fromComponent(project, it.dependency) } ?: emptyMap() - val baseDir = project.buildDir.resolve("tmp/kotlinMetadata/$name/${scope.scopeName}") + val baseDir = SourceSetMetadataStorageForIde.sourceSetStorage(project, this@DefaultKotlinSourceSet.name) + if (metadataDependencyResolutionByModule.values.any { it is MetadataDependencyResolution.ChooseVisibleSourceSets }) { if (baseDir.isDirectory) { baseDir.deleteRecursively() 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 new file mode 100644 index 00000000000..922ad99e095 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetMetadataStorageForIde.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.sources + +import org.gradle.api.Project +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 -> + // 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 } + directory.listFiles().orEmpty().filter { it.isDirectory }.forEach { subdirectory -> + if (subdirectory.name !in sourceSetNames) + subdirectory.deleteRecursively() + } + } + } + } + + private fun getStorageRoot(project: Project): File = project.rootDir.resolve(".gradle/kotlin/sourceSetMetadata") + + private 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. + projectPathSegments.joinToString(".") { it.replace(".", "_.") } + ) + } + + fun sourceSetStorage(project: Project, sourceSetName: String) = projectStorage(project).resolve(sourceSetName) +} \ No newline at end of file