Model mapping: support Android targets

This commit is contained in:
Sergey Igushkin
2022-02-01 01:15:36 +04:00
committed by Space
parent bfbefed331
commit a81049a180
7 changed files with 62 additions and 15 deletions
@@ -330,7 +330,7 @@ open class DefaultCompilationDetails<T : KotlinCommonOptions>(
}
}
internal open class DefaultCompilationDetailsWithRuntime<T : KotlinCommonOptions>(
open class DefaultCompilationDetailsWithRuntime<T : KotlinCommonOptions>(
target: KotlinTarget,
compilationPurpose: String,
createKotlinOptions: DefaultCompilationDetails<*>.() -> T
@@ -517,18 +517,19 @@ internal class WithJavaCompilationDetails<T : KotlinCommonOptions>(
}
}
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<KotlinJvmOptions>(
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)
@@ -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
@@ -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)
}
}
@@ -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<String> {
val capitalizedMatches = Regex("[A-Z][^A-Z]*").findAll(this).toList()
val firstNonCapitalizedWord = capitalizedMatches.firstOrNull()?.let { substring(it.range.first) } ?: this
@@ -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"
}
@@ -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<KotlinJvmOptions>(AndroidCompilationDetails(target, name)) {
compilationDetails: AndroidCompilationDetails
) : AbstractKotlinCompilationToRunnableFiles<KotlinJvmOptions>(compilationDetails) {
lateinit var androidVariant: BaseVariant
internal set
@@ -16,6 +16,10 @@ class KotlinJvmAndroidCompilationFactory(
override val itemClass: Class<KotlinJvmAndroidCompilation>
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
}
}