diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/kpm/MultiplatformSourceSetMappedFragmentLocatorTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/kpm/MultiplatformSourceSetMappedFragmentLocatorTest.kt new file mode 100644 index 00000000000..01db04ac2a2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/kpm/MultiplatformSourceSetMappedFragmentLocatorTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2010-2022 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. + */ + +/* Associate compilations are not yet supported by the IDE. KT-34102 */ +@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode") + +package org.jetbrains.kotlin.gradle.sources.kpm + +import org.jetbrains.kotlin.gradle.MultiplatformExtensionTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import org.jetbrains.kotlin.gradle.plugin.sources.kpm.MultiplatformSourceSetMappedFragmentLocator +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.kpmModules +import org.jetbrains.kotlin.gradle.plugin.sources.kpm.SourceSetMappedFragmentLocator + +internal open class MultiplatformSourceSetMappedFragmentLocatorTest : MultiplatformExtensionTest() { + + protected val locator = MultiplatformSourceSetMappedFragmentLocator() + + @BeforeTest + override fun setup() { + project.extensions.extraProperties.set(PropertiesProvider.PropertyNames.KOTLIN_KPM_EXPERIMENTAL_MODEL_MAPPING, "true") + super.setup() + } + + protected fun doTest(sourceSetName: String, checkResult: SourceSetMappedFragmentLocator.FragmentLocation.() -> Unit) { + val result = checkNotNull(locator.locateFragmentForSourceSet(project, sourceSetName)) + checkResult(result) + } + + protected fun doTest(sourceSetName: String, fragmentName: String, moduleName: String) { + doTest(sourceSetName) { + assertEquals(fragmentName, this.fragmentName) + assertEquals(moduleName, this.moduleName) + } + } + + @Test + fun `name with one part is returned as fragment name with main module`() { + doTest("foo", "foo", KotlinGradleModule.MAIN_MODULE_NAME) + } + + @Test + fun `main is truncated from name`() { + doTest("fooBarMain", "fooBar", KotlinGradleModule.MAIN_MODULE_NAME) + } + + @Test + fun `module chosen for test fragment`() { + doTest("abcDefGhiTest", "abcDefGhi", KotlinGradleModule.TEST_MODULE_NAME) + } + + @Test + fun `digits accepted`() { + val nameWithDigits = "testDigits123Accepted123" + project.kpmModules.create(nameWithDigits) + doTest("foo${nameWithDigits.capitalize()}", "foo", nameWithDigits) + doTest("${nameWithDigits}Main", nameWithDigits, KotlinGradleModule.MAIN_MODULE_NAME) + } + + @Test + fun `capitalized name handling`() { + doTest("IAmTheMain", "IAmThe", KotlinGradleModule.MAIN_MODULE_NAME) + } + + @Test + fun `live module logic update`() { + val nameForTest = "someNewTest" + doTest(nameForTest, "someNew", "test") + project.kpmModules.create("newTest") + doTest(nameForTest, "some", "newTest") + + // also check for the same logic in fragments that go to main if no suffix matched: + + val nameForMain = "fooBarBaz" + doTest(nameForMain, nameForMain, KotlinGradleModule.MAIN_MODULE_NAME) + val moduleSuffixName = "barBaz" + project.kpmModules.create(moduleSuffixName) + doTest(nameForMain, "foo", moduleSuffixName) + } +} + +internal class MultiplatformSourceSetMappedFragmentLocatorTestWithAndroid : MultiplatformSourceSetMappedFragmentLocatorTest() { + + override fun setup() { + super.setup() + project.plugins.apply("android-library") + kotlin.android() + } + + @Test + fun `android source set names`() { + doTest("androidMain", "android", KotlinGradleModule.MAIN_MODULE_NAME) + doTest("androidTest", "android", KotlinGradleModule.TEST_MODULE_NAME) + doTest("androidAndroidTest", "androidAndroid", KotlinGradleModule.TEST_MODULE_NAME) + + doTest("androidDebug", "androidDebug", KotlinGradleModule.MAIN_MODULE_NAME) + doTest("androidDebugTest", "androidDebug", KotlinGradleModule.TEST_MODULE_NAME) + doTest("androidDebugAndroidTest", "androidDebugAndroid", KotlinGradleModule.TEST_MODULE_NAME) + } +} \ No newline at end of file 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 8fbf7915801..2348afb93ef 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 @@ -224,7 +224,7 @@ internal class PropertiesProvider private constructor(private val project: Proje get() = booleanProperty("kotlin.wasm.stability.nowarn") ?: false val experimentalKpmModelMapping: Boolean - get() = booleanProperty("kotlin.kpm.experimentalModelMapping") ?: false + get() = booleanProperty(PropertyNames.KOTLIN_KPM_EXPERIMENTAL_MODEL_MAPPING) ?: false val ignoreDisabledNativeTargets: Boolean? get() = booleanProperty(DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME) @@ -488,6 +488,7 @@ internal class PropertiesProvider private constructor(private val project: Proje const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT = "kotlin.mpp.hierarchicalStructureSupport" const val KOTLIN_NATIVE_DEPENDENCY_PROPAGATION = "kotlin.native.enableDependencyPropagation" const val KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION = "kotlin.mpp.enableOptimisticNumberCommonization" + const val KOTLIN_KPM_EXPERIMENTAL_MODEL_MAPPING = "kotlin.kpm.experimentalModelMapping" } companion object { 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 ee4f9e5ce71..6d80fe63945 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 @@ -17,9 +17,8 @@ class FragmentMappedKotlinSourceSetFactory constructor(private val project: Proj 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.kpmModules - val fragment = modules.maybeCreate(moduleName).fragments.maybeCreate(fragmentName) + val fragment = project.kpmModules.maybeCreate(location.moduleName).fragments.maybeCreate(location.fragmentName) + /** TODO setup JS-specific attributes similar to [org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory]*/ return FragmentMappedKotlinSourceSet(name, project, fragment) } 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 65a4779cd89..7516a827261 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 @@ -7,7 +7,6 @@ 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 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTargetPreset @@ -33,11 +32,11 @@ interface SourceSetMappedFragmentLocator { } } -private class MultiplatformSourceSetMappedFragmentLocator : SourceSetMappedFragmentLocator { - override fun locateFragmentForSourceSet(project: Project, sourceSetName: String): SourceSetMappedFragmentLocator.FragmentLocation? { +internal class MultiplatformSourceSetMappedFragmentLocator : SourceSetMappedFragmentLocator { + override fun locateFragmentForSourceSet(project: Project, sourceSetName: String): SourceSetMappedFragmentLocator.FragmentLocation { val camelCaseParts = sourceSetName.camelCaseParts() if (camelCaseParts.size < 2) { - SourceSetMappedFragmentLocator.FragmentLocation(KotlinGradleModule.MAIN_MODULE_NAME, sourceSetName) + return SourceSetMappedFragmentLocator.FragmentLocation(KotlinGradleModule.MAIN_MODULE_NAME, sourceSetName) } val androidTarget = when (val ext = project.topLevelExtension) { @@ -55,7 +54,7 @@ private class MultiplatformSourceSetMappedFragmentLocator : SourceSetMappedFragm if (androidTarget != null && sourceSetName.startsWith(androidTarget.name)) androidSourceSetModuleName(androidTarget.name, sourceSetName) else null - } ?: candidateModuleNames.firstOrNull { project.kpmModules.findByName(it) != null } + } ?: candidateModuleNames.lastOrNull { project.kpmModules.findByName(it) != null } ?: KotlinGradleModule.MAIN_MODULE_NAME val fragmentName =