From a81049a1804953c50671564a00b82f699b960a91 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Tue, 1 Feb 2022 01:15:36 +0400 Subject: [PATCH] Model mapping: support Android targets --- .../gradle/plugin/mpp/CompilationDetails.kt | 11 +++--- .../kpm/FragmentMappedKotlinSourceSet.kt | 3 +- .../FragmentMappedKotlinSourceSetFactory.kt | 2 +- .../kpm/SourceSetMappedFragmentLocator.kt | 34 +++++++++++++++++-- .../android/KotlinAndroidTargetPreset.kt | 14 ++++++++ .../jvm/KotlinJvmAndroidCompilation.kt | 5 ++- .../jvm/KotlinJvmAndroidCompilationFactory.kt | 8 +++-- 7 files changed, 62 insertions(+), 15 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt index deec4e8d148..1a51af8b59e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt @@ -330,7 +330,7 @@ open class DefaultCompilationDetails( } } -internal open class DefaultCompilationDetailsWithRuntime( +open class DefaultCompilationDetailsWithRuntime( target: KotlinTarget, compilationPurpose: String, createKotlinOptions: DefaultCompilationDetails<*>.() -> T @@ -517,18 +517,19 @@ internal class WithJavaCompilationDetails( } } -internal class AndroidCompilationDetails( +class AndroidCompilationDetails( target: KotlinTarget, compilationPurpose: String, + /** Workaround mutual creation order: a compilation is not added to the target's compilations collection until some point, pass it here */ + private val getCompilationInstance: () -> KotlinJvmAndroidCompilation ) : DefaultCompilationDetailsWithRuntime( target, compilationPurpose, { KotlinJvmOptionsImpl() } ) { - override val compilation: KotlinJvmAndroidCompilation - get() = super.compilation as KotlinJvmAndroidCompilation + override val compilation: KotlinJvmAndroidCompilation get() = getCompilationInstance() - val androidVariant = compilation.androidVariant + val androidVariant get() = compilation.androidVariant override val friendArtifacts: FileCollection get() = target.project.files(super.friendArtifacts, compilation.testedVariantArtifacts) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt index f8b857ff2f4..2e2fd78ea56 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt @@ -19,11 +19,12 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.unambiguousNameInProject import org.jetbrains.kotlin.gradle.plugin.sources.createDefaultSourceDirectorySet class FragmentMappedKotlinSourceSet( + private val sourceSetName: String, private val project: Project, internal val underlyingFragment: KotlinGradleFragment ) : KotlinSourceSet { val displayName: String - get() = underlyingFragment.unambiguousNameInProject + get() = sourceSetName override val apiConfigurationName: String get() = underlyingFragment.apiConfigurationName override val implementationConfigurationName: String get() = underlyingFragment.implementationConfigurationName diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt index 0c230d43046..ee4f9e5ce71 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt @@ -21,6 +21,6 @@ class FragmentMappedKotlinSourceSetFactory constructor(private val project: Proj val modules = project.kpmModules val fragment = modules.maybeCreate(moduleName).fragments.maybeCreate(fragmentName) /** TODO setup JS-specific attributes similar to [org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory]*/ - return FragmentMappedKotlinSourceSet(project, fragment) + return FragmentMappedKotlinSourceSet(name, project, fragment) } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt index c5836a3f1d5..65a4779cd89 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt @@ -9,6 +9,9 @@ import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.pm20Extension import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTargetPreset +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.kpmModules interface SourceSetMappedFragmentLocator { @@ -34,16 +37,41 @@ private class MultiplatformSourceSetMappedFragmentLocator : SourceSetMappedFragm override fun locateFragmentForSourceSet(project: Project, sourceSetName: String): SourceSetMappedFragmentLocator.FragmentLocation? { val camelCaseParts = sourceSetName.camelCaseParts() if (camelCaseParts.size < 2) { - return null + SourceSetMappedFragmentLocator.FragmentLocation(KotlinGradleModule.MAIN_MODULE_NAME, sourceSetName) } + + val androidTarget = when (val ext = project.topLevelExtension) { + is KotlinAndroidProjectExtension -> ext.target + is KotlinMultiplatformExtension -> + ext.targets.withType(KotlinAndroidTarget::class.java).singleOrNull() + ?: ext.presets.withType(KotlinAndroidTargetPreset::class.java).single().targetUnderConstruction + else -> null + } + val candidateModuleNames = (1..camelCaseParts.size).asSequence().map { camelCaseParts.takeLast(it).joinToString("").decapitalize() } + val moduleName = - candidateModuleNames.firstOrNull { project.kpmModules.findByName(it) != null } ?: candidateModuleNames.first() - val fragmentName = sourceSetName.dropLast(moduleName.length) + run { + if (androidTarget != null && sourceSetName.startsWith(androidTarget.name)) + androidSourceSetModuleName(androidTarget.name, sourceSetName) + else null + } ?: candidateModuleNames.firstOrNull { project.kpmModules.findByName(it) != null } + ?: KotlinGradleModule.MAIN_MODULE_NAME + + val fragmentName = + if (sourceSetName.endsWith(moduleName, ignoreCase = true)) sourceSetName.dropLast(moduleName.length) else sourceSetName return SourceSetMappedFragmentLocator.FragmentLocation(moduleName, fragmentName) } + private fun androidSourceSetModuleName(targetName: String, sourceSetName: String): String? { + val compilationName = sourceSetName.removePrefix(targetName).takeIf { it != sourceSetName } ?: return null + return when { + "Test" in compilationName -> "test" + else -> KotlinGradleModule.MAIN_MODULE_NAME + } + } + private fun String.camelCaseParts(): List { val capitalizedMatches = Regex("[A-Z][^A-Z]*").findAll(this).toList() val firstNonCapitalizedWord = capitalizedMatches.firstOrNull()?.let { substring(it.range.first) } ?: this diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt index 367c32d0937..7eed73ef8ff 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt @@ -9,6 +9,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPlugin.Companion.dynamicallyApplyWhenAndroidPluginIsApplied import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.PublicationRegistrationMode +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.mapTargetCompilationsToKpmVariants class KotlinAndroidTargetPreset( private val project: Project @@ -20,12 +23,23 @@ class KotlinAndroidTargetPreset( val result = KotlinAndroidTarget(name, project).apply { disambiguationClassifier = name preset = this@KotlinAndroidTargetPreset + targetUnderConstruction = this } project.dynamicallyApplyWhenAndroidPluginIsApplied({ result }) + + if (project.hasKpmModel) { + mapTargetCompilationsToKpmVariants(result, PublicationRegistrationMode.AFTER_EVALUATE) + } + + targetUnderConstruction = null + return result } + /** This is a way to check if there's an Android target being configured now despite it not being added to the `kotlin.targets` yet */ + internal var targetUnderConstruction: KotlinAndroidTarget? = null + companion object { const val PRESET_NAME = "android" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilation.kt index ebba65e4db6..9d34a4cad14 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilation.kt @@ -18,9 +18,8 @@ import org.jetbrains.kotlin.gradle.plugin.getJavaTaskProvider import org.jetbrains.kotlin.gradle.plugin.getTestedVariantData class KotlinJvmAndroidCompilation( - target: KotlinAndroidTarget, - name: String -) : AbstractKotlinCompilationToRunnableFiles(AndroidCompilationDetails(target, name)) { + compilationDetails: AndroidCompilationDetails +) : AbstractKotlinCompilationToRunnableFiles(compilationDetails) { lateinit var androidVariant: BaseVariant internal set diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilationFactory.kt index 0b570da9b65..4cf1c486f0c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmAndroidCompilationFactory.kt @@ -16,6 +16,10 @@ class KotlinJvmAndroidCompilationFactory( override val itemClass: Class get() = KotlinJvmAndroidCompilation::class.java - override fun create(name: String): KotlinJvmAndroidCompilation = - KotlinJvmAndroidCompilation(target, name) + override fun create(name: String): KotlinJvmAndroidCompilation { + lateinit var result: KotlinJvmAndroidCompilation + val details = AndroidCompilationDetails(target, name) { result } + result = KotlinJvmAndroidCompilation(details) + return result + } } \ No newline at end of file