From a998e2d29119a30b51597290196ac0134c2bc5d0 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 11 Nov 2021 12:30:10 +0400 Subject: [PATCH] Model mapping experiment: map source sets to KPM fragments TODO: keep type compatibility, inject the behavior in DefaultKotlinSourceSet --- .../gradle/dsl/KotlinProjectExtension.kt | 5 + .../internal/KotlinDependenciesManagement.kt | 8 +- .../gradle/plugin/KotlinPluginWrapper.kt | 5 +- .../kotlin/gradle/plugin/KotlinProperties.kt | 3 + .../gradle/plugin/mpp/CompilationDetails.kt | 0 .../gradle/plugin/mpp/pm20/GradlePlugin.kt | 9 ++ .../mpp/pm20/KotlinGradleModuleFactory.kt | 2 +- .../plugin/mpp/pm20/fragmentFactories.kt | 0 .../mpp/pm20/util/KotlinNameDisambiguation.kt | 3 + .../plugin/sources/DefaultKotlinSourceSet.kt | 2 +- .../kpm/FragmentMappedKotlinSourceSet.kt | 111 ++++++++++++++++++ .../FragmentMappedKotlinSourceSetFactory.kt | 25 ++++ .../kpm/SourceSetMappedFragmentLocator.kt | 59 ++++++++++ .../KotlinMetadataTargetConfigurator.kt | 43 +++---- 14 files changed, 249 insertions(+), 26 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/fragmentFactories.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt index a88029b9536..f9f9e601095 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinProjectExtension.kt @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsSingleTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModuleFactory import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.targets.js.calculateJsCompilerType @@ -115,6 +117,9 @@ open class KotlinProjectExtension @Inject constructor(project: Project) : Kotlin internal set(value) { DslObject(this).extensions.add("sourceSets", value) } + + internal val kpmModules: NamedDomainObjectContainer = + project.objects.domainObjectContainer(KotlinGradleModule::class.java, KotlinGradleModuleFactory(project)) } abstract class KotlinSingleTargetExtension(project: Project) : KotlinProjectExtension(project) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt index bc5554d07d4..976707052a2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt @@ -73,8 +73,12 @@ private fun KotlinTarget.excludeStdlibAndKotlinTestCommonFromPlatformCompilation compilations.all { listOfNotNull( it.compileDependencyConfigurationName, - it.defaultSourceSet.apiMetadataConfigurationName, - it.defaultSourceSet.implementationMetadataConfigurationName, + if (!PropertiesProvider(project).experimentalKpmModelMapping) + it.defaultSourceSet.apiMetadataConfigurationName + else null, + if (!PropertiesProvider(project).experimentalKpmModelMapping) + it.defaultSourceSet.implementationMetadataConfigurationName + else null, (it as? KotlinCompilationToRunnableFiles<*>)?.runtimeDependencyConfigurationName, // Additional configurations for (old) jvmWithJava-preset. Remove it when we drop it completely diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 8cc58ba8ab3..4970684dea1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20GradlePlugin import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory +import org.jetbrains.kotlin.gradle.plugin.sources.kpm.FragmentMappedKotlinSourceSetFactory import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService import org.jetbrains.kotlin.gradle.report.HttpReportService @@ -68,7 +69,9 @@ abstract class KotlinBasePluginWrapper : Plugin { open val projectExtensionClass: KClass get() = KotlinProjectExtension::class internal open fun kotlinSourceSetFactory(project: Project): NamedDomainObjectFactory = - DefaultKotlinSourceSetFactory(project) + if (PropertiesProvider(project).experimentalKpmModelMapping) + FragmentMappedKotlinSourceSetFactory(project) + else DefaultKotlinSourceSetFactory(project) override fun apply(project: Project) { val kotlinPluginVersion = project.getKotlinPluginVersion() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt index 5fcddf59087..7c1dd27c682 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt @@ -220,6 +220,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val wasmStabilityNoWarn: Boolean get() = booleanProperty("kotlin.wasm.stability.nowarn") ?: false + val experimentalKpmModelMapping: Boolean + get() = booleanProperty("kotlin.kpm.experimentalModelMapping") ?: false + val ignoreDisabledNativeTargets: Boolean? get() = booleanProperty(DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME) 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 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/GradlePlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/GradlePlugin.kt index f83c22162ff..eab7077670c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/GradlePlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/GradlePlugin.kt @@ -14,8 +14,10 @@ import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension import org.jetbrains.kotlin.gradle.dsl.pm20Extension +import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility import org.jetbrains.kotlin.project.model.KotlinModuleIdentifier @@ -117,6 +119,13 @@ abstract class KotlinPm20GradlePlugin @Inject constructor( fun rootPublicationComponentName(module: KotlinGradleModule) = module.disambiguateName("root") +internal val Project.kpmModules: NamedDomainObjectContainer + get() = when (val ext = project.topLevelExtensionOrNull) { + is KotlinPm20ProjectExtension -> ext.modules + is KotlinProjectExtension -> ext.kpmModules + else -> error("can't find Kotlin KPM modules in $project") + } + open class KotlinPm20ProjectExtension(project: Project) : KotlinTopLevelExtension(project) { val modules: NamedDomainObjectContainer = project.objects.domainObjectContainer( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleModuleFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleModuleFactory.kt index 04a078820bf..9b495b983ac 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleModuleFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleModuleFactory.kt @@ -35,7 +35,7 @@ open class KotlinGradleModuleFactory(private val project: Project) : NamedDomain .matching { it.fragmentName == KotlinGradleFragment.COMMON_FRAGMENT_NAME } .configureEach { commonFragment -> commonFragment.dependencies { - api(module.project.pm20Extension.modules.getByName(KotlinGradleModule.MAIN_MODULE_NAME)) + api(module.project.kpmModules.getByName(KotlinGradleModule.MAIN_MODULE_NAME)) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/fragmentFactories.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/fragmentFactories.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/util/KotlinNameDisambiguation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/util/KotlinNameDisambiguation.kt index ffa3f5da85f..851b37d7d14 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/util/KotlinNameDisambiguation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/util/KotlinNameDisambiguation.kt @@ -52,6 +52,9 @@ private class DefaultKotlinFragmentNameDisambiguation( internal fun KotlinModuleFragment.disambiguateName(simpleName: String) = KotlinModuleFragment.disambiguateName(containingModule, fragmentName, simpleName) +internal val KotlinModuleFragment.unambiguousNameInProject + get() = disambiguateName("") + internal fun KotlinModuleFragment.Companion.disambiguateName(module: KotlinModule, fragmentName: String, simpleName: String) = lowerCamelCaseName(fragmentName, module.moduleIdentifier.moduleClassifier ?: KotlinGradleModule.MAIN_MODULE_NAME, simpleName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt index 0e9c459f6e2..8e6b499d61c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt @@ -249,7 +249,7 @@ internal fun KotlinSourceSet.disambiguateName(simpleName: String): String { return lowerCamelCaseName(*nameParts.toTypedArray()) } -private fun createDefaultSourceDirectorySet(project: Project, name: String?): SourceDirectorySet = +internal fun createDefaultSourceDirectorySet(project: Project, name: String?): SourceDirectorySet = project.objects.sourceDirectorySet(name!!, name) /** 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 new file mode 100644 index 00000000000..c5b128df23f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSet.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2021 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.sources.kpm + +import groovy.lang.Closure +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Project +import org.gradle.api.file.SourceDirectorySet +import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.unambiguousNameInProject +import org.jetbrains.kotlin.gradle.plugin.sources.createDefaultSourceDirectorySet + +class FragmentMappedKotlinSourceSet( + private val project: Project, + internal val underlyingFragment: KotlinGradleFragment +) : KotlinSourceSet { + val displayName: String + get() = underlyingFragment.unambiguousNameInProject + + override val apiConfigurationName: String get() = underlyingFragment.apiConfigurationName + override val implementationConfigurationName: String get() = underlyingFragment.implementationConfigurationName + override val compileOnlyConfigurationName: String get() = underlyingFragment.compileOnlyConfigurationName + override val runtimeOnlyConfigurationName: String get() = underlyingFragment.runtimeOnlyConfigurationName + + // FIXME deprecate the property in the supertype? + override val apiMetadataConfigurationName: String + get() = error("should not be used with KPM mapped model") + override val implementationMetadataConfigurationName: String + get() = error("should not be used with KPM mapped model") + override val compileOnlyMetadataConfigurationName: String + get() = error("should not be used with KPM mapped model") + override val runtimeOnlyMetadataConfigurationName: String + get() = error("should not be used with KPM mapped model") + + override val kotlin: SourceDirectorySet = underlyingFragment.kotlinSourceRoots + + override val languageSettings: LanguageSettingsBuilder = underlyingFragment.languageSettings + + // FIXME: not supported yet + override val resources: SourceDirectorySet = createDefaultSourceDirectorySet(project, "$name resources") + + override fun kotlin(configureClosure: Closure): SourceDirectorySet = + kotlin.apply { project.configure(this, configureClosure) } + + override fun languageSettings(configureClosure: Closure): LanguageSettingsBuilder = languageSettings.apply { + project.configure(this, configureClosure) + } + + override fun languageSettings(configure: LanguageSettingsBuilder.() -> Unit): LanguageSettingsBuilder = + languageSettings.apply { configure(this) } + + override fun getName(): String = displayName + + override fun dependencies(configure: KotlinDependencyHandler.() -> Unit): Unit = + underlyingFragment.dependencies(configure) + + override fun dependencies(configureClosure: Closure) = + underlyingFragment.dependencies(configureClosure) + + override fun dependsOn(other: KotlinSourceSet) { + if (other !is FragmentMappedKotlinSourceSet) { + throw InvalidUserDataException("Could set up dependsOn relationship with an unknown source set $other") + } + val otherFragment = other.underlyingFragment + underlyingFragment.refines(otherFragment) + } + + override val dependsOn: Set + get() = project.kotlinExtension.sourceSets.filter { + it is FragmentMappedKotlinSourceSet && it.underlyingFragment in underlyingFragment.directRefinesDependencies + }.toSet() + + override fun toString(): String = "source set $name" + + override val requiresVisibilityOf: Set + get() = emptySet() + + override fun requiresVisibilityOf(other: KotlinSourceSet) { + throw UnsupportedOperationException("requiresVisibilityOf is not supported for the mapped model") + } + + // region Ported with copy & paste + // FIXME move to fragment? + private val explicitlyAddedCustomSourceFilesExtensions = ArrayList() + + override val customSourceFilesExtensions: Iterable + get() = Iterable { + val fromExplicitFilters = kotlin.filter.includes.mapNotNull { pattern -> + pattern.substringAfterLast('.') + } + val merged = (fromExplicitFilters + explicitlyAddedCustomSourceFilesExtensions).filterNot { extension -> + DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS.any { extension.equals(it, ignoreCase = true) } + || extension.any { it == '\\' || it == '/' } + }.distinct() + merged.iterator() + } + + override fun addCustomSourceFilesExtensions(extensions: List) { + explicitlyAddedCustomSourceFilesExtensions.addAll(extensions) + } + //endregion +} + 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 new file mode 100644 index 00000000000..14b2aef350c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/FragmentMappedKotlinSourceSetFactory.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2021 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.sources.kpm + +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet + +class FragmentMappedKotlinSourceSetFactory constructor(private val project: Project) : + NamedDomainObjectFactory { + + override fun create(name: String): FragmentMappedKotlinSourceSet { + val locator = SourceSetMappedFragmentLocator.get(project) + val location = locator.locateFragmentForSourceSet(project, name) ?: error("Couldn't map the source set name $name to KPM fragment") + val (moduleName, fragmentName) = location + val modules = project.multiplatformExtension.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) + } +} \ 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 new file mode 100644 index 00000000000..7bfb8a97949 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/kpm/SourceSetMappedFragmentLocator.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2021 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.sources.kpm + +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 + +interface SourceSetMappedFragmentLocator { + data class FragmentLocation(val moduleName: String, val fragmentName: String) { + init { + require(moduleName.isNotEmpty()) { "module name should not be empty, got empty one and fragment name $fragmentName" } + require(fragmentName.isNotEmpty()) { "fragment name should not be empty, got empty one and module name $moduleName" } + } + } + + fun locateFragmentForSourceSet(project: Project, sourceSetName: String): FragmentLocation? + + companion object { + fun get(project: Project): SourceSetMappedFragmentLocator = when (project.topLevelExtensionOrNull) { + is KotlinMultiplatformExtension -> MultiplatformSourceSetMappedFragmentLocator() + is KotlinSingleTargetExtension -> TODO() + else -> error("couldn't provide model mapping utilities for project $project") + } + } +} + +private class MultiplatformSourceSetMappedFragmentLocator : SourceSetMappedFragmentLocator { + override fun locateFragmentForSourceSet(project: Project, sourceSetName: String): SourceSetMappedFragmentLocator.FragmentLocation? { + val camelCaseParts = sourceSetName.camelCaseParts() + if (camelCaseParts.size < 2) { + return null + } + val candidateModuleNames = (1..camelCaseParts.size).asSequence().map { camelCaseParts.takeLast(it).joinToString("").decapitalize() } + val moduleName = + candidateModuleNames.firstOrNull { project.kotlinExtension.kpmModules.findByName(it) != null } ?: candidateModuleNames.first() + val fragmentName = sourceSetName.dropLast(moduleName.length) + + return SourceSetMappedFragmentLocator.FragmentLocation(moduleName, fragmentName) + } + + 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 + return listOf(firstNonCapitalizedWord) + capitalizedMatches.map { it.value } + } +} + +private class SingleTargetSourceSetMappedFragmentLocator : SourceSetMappedFragmentLocator { + override fun locateFragmentForSourceSet(project: Project, sourceSetName: String): SourceSetMappedFragmentLocator.FragmentLocation { + return SourceSetMappedFragmentLocator.FragmentLocation(sourceSetName, SINGLE_PLATFORM_FRAGMENT_NAME) + } +} + +internal const val SINGLE_PLATFORM_FRAGMENT_NAME = "common" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index 16751b4b7ee..ae88958f826 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -358,40 +358,41 @@ class KotlinMetadataTargetConfigurator : } ) - (sourceSet as DefaultKotlinSourceSet).dependencyTransformations[scope] = granularMetadataTransformation - - val sourceSetMetadataConfigurationByScope = project.sourceSetMetadataConfigurationByScope(sourceSet, scope) - - granularMetadataTransformation.applyToConfiguration(sourceSetMetadataConfigurationByScope) + if (sourceSet is DefaultKotlinSourceSet) + sourceSet.dependencyTransformations[scope] = granularMetadataTransformation val sourceSetDependencyConfigurationByScope = project.sourceSetDependencyConfigurationByScope(sourceSet, scope) - // All source set dependencies except for compileOnly agree in versions with all other published runtime dependencies: - if (scope != KotlinDependencyScope.COMPILE_ONLY_SCOPE) { - if (isSourceSetPublished) { + if (isSourceSetPublished) { + if (scope != KotlinDependencyScope.COMPILE_ONLY_SCOPE) { project.addExtendsFromRelation( ALL_RUNTIME_METADATA_CONFIGURATION_NAME, sourceSetDependencyConfigurationByScope.name ) } - project.addExtendsFromRelation( - sourceSetMetadataConfigurationByScope.name, - ALL_RUNTIME_METADATA_CONFIGURATION_NAME - ) - } - - // All source set dependencies except for runtimeOnly agree in versions with all other published compile dependencies: - if (scope != KotlinDependencyScope.RUNTIME_ONLY_SCOPE) { - if (isSourceSetPublished) { + if (scope != KotlinDependencyScope.RUNTIME_ONLY_SCOPE) { project.addExtendsFromRelation( ALL_COMPILE_METADATA_CONFIGURATION_NAME, sourceSetDependencyConfigurationByScope.name ) } - project.addExtendsFromRelation( - sourceSetMetadataConfigurationByScope.name, - ALL_COMPILE_METADATA_CONFIGURATION_NAME - ) + } + + if (!PropertiesProvider(project).experimentalKpmModelMapping) { + val sourceSetMetadataConfigurationByScope = project.sourceSetMetadataConfigurationByScope(sourceSet, scope) + granularMetadataTransformation.applyToConfiguration(sourceSetMetadataConfigurationByScope) + if (scope != KotlinDependencyScope.COMPILE_ONLY_SCOPE) { + project.addExtendsFromRelation( + sourceSetMetadataConfigurationByScope.name, + ALL_COMPILE_METADATA_CONFIGURATION_NAME + ) + } + if (scope != KotlinDependencyScope.RUNTIME_ONLY_SCOPE) { + project.addExtendsFromRelation( + sourceSetMetadataConfigurationByScope.name, + ALL_COMPILE_METADATA_CONFIGURATION_NAME + ) + } } } }