[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
This commit is contained in:
Yahor Berdnikau
2023-12-19 09:53:10 +01:00
committed by Space Team
parent 6c6f2ccacd
commit 470ec8aef1
5 changed files with 98 additions and 14 deletions
@@ -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<CustomResGenerator>("customResGenerator1") {
| outputFile.value(project.layout.buildDirectory.dir("custom-res-1"))
|}
|project.extensions.getByType<org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension>()
| .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")
}
}
}
}
@@ -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>("customResGenerator") {
outputFile.value(project.layout.buildDirectory.dir("custom-res"))
}
sourceSets.getByName("main").resources.srcDir(customResGenerator)
plugins.apply("org.jetbrains.kotlin.jvm")
@@ -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)
@@ -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)
@@ -36,7 +36,7 @@ internal abstract class KotlinSourceSetFactory<T : KotlinSourceSet> 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<T : KotlinSourceSet> 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")