Add an API for KotlinAndroidTarget to publish assets

^KT-65540
This commit is contained in:
Timofey Solonin
2024-02-26 17:56:15 +01:00
committed by Space Team
parent bf6a82fdc1
commit 43a4fd5ddf
5 changed files with 119 additions and 0 deletions
@@ -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
}
@@ -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(
"""
@@ -29,6 +29,12 @@ interface KotlinTargetResourcesPublication {
relativeResourcePlacement: Provider<File>,
)
fun publishInAndroidAssets(
target: KotlinAndroidTarget,
resourcePathForSourceSet: (KotlinSourceSet) -> (ResourceRoot),
relativeResourcePlacement: Provider<File>,
)
companion object {
const val EXTENSION_NAME = "multiplatformResourcesPublication"
}
@@ -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<KotlinTarget, TargetResources> = mutableMapOf()
private val androidTargetAssetsMap: MutableMap<KotlinAndroidTarget, TargetResources> = mutableMapOf()
private val targetResourcesSubscribers: MutableMap<KotlinTarget, MutableList<(TargetResources) -> (Unit)>> = mutableMapOf()
private val androidTargetAssetsSubscribers: MutableMap<KotlinAndroidTarget, MutableList<(TargetResources) -> (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<File>,
) {
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"
}
@@ -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") },
)
}
}