diff --git a/build.gradle.kts b/build.gradle.kts
index 4d9297316a2..f264ceaeff0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -360,6 +360,7 @@ val projectsWithDisabledFirBootstrap = coreLibProjects + listOf(
val gradlePluginProjects = listOf(
":kotlin-gradle-plugin",
":kotlin-gradle-plugin-api",
+ ":kotlin-gradle-plugin-kpm-android",
":kotlin-allopen",
":kotlin-annotation-processing-gradle",
":kotlin-noarg",
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 6617592750b..356d3b230aa 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -211,6 +211,11 @@
+
+
+
+
+
@@ -595,6 +600,11 @@
+
+
+
+
+
@@ -611,6 +621,11 @@
+
+
+
+
+
@@ -653,6 +668,11 @@
+
+
+
+
+
@@ -689,6 +709,11 @@
+
+
+
+
+
@@ -777,6 +802,11 @@
+
+
+
+
+
@@ -803,6 +833,11 @@
+
+
+
+
+
@@ -859,6 +894,11 @@
+
+
+
+
+
@@ -1861,6 +1901,7 @@
+
@@ -5787,6 +5828,7 @@
+
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts
new file mode 100644
index 00000000000..325e43c0519
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts
@@ -0,0 +1,27 @@
+plugins {
+ kotlin("jvm")
+ `java-gradle-plugin`
+ `maven-publish`
+}
+
+dependencies {
+ implementation(gradleKotlinDsl())
+ compileOnly(project(":kotlin-gradle-plugin"))
+ compileOnly("com.android.tools.build:gradle:7.0.0")
+}
+
+gradlePlugin {
+ plugins {
+ create("kotlinAndroidKpmPlugin") {
+ id = "kotlin-kpm-android"
+ implementationClass = "org.jetbrains.kotlin.gradle.android.KotlinAndroidKpmPlugin"
+ }
+ }
+}
+
+/* This module is just for local development / prototyping and demos */
+if (!kotlinBuildProperties.isTeamcityBuild) {
+ tasks.register("install") {
+ dependsOn(tasks.named("publishToMavenLocal"))
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidRuntimeJars.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidRuntimeJars.kt
new file mode 100644
index 00000000000..047df9c76f4
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidRuntimeJars.kt
@@ -0,0 +1,16 @@
+/*
+ * 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.android
+
+import com.android.build.gradle.BaseExtension
+import org.gradle.api.Project
+import org.gradle.api.file.FileCollection
+import org.gradle.kotlin.dsl.getByType
+import java.util.concurrent.Callable
+
+internal fun Project.getAndroidRuntimeJars(): FileCollection {
+ return project.files(Callable { project.extensions.getByType().bootClasspath })
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetExtension.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetExtension.kt
new file mode 100644
index 00000000000..c094a95a618
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetExtension.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+@file:Suppress("DEPRECATION")
+@file:OptIn(ExternalVariantApi::class)
+
+package org.jetbrains.kotlin.gradle.android
+
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.LibraryExtension
+import org.gradle.kotlin.dsl.findByType
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalVariantApi
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.project
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
+
+fun KotlinPm20ProjectExtension.android() {
+ project.extensions.findByType()?.applicationVariants?.all { androidVariant ->
+ main { createKotlinAndroidVariant(androidVariant) }
+ test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
+ instrumentedTest { createKotlinAndroidVariant(androidVariant.testVariant ?: return@instrumentedTest) }
+ }
+
+ project.extensions.findByType()?.libraryVariants?.all { androidVariant ->
+ main { createKotlinAndroidVariant(androidVariant) }
+ test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
+ instrumentedTest { createKotlinAndroidVariant(androidVariant.testVariant ?: return@instrumentedTest) }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidKpmPlugin.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidKpmPlugin.kt
new file mode 100644
index 00000000000..680ee3d7579
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidKpmPlugin.kt
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+@file:Suppress("unused")
+
+package org.jetbrains.kotlin.gradle.android
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class KotlinAndroidKpmPlugin : Plugin {
+ override fun apply(target: Project) {
+ target.logger.quiet("Applied Kotlin/Android KPM prototype. This is just a proof of concept implementation.")
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt
new file mode 100644
index 00000000000..a43a53f9ab0
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+@file:Suppress("DEPRECATION")
+@file:OptIn(ExternalVariantApi::class)
+
+package org.jetbrains.kotlin.gradle.android
+
+import com.android.build.api.attributes.BuildTypeAttr
+import com.android.build.gradle.api.BaseVariant
+import com.android.build.gradle.api.LibraryVariant
+import com.android.build.gradle.internal.publishing.AndroidArtifacts
+import org.gradle.api.attributes.java.TargetJvmEnvironment
+import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE
+import org.gradle.kotlin.dsl.named
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalVariantApi
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.createExternalJvmVariant
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
+
+fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) {
+ val androidOutgoingArtifacts = FragmentArtifacts { fragment ->
+ variants.create("classes") { variant ->
+ variant.attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
+ variant.artifact(project.provider { fragment.compilationOutputs.classesDirs.singleFile }) {
+ it.builtBy(fragment.compilationOutputs.classesDirs)
+ }
+ }
+
+ if (androidVariant is LibraryVariant) {
+ variants.create("aar") { variant ->
+ variant.attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.AAR.type)
+ variant.artifact(androidVariant.packageLibraryProvider)
+ }
+ }
+ }
+
+ val androidElementsAttributes = FragmentAttributes {
+ attribute(BuildTypeAttr.ATTRIBUTE, project.objects.named(androidVariant.buildType.name))
+ attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
+ }
+
+ val kotlinVariant = createExternalJvmVariant(
+ "android${androidVariant.buildType.name.capitalize()}", KotlinJvmVariantConfig(
+ /* Only swap out configuration that is used. Default setup shall still be applied */
+ compileDependencies = DefaultKotlinCompileDependenciesDefinition
+ .withConfigurationProvider { androidVariant.compileConfiguration },
+
+ /* Only swap out configuration that is used. Default setup shall still be applied */
+ runtimeDependencies = DefaultKotlinRuntimeDependenciesDefinition
+ .withConfigurationProvider { androidVariant.runtimeConfiguration },
+
+ /* Add android artifacts and attributes */
+ apiElements = DefaultKotlinApiElementsDefinition + androidElementsAttributes + androidOutgoingArtifacts,
+
+ /* Add android artifacts and attributes */
+ runtimeElements = DefaultKotlinRuntimeElementsDefinition + androidElementsAttributes + androidOutgoingArtifacts,
+
+ /* For now: Just publish 'release' (non-debuggable) variants */
+ publicationConfigurator = if (androidVariant.buildType.isDebuggable) KotlinPublicationConfigurator.NoPublication else
+ KotlinPublicationConfigurator.SingleVariantPublication
+ )
+ )
+
+ // "Disable" configurations from plain Android plugin
+ project.configurations.findByName("${androidVariant.name}ApiElements")?.isCanBeConsumed = false
+ project.configurations.findByName("${androidVariant.name}RuntimeElements")?.isCanBeConsumed = false
+
+ // TODO: Move this into configurator!
+ kotlinVariant.refines(androidCommon)
+
+ val mainBytecodeKey = androidVariant.registerPreJavacGeneratedBytecode(
+ kotlinVariant.compilationOutputs.classesDirs
+ )
+
+ kotlinVariant.compileDependencyFiles = project.files(
+ androidVariant.getCompileClasspath(mainBytecodeKey),
+ project.getAndroidRuntimeJars()
+ )
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt
new file mode 100644
index 00000000000..c0c35b8e887
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.android
+
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
+
+val KotlinGradleModule.androidCommon: KotlinGradleFragment
+ get() = fragments.findByName("android") ?: fragments.create("android") { fragment ->
+ fragment.refines(common)
+ }
diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/instrumentedTest.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/instrumentedTest.kt
new file mode 100644
index 00000000000..55934269839
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/instrumentedTest.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.gradle.android
+
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
+
+val KotlinPm20ProjectExtension.instrumentedTest
+ get() = modules.maybeCreate("instrumentedTest")
+
+fun KotlinPm20ProjectExtension.instrumentedTest(configure: KotlinGradleModule.() -> Unit) {
+ instrumentedTest.apply(configure)
+}
diff --git a/settings.gradle b/settings.gradle
index 07dbc21e7c4..5c2e5f3c6c9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -232,6 +232,7 @@ include ":benchmarks",
":kotlin-gradle-statistics",
":kotlin-gradle-build-metrics",
":kotlin-gradle-plugin",
+ ":kotlin-gradle-plugin-kpm-android",
":kotlin-gradle-plugin-model",
":kotlin-gradle-plugin-test-utils-embeddable",
":kotlin-gradle-plugin-integration-tests",
@@ -630,6 +631,7 @@ project(':kotlin-gradle-plugin-npm-versions-codegen').projectDir = "$rootDir/lib
project(':kotlin-gradle-statistics').projectDir = "$rootDir/libraries/tools/kotlin-gradle-statistics" as File
project(':kotlin-gradle-build-metrics').projectDir = "$rootDir/libraries/tools/kotlin-gradle-build-metrics" as File
project(':kotlin-gradle-plugin').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin" as File
+project(':kotlin-gradle-plugin-kpm-android').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-kpm-android" as File
project(':kotlin-gradle-plugin-model').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-model" as File
project(':kotlin-gradle-plugin-test-utils-embeddable').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-test-utils-embeddable" as File
project(':kotlin-gradle-plugin-integration-tests').projectDir = "$rootDir/libraries/tools/kotlin-gradle-plugin-integration-tests" as File