diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index b7c117d82c0..a6cc312853c 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -8,4 +8,7 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt
index e6cf516a816..9051d3922b0 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/KotlinDependenciesManagement.kt
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.internal
-import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import org.gradle.api.InvalidUserDataException
@@ -27,10 +26,12 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.kpmModules
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
+import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidBaseSourceSetName
+import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
+import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.plugin.sources.dependsOnClosure
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.plugin.sources.withDependsOnClosure
-import org.jetbrains.kotlin.gradle.targets.android.findAndroidTarget
import org.jetbrains.kotlin.gradle.targets.jvm.JvmCompilationsTestRunSource
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun
@@ -112,10 +113,7 @@ internal fun configureStdlibDefaultDependency(project: Project) = with(project)
project.tryWithDependenciesIfUnresolved(scopeConfiguration) { dependencies ->
val scopeToAddStdlibDependency =
- if (isRelatedToAndroidTestSourceSet(
- project,
- kotlinSourceSet
- )
+ if (isRelatedToAndroidTestSourceSet(kotlinSourceSet)
) // AGP deprecates API configurations in test source sets
KotlinDependencyScope.IMPLEMENTATION_SCOPE
else KotlinDependencyScope.API_SCOPE
@@ -195,7 +193,9 @@ private fun chooseAndAddStdlibDependency(
val stdlibModuleName = when (sourceSetStdlibPlatformType) {
KotlinPlatformType.jvm -> stdlibModuleForJvmCompilations(compilations)
KotlinPlatformType.androidJvm ->
- if (kotlinSourceSet.name == androidMainSourceSetName(project)) stdlibModuleForJvmCompilations(compilations) else null
+ if (kotlinSourceSet.androidSourceSetInfoOrNull?.androidSourceSetName == AndroidBaseSourceSetName.Main.name)
+ stdlibModuleForJvmCompilations(compilations) else null
+
KotlinPlatformType.js -> "kotlin-stdlib-js"
KotlinPlatformType.wasm -> "kotlin-stdlib-wasm"
KotlinPlatformType.native -> null
@@ -214,28 +214,9 @@ private fun chooseAndAddStdlibDependency(
}
}
-private fun androidMainSourceSetName(project: Project): String {
- val target = project.findAndroidTarget() ?: error("No Android target found")
- return AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(target, "main")
-}
-
-private fun isRelatedToAndroidTestSourceSet(project: Project, kotlinSourceSet: KotlinSourceSet): Boolean {
- val androidExtension = project.extensions.findByName("android") as? TestedExtension ?: return false
- val androidTarget = project.findAndroidTarget() ?: return false
-
- if (androidTarget.compilations.any {
- (it.androidVariant is UnitTestVariant || it.androidVariant is TestVariant) && it.defaultSourceSet === kotlinSourceSet
- }
- ) return true
-
- (androidExtension.testVariants + androidExtension.unitTestVariants).forEach { variant ->
- if (variant.sourceSets.any {
- kotlinSourceSet.name == AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(androidTarget, it.name)
- }
- ) return true
- }
-
- return false
+private fun isRelatedToAndroidTestSourceSet(kotlinSourceSet: KotlinSourceSet): Boolean {
+ val androidVariantType = kotlinSourceSet.androidSourceSetInfoOrNull?.androidVariantType ?: return false
+ return androidVariantType in setOf(AndroidVariantType.UnitTest, AndroidVariantType.InstrumentedTest)
}
private val stdlibModules = setOf("kotlin-stdlib-common", "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", "kotlin-stdlib-js")
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt
index f7d719d0aa5..83311757283 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformPlugin.kt
@@ -191,7 +191,7 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
@Deprecated("Migrate to the new Kotlin source sets and use the addCommonSourceSetToPlatformSourceSet(Named, Project) overload")
protected open fun addCommonSourceSetToPlatformSourceSet(sourceSet: SourceSet, platformProject: Project) = Unit
- @Deprecated("Retained for older Kotlin/Native MPP plugin binary compatibility")
+ @Deprecated("Retained for older Kotlin/Native MPP plugin binary compatibility", level = DeprecationLevel.ERROR)
protected val SourceSet.kotlin: SourceDirectorySet?
get() {
@Suppress("DEPRECATION")
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
index d25e6b640a9..1c79ec4be37 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessageOutputStream
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.Companion.jsCompilerProperty
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_ABI_SNAPSHOT
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_GRADLE_PLUGIN_COMPATIBILITY_NO_WARN
+import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_OPTIMISTIC_NUMBER_COMMONIZATION
@@ -209,6 +210,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val ignoreAndroidGradlePluginCompatibilityIssues: Boolean
get() = booleanProperty(KOTLIN_MPP_ANDROID_GRADLE_PLUGIN_COMPATIBILITY_NO_WARN) ?: false
+ val mppAndroidSourceSetLayoutVersion: Int?
+ get() = property(KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION)?.toIntOrNull()
+
val ignoreDisabledCInteropCommonization: Boolean
get() = booleanProperty("$KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION.nowarn") ?: false
@@ -467,6 +471,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT = "kotlin.internal.mpp.hierarchicalStructureByDefault"
const val KOTLIN_MPP_HIERARCHICAL_STRUCTURE_SUPPORT = "kotlin.mpp.hierarchicalStructureSupport"
const val KOTLIN_MPP_ANDROID_GRADLE_PLUGIN_COMPATIBILITY_NO_WARN = "kotlin.mpp.androidGradlePluginCompatibility.nowarn"
+ const val KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION = "kotlin.mpp.androidSourceSetLayoutVersion"
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"
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/SubpluginEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/SubpluginEnvironment.kt
index 5bf6e6f3a88..9fefbd09330 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/SubpluginEnvironment.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/SubpluginEnvironment.kt
@@ -94,7 +94,7 @@ internal fun addCompilationSourcesToExternalCompileTask(
task: TaskProvider>
) {
if (compilation is KotlinJvmAndroidCompilation) {
- compilation.androidVariant.forEachKotlinSourceDirectorySet { sourceSet ->
+ compilation.androidVariant.forEachKotlinSourceDirectorySet(compilation.project) { sourceSet ->
task.configure { it.setSource(sourceSet) }
}
compilation.androidVariant.forEachJavaSourceDir { sources ->
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt
index 9162e97e5f9..5959b378c26 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt
@@ -9,6 +9,7 @@ import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.extraProperties
+import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
object UnusedSourceSetsChecker {
@@ -39,13 +40,10 @@ object UnusedSourceSetsChecker {
fun checkSourceSets(project: Project) {
project.whenEvaluated {
val compilationsBySourceSet = CompilationSourceSetUtil.compilationsBySourceSets(project)
- val unusedSourceSets = project.kotlinExtension.sourceSets.filter {
+ val unusedSourceSets = project.kotlinExtension.sourceSets
// Ignoring Android source sets
- val isAndroidSourceSet = it.extraProperties.has(IS_ANDROID_SOURCE_SET) &&
- (it.extraProperties[IS_ANDROID_SOURCE_SET] as Boolean)
-
- !isAndroidSourceSet && compilationsBySourceSet[it]?.isEmpty() ?: true
- }
+ .filter { it.androidSourceSetInfoOrNull == null }
+ .filter { compilationsBySourceSet[it]?.isEmpty() ?: true }
if (unusedSourceSets.isNotEmpty()) {
reportUnusedSourceSets(project, unusedSourceSets.toSet())
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt
deleted file mode 100644
index 51721588769..00000000000
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010-2020 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.mpp
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.api.AndroidSourceSet
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.internal.plugins.DslObject
-import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
-import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
-import org.jetbrains.kotlin.gradle.plugin.*
-import org.jetbrains.kotlin.gradle.plugin.AndroidProjectHandler.Companion.kotlinSourceSetNameForAndroidSourceSet
-import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory.Companion.defaultSourceFolder
-
-internal const val IS_ANDROID_SOURCE_SET = "android-source-set"
-
-internal fun syncKotlinAndAndroidSourceSets(target: KotlinAndroidTarget) {
- val project = target.project
- val android = project.extensions.getByName("android") as BaseExtension
- val kotlinExtension = project.kotlinExtension
-
- val createdKotlinSourceSets = mutableSetOf()
- android.sourceSets.all { androidSourceSet ->
- val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(target, androidSourceSet.name)
- createdKotlinSourceSets.add(kotlinSourceSetName)
- val kotlinSourceSet = kotlinExtension.sourceSets.maybeCreate(kotlinSourceSetName)
-
- // Marking KotlinSource set is created for Android compilation to ignore it in UnusedSourceSetChecker
- kotlinSourceSet.extraProperties.set(IS_ANDROID_SOURCE_SET, true)
-
- androidSourceSet.addKotlinSources(kotlinSourceSet)
-
- createDefaultDependsOnEdges(target, kotlinSourceSet, androidSourceSet)
- syncKotlinAndAndroidSourceDirs(target, kotlinSourceSet, androidSourceSet)
- syncKotlinAndAndroidResources(target, kotlinSourceSet, androidSourceSet)
-
- ifKaptEnabled(project) {
- Kapt3GradleSubplugin.createAptConfigurationIfNeeded(project, androidSourceSet.name)
- }
- }
-}
-
-internal val AndroidSourceSet.kotlinSourceSet: SourceDirectorySet
- get() = checkNotNull(getExtension(KOTLIN_DSL_NAME)) { "Missing kotlinSourceSet for Android source set $name" }
-
-internal fun AndroidSourceSet.addKotlinSources(kotlinSourceSet: KotlinSourceSet) {
- @Suppress("DEPRECATION") addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
- DslObject(this).addExtension(KOTLIN_DSL_NAME, kotlinSourceSet.kotlin)
-}
-
-private fun createDefaultDependsOnEdges(
- target: KotlinAndroidTarget,
- kotlinSourceSet: KotlinSourceSet,
- androidSourceSet: AndroidSourceSet
-) {
- val commonSourceSetName = when (androidSourceSet.name) {
- "main" -> "commonMain"
- "test" -> "commonTest"
- "androidTest" -> "commonTest"
- else -> return
- }
- val commonSourceSet = target.project.kotlinExtension.sourceSets.findByName(commonSourceSetName) ?: return
- kotlinSourceSet.dependsOn(commonSourceSet)
-}
-
-private fun syncKotlinAndAndroidSourceDirs(
- target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet
-) {
- val disambiguationClassifier = target.disambiguationClassifier
-
- /*
- Mitigate ambiguity!
- Example: disambiguationClassifier="android"
- Source Directory "src/androidTest/kotlin"
- -- could be claimed by kotlin {android}Test (unit test)
- -- could be claimed by Android androidTest (instrumented test)
-
- The Kotlin source set would win in this scenario.
- */
- if (!androidSourceSet.name.startsWith(disambiguationClassifier)) {
- kotlinSourceSet.kotlin.srcDir("src/${androidSourceSet.name}/kotlin")
- }
-
- kotlinSourceSet.kotlin.srcDirs(*androidSourceSet.java.srcDirs.toTypedArray())
-
- /*
- Make sure to include user configuration as well.
- Unfortunately, there does not exist any "ad-hoc" API like `all`.
- Therefore we sync the directories once again after evaluation
- */
- target.project.whenEvaluated {
- kotlinSourceSet.kotlin.srcDirs(*androidSourceSet.java.srcDirs.toTypedArray())
- }
-}
-
-private fun syncKotlinAndAndroidResources(
- target: KotlinAndroidTarget,
- kotlinSourceSet: KotlinSourceSet,
- androidSourceSet: AndroidSourceSet
-) {
- /*
- Non-MPP projects will not have a different name for kotlin source sets vs Android source sets
- Registering resource folders is unnecessary therefore.
- */
- if (kotlinSourceSet.name == androidSourceSet.name) {
- return
- }
-
- val project = target.project
-
- androidSourceSet.resources.srcDirs(kotlinSourceSet.resources.srcDirs)
- if (androidSourceSet.resources.srcDirs.isNotEmpty()) {
- androidSourceSet.resources.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "resources"))
- kotlinSourceSet.resources.srcDirs(androidSourceSet.resources.srcDirs)
- }
-
- if (androidSourceSet.assets.srcDirs.isNotEmpty()) {
- androidSourceSet.assets.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "assets"))
- }
-
- if (androidSourceSet.res.srcDirs.isNotEmpty()) {
- androidSourceSet.res.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "res"))
- }
-
- if (androidSourceSet.aidl.srcDirs.isNotEmpty()) {
- androidSourceSet.aidl.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "aidl"))
- }
-
- if (androidSourceSet.renderscript.srcDirs.isNotEmpty()) {
- androidSourceSet.renderscript.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "rs"))
- }
-
- if (androidSourceSet.jni.srcDirs.isNotEmpty()) {
- androidSourceSet.jni.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "jni"))
- }
-
- if (androidSourceSet.jniLibs.srcDirs.isNotEmpty()) {
- androidSourceSet.jniLibs.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "jniLibs"))
- }
-
- if (androidSourceSet.shaders.srcDirs.isNotEmpty()) {
- androidSourceSet.shaders.srcDir(defaultSourceFolder(project, kotlinSourceSet.name, "shaders"))
- }
-}
-
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidBaseSourceSetName.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidBaseSourceSetName.kt
new file mode 100644
index 00000000000..67ee86480cf
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidBaseSourceSetName.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.plugin.sources.android
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.gradle.api.NamedDomainObjectContainer
+
+internal val NamedDomainObjectContainer.main: AndroidSourceSet
+ get() = getByName(AndroidBaseSourceSetName.Main.name)
+
+internal val NamedDomainObjectContainer.test: AndroidSourceSet
+ get() = getByName(AndroidBaseSourceSetName.Test.name)
+
+internal val NamedDomainObjectContainer.androidTest: AndroidSourceSet
+ get() = getByName(AndroidBaseSourceSetName.AndroidTest.name)
+
+/*
+Not written as enum class to avoid Enum.name ambiguity with 'source set name' semantics.
+ */
+internal sealed class AndroidBaseSourceSetName(val name: String) {
+ final override fun toString(): String = name
+
+ object Main : AndroidBaseSourceSetName("main")
+ object Test : AndroidBaseSourceSetName("test")
+ object AndroidTest : AndroidBaseSourceSetName("androidTest")
+
+ companion object {
+ fun byName(name: String): AndroidBaseSourceSetName? = when (name) {
+ Main.name -> Main
+ Test.name -> Test
+ AndroidTest.name -> AndroidTest
+ else -> null
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidVariantType.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidVariantType.kt
new file mode 100644
index 00000000000..6d658f99a28
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/AndroidVariantType.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.plugin.sources.android
+
+import com.android.build.gradle.api.*
+
+internal val BaseVariant.type: AndroidVariantType
+ get() = when (this) {
+ is UnitTestVariant -> AndroidVariantType.UnitTest
+ is TestVariant -> AndroidVariantType.InstrumentedTest
+ is ApplicationVariant, is LibraryVariant -> AndroidVariantType.Main
+ else -> AndroidVariantType.Unknown
+ }
+
+internal val AndroidBaseSourceSetName.variantType: AndroidVariantType
+ get() = when (this) {
+ AndroidBaseSourceSetName.Main -> AndroidVariantType.Main
+ AndroidBaseSourceSetName.Test -> AndroidVariantType.UnitTest
+ AndroidBaseSourceSetName.AndroidTest -> AndroidVariantType.InstrumentedTest
+ }
+
+internal enum class AndroidVariantType {
+ Main, UnitTest, InstrumentedTest, Unknown;
+
+ /**
+ * Every known type of Android variant has a 'base source set', which
+ * participates in all variants of sad type (main, test, androidTest, ...)
+ */
+ val androidBaseSourceSetName: AndroidBaseSourceSetName?
+ get() = when (this) {
+ Main -> AndroidBaseSourceSetName.Main
+ UnitTest -> AndroidBaseSourceSetName.Test
+ InstrumentedTest -> AndroidBaseSourceSetName.AndroidTest
+ Unknown -> null
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetInfo.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetInfo.kt
new file mode 100644
index 00000000000..d605677817a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetInfo.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.plugin.sources.android
+
+import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.api.AndroidSourceSet
+import com.android.builder.model.SourceProvider
+import org.gradle.api.Project
+import org.gradle.api.UnknownDomainObjectException
+import org.gradle.api.plugins.ExtensionAware
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull
+import org.jetbrains.kotlin.gradle.plugin.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.utils.findByType
+
+@ExperimentalKotlinGradlePluginApi
+val KotlinSourceSet.androidSourceSetInfoOrNull: KotlinAndroidSourceSetInfo?
+ get() = (this as? ExtensionAware)?.extensions?.findByType()
+
+@ExperimentalKotlinGradlePluginApi
+var KotlinSourceSet.androidSourceSetInfo: KotlinAndroidSourceSetInfo
+ get() = androidSourceSetInfoOrNull ?: throw UnknownDomainObjectException("No 'androidSourceSetInfo' found on KotlinSourceSet: $name")
+ internal set(value) {
+ (this as ExtensionAware).extensions.add("androidSourceSetInfo", value)
+ }
+
+@ExperimentalKotlinGradlePluginApi
+fun Project.findAndroidSourceSet(kotlinSourceSet: KotlinSourceSet): AndroidSourceSet? {
+ val androidSourceSetInfo = kotlinSourceSet.androidSourceSetInfoOrNull ?: return null
+ val android = extensions.findByType() ?: return null
+ return android.sourceSets.getByName(androidSourceSetInfo.androidSourceSetName)
+}
+
+@ExperimentalKotlinGradlePluginApi
+fun Project.findKotlinSourceSet(androidSourceSet: AndroidSourceSet): KotlinSourceSet? {
+ return findKotlinSourceSet(androidSourceSet.name)
+}
+
+@ExperimentalKotlinGradlePluginApi
+fun Project.findKotlinSourceSet(androidSourceSet: SourceProvider): KotlinSourceSet? {
+ return findKotlinSourceSet(androidSourceSet.name)
+}
+
+private fun Project.findKotlinSourceSet(androidSourceSetName: String): KotlinSourceSet? {
+ return kotlinExtensionOrNull?.sourceSets.orEmpty()
+ .find { kotlinSourceSet -> kotlinSourceSet.androidSourceSetInfoOrNull?.androidSourceSetName == androidSourceSetName }
+}
+
+@ExperimentalKotlinGradlePluginApi
+sealed class KotlinAndroidSourceSetInfo {
+ abstract val kotlinSourceSetName: String
+ abstract val androidSourceSetName: String
+ internal abstract val androidVariantType: AndroidVariantType
+ abstract val androidVariantNames: Set
+
+ internal data class Mutable(
+ override val kotlinSourceSetName: String,
+ override val androidSourceSetName: String,
+ override var androidVariantType: AndroidVariantType = AndroidVariantType.Unknown,
+ override val androidVariantNames: MutableSet = mutableSetOf()
+ ) : KotlinAndroidSourceSetInfo()
+
+ internal fun asMutable(): Mutable = when (this) {
+ is Mutable -> this
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetLayout.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetLayout.kt
new file mode 100644
index 00000000000..1e33785d6fd
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetLayout.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.plugin.sources.android
+
+import org.jetbrains.kotlin.gradle.plugin.sources.android.configurator.*
+
+internal data class KotlinAndroidSourceSetLayout(
+ val name: String,
+ val naming: KotlinAndroidSourceSetNaming,
+ val sourceSetConfigurator: KotlinAndroidSourceSetConfigurator
+) {
+ override fun toString(): String = "KotlinAndroidSourceSetLayout: $name"
+}
+
+internal val singleTargetAndroidSourceSetLayout = KotlinAndroidSourceSetLayout(
+ name = "Kotlin/Android",
+ naming = SingleTargetKotlinAndroidSourceSetNaming,
+ sourceSetConfigurator = KotlinAndroidSourceSetConfigurator(
+ KotlinAndroidSourceSetInfoConfigurator,
+ AndroidKaptSourceSetConfigurator,
+ AndroidSourceSetConventionConfigurator,
+ SingleTargetSourceDirConfigurator,
+ )
+)
+
+internal val multiplatformAndroidSourceSetLayoutV1 = KotlinAndroidSourceSetLayout(
+ name = "Multiplatform/Android V1",
+ naming = MultiplatformLayoutV1KotlinAndroidSourceSetNaming,
+ sourceSetConfigurator = KotlinAndroidSourceSetConfigurator(
+ KotlinAndroidSourceSetInfoConfigurator,
+ AndroidKaptSourceSetConfigurator,
+ AndroidSourceSetConventionConfigurator,
+ MultiplatformAndroidResourceDirConfigurator,
+ MultiplatformLayoutV1DependsOnConfigurator,
+ MultiplatformLayoutV1SourceDirConfigurator
+ )
+)
+
+internal val multiplatformAndroidSourceSetLayoutV2 = KotlinAndroidSourceSetLayout(
+ name = "Multiplatform/Android V2",
+ naming = MultiplatformLayoutV2KotlinAndroidSourceSetNaming,
+ sourceSetConfigurator = KotlinAndroidSourceSetConfigurator(
+ KotlinAndroidSourceSetInfoConfigurator,
+ AndroidKaptSourceSetConfigurator,
+ MultiplatformAndroidResourceDirConfigurator,
+ MultiplatformLayoutV2DependsOnConfigurator,
+ MultiplatformLayoutV2SourceDirConfigurator,
+ MultiplatformLayoutV2DefaultManifestLocationConfigurator
+ )
+)
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
new file mode 100644
index 00000000000..3d7e5174659
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSetNaming.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.plugin.sources.android
+
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+
+internal interface KotlinAndroidSourceSetNaming {
+
+ /**
+ * Returns non-null name of the corresponding [KotlinSourceSet], if the name of said source set can be
+ * determined by the [disambiguationClassifier] and [androidSourceSetName] alone.
+ *
+ * Returns `null`, if KotlinSourceSet name requires additional [AndroidVariantType] to be determined.
+ */
+ fun kotlinSourceSetName(
+ disambiguationClassifier: String,
+ androidSourceSetName: String
+ ): String?
+
+ /**
+ * Returns the name of the corresponding [KotlinSourceSet], if the name of said source set cannot be determined
+ * by the [disambiguationClassifier] and [androidSourceSetName] alone.
+ */
+ fun kotlinSourceSetName(
+ disambiguationClassifier: String,
+ androidSourceSetName: String,
+ type: AndroidVariantType
+ ): String
+
+ /**
+ * Always capable of creating the [KotlinSourceSet]'s name based upon the disambiguationClassifier and androidSourceSetName alone.
+ */
+ interface Simple : KotlinAndroidSourceSetNaming {
+ override fun kotlinSourceSetName(disambiguationClassifier: String, androidSourceSetName: String): String
+ override fun kotlinSourceSetName(disambiguationClassifier: String, androidSourceSetName: String, type: AndroidVariantType): String =
+ kotlinSourceSetName(disambiguationClassifier, androidSourceSetName)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSets.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSets.kt
new file mode 100644
index 00000000000..9d062609394
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/KotlinAndroidSourceSets.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.plugin.sources.android
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.gradle.api.logging.Logging
+import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.forEachVariant
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.utils.androidExtension
+import org.jetbrains.kotlin.gradle.utils.getOrCreate
+
+internal object KotlinAndroidSourceSets {
+
+ private val logger = Logging.getLogger(this::class.java)
+
+ fun setupKotlinAndroidSourceSets(
+ target: KotlinAndroidTarget,
+ layout: KotlinAndroidSourceSetLayout = target.project.kotlinAndroidSourceSetLayout
+ ) {
+ logger.debug("Applying ${KotlinAndroidSourceSetLayout::class.java.simpleName}: ${layout.name}")
+
+ val project = target.project
+ val kotlin = project.kotlinExtension
+ val android = project.androidExtension
+ val naming = layout.naming
+ val configurator = layout.sourceSetConfigurator
+
+ /* Ensures that each KotlinSourceSet only invokes the 'configurator' once */
+ val configuredKotlinSourceSets = mutableSetOf()
+
+ /* Hook eagerly into AndroidSourceSet creation */
+ android.sourceSets.all { androidSourceSet ->
+ val kotlinSourceSetName = naming.kotlinSourceSetName(target.disambiguationClassifier, androidSourceSet.name) ?: return@all
+ val kotlinSourceSet = kotlin.getOrCreateKotlinSourceSet(kotlinSourceSetName, androidSourceSet)
+
+ if (configuredKotlinSourceSets.add(kotlinSourceSet)) {
+ configurator.configure(target, kotlinSourceSet, androidSourceSet)
+ }
+ }
+
+ /* Hook into Android's variant creation: This is invoked in 'afterEvaluate' */
+ forEachVariant(project) { variant ->
+ variant.sourceSets.forEach { sourceProvider ->
+ val androidSourceSet = android.sourceSets.findByName(sourceProvider.name) ?: return@forEach
+ val kotlinSourceSetName = naming.kotlinSourceSetName(target.disambiguationClassifier, sourceProvider.name)
+ ?: naming.kotlinSourceSetName(target.disambiguationClassifier, sourceProvider.name, variant.type)
+ val kotlinSourceSet = kotlin.getOrCreateKotlinSourceSet(kotlinSourceSetName, androidSourceSet)
+
+ if (configuredKotlinSourceSets.add(kotlinSourceSet)) {
+ configurator.configure(target, kotlinSourceSet, androidSourceSet)
+ }
+ configurator.configureWithVariant(target, kotlinSourceSet, variant)
+ }
+ }
+ }
+
+
+ private fun KotlinProjectExtension.getOrCreateKotlinSourceSet(
+ name: String, androidSourceSet: AndroidSourceSet
+ ): KotlinSourceSet {
+ return sourceSets.getOrCreate(name) { kotlinSourceSet ->
+ kotlinSourceSet.androidSourceSetInfoOrNull?.let { info ->
+ check(info.kotlinSourceSetName == name) { "Bad 'androidSourceSetInfo' on $name: Bad 'kotlinSourceSetName'" }
+ check(info.androidSourceSetName == androidSourceSet.name) { "Bad 'androidSourceSetInfo' on $name: Bad 'androidSourceSetName'" }
+ return@getOrCreate
+ }
+
+ kotlinSourceSet.androidSourceSetInfo = KotlinAndroidSourceSetInfo.Mutable(
+ kotlinSourceSetName = name,
+ androidSourceSetName = androidSourceSet.name,
+ androidVariantType = AndroidBaseSourceSetName.byName(androidSourceSet.name)?.variantType ?: AndroidVariantType.Unknown
+ )
+
+ logger.debug("Created KotlinSourceSet: ${kotlinSourceSet.name} for AndroidSourceSet: ${androidSourceSet.name}")
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV1KotlinAndroidSourceSetNaming.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV1KotlinAndroidSourceSetNaming.kt
new file mode 100644
index 00000000000..375998d6546
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV1KotlinAndroidSourceSetNaming.kt
@@ -0,0 +1,13 @@
+/*
+ * 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.plugin.sources.android
+
+import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+
+internal object MultiplatformLayoutV1KotlinAndroidSourceSetNaming : KotlinAndroidSourceSetNaming.Simple {
+ override fun kotlinSourceSetName(disambiguationClassifier: String, androidSourceSetName: String): String =
+ lowerCamelCaseName(disambiguationClassifier, androidSourceSetName)
+}
\ No newline at end of file
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
new file mode 100644
index 00000000000..5bfd5bdafa4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/MultiplatformLayoutV2KotlinAndroidSourceSetNaming.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.plugin.sources.android
+
+import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+
+internal object MultiplatformLayoutV2KotlinAndroidSourceSetNaming : KotlinAndroidSourceSetNaming {
+ private val AndroidBaseSourceSetName.kotlinName
+ get() = when (this) {
+ AndroidBaseSourceSetName.Main -> "main"
+ AndroidBaseSourceSetName.Test -> "unitTest"
+ AndroidBaseSourceSetName.AndroidTest -> "instrumentedTest"
+ }
+
+ override fun kotlinSourceSetName(
+ disambiguationClassifier: String,
+ androidSourceSetName: String,
+ ): String? {
+ val androidBaseSourceSetName = AndroidBaseSourceSetName.byName(androidSourceSetName) ?: return null
+ return lowerCamelCaseName(disambiguationClassifier, androidBaseSourceSetName.kotlinName)
+ }
+
+ override fun kotlinSourceSetName(
+ disambiguationClassifier: String,
+ androidSourceSetName: String,
+ type: AndroidVariantType
+ ): String {
+ return lowerCamelCaseName(disambiguationClassifier, replaceAndroidBaseSourceSetName(androidSourceSetName, type))
+ }
+
+ private fun replaceAndroidBaseSourceSetName(
+ androidSourceSetName: String,
+ type: AndroidVariantType
+ ): String {
+ if (type == AndroidVariantType.Main) return androidSourceSetName
+ val androidBaseSourceSetName = type.androidBaseSourceSetName ?: return androidSourceSetName
+ return lowerCamelCaseName(androidBaseSourceSetName.kotlinName, androidSourceSetName.removePrefix(androidBaseSourceSetName.name))
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/SingleTargetKotlinAndroidSourceSetNaming.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/SingleTargetKotlinAndroidSourceSetNaming.kt
new file mode 100644
index 00000000000..cc5510a3f47
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/SingleTargetKotlinAndroidSourceSetNaming.kt
@@ -0,0 +1,15 @@
+/*
+ * 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.plugin.sources.android
+
+import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+
+internal object SingleTargetKotlinAndroidSourceSetNaming : KotlinAndroidSourceSetNaming.Simple {
+ override fun kotlinSourceSetName(disambiguationClassifier: String, androidSourceSetName: String): String {
+ assert(disambiguationClassifier.isEmpty()) { "Unexpected non-empty disambiguationClassifier found: $disambiguationClassifier" }
+ return lowerCamelCaseName(disambiguationClassifier, androidSourceSetName)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidKaptSourceSetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidKaptSourceSetConfigurator.kt
new file mode 100644
index 00000000000..e3ba8a7388e
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidKaptSourceSetConfigurator.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.ifKaptEnabled
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object AndroidKaptSourceSetConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(
+ target: KotlinAndroidTarget,
+ kotlinSourceSet: KotlinSourceSet,
+ androidSourceSet: AndroidSourceSet
+ ) = ifKaptEnabled(target.project) {
+ Kapt3GradleSubplugin.createAptConfigurationIfNeeded(target.project, androidSourceSet.name)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidSourceSetConventionConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidSourceSetConventionConfigurator.kt
new file mode 100644
index 00000000000..11d21618fb5
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/AndroidSourceSetConventionConfigurator.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KOTLIN_DSL_NAME
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.addConvention
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+/*
+Used by AGP com.android.build.gradle.internal.utils.syncAgpAndKgpSources to automatically pick up sources
+from this KotlinSourceSet. This mechanism is deprecated.
+*/
+@Suppress("DEPRECATION")
+internal object AndroidSourceSetConventionConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ androidSourceSet.addConvention(KOTLIN_DSL_NAME, kotlinSourceSet)
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetConfigurator.kt
new file mode 100644
index 00000000000..42b4ba748eb
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetConfigurator.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import com.android.build.gradle.api.BaseVariant
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal interface KotlinAndroidSourceSetConfigurator {
+ /**
+ * Called once, when the corresponding KotlinSourceSet is created for a given [AndroidSourceSet].
+ * Note, this can also be called in 'afterEvaluate', when Android is finalizing its variants.
+ */
+ fun configure(
+ target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet
+ ) = Unit
+
+ /**
+ * Called every time, when a given [KotlinSourceSet] participates in a given Android variant.
+ */
+ fun configureWithVariant(
+ target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, variant: BaseVariant
+ ) = Unit
+}
+
+/* Composite implementation */
+
+internal fun KotlinAndroidSourceSetConfigurator(
+ vararg configurators: KotlinAndroidSourceSetConfigurator?
+): KotlinAndroidSourceSetConfigurator {
+ return CompositeKotlinAndroidSourceSetConfigurator(configurators.filterNotNull())
+}
+
+internal operator fun KotlinAndroidSourceSetConfigurator.plus(
+ other: KotlinAndroidSourceSetConfigurator
+): KotlinAndroidSourceSetConfigurator {
+ val configurators = when {
+ this is CompositeKotlinAndroidSourceSetConfigurator && other is CompositeKotlinAndroidSourceSetConfigurator ->
+ this.configurators + other.configurators
+
+ this is CompositeKotlinAndroidSourceSetConfigurator -> this.configurators + other
+ other is CompositeKotlinAndroidSourceSetConfigurator -> listOf(this) + other.configurators
+ else -> listOf(this, other)
+ }
+
+ return CompositeKotlinAndroidSourceSetConfigurator(configurators)
+}
+
+private class CompositeKotlinAndroidSourceSetConfigurator(
+ val configurators: List
+) : KotlinAndroidSourceSetConfigurator {
+ override fun configure(
+ target: KotlinAndroidTarget,
+ kotlinSourceSet: KotlinSourceSet,
+ androidSourceSet: AndroidSourceSet
+ ) {
+ configurators.forEach { configurator ->
+ configurator.configure(target, kotlinSourceSet, androidSourceSet)
+ }
+ }
+
+ override fun configureWithVariant(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, variant: BaseVariant) {
+ configurators.forEach { configurator ->
+ configurator.configureWithVariant(target, kotlinSourceSet, variant)
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetInfoConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetInfoConfigurator.kt
new file mode 100644
index 00000000000..0335454f81a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/KotlinAndroidSourceSetInfoConfigurator.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.BaseVariant
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfo
+import org.jetbrains.kotlin.gradle.plugin.sources.android.type
+
+internal object KotlinAndroidSourceSetInfoConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configureWithVariant(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, variant: BaseVariant) {
+ val info = kotlinSourceSet.androidSourceSetInfo.asMutable()
+ info.androidVariantType = variant.type
+ info.androidVariantNames.add(variant.name)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformAndroidResourceDirConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformAndroidResourceDirConfigurator.kt
new file mode 100644
index 00000000000..3a7f7abe175
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformAndroidResourceDirConfigurator.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory
+
+internal object MultiplatformAndroidResourceDirConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(
+ target: KotlinAndroidTarget,
+ kotlinSourceSet: KotlinSourceSet,
+ androidSourceSet: AndroidSourceSet
+ ) {
+
+ /*
+ Non-MPP projects will not have a different name for kotlin source sets vs Android source sets
+ Registering resource folders is unnecessary therefore.
+ */
+ if (kotlinSourceSet.name == androidSourceSet.name) {
+ return
+ }
+
+ val project = target.project
+
+ androidSourceSet.resources.srcDirs(kotlinSourceSet.resources.srcDirs)
+ if (androidSourceSet.resources.srcDirs.isNotEmpty()) {
+ androidSourceSet.resources.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "resources"))
+ kotlinSourceSet.resources.srcDirs(androidSourceSet.resources.srcDirs)
+ }
+
+ if (androidSourceSet.assets.srcDirs.isNotEmpty()) {
+ androidSourceSet.assets.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "assets"))
+ }
+
+ if (androidSourceSet.res.srcDirs.isNotEmpty()) {
+ androidSourceSet.res.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "res"))
+ }
+
+ if (androidSourceSet.aidl.srcDirs.isNotEmpty()) {
+ androidSourceSet.aidl.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "aidl"))
+ }
+
+ if (androidSourceSet.renderscript.srcDirs.isNotEmpty()) {
+ androidSourceSet.renderscript.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "rs"))
+ }
+
+ if (androidSourceSet.jni.srcDirs.isNotEmpty()) {
+ androidSourceSet.jni.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "jni"))
+ }
+
+ if (androidSourceSet.jniLibs.srcDirs.isNotEmpty()) {
+ androidSourceSet.jniLibs.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "jniLibs"))
+ }
+
+ if (androidSourceSet.shaders.srcDirs.isNotEmpty()) {
+ androidSourceSet.shaders.srcDir(KotlinSourceSetFactory.defaultSourceFolder(project, kotlinSourceSet.name, "shaders"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1DependsOnConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1DependsOnConfigurator.kt
new file mode 100644
index 00000000000..447c2ce55a8
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1DependsOnConfigurator.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object MultiplatformLayoutV1DependsOnConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(
+ target: KotlinAndroidTarget,
+ kotlinSourceSet: KotlinSourceSet,
+ androidSourceSet: AndroidSourceSet
+ ) {
+ val commonSourceSetName = when (androidSourceSet.name) {
+ "main" -> COMMON_MAIN_SOURCE_SET_NAME
+ "test" -> COMMON_TEST_SOURCE_SET_NAME
+ "androidTest" -> COMMON_TEST_SOURCE_SET_NAME
+ else -> return
+ }
+ val commonSourceSet = target.project.kotlinExtension.sourceSets.findByName(commonSourceSetName) ?: return
+ kotlinSourceSet.dependsOn(commonSourceSet)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1SourceDirConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1SourceDirConfigurator.kt
new file mode 100644
index 00000000000..f4d0ab09927
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV1SourceDirConfigurator.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object MultiplatformLayoutV1SourceDirConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ /*
+ Mitigate ambiguity!
+ Example: disambiguationClassifier="android"
+ Source Directory "src/androidTest/kotlin"
+ -- could be claimed by kotlin {android}Test (unit test)
+ -- could be claimed by Android androidTest (instrumented test)
+
+ The Kotlin source set would win in this scenario.
+ */
+ if (!androidSourceSet.name.startsWith(target.disambiguationClassifier)) {
+ kotlinSourceSet.kotlin.srcDir("src/${androidSourceSet.name}/kotlin")
+ }
+
+ kotlinSourceSet.kotlin.srcDir(target.project.provider { androidSourceSet.java.srcDirs })
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DefaultManifestLocationConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DefaultManifestLocationConfigurator.kt
new file mode 100644
index 00000000000..3ebd4ea93a8
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DefaultManifestLocationConfigurator.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object MultiplatformLayoutV2DefaultManifestLocationConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ /* Default can only be set when the entity is created 'fresh'. Changes here in afterEvaluate might overwrite user setup */
+ if (!target.project.state.executed) {
+ androidSourceSet.manifest.srcFile("src/${kotlinSourceSet.name}/AndroidManifest.xml")
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DependsOnConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DependsOnConfigurator.kt
new file mode 100644
index 00000000000..639c61e5998
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2DependsOnConfigurator.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import com.android.build.gradle.api.BaseVariant
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidBaseSourceSetName
+import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
+import org.jetbrains.kotlin.gradle.plugin.sources.android.type
+import org.jetbrains.kotlin.gradle.plugin.sources.android.variantType
+
+internal object MultiplatformLayoutV2DependsOnConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ val androidBaseSourceSetName = AndroidBaseSourceSetName.byName(androidSourceSet.name) ?: return
+ setDefaultDependsOn(target, kotlinSourceSet, androidBaseSourceSetName.variantType)
+ }
+
+ override fun configureWithVariant(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, variant: BaseVariant) {
+ setDefaultDependsOn(target, kotlinSourceSet, variant.type)
+ }
+
+ private fun setDefaultDependsOn(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, variantType: AndroidVariantType) {
+ /* Add default dependency on 'commonMain' or 'commonTest' */
+ val commonMain = target.project.kotlinExtension.sourceSets.getByName(COMMON_MAIN_SOURCE_SET_NAME)
+ val commonTest = target.project.kotlinExtension.sourceSets.getByName(COMMON_TEST_SOURCE_SET_NAME)
+
+ when (variantType) {
+ AndroidVariantType.Main -> kotlinSourceSet.dependsOn(commonMain)
+ AndroidVariantType.UnitTest -> kotlinSourceSet.dependsOn(commonTest)
+ AndroidVariantType.InstrumentedTest, AndroidVariantType.Unknown -> Unit
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2SourceDirConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2SourceDirConfigurator.kt
new file mode 100644
index 00000000000..10bb687644c
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/MultiplatformLayoutV2SourceDirConfigurator.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceDirectorySet
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object MultiplatformLayoutV2SourceDirConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ val androidKotlinSourceDirectorySet = androidSourceSet.javaClass.getMethod("getKotlin")
+ .invoke(androidSourceSet) as AndroidSourceDirectorySet
+
+ androidKotlinSourceDirectorySet.srcDirs(target.project.provider { kotlinSourceSet.kotlin.srcDirs })
+ kotlinSourceSet.kotlin.srcDir(target.project.provider { androidSourceSet.java.srcDirs })
+ kotlinSourceSet.kotlin.srcDir("src/${androidSourceSet.name}/kotlin")
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/SingleTargetSourceDirConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/SingleTargetSourceDirConfigurator.kt
new file mode 100644
index 00000000000..f31a393bbf4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/configurator/SingleTargetSourceDirConfigurator.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.plugin.sources.android.configurator
+
+import com.android.build.gradle.api.AndroidSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
+
+internal object SingleTargetSourceDirConfigurator : KotlinAndroidSourceSetConfigurator {
+ override fun configure(target: KotlinAndroidTarget, kotlinSourceSet: KotlinSourceSet, androidSourceSet: AndroidSourceSet) {
+ kotlinSourceSet.kotlin.srcDir("src/${androidSourceSet.name}/kotlin")
+ kotlinSourceSet.kotlin.srcDir(target.project.provider { androidSourceSet.java.srcDirs })
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/kotlinAndroidSourceSetLayoutExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/kotlinAndroidSourceSetLayoutExtension.kt
new file mode 100644
index 00000000000..f7eb942a9b1
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/android/kotlinAndroidSourceSetLayoutExtension.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.plugin.sources.android
+
+import org.gradle.api.Project
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
+import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
+import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
+
+internal val Project.kotlinAndroidSourceSetLayout: KotlinAndroidSourceSetLayout
+ get() {
+ return if (kotlinExtension is KotlinMultiplatformExtension) {
+ when (val version = kotlinPropertiesProvider.mppAndroidSourceSetLayoutVersion) {
+ null, 1 -> multiplatformAndroidSourceSetLayoutV1
+ 2 -> multiplatformAndroidSourceSetLayoutV2
+ else -> throw IllegalArgumentException(
+ "Unsupported '${PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION}=$version' Supported versions: {1, 2}"
+ )
+ }
+ } else singleTargetAndroidSourceSetLayout
+ }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/AndroidProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/AndroidProjectHandler.kt
index 6590029af8b..93f9d8c7a36 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/AndroidProjectHandler.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/AndroidProjectHandler.kt
@@ -28,7 +28,6 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl
-import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage
@@ -36,7 +35,8 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.android.AndroidGradleWrapper
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
-import org.jetbrains.kotlin.gradle.plugin.mpp.syncKotlinAndAndroidSourceSets
+import org.jetbrains.kotlin.gradle.plugin.sources.android.KotlinAndroidSourceSets.setupKotlinAndroidSourceSets
+import org.jetbrains.kotlin.gradle.plugin.sources.android.findKotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
@@ -55,19 +55,14 @@ import java.util.concurrent.Callable
internal class AndroidProjectHandler(
private val kotlinConfigurationTools: KotlinConfigurationTools
) {
- companion object {
- fun kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget: KotlinAndroidTarget, androidSourceSetName: String) =
- lowerCamelCaseName(kotlinAndroidTarget.disambiguationClassifier, androidSourceSetName)
- }
-
private val logger = Logging.getLogger(this.javaClass)
fun configureTarget(kotlinAndroidTarget: KotlinAndroidTarget) {
- syncKotlinAndAndroidSourceSets(kotlinAndroidTarget)
-
val project = kotlinAndroidTarget.project
val ext = project.extensions.getByName("android") as BaseExtension
+ setupKotlinAndroidSourceSets(kotlinAndroidTarget)
+
val kotlinOptions = KotlinJvmOptionsImpl()
kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
@@ -109,7 +104,7 @@ internal class AndroidProjectHandler(
}
checkAndroidAnnotationProcessorDependencyUsage(project)
- addKotlinDependenciesToAndroidSourceSets(project, kotlinAndroidTarget)
+ addKotlinDependenciesToAndroidSourceSets(project)
}
project.includeKotlinToolingMetadataInApk()
@@ -123,10 +118,7 @@ internal class AndroidProjectHandler(
* source set dependencies as well, we need to add them to the Android source sets' api/implementation-like configurations,
* not just the classpath-like configurations of the variants.
*/
- private fun addKotlinDependenciesToAndroidSourceSets(
- project: Project,
- kotlinAndroidTarget: KotlinAndroidTarget
- ) {
+ private fun addKotlinDependenciesToAndroidSourceSets(project: Project) {
fun addDependenciesToAndroidSourceSet(
androidSourceSet: AndroidSourceSet,
apiConfigurationName: String,
@@ -154,8 +146,7 @@ internal class AndroidProjectHandler(
* see [org.jetbrains.kotlin.gradle.plugin.AndroidProjectHandler.configureTarget]
*/
(project.extensions.getByName("android") as BaseExtension).sourceSets.forEach { androidSourceSet ->
- val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, androidSourceSet.name)
- project.kotlinExtension.sourceSets.findByName(kotlinSourceSetName)?.let { kotlinSourceSet ->
+ project.findKotlinSourceSet(androidSourceSet)?.let { kotlinSourceSet ->
addDependenciesToAndroidSourceSet(
androidSourceSet,
kotlinSourceSet.apiConfigurationName,
@@ -212,10 +203,7 @@ internal class AndroidProjectHandler(
// Register the source only after the task is created, because the task is required for that:
compilation.source(defaultSourceSet)
- compilation.androidVariant.forEachKotlinSourceSet(
- compilation.target as KotlinAndroidTarget,
- project.kotlinExtension
- ) { kotlinSourceSet ->
+ compilation.androidVariant.forEachKotlinSourceSet(project) { kotlinSourceSet ->
compilation.source(kotlinSourceSet)
}
}
@@ -464,22 +452,17 @@ class AndroidTestedVariantArtifactsFilter(
}
internal inline fun BaseVariant.forEachKotlinSourceSet(
- kotlinAndroidTarget: KotlinAndroidTarget,
- kotlinExtension: KotlinProjectExtension,
- action: (KotlinSourceSet) -> Unit
+ project: Project, action: (KotlinSourceSet) -> Unit
) {
sourceSets
- .mapNotNull { provider ->
- val kotlinSourceSetName = AndroidProjectHandler.kotlinSourceSetNameForAndroidSourceSet(kotlinAndroidTarget, provider.name)
- kotlinExtension.sourceSets.findByName(kotlinSourceSetName)
- }
- .forEach(action)
+ .forEach { provider -> action(project.findKotlinSourceSet(provider) ?: return@forEach) }
}
-internal inline fun BaseVariant.forEachKotlinSourceDirectorySet(action: (SourceDirectorySet) -> Unit) {
+internal inline fun BaseVariant.forEachKotlinSourceDirectorySet(
+ project: Project, action: (SourceDirectorySet) -> Unit
+) {
sourceSets
- .mapNotNull { it.getExtension(KOTLIN_DSL_NAME) }
- .forEach(action)
+ .forEach { androidSourceSet -> action(project.findKotlinSourceSet(androidSourceSet)?.kotlin ?: return@forEach) }
}
internal inline fun BaseVariant.forEachJavaSourceDir(action: (ConfigurableFileTree) -> Unit) {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ObjectCollectionExt.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ObjectCollectionExt.kt
index 743972a5dc8..17cdf111ea9 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ObjectCollectionExt.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/ObjectCollectionExt.kt
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.DomainObjectCollection
+import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Task
import org.gradle.api.tasks.TaskCollection
import org.gradle.api.tasks.TaskProvider
@@ -13,8 +14,18 @@ import org.gradle.api.tasks.TaskProvider
internal inline fun DomainObjectCollection.withType(): DomainObjectCollection =
withType(S::class.java)
-
@Suppress("extension_shadowed_by_member", "UNCHECKED_CAST")
inline fun TaskCollection.named(name: String): TaskProvider =
(this as TaskCollection).named(name, T::class.java)
+internal fun NamedDomainObjectContainer.getOrCreate(
+ name: String,
+ invokeWhenCreated: ((T) -> Unit)? = null,
+ configure: ((T) -> Unit)? = null
+): T {
+ return if (name in names) named(name).also { provider ->
+ if (configure != null) provider.configure(configure)
+ }.get() else (if (configure != null) create(name, configure) else create(name)).also { value ->
+ if (invokeWhenCreated != null) invokeWhenCreated(value)
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/androidExtensionUitls.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/androidExtensionUitls.kt
new file mode 100644
index 00000000000..a797643cfa0
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/androidExtensionUitls.kt
@@ -0,0 +1,13 @@
+/*
+ * 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.utils
+
+import com.android.build.gradle.BaseExtension
+import org.gradle.api.Project
+
+internal val Project.androidExtensionOrNull: BaseExtension? get() = extensions.findByName("android")?.let { it as? BaseExtension }
+
+internal val Project.androidExtension: BaseExtension get() = extensions.getByName("android") as BaseExtension
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt
index 101db77e1b4..7eb55ffe4cb 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/MultiplatformExtensionTest.kt
@@ -15,6 +15,7 @@ import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_KPM_EXPERIMENTAL_MODEL_MAPPING
+import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_HIERARCHICAL_STRUCTURE_BY_DEFAULT
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_STDLIB_DEFAULT_DEPENDENCY
@@ -111,3 +112,7 @@ fun Project.enableKpmModelMapping(enabled: Boolean = true) {
fun Project.enableDefaultStdlibDependency(enabled: Boolean = true) {
project.propertiesExtension.set(KOTLIN_STDLIB_DEFAULT_DEPENDENCY, enabled.toString())
}
+
+fun Project.setAndroidMultiplatformSourceSetLayoutVersion(version: Int) {
+ project.propertiesExtension.set(KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION, version.toString())
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SyncKotlinAndAndroidSourceSetsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV1Test.kt
similarity index 81%
rename from libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SyncKotlinAndAndroidSourceSetsTest.kt
rename to libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV1Test.kt
index 800c135e71c..b8003566cc5 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/SyncKotlinAndAndroidSourceSetsTest.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/sources/android/MultiplatformAndroidSourceSetLayoutV1Test.kt
@@ -1,23 +1,27 @@
/*
- * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * 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")
+@file:Suppress("DEPRECATION", "DuplicatedCode", "FunctionName")
-package org.jetbrains.kotlin.gradle
+package org.jetbrains.kotlin.gradle.sources.android
import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.api.AndroidSourceSet
import org.gradle.api.Action
+import org.gradle.api.internal.HasConvention
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
+import org.jetbrains.kotlin.gradle.addBuildEventsListenerRegistryMock
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
-import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.sources.android.findKotlinSourceSet
+import org.jetbrains.kotlin.gradle.setAndroidMultiplatformSourceSetLayoutVersion
import kotlin.test.*
-class SyncKotlinAndAndroidSourceSetsTest {
+class MultiplatformAndroidSourceSetLayoutV1Test {
private lateinit var project: ProjectInternal
private lateinit var kotlin: KotlinMultiplatformExtension
@@ -27,6 +31,8 @@ class SyncKotlinAndAndroidSourceSetsTest {
fun setup() {
project = ProjectBuilder.builder().build() as ProjectInternal
addBuildEventsListenerRegistryMock(project)
+ project.setAndroidMultiplatformSourceSetLayoutVersion(1)
+
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("android-library")
@@ -36,7 +42,6 @@ class SyncKotlinAndAndroidSourceSetsTest {
/* Kotlin Setup */
kotlin = project.multiplatformExtension
-
}
@@ -93,21 +98,19 @@ class SyncKotlinAndAndroidSourceSetsTest {
@Test
fun `two product flavor dimensions`() {
android.flavorDimensions("pricing", "releaseType")
- android.productFlavors(
- Action {
- it.create("beta").dimension = "releaseType"
- it.create("production").dimension = "releaseType"
- it.create("free").dimension = "pricing"
- it.create("paid").dimension = "pricing"
- }
- )
+ android.productFlavors {
+ it.create("beta").dimension = "releaseType"
+ it.create("production").dimension = "releaseType"
+ it.create("free").dimension = "pricing"
+ it.create("paid").dimension = "pricing"
+ }
kotlin.android()
project.evaluate()
fun assertSourceSetsExist(androidName: String, kotlinName: String) {
val androidSourceSet = assertNotNull(android.sourceSets.findByName(androidName), "Expected Android source set '$androidName'")
val kotlinSourceSet = assertNotNull(kotlin.sourceSets.findByName(kotlinName), "Expected Kotlin source set '$kotlinName'")
- assertSame(kotlinSourceSet.kotlin, androidSourceSet.kotlinSourceSet)
+ assertSame(kotlinSourceSet, androidSourceSet.kotlinSourceSet)
}
assertSourceSetsExist("freeBetaDebug", "androidFreeBetaDebug")
@@ -178,18 +181,24 @@ class SyncKotlinAndAndroidSourceSetsTest {
}
@Test
- fun `AndroidSourceSet#kotlinSourceSet extension`() {
+ fun `AndroidSourceSet#kotlinSourceSet convention`() {
kotlin.android()
+ fun AndroidSourceSet.kotlinSourceSetByConvention(): KotlinSourceSet =
+ (this as HasConvention).convention.plugins["kotlin"] as KotlinSourceSet
+
val main = android.sourceSets.getByName("main")
- assertSame(kotlin.sourceSets.getByName("androidMain").kotlin, main.kotlinSourceSet)
+ assertSame(kotlin.sourceSets.getByName("androidMain"), main.kotlinSourceSetByConvention())
val test = android.sourceSets.getByName("test")
- assertSame(kotlin.sourceSets.getByName("androidTest").kotlin, test.kotlinSourceSet)
+ assertSame(kotlin.sourceSets.getByName("androidTest"), test.kotlinSourceSetByConvention())
val androidTest = android.sourceSets.getByName("androidTest")
- assertSame(kotlin.sourceSets.getByName("androidAndroidTest").kotlin, androidTest.kotlinSourceSet)
+ assertSame(kotlin.sourceSets.getByName("androidAndroidTest"), androidTest.kotlinSourceSetByConvention())
}
+
+ private val AndroidSourceSet.kotlinSourceSet
+ get() = project.findKotlinSourceSet(this) ?: fail("Missing KotlinSourceSet for AndroidSourceSet: $name")
}
private fun Set.allPairs(): Sequence> {
@@ -203,4 +212,4 @@ private fun Set.allPairs(): Sequence> {
}
}
}
-}
+}
\ No newline at end of file