[KPM] Implement kotlin-gradle-plugin-kpm-android module for prototyping

This commit is contained in:
sebastian.sellmair
2022-01-19 11:53:55 +01:00
committed by Space
parent 590bf6ed9a
commit d5823a5f7b
10 changed files with 246 additions and 0 deletions
@@ -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"))
}
}
@@ -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<BaseExtension>().bootClasspath })
}
@@ -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<AppExtension>()?.applicationVariants?.all { androidVariant ->
main { createKotlinAndroidVariant(androidVariant) }
test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
instrumentedTest { createKotlinAndroidVariant(androidVariant.testVariant ?: return@instrumentedTest) }
}
project.extensions.findByType<LibraryExtension>()?.libraryVariants?.all { androidVariant ->
main { createKotlinAndroidVariant(androidVariant) }
test { createKotlinAndroidVariant(androidVariant.unitTestVariant ?: return@test) }
instrumentedTest { createKotlinAndroidVariant(androidVariant.testVariant ?: return@instrumentedTest) }
}
}
@@ -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<Project> {
override fun apply(target: Project) {
target.logger.quiet("Applied Kotlin/Android KPM prototype. This is just a proof of concept implementation.")
}
}
@@ -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<KotlinJvmVariant> { 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<KotlinJvmVariant> {
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()
)
}
@@ -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)
}
@@ -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)
}