diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/publication/KotlinJvmTargetResourcesPublication.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/publication/KotlinJvmTargetResourcesPublication.kt new file mode 100644 index 00000000000..4b7b0c72094 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/resources/publication/KotlinJvmTargetResourcesPublication.kt @@ -0,0 +1,38 @@ +/* + * 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.plugin.mpp.resources.publication + +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinProjectSetupAction +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.assembleHierarchicalResources +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget + + +internal val SetUpMultiplatformJvmResourcesPublicationAction = KotlinProjectSetupAction { + if (!kotlinPropertiesProvider.mppResourcesPublication) return@KotlinProjectSetupAction + + multiplatformExtension.targets.all { target -> + if (target is KotlinJvmTarget) { + target.setUpMultiplatformResourcesAndAssets() + } + } +} + +private fun KotlinJvmTarget.setUpMultiplatformResourcesAndAssets() { + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources( + this + ) { resources -> + val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) + val copyResourcesTask = mainCompilation.assembleHierarchicalResources( + targetName, + resources, + ) + mainCompilation.defaultSourceSet.resources.srcDir(copyResourcesTask) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt index fc7e583f925..94121f9685d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/registerKotlinPluginExtensions.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionSetupAction import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.* import org.jetbrains.kotlin.gradle.plugin.mpp.internal.DeprecatedMppGradlePropertiesMigrationSetupAction import org.jetbrains.kotlin.gradle.plugin.mpp.resources.RegisterMultiplatformResourcesPublicationExtensionAction +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.SetUpMultiplatformJvmResourcesPublicationAction import org.jetbrains.kotlin.gradle.plugin.sources.KotlinMultiplatformSourceSetSetupAction import org.jetbrains.kotlin.gradle.plugin.sources.LanguageSettingsSetupAction import org.jetbrains.kotlin.gradle.plugin.statistics.MultiplatformBuildStatsReportSetupAction @@ -82,6 +83,7 @@ internal fun Project.registerKotlinPluginExtensions() { register(project, ExcludeDefaultPlatformDependenciesFromKotlinNativeCompileTasks) register(project, SetupConsistentMetadataDependenciesResolution) register(project, RegisterMultiplatformResourcesPublicationExtensionAction) + register(project, SetUpMultiplatformJvmResourcesPublicationAction) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinJvmTargetResourcesPublicationTests.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinJvmTargetResourcesPublicationTests.kt new file mode 100644 index 00000000000..bb4a84ea581 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KotlinJvmTargetResourcesPublicationTests.kt @@ -0,0 +1,84 @@ +/* + * 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.gradle.api.file.FileCollection +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.KotlinTargetResourcesPublication +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.resourcesPublicationExtension +import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP +import org.jetbrains.kotlin.gradle.util.kotlin +import org.junit.Test +import java.io.File + +class KotlinJvmTargetResourcesPublicationTests { + + private val Project.expectedResourcePath get() = layout.buildDirectory.dir( + "kotlin-multiplatform-resources/assemble-hierarchically/jvm" + ).get().asFile + + @Test + fun `test publishing jvm resources - reflects in jvm resource directories`() { + val project = buildProjectWithMPP { + kotlin { + jvm() + } + } + + project.evaluate() + project.publishFakeResources( + project.multiplatformExtension.jvm() + ) + + val actualSourcesDirectories = project.multiplatformExtension.jvm() + .compilations.getByName("main") + .defaultSourceSet.resources.sourceDirectories + + assert( + actualSourcesDirectories.contains(project.expectedResourcePath) + ) { actualSourcesDirectories.dumpPaths() } + } + + @Test + fun `test publishing jvm resources - doesn't show up in resources - when there is no publication`() { + val project = buildProjectWithMPP { + kotlin { + jvm() + } + } + + project.evaluate() + + val actualSourcesDirectories = project.multiplatformExtension.jvm() + .compilations.getByName("main") + .defaultSourceSet.resources.sourceDirectories + + assert( + !actualSourcesDirectories.contains(project.expectedResourcePath) + ) { actualSourcesDirectories.dumpPaths() } + } + + 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") }, + ) + } + + private fun FileCollection.dumpPaths(): String { + return "Actual paths:" + if (isEmpty) " " else joinToString("") { "\n${it.path}" } + } + +} \ 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 index f3dbab8bd2f..39bd3b3524c 100644 --- 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 @@ -6,12 +6,15 @@ package org.jetbrains.kotlin.gradle.unitTests import org.gradle.api.Project +import org.gradle.api.internal.project.ProjectInternal 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.diagnostics.ToolingDiagnosticFactory 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.assertNoDiagnostics import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP import org.jetbrains.kotlin.gradle.util.kotlin import org.junit.Test @@ -29,21 +32,14 @@ class KotlinTargetResourcesPublicationImplTests { } 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) + testCallbacksAfterApiCall( + callback = { back -> + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources(target) { + back(Unit) + } + }, + apiCall = { project.publishFakeResources(target) } + ) } @Test @@ -55,21 +51,14 @@ class KotlinTargetResourcesPublicationImplTests { } 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) + testCallbacksBeforeAndAfterApiCall( + callback = { back -> + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnPublishResources(target) { + back(Unit) + } + }, + apiCall = { project.publishFakeResources(target) } + ) } @Test @@ -81,10 +70,59 @@ class KotlinTargetResourcesPublicationImplTests { } val target = project.multiplatformExtension.jvm() - project.publishFakeResources(target) - project.publishFakeResources(target) + project.testMultipleApiCallsEmitDiagnostic( + apiCall = { project.publishFakeResources(target) }, + diagnostic = KotlinToolingDiagnostics.ResourcePublishedMoreThanOncePerTarget, + ) + } - project.assertContainsDiagnostic(KotlinToolingDiagnostics.ResourcePublishedMoreThanOncePerTarget) + private fun testCallbacksAfterApiCall( + callback: ((Unit) -> Unit) -> Unit, + apiCall: (Unit) -> Unit, + ) { + var callbacks = 0 + + apiCall.invoke(Unit) + callback { + callbacks += 1 + } + assertEquals(callbacks, 1) + + callback { + callbacks += 1 + } + assertEquals(callbacks, 2) + } + + private fun testCallbacksBeforeAndAfterApiCall( + callback: ((Unit) -> Unit) -> Unit, + apiCall: (Unit) -> Unit, + ) { + var callbacks = 0 + + callback { + callbacks += 1 + } + assertEquals(callbacks, 0) + + apiCall.invoke(Unit) + assertEquals(callbacks, 1) + + callback { + callbacks += 1 + } + assertEquals(callbacks, 2) + } + + private fun ProjectInternal.testMultipleApiCallsEmitDiagnostic( + apiCall: (Unit) -> Unit, + diagnostic: ToolingDiagnosticFactory, + ) { + assertNoDiagnostics(diagnostic) + apiCall(Unit) + assertNoDiagnostics(diagnostic) + apiCall(Unit) + assertContainsDiagnostic(diagnostic) } private fun Project.publishFakeResources(target: KotlinTarget) {