diff --git a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api index f0c953046da..361845ad2aa 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -910,6 +910,8 @@ public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotli public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication { public static final field Companion Lorg/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication$Companion; public static final field EXTENSION_NAME Ljava/lang/String; + public abstract fun canPublishResources (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;)Z + public abstract fun publishResourcesAsKotlinComponent (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;Lkotlin/jvm/functions/Function1;Lorg/gradle/api/provider/Provider;)V } public abstract class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index 9c391e804e8..4dc88c2dc9e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -776,6 +776,16 @@ object KotlinToolingDiagnostics { ) } + object ResourceMayNotBePublishedForTarget : ToolingDiagnosticFactory(ERROR) { + operator fun invoke(targetName: String) = build( + """ + Resources publication for target $targetName is not supported yet. + + $resourcesBugReportRequest + """.trimIndent() + ) + } + object DependencyDoesNotPhysicallyExist : ToolingDiagnosticFactory(WARNING) { operator fun invoke(dependency: File) = build( """ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication.kt index eb98edce6bd..03d319a5b20 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublication.kt @@ -21,6 +21,14 @@ interface KotlinTargetResourcesPublication { val excludes: List, ) + fun canPublishResources(target: KotlinTarget): Boolean + + fun publishResourcesAsKotlinComponent( + target: KotlinTarget, + resourcePathForSourceSet: (KotlinSourceSet) -> (ResourceRoot), + relativeResourcePlacement: Provider, + ) + companion object { const val EXTENSION_NAME = "multiplatformResourcesPublication" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublicationImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublicationImpl.kt index 09da9452fd2..b03229466c2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublicationImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/KotlinTargetResourcesPublicationImpl.kt @@ -11,6 +11,8 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic import org.jetbrains.kotlin.gradle.plugin.launchInStage import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget @@ -32,8 +34,11 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor val relativeResourcePlacement: Provider, ) - private val targetToResourcesMap: MutableMap = mutableMapOf() + private val targetsThatSupportPublication = listOf( + KotlinJvmTarget::class, + ) + private val targetToResourcesMap: MutableMap = mutableMapOf() private val targetResourcesSubscribers: MutableMap (Unit)>> = mutableMapOf() internal fun subscribeOnPublishResources( @@ -45,8 +50,36 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor } + override fun canPublishResources(target: KotlinTarget): Boolean { + if (targetsThatSupportPublication.none { it.isInstance(target) }) return false + return true + } + + override fun publishResourcesAsKotlinComponent( + target: KotlinTarget, + resourcePathForSourceSet: (KotlinSourceSet) -> (KotlinTargetResourcesPublication.ResourceRoot), + relativeResourcePlacement: Provider, + ) { + if (!canPublishResources(target)) { + target.project.reportDiagnostic(KotlinToolingDiagnostics.ResourceMayNotBePublishedForTarget(target.name)) + return + } + if (targetToResourcesMap[target] != null) { + target.project.reportDiagnostic(KotlinToolingDiagnostics.ResourcePublishedMoreThanOncePerTarget(target.name)) + return + } + + val resources = TargetResources( + resourcePathForSourceSet = resourcePathForSourceSet, + relativeResourcePlacement = relativeResourcePlacement, + ) + targetToResourcesMap[target] = resources + targetResourcesSubscribers[target].orEmpty().forEach { notify -> + notify(resources) + } + } internal companion object { const val MULTIPLATFORM_RESOURCES_DIRECTORY = "kotlin-multiplatform-resources" } -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinTargetResourcesPublicationImplTests.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinTargetResourcesPublicationImplTests.kt new file mode 100644 index 00000000000..f3dbab8bd2f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinTargetResourcesPublicationImplTests.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2024 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.unitTests + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublication +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension +import org.jetbrains.kotlin.gradle.util.assertContainsDiagnostic +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.kotlin +import org.junit.Test +import java.io.File +import kotlin.test.assertEquals + +class KotlinTargetResourcesPublicationImplTests { + + @Test + fun `test publication callback - after resources publication`() { + val project = buildProjectWithMPP { + kotlin { + jvm() + } + } + val target = project.multiplatformExtension.jvm() + + var callbacks = 0 + project.publishFakeResources(target) + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources( + target + ) { + callbacks += 1 + } + assertEquals(callbacks, 1) + + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources( + target + ) { + callbacks += 1 + } + assertEquals(callbacks, 2) + } + + @Test + fun `test publication callback - before and after resources publication`() { + val project = buildProjectWithMPP { + kotlin { + jvm() + } + } + val target = project.multiplatformExtension.jvm() + + var callbacks = 0 + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources( + target + ) { + callbacks += 1 + } + project.publishFakeResources(target) + assertEquals(callbacks, 1) + + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources( + target + ) { + callbacks += 1 + } + assertEquals(callbacks, 2) + } + + @Test + fun `test publication - reports a diagnostic when publishing multiple times per target`() { + val project = buildProjectWithMPP { + kotlin { + jvm() + } + } + val target = project.multiplatformExtension.jvm() + + project.publishFakeResources(target) + project.publishFakeResources(target) + + project.assertContainsDiagnostic(KotlinToolingDiagnostics.ResourcePublishedMoreThanOncePerTarget) + } + + private fun Project.publishFakeResources(target: KotlinTarget) { + project.multiplatformExtension.resourcesPublicationExtension?.publishResourcesAsKotlinComponent( + target, + resourcePathForSourceSet = { + KotlinTargetResourcesPublication.ResourceRoot( + project.provider { File(it.name) }, + emptyList(), + emptyList(), + ) + }, + relativeResourcePlacement = project.provider { File("test") }, + ) + } + +} \ No newline at end of file