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