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 361845ad2aa..4d066064ce3 100644 --- a/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api +++ b/libraries/tools/kotlin-gradle-plugin/api/kotlin-gradle-plugin.api @@ -911,6 +911,7 @@ public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/resources 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 publishInAndroidAssets (Lorg/jetbrains/kotlin/gradle/plugin/mpp/KotlinAndroidTarget;Lkotlin/jvm/functions/Function1;Lorg/gradle/api/provider/Provider;)V public abstract fun publishResourcesAsKotlinComponent (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;Lkotlin/jvm/functions/Function1;Lorg/gradle/api/provider/Provider;)V } 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 3802b4c2b78..00cae3debf0 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 AssetsPublishedMoreThanOncePerTarget : ToolingDiagnosticFactory(ERROR) { + operator fun invoke() = build( + """ + Only one assets publication per android target is allowed. + + $resourcesBugReportRequest + """.trimIndent() + ) + } + object ResourceMayNotBePublishedForTarget : ToolingDiagnosticFactory(ERROR) { operator fun invoke(targetName: String) = 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 03d319a5b20..11fb49ad04a 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 @@ -29,6 +29,12 @@ interface KotlinTargetResourcesPublication { relativeResourcePlacement: Provider, ) + fun publishInAndroidAssets( + target: KotlinAndroidTarget, + 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 b03229466c2..8dda3083ac7 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 @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName import org.jetbrains.kotlin.gradle.plugin.mpp.internal +import org.jetbrains.kotlin.gradle.plugin.mpp.resources.publication.KotlinAndroidTargetResourcesPublication import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.tasks.locateTask @@ -36,10 +37,14 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor private val targetsThatSupportPublication = listOf( KotlinJvmTarget::class, + KotlinAndroidTarget::class, ) private val targetToResourcesMap: MutableMap = mutableMapOf() + private val androidTargetAssetsMap: MutableMap = mutableMapOf() + private val targetResourcesSubscribers: MutableMap (Unit)>> = mutableMapOf() + private val androidTargetAssetsSubscribers: MutableMap (Unit)>> = mutableMapOf() internal fun subscribeOnPublishResources( target: KotlinTarget, @@ -49,9 +54,19 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor targetResourcesSubscribers.getOrPut(target, { mutableListOf() }).add(notify) } + internal fun subscribeOnAndroidPublishAssets( + target: KotlinAndroidTarget, + notify: (TargetResources) -> (Unit), + ) { + androidTargetAssetsMap[target]?.let(notify) + androidTargetAssetsSubscribers.getOrPut(target, { mutableListOf() }).add(notify) + } override fun canPublishResources(target: KotlinTarget): Boolean { if (targetsThatSupportPublication.none { it.isInstance(target) }) return false + if (target is KotlinAndroidTarget) { + return AndroidGradlePluginVersion.current >= KotlinAndroidTargetResourcesPublication.MIN_AGP_VERSION + } return true } @@ -78,6 +93,25 @@ internal abstract class KotlinTargetResourcesPublicationImpl @Inject constructor notify(resources) } } + + override fun publishInAndroidAssets( + target: KotlinAndroidTarget, + resourcePathForSourceSet: (KotlinSourceSet) -> (KotlinTargetResourcesPublication.ResourceRoot), + relativeResourcePlacement: Provider, + ) { + if (androidTargetAssetsMap[target] != null) { + target.project.reportDiagnostic(KotlinToolingDiagnostics.AssetsPublishedMoreThanOncePerTarget()) + } + val resources = TargetResources( + resourcePathForSourceSet = resourcePathForSourceSet, + relativeResourcePlacement = relativeResourcePlacement, + ) + androidTargetAssetsMap[target] = resources + androidTargetAssetsSubscribers[target].orEmpty().forEach { notify -> + notify(resources) + } + } + internal companion object { const val MULTIPLATFORM_RESOURCES_DIRECTORY = "kotlin-multiplatform-resources" } 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 39bd3b3524c..8721f802a5f 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 @@ -5,12 +5,14 @@ package org.jetbrains.kotlin.gradle.unitTests +import com.android.build.gradle.LibraryExtension 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.KotlinAndroidTarget 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 @@ -42,6 +44,21 @@ class KotlinTargetResourcesPublicationImplTests { ) } + @Test + fun `test assets callback - after resources publication`() { + val project = mppProjectWithAndroidTarget() + val target = project.multiplatformExtension.androidTarget() + + testCallbacksAfterApiCall( + callback = { back -> + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnAndroidPublishAssets(target) { + back(Unit) + } + }, + apiCall = { project.publishFakeAssets(target) } + ) + } + @Test fun `test publication callback - before and after resources publication`() { val project = buildProjectWithMPP { @@ -61,6 +78,21 @@ class KotlinTargetResourcesPublicationImplTests { ) } + @Test + fun `test assets callback - before and after resources publication`() { + val project = mppProjectWithAndroidTarget() + val target = project.multiplatformExtension.androidTarget() + + testCallbacksBeforeAndAfterApiCall( + callback = { back -> + project.multiplatformExtension.resourcesPublicationExtension?.subscribeOnAndroidPublishAssets(target) { + back(Unit) + } + }, + apiCall = { project.publishFakeAssets(target) } + ) + } + @Test fun `test publication - reports a diagnostic when publishing multiple times per target`() { val project = buildProjectWithMPP { @@ -76,6 +108,17 @@ class KotlinTargetResourcesPublicationImplTests { ) } + @Test + fun `test assets - reports a diagnostic when publishing multiple times per target`() { + val project = mppProjectWithAndroidTarget() + val target = project.multiplatformExtension.androidTarget() + + project.testMultipleApiCallsEmitDiagnostic( + apiCall = { project.publishFakeAssets(target) }, + diagnostic = KotlinToolingDiagnostics.AssetsPublishedMoreThanOncePerTarget, + ) + } + private fun testCallbacksAfterApiCall( callback: ((Unit) -> Unit) -> Unit, apiCall: (Unit) -> Unit, @@ -125,6 +168,17 @@ class KotlinTargetResourcesPublicationImplTests { assertContainsDiagnostic(diagnostic) } + private fun mppProjectWithAndroidTarget(): ProjectInternal { + val project = buildProjectWithMPP { + plugins.apply("com.android.library") + kotlin { + androidTarget() + } + } + (project.extensions.getByName("android") as LibraryExtension).compileSdk = 30 + return project + } + private fun Project.publishFakeResources(target: KotlinTarget) { project.multiplatformExtension.resourcesPublicationExtension?.publishResourcesAsKotlinComponent( target, @@ -139,4 +193,18 @@ class KotlinTargetResourcesPublicationImplTests { ) } + private fun Project.publishFakeAssets(target: KotlinAndroidTarget) { + project.multiplatformExtension.resourcesPublicationExtension?.publishInAndroidAssets( + target, + resourcePathForSourceSet = { + KotlinTargetResourcesPublication.ResourceRoot( + project.provider { File(it.name) }, + emptyList(), + emptyList(), + ) + }, + relativeResourcePlacement = project.provider { File("test") }, + ) + } + } \ No newline at end of file