From 470ec8aef121e1ddf4d1643c0baff9fd561cf39c Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Tue, 19 Dec 2023 09:53:10 +0100 Subject: [PATCH] [Gradle] Fix KGP removes resource-producing task dependency Previously, we tried to sync existing Java SourceSet resources into KotlinSourceSet and then replace Java one from Kotlin. For such an approach, we called 'files' on existing resources, but this does not carry information about producing task dependency. This fix goes a little further and just replaces default KotlinSourceSet 'resources' SourceDirectorySet with a similar object from Java SourceSet, so any change to Java or Kotlin resources is reflected in both. ^KT-62490 Verification Pending --- .../jetbrains/kotlin/gradle/ResourcesIT.kt | 50 ++++++++++++++++++- .../testProject/kt-62490/build.gradle.kts | 24 +++++++++ .../gradle/plugin/AbstractKotlinPlugin.kt | 27 ++++++---- .../plugin/sources/DefaultKotlinSourceSet.kt | 4 +- .../plugin/sources/KotlinSourceSetFactory.kt | 7 ++- 5 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-62490/build.gradle.kts diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ResourcesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ResourcesIT.kt index d71928a5e64..3d5efdbf66c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ResourcesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ResourcesIT.kt @@ -60,7 +60,7 @@ class ResourcesIT : KGPBaseTest() { build("assemble") } } - + @GradleTest @DisplayName("KT-60459: should not overwrite custom resource directories") @JvmGradlePluginTests @@ -107,4 +107,52 @@ class ResourcesIT : KGPBaseTest() { } } } + + @GradleTest + @DisplayName("KT-62490: should not drop reference to task producing resources") + @JvmGradlePluginTests + fun keepReferenceToResourcesProducingTask(gradleVersion: GradleVersion) { + project("kt-62490", gradleVersion) { + build("processResources") { + assertTasksExecuted(":customResGenerator") + assertFileInProjectExists("build/custom-res/myres.txt") + } + } + } + + @GradleTest + @DisplayName("KT-62490: should keep previously configured Kotlin resources") + @JvmGradlePluginTests + fun keepConfiguredKotlinResources(gradleVersion: GradleVersion) { + project( + "kt-62490", + gradleVersion, + buildOptions = defaultBuildOptions.suppressDeprecationWarningsOn( + reason = "KT-64462: Gradle complains about already created configurations", + predicate = { gradleVersion >= GradleVersion.version(TestVersions.Gradle.G_8_2) } + ) + ) { + buildGradleKts.appendText( + //language=kotlin + """ + | + |val customResGenerator1 = tasks.register("customResGenerator1") { + | outputFile.value(project.layout.buildDirectory.dir("custom-res-1")) + |} + |project.extensions.getByType() + | .sourceSets.create("x").resources.srcDir(customResGenerator1) + |sourceSets.create("x") + """.trimMargin() + ) + + val xResFile = projectPath.resolve("src/x/resources").resolve("test.txt").toFile() + xResFile.parentFile.mkdirs() + xResFile.writeText("some resource") + + build("processXResources", enableGradleDebug = true) { + assertTasksExecuted(":customResGenerator1") + assertFileInProjectExists("build/custom-res-1/myres.txt") + } + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-62490/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-62490/build.gradle.kts new file mode 100644 index 00000000000..c3fc55965c3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kt-62490/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + `java` + id("org.jetbrains.kotlin.jvm") apply false +} + +abstract class CustomResGenerator : DefaultTask() { + @get:OutputDirectory + abstract val outputFile: DirectoryProperty + + @TaskAction + fun doGenerate() { + val dir = outputFile.get().asFile + dir.mkdirs() + dir.resolve("myres.txt").writeText("Banana!") + } +} + +val customResGenerator = tasks.register("customResGenerator") { + outputFile.value(project.layout.buildDirectory.dir("custom-res")) +} + +sourceSets.getByName("main").resources.srcDir(customResGenerator) + +plugins.apply("org.jetbrains.kotlin.jvm") diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt index 0577c481703..454aba55f38 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/AbstractKotlinPlugin.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor import org.jetbrains.kotlin.gradle.plugin.internal.MavenPluginConfigurator import org.jetbrains.kotlin.gradle.plugin.internal.compatibilityConventionRegistrar import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory import org.jetbrains.kotlin.gradle.tasks.InspectClassesForMultiModuleIC import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider import org.jetbrains.kotlin.gradle.tasks.locateTask @@ -180,19 +182,24 @@ internal abstract class AbstractKotlinPlugin( val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinCompilation.name) kotlinSourceSet.kotlin.source(javaSourceSet.java) - // Registering resources from KotlinSourceSet as Java SourceSet resources. + // Registering resources from JavaSourceSet as KotlinSourceSet resources. // In the case of KotlinPlugin Java Sources set will create ProcessResources task to process all resources into output // 'kotlinSourceSet.resources' should contain Java SourceSet default resource directories, - // and to avoid duplication error, we are replacing the already configured default one. + // and to avoid duplication error, we are replacing the already created default one. + with(kotlinSourceSet as DefaultKotlinSourceSet) { + val defaultResources = actualResources + actualResources = javaSourceSet.resources + // Filtering out default resource directory to avoid duplicates error + val defaultResourcesDir = KotlinSourceSetFactory.defaultSourceFolder( + project, + javaSourceSet.name, + javaSourceSet.resources.name + ) + resources.srcDir(defaultResources.sourceDirectories.filter { + !it.startsWith(defaultResourcesDir) + }) + } - // If KGP was applied with delay, it is possible that 'javaSourceSet.resources' may already have some additional - // configuration. So we are syncing it into 'kotlinSourceSet.resources'. - kotlinSourceSet.resources.srcDirs(javaSourceSet.resources.sourceDirectories.files) - javaSourceSet.resources.setSrcDirs( - listOf { - kotlinSourceSet.resources.sourceDirectories - } - ) @Suppress("DEPRECATION") kotlinCompilation.addSourceSet(kotlinSourceSet) project.compatibilityConventionRegistrar.addConvention(javaSourceSet, kotlinSourceSetDslName, kotlinSourceSet) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt index d67458b6d65..bc1a5292ff2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt @@ -73,7 +73,9 @@ abstract class DefaultKotlinSourceSet @Inject constructor( override val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder(project) - override val resources: SourceDirectorySet = createDefaultSourceDirectorySet(project, "$name resources") + internal var actualResources: SourceDirectorySet = createDefaultSourceDirectorySet(project, "$name resources") + + override val resources: SourceDirectorySet get() = actualResources override fun kotlin(configure: SourceDirectorySet.() -> Unit): SourceDirectorySet = kotlin.apply { configure(this) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt index 7c55c23de62..22355bb8c56 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/KotlinSourceSetFactory.kt @@ -36,7 +36,7 @@ internal abstract class KotlinSourceSetFactory internal con } protected open fun setUpSourceSetDefaults(sourceSet: T) { - sourceSet.kotlin.srcDir(defaultSourceFolder(project, sourceSet.name, "kotlin")) + sourceSet.kotlin.srcDir(defaultSourceFolder(project, sourceSet.name, SOURCE_SET_TYPE_KOTLIN)) defineSourceSetConfigurations(project, sourceSet) } @@ -68,6 +68,9 @@ internal abstract class KotlinSourceSetFactory internal con fun defaultSourceFolder(project: Project, sourceSetName: String, type: String): File { return project.file("src/$sourceSetName/$type") } + + internal const val SOURCE_SET_TYPE_RESOURCES = "resources" + internal const val SOURCE_SET_TYPE_KOTLIN = "kotlin" } } @@ -81,7 +84,7 @@ internal class DefaultKotlinSourceSetFactory( override fun setUpSourceSetDefaults(sourceSet: DefaultKotlinSourceSet) { super.setUpSourceSetDefaults(sourceSet) - sourceSet.resources.srcDir(defaultSourceFolder(project, sourceSet.name, "resources")) + sourceSet.resources.srcDir(defaultSourceFolder(project, sourceSet.name, SOURCE_SET_TYPE_RESOURCES)) val dependencyConfigurationWithMetadata = with(sourceSet) { @Suppress("DEPRECATION")