diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformAndroidSourceSetLayoutV2IT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformAndroidSourceSetLayoutV2IT.kt new file mode 100644 index 00000000000..063a96ff526 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/MultiplatformAndroidSourceSetLayoutV2IT.kt @@ -0,0 +1,21 @@ +/* + * 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. + */ + +package org.jetbrains.kotlin.gradle + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName + +@MppGradlePluginTests +@DisplayName("Multiplatform Build Reproducibility") +class MultiplatformAndroidSourceSetLayoutV2IT : KGPBaseTest() { + + @GradleAndroidTest + @AndroidTestVersions(minVersion = "7.0.4") + fun testProjectWithFlavors(gradleVersion: GradleVersion, agpVersion: String, jdkVersion: JdkVersions.ProvidedJdk) { + val buildOptions = defaultBuildOptions.copy(androidVersion = agpVersion) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt index 2214b647ea0..1bdad6ca322 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.internal.KotlinCompilationsModuleGroups import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.* +import org.jetbrains.kotlin.gradle.plugin.sources.android.kotlinAndroidSourceSetLayout import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker import org.jetbrains.kotlin.gradle.plugin.sources.dependsOnClosure import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations @@ -523,6 +524,10 @@ class AndroidCompilationDetails( override val friendArtifacts: FileCollection get() = target.project.files(super.friendArtifacts, compilation.testedVariantArtifacts) + override val defaultSourceSetName: String by lazy { + project.kotlinAndroidSourceSetLayout.naming.defaultKotlinSourceSetName(this) ?: super.defaultSourceSetName + } + /* * Example of how multiplatform dependencies from common would get to Android test classpath: * commonMainImplementation -> androidDebugImplementation -> debugImplementation -> debugAndroidTestCompileClasspath diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetNaming.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetNaming.kt index 3d7e5174659..aa9a2607090 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetNaming.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetNaming.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.plugin.sources.android import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.AndroidCompilationDetails internal interface KotlinAndroidSourceSetNaming { @@ -30,6 +31,14 @@ internal interface KotlinAndroidSourceSetNaming { type: AndroidVariantType ): String + + /** + * Returns the name of the default KotlinSourceSet for a given Android compilation. + * Returns `null`, if this naming schema does not know about it. In this case, the + * 'default' defaultSourceSetName will be constructed by the compilation. + */ + fun defaultKotlinSourceSetName(compilation: AndroidCompilationDetails): String? = null + /** * Always capable of creating the [KotlinSourceSet]'s name based upon the disambiguationClassifier and androidSourceSetName alone. */ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV2KotlinAndroidSourceSetNaming.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV2KotlinAndroidSourceSetNaming.kt index 5bfd5bdafa4..0c155771952 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV2KotlinAndroidSourceSetNaming.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV2KotlinAndroidSourceSetNaming.kt @@ -5,9 +5,13 @@ package org.jetbrains.kotlin.gradle.plugin.sources.android +import org.gradle.api.logging.Logging +import org.jetbrains.kotlin.gradle.plugin.mpp.AndroidCompilationDetails import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName internal object MultiplatformLayoutV2KotlinAndroidSourceSetNaming : KotlinAndroidSourceSetNaming { + private val logger = Logging.getLogger(this::class.java) + private val AndroidBaseSourceSetName.kotlinName get() = when (this) { AndroidBaseSourceSetName.Main -> "main" @@ -31,6 +35,24 @@ internal object MultiplatformLayoutV2KotlinAndroidSourceSetNaming : KotlinAndroi return lowerCamelCaseName(disambiguationClassifier, replaceAndroidBaseSourceSetName(androidSourceSetName, type)) } + override fun defaultKotlinSourceSetName(compilation: AndroidCompilationDetails): String? { + val kotlinSourceSetName: String? = run { + val baseSourceSetName = compilation.androidVariant.type.androidBaseSourceSetName ?: return@run null + val androidSourceSetName = lowerCamelCaseName( + baseSourceSetName.takeIf { it != AndroidBaseSourceSetName.Main }?.name, + compilation.androidVariant.flavorName, + compilation.androidVariant.buildType.name + ) + val androidSourceSet = compilation.androidVariant.sourceSets.find { it.name == androidSourceSetName } ?: return@run null + compilation.project.findKotlinSourceSet(androidSourceSet)?.name + } + + if (kotlinSourceSetName == null) { + logger.warn("Can't determine 'defaultKotlinSourceSet' for android compilation: ${compilation.androidVariant.name}") + } + return kotlinSourceSetName + } + private fun replaceAndroidBaseSourceSetName( androidSourceSetName: String, type: AndroidVariantType diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV2Test.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV2Test.kt index 2ddc803f171..bd2a0ce3cd7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV2Test.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV2Test.kt @@ -14,14 +14,14 @@ import org.gradle.api.internal.project.ProjectInternal import org.gradle.testfixtures.ProjectBuilder import org.jetbrains.kotlin.gradle.applyMultiplatformPlugin import org.jetbrains.kotlin.gradle.plugin.forEachVariant -import org.jetbrains.kotlin.gradle.plugin.sources.android.androidTest -import org.jetbrains.kotlin.gradle.plugin.sources.android.main -import org.jetbrains.kotlin.gradle.plugin.sources.android.test +import org.jetbrains.kotlin.gradle.plugin.mpp.AndroidCompilationDetails +import org.jetbrains.kotlin.gradle.plugin.sources.android.* import org.jetbrains.kotlin.gradle.setMultiplatformAndroidSourceSetLayoutVersion import org.jetbrains.kotlin.gradle.utils.androidExtension import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotNull import kotlin.test.assertTrue class MultiplatformAndroidSourceSetLayoutV2Test { @@ -243,4 +243,74 @@ class MultiplatformAndroidSourceSetLayoutV2Test { project.evaluate() assertEquals(project.file("custom.xml"), android.sourceSets.main.manifest.srcFile) } + + @Test + fun `test - defaultKotlinSourceSetName - is determined for all compilations`() { + kotlin.android() + android.flavorDimensions.add("market") + android.flavorDimensions.add("price") + android.productFlavors.create("german").dimension = "market" + android.productFlavors.create("usa").dimension = "market" + android.productFlavors.create("paid").dimension = "price" + android.productFlavors.create("free").dimension = "price" + project.evaluate() + + kotlin.android().compilations.all { compilation -> + val compilationDetails = compilation.compilationDetails as AndroidCompilationDetails + val defaultKotlinSourceSetName = multiplatformAndroidSourceSetLayoutV2.naming.defaultKotlinSourceSetName(compilationDetails) + assertNotNull( + defaultKotlinSourceSetName, + "Expected non-null 'defaultKotlinSourceSetName' for compilation ${compilationDetails.compilation.name}" + ) + + val kotlinSourceSet = kotlin.sourceSets.getByName(defaultKotlinSourceSetName) + + assertEquals( + setOf(compilation.androidVariant.name), kotlinSourceSet.androidSourceSetInfo.androidVariantNames, + "Expected KotlinSourceSet ${kotlinSourceSet.name} to only mention androidVariant ${compilation.androidVariant.name}" + ) + } + } + + @Test + fun `test - defaultKotlinSourceSetName`() { + kotlin.android() + android.flavorDimensions.add("market") + android.flavorDimensions.add("price") + android.productFlavors.create("german").dimension = "market" + android.productFlavors.create("usa").dimension = "market" + android.productFlavors.create("paid").dimension = "price" + android.productFlavors.create("free").dimension = "price" + project.evaluate() + + assertEquals( + "androidGermanFreeDebug", + kotlin.android().compilations.getByName("germanFreeDebug").defaultSourceSet.name + ) + + assertEquals( + "androidUsaFreeDebug", + kotlin.android().compilations.getByName("usaFreeDebug").defaultSourceSet.name + ) + + assertEquals( + "androidGermanPaidRelease", + kotlin.android().compilations.getByName("germanPaidRelease").defaultSourceSet.name + ) + + assertEquals( + "androidUsaPaidRelease", + kotlin.android().compilations.getByName("usaPaidRelease").defaultSourceSet.name + ) + + assertEquals( + "androidUnitTestGermanFreeDebug", + kotlin.android().compilations.getByName("germanFreeDebugUnitTest").defaultSourceSet.name + ) + + assertEquals( + "androidInstrumentedTestGermanFreeDebug", + kotlin.android().compilations.getByName("germanFreeDebugAndroidTest").defaultSourceSet.name + ) + } }