[Gradle] Initial 'externalTargetApi' w/ compilable Android prototype
^KT-54766 Verification Pending
diff --git a/libraries/tools/kotlin-gradle-plugin-annotations/src/main/kotlin/org/jetbrains/kotlin/gradle/ExternalKotlinTargetApi.kt b/libraries/tools/kotlin-gradle-plugin-annotations/src/main/kotlin/org/jetbrains/kotlin/gradle/ExternalKotlinTargetApi.kt
new file mode 100644
index 0000000..d2339db
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-annotations/src/main/kotlin/org/jetbrains/kotlin/gradle/ExternalKotlinTargetApi.kt
@@ -0,0 +1,12 @@
+/*
+ * 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
+
+@RequiresOptIn(
+ message = "This API is intended to be used by Google to maintain KotlinTargets outside of kotlin.git",
+ level = RequiresOptIn.Level.ERROR
+)
+annotation class ExternalKotlinTargetApi
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts
index 7012a3d..4212e16 100644
--- a/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/build.gradle.kts
@@ -9,9 +9,16 @@
compileOnly(project(":kotlin-gradle-plugin"))
}
+configureKotlinCompileTasksGradleCompatibility()
+
+kotlin {
+ sourceSets.all {
+ languageSettings.optIn("org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi")
+ }
+}
+
/* This module is just for local development / prototyping and demos */
if (!kotlinBuildProperties.isTeamcityBuild) {
- tasks.register("install") {
- dependsOn(tasks.named("publishToMavenLocal"))
- }
+ publish()
+ standardPublicJars()
}
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTarget.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTarget.kt
new file mode 100644
index 0000000..003d47c
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTarget.kt
@@ -0,0 +1,27 @@
+/*
+ * 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 org.gradle.api.NamedDomainObjectContainer
+import org.gradle.kotlin.dsl.getByType
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.DecoratedExternalKotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTarget
+
+data class AndroidDsl(
+ var compileSdk: Int
+)
+
+class AndroidTarget(
+ private val target: ExternalKotlinTarget,
+ val androidDsl: AndroidDsl
+) : DecoratedExternalKotlinTarget(target) {
+ internal val kotlin = target.project.extensions.getByType<KotlinMultiplatformExtension>()
+
+ @Suppress("unchecked_cast")
+ override val compilations: NamedDomainObjectContainer<KotlinAndroidCompilation>
+ get() = target.compilations as NamedDomainObjectContainer<KotlinAndroidCompilation>
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetPrototype.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetPrototype.kt
new file mode 100644
index 0000000..1e3547d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidTargetPrototype.kt
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("DEPRECATION", "DuplicatedCode")
+
+package org.jetbrains.kotlin.gradle.android
+
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.internal.publishing.AndroidArtifacts
+import org.gradle.api.attributes.Usage
+import org.gradle.api.attributes.java.TargetJvmEnvironment
+import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE
+import org.gradle.kotlin.dsl.getByType
+import org.gradle.kotlin.dsl.named
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi
+import org.jetbrains.kotlin.gradle.kpm.external.project
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetDescriptor.DecoratedExternalTargetFactory
+
+@OptIn(ExternalVariantApi::class)
+fun KotlinMultiplatformExtension.androidTargetPrototype(): AndroidTarget {
+ val project = this.project
+ val androidExtension = project.extensions.getByType<AppExtension>()
+
+ /*
+ Set a variant filter and only allow 'debug'.
+ Reason: This prototype will not deal w/ buildTypes or flavors.
+ Only 'debug' will be supported. As of agreed w/ AGP team, this is the initial goal
+ for the APIs.
+ */
+ androidExtension.variantFilter { variant ->
+ if (variant.name != "debug") {
+ variant.ignore = true
+ }
+ }
+
+ /*
+ Create our 'AndroidTarget':
+ This uses the 'KotlinPlatformType.jvm' instead of androidJvm, since from the perspective of
+ Kotlin, this is just another 'jvm' like target (using the jvm compiler)
+ */
+ val androidTarget = createExternalKotlinTarget<AndroidTarget> {
+ targetName = "android"
+ platformType = KotlinPlatformType.jvm
+ decoratedExternalTargetFactory = DecoratedExternalTargetFactory { externalTarget ->
+ AndroidTarget(externalTarget, AndroidDsl(31))
+ }
+ }
+
+ /*
+ Whilst using the .all hook, we only expect the single 'debug' variant to be available through this API.
+ */
+ androidExtension.applicationVariants.all { applicationVariant ->
+ project.logger.quiet("Setting up applicationVariant: ${applicationVariant.name}")
+
+ /*
+ Create Compilations: main, unitTest and instrumentedTest
+ (as proposed in the new Multiplatform/Android SourceSetLayout v2)
+ */
+ val mainCompilation = androidTarget.createAndroidCompilation("main")
+ val unitTestCompilation = androidTarget.createAndroidCompilation("unitTest")
+ val instrumentedTestCompilation = androidTarget.createAndroidCompilation("instrumentedTest")
+
+ /*
+ Associate unitTest/instrumentedTest compilations with main
+ */
+ unitTestCompilation.associateWith(mainCompilation)
+ instrumentedTestCompilation.associateWith(mainCompilation)
+
+ /*
+ Setup dependsOn edges as in Multiplatform/Android SourceSetLayout v2:
+ android/main dependsOn commonMain
+ android/unitTest dependsOn commonTest
+ android/instrumentedTest *does not depend on a common SourceSet*
+ */
+ mainCompilation.defaultSourceSet.dependsOn(sourceSets.getByName("commonMain"))
+ unitTestCompilation.defaultSourceSet.dependsOn(sourceSets.getByName("commonTest"))
+
+ /*
+ Wire the Kotlin Compilations output (.class files) into the Android artifacts
+ by using the 'registerPreJavacGeneratedBytecode' function
+ */
+ applicationVariant.registerPreJavacGeneratedBytecode(mainCompilation.output.classesDirs)
+ applicationVariant.unitTestVariant.registerPreJavacGeneratedBytecode(unitTestCompilation.output.classesDirs)
+ applicationVariant.testVariant.registerPreJavacGeneratedBytecode(instrumentedTestCompilation.output.classesDirs)
+
+
+ /*
+ Add dependencies coming from Kotlin to Android by adding all dependencies from Kotlin to the variants
+ compileConfiguration or runtimeConfiguration
+ */
+ applicationVariant.compileConfiguration.extendsFrom(mainCompilation.configurations.compileDependencyConfiguration)
+ applicationVariant.runtimeConfiguration.extendsFrom(mainCompilation.configurations.runtimeDependencyConfiguration)
+ applicationVariant.unitTestVariant.compileConfiguration.extendsFrom(unitTestCompilation.configurations.compileDependencyConfiguration)
+ applicationVariant.unitTestVariant.runtimeConfiguration.extendsFrom(unitTestCompilation.configurations.runtimeDependencyConfiguration)
+ applicationVariant.testVariant.compileConfiguration.extendsFrom(instrumentedTestCompilation.configurations.compileDependencyConfiguration)
+ applicationVariant.testVariant.runtimeConfiguration.extendsFrom(instrumentedTestCompilation.configurations.runtimeDependencyConfiguration)
+
+
+ /*
+ Add the 'android boot classpath' to the compilation dependencies to compile against
+ */
+ mainCompilation.configurations.compileDependencyConfiguration.dependencies.add(
+ project.dependencies.create(project.androidBootClasspath())
+ )
+
+
+ /*
+ Setup apiElements configuration:
+ Usage: JAVA_API
+ jvmEnvironment: Android
+ variants:
+ - classes (provides access to the compiled .class files)
+ artifactType: CLASSES_JAR
+ */
+ project.configurations.getByName(androidTarget.apiElementsConfigurationName).apply {
+ attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_API))
+ attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
+ outgoing.variants.create("classes").let { variant ->
+ variant.attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
+ variant.artifact(mainCompilation.output.classesDirs.singleFile) {
+ it.builtBy(mainCompilation.output.classesDirs)
+ }
+ }
+ }
+
+
+ /*
+ Setup runtimeElements configuration:
+ Usage: JAVA_RUNTIME
+ jvmEnvironment: Android
+ variants:
+ - classes (provides access to the compiled .class files)
+ artifactType: CLASSES_JAR
+ */
+ project.configurations.getByName(androidTarget.runtimeElementsConfigurationName).apply {
+ attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
+ attributes.attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
+ outgoing.variants.create("classes").let { variant ->
+ variant.attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
+ variant.artifact(mainCompilation.output.classesDirs.singleFile) {
+ it.builtBy(mainCompilation.output.classesDirs)
+ }
+ }
+ }
+
+
+ /*
+ "Disable" configurations from plain Android plugin
+ This hack will not be necessary in the final implementation
+ */
+ project.configurations.findByName("${applicationVariant.name}ApiElements")?.isCanBeConsumed = false
+ project.configurations.findByName("${applicationVariant.name}RuntimeElements")?.isCanBeConsumed = false
+ }
+
+ return androidTarget
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidCompilation.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidCompilation.kt
new file mode 100644
index 0000000..9002322
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidCompilation.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
+
+package org.jetbrains.kotlin.gradle.android
+
+import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
+import org.jetbrains.kotlin.gradle.plugin.HasCompilerOptions
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilation
+
+class KotlinAndroidCompilation(delegate: Delegate) : ExternalKotlinCompilation(delegate) {
+ override val kotlinOptions: KotlinCommonOptions
+ get() = super.kotlinOptions as KotlinJvmOptions
+
+ @Suppress("UNCHECKED_CAST")
+ override val compilerOptions: HasCompilerOptions<KotlinJvmCompilerOptions>
+ get() = super.compilerOptions as HasCompilerOptions<KotlinJvmCompilerOptions>
+
+ var androidCompilationSpecificStuff = 10
+}
+
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidBootClasspath.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidBootClasspath.kt
new file mode 100644
index 0000000..14d4d1d
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidBootClasspath.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.androidBootClasspath(): FileCollection {
+ return project.files(Callable { project.extensions.getByType<BaseExtension>().bootClasspath })
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/createAndroidCompilation.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/createAndroidCompilation.kt
new file mode 100644
index 0000000..eb631c3
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/createAndroidCompilation.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.internal.publishing.AndroidArtifacts
+import org.gradle.api.attributes.java.TargetJvmEnvironment
+import org.gradle.kotlin.dsl.named
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.DecoratedKotlinCompilationFactory
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.createCompilation
+
+internal fun AndroidTarget.createAndroidCompilation(name: String): KotlinAndroidCompilation {
+ return createCompilation {
+ compilationName = name
+ defaultSourceSet = kotlin.sourceSets.maybeCreate(camelCase("prototype", targetName, name))
+ decoratedKotlinCompilationFactory = DecoratedKotlinCompilationFactory(::KotlinAndroidCompilation)
+ compileTaskName = camelCase("prototype", "compile", targetName, name)
+
+ /*
+ Replace Kotlin's compilation association (main <-> test) with noop,
+ since Android goes through adding a dependency on the project itself
+ */
+ compilationAssociator = ExternalKotlinCompilationDescriptor.CompilationAssociator { first, second ->
+ first.configurations.compileDependencyConfiguration.extendsFrom(
+ second.configurations.apiConfiguration,
+ second.configurations.implementationConfiguration,
+ second.configurations.compileOnlyConfiguration
+ )
+ }
+
+ /* Configure the compilation before it is accessible for user code */
+ configure { compilation ->
+ /* Setup attributes for the compile dependencies */
+ compilation.configurations.compileDependencyConfiguration.apply {
+ attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
+ attributes.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
+ }
+
+ /* Setup attributes for the runtime dependencies */
+ compilation.configurations.runtimeDependencyConfiguration?.apply {
+ attributes.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.CLASSES_JAR.type)
+ attributes.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, project.objects.named(TargetJvmEnvironment.ANDROID))
+ }
+ }
+ }
+}
diff --git a/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/utils.kt b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/utils.kt
new file mode 100644
index 0000000..c9cb725
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-tcs-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/utils.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.
+ */
+
+@file:Suppress("DEPRECATION")
+
+package org.jetbrains.kotlin.gradle.android
+
+fun camelCase(vararg parts: String): String {
+ if (parts.isEmpty()) return ""
+ return parts.joinToString("") { it.capitalize() }.decapitalize()
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt
index 4af920e..2f03dcb 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt
@@ -32,6 +32,7 @@
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.*
@@ -143,7 +144,7 @@
compilation.compileKotlinTaskProvider.map { it.outputs.files }
})
- if (compilation is KotlinJvmCompilation && compilation.target.withJavaEnabled) {
+ if (compilation is KotlinJvmCompilation && (compilation.target as? KotlinJvmTarget)?.withJavaEnabled == true) {
it.inputs.files({ compilation.compileJavaTaskProvider?.map { it.outputs.files } })
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinTarget.kt
index 5b3be3f..bc148b9 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinTarget.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinTarget.kt
@@ -20,16 +20,9 @@
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.publish.maven.MavenPublication
-import org.gradle.api.tasks.TaskProvider
-import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
-import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl
-import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl
-import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
-import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
-import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -84,61 +77,7 @@
}
override val components: Set<SoftwareComponent> by lazy {
- buildAdhocComponentsFromKotlinVariants(kotlinComponents)
- }
-
- private fun buildAdhocComponentsFromKotlinVariants(kotlinVariants: Set<KotlinTargetComponent>): Set<SoftwareComponent> {
- val softwareComponentFactoryClass = SoftwareComponentFactory::class.java
- // TODO replace internal API access with injection (not possible until we have this class on the compile classpath)
- val softwareComponentFactory = (project as ProjectInternal).services.get(softwareComponentFactoryClass)
-
- return kotlinVariants.map { kotlinVariant ->
- val adhocVariant = softwareComponentFactory.adhoc(kotlinVariant.name)
-
- project.whenEvaluated {
- (kotlinVariant as SoftwareComponentInternal).usages.filterIsInstance<KotlinUsageContext>().forEach { kotlinUsageContext ->
- val publishedConfigurationName = publishedConfigurationName(kotlinUsageContext.name)
- val configuration = project.configurations.findByName(publishedConfigurationName)
- ?: project.configurations.create(publishedConfigurationName).also { configuration ->
- configuration.isCanBeConsumed = false
- configuration.isCanBeResolved = false
- configuration.extendsFrom(project.configurations.getByName(kotlinUsageContext.dependencyConfigurationName))
- configuration.artifacts.addAll(kotlinUsageContext.artifacts)
-
- val attributes = kotlinUsageContext.attributes
- attributes.keySet().forEach {
- // capture type parameter T
- fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
- to.attribute(key, from.getAttribute(key)!!)
- }
- copyAttribute(it, attributes, configuration.attributes)
- }
- }
-
- adhocVariant.addVariantsFromConfiguration(configuration) { configurationVariantDetails ->
- val mavenScope = when (kotlinUsageContext.usage.name) {
- "java-api-jars" -> "compile"
- "java-runtime-jars" -> "runtime"
- else -> error("unexpected usage value '${kotlinUsageContext.usage.name}'")
- }
- configurationVariantDetails.mapToMavenScope(mavenScope)
- }
- }
- }
-
- adhocVariant as SoftwareComponent
-
- object : ComponentWithVariants, ComponentWithCoordinates, SoftwareComponentInternal {
- override fun getCoordinates() =
- (kotlinVariant as? ComponentWithCoordinates)?.coordinates ?: error("kotlinVariant is not ComponentWithCoordinates")
-
- override fun getVariants(): Set<SoftwareComponent> =
- (kotlinVariant as? KotlinVariantWithMetadataVariant)?.variants.orEmpty()
-
- override fun getName(): String = adhocVariant.name
- override fun getUsages(): MutableSet<out UsageContext> = (adhocVariant as SoftwareComponentInternal).usages
- }
- }.toSet()
+ project.buildAdhocComponentsFromKotlinVariants(kotlinComponents)
}
protected open fun createKotlinVariant(
@@ -228,3 +167,56 @@
internal fun javaApiUsageForMavenScoping() = "java-api-jars"
+internal fun Project.buildAdhocComponentsFromKotlinVariants(kotlinVariants: Set<KotlinTargetComponent>): Set<SoftwareComponent> {
+ val softwareComponentFactoryClass = SoftwareComponentFactory::class.java
+ // TODO replace internal API access with injection (not possible until we have this class on the compile classpath)
+ val softwareComponentFactory = (project as ProjectInternal).services.get(softwareComponentFactoryClass)
+
+ return kotlinVariants.map { kotlinVariant ->
+ val adhocVariant = softwareComponentFactory.adhoc(kotlinVariant.name)
+
+ project.whenEvaluated {
+ (kotlinVariant as SoftwareComponentInternal).usages.filterIsInstance<KotlinUsageContext>().forEach { kotlinUsageContext ->
+ val publishedConfigurationName = publishedConfigurationName(kotlinUsageContext.name)
+ val configuration = project.configurations.findByName(publishedConfigurationName)
+ ?: project.configurations.create(publishedConfigurationName).also { configuration ->
+ configuration.isCanBeConsumed = false
+ configuration.isCanBeResolved = false
+ configuration.extendsFrom(project.configurations.getByName(kotlinUsageContext.dependencyConfigurationName))
+ configuration.artifacts.addAll(kotlinUsageContext.artifacts)
+
+ val attributes = kotlinUsageContext.attributes
+ attributes.keySet().forEach {
+ // capture type parameter T
+ fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
+ to.attribute(key, from.getAttribute(key)!!)
+ }
+ copyAttribute(it, attributes, configuration.attributes)
+ }
+ }
+
+ adhocVariant.addVariantsFromConfiguration(configuration) { configurationVariantDetails ->
+ val mavenScope = when (kotlinUsageContext.usage.name) {
+ "java-api-jars" -> "compile"
+ "java-runtime-jars" -> "runtime"
+ else -> error("unexpected usage value '${kotlinUsageContext.usage.name}'")
+ }
+ configurationVariantDetails.mapToMavenScope(mavenScope)
+ }
+ }
+ }
+
+ adhocVariant as SoftwareComponent
+
+ object : ComponentWithVariants, ComponentWithCoordinates, SoftwareComponentInternal {
+ override fun getCoordinates() =
+ (kotlinVariant as? ComponentWithCoordinates)?.coordinates ?: error("kotlinVariant is not ComponentWithCoordinates")
+
+ override fun getVariants(): Set<SoftwareComponent> =
+ (kotlinVariant as? KotlinVariantWithMetadataVariant)?.variants.orEmpty()
+
+ override fun getName(): String = adhocVariant.name
+ override fun getUsages(): MutableSet<out UsageContext> = (adhocVariant as SoftwareComponentInternal).usages
+ }
+ }.toSet()
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationConfigurationsContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationConfigurationsContainer.kt
index e2ce160..8389969 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationConfigurationsContainer.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationConfigurationsContainer.kt
@@ -12,7 +12,7 @@
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler
-internal interface KotlinCompilationConfigurationsContainer {
+interface KotlinCompilationConfigurationsContainer {
val deprecatedCompileConfiguration: Configuration?
val deprecatedRuntimeConfiguration: Configuration?
val apiConfiguration: Configuration
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationFriendPathsResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationFriendPathsResolver.kt
index dee1b27..e3c091d 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationFriendPathsResolver.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationFriendPathsResolver.kt
@@ -32,7 +32,7 @@
/* Resolution of friend artifacts */
- interface FriendArtifactResolver {
+ fun interface FriendArtifactResolver {
fun resolveFriendArtifacts(compilation: InternalKotlinCompilation<*>): FileCollection
companion object {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationImpl.kt
index bef3662..0472e45 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationImpl.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationImpl.kt
@@ -25,16 +25,14 @@
import org.jetbrains.kotlin.gradle.utils.ObservableSet
import org.jetbrains.kotlin.tooling.core.MutableExtras
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
-import javax.inject.Inject
-
-internal class KotlinCompilationImpl @Inject constructor(
+internal class KotlinCompilationImpl internal constructor(
private val params: Params
) : InternalKotlinCompilation<KotlinCommonOptions> {
//region Params
- data class Params(
+ internal data class Params(
val target: KotlinTarget,
val compilationName: String,
val sourceSets: KotlinCompilationSourceSetsContainer,
@@ -61,7 +59,7 @@
override val extras: MutableExtras = mutableExtrasOf()
- val sourceSets get() = params.sourceSets
+ internal val sourceSets get() = params.sourceSets
override val configurations: KotlinCompilationConfigurationsContainer
get() = params.dependencyConfigurations
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt
new file mode 100644
index 0000000..ca58188
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/DecoratedExternalKotlinTarget.kt
@@ -0,0 +1,14 @@
+/*
+ * 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.mpp.external
+
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+
+@ExternalKotlinTargetApi
+abstract class DecoratedExternalKotlinTarget(
+ internal val delegate: ExternalKotlinTarget
+) : KotlinTarget by delegate
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilation.kt
new file mode 100644
index 0000000..4d9f90a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilation.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.
+ */
+
+@file:Suppress("DEPRECATION")
+
+package org.jetbrains.kotlin.gradle.plugin.mpp.external
+
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
+import org.jetbrains.kotlin.gradle.plugin.mpp.DecoratedKotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationImpl
+
+@ExternalKotlinTargetApi
+abstract class ExternalKotlinCompilation(delegate: Delegate) :
+ DecoratedKotlinCompilation<KotlinCommonOptions>(delegate.compilation) {
+ class Delegate internal constructor(internal val compilation: KotlinCompilationImpl)
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor.kt
new file mode 100644
index 0000000..38292eb
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.mpp.external
+
+import org.gradle.api.file.FileCollection
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.gradle.plugin.mpp.DecoratedKotlinCompilation
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.*
+import kotlin.properties.Delegates
+
+@ExternalKotlinTargetApi
+interface ExternalKotlinCompilationDescriptor<T : ExternalKotlinCompilation> {
+ fun interface DecoratedKotlinCompilationFactory<T : DecoratedKotlinCompilation<*>> {
+ fun create(delegate: ExternalKotlinCompilation.Delegate): T
+ }
+
+ fun interface FriendArtifactResolver<T : ExternalKotlinCompilation> {
+ fun resolveFriendPaths(compilation: T): FileCollection
+ }
+
+ fun interface CompilationAssociator<T : ExternalKotlinCompilation> {
+ fun associate(compilation: T, main: ExternalKotlinCompilation)
+ }
+
+ val compilationName: String
+ val compileTaskName: String?
+ val compileAllTaskName: String?
+ val defaultSourceSet: KotlinSourceSet
+ val decoratedKotlinCompilationFactory: DecoratedKotlinCompilationFactory<T>
+ val friendArtifactResolver: FriendArtifactResolver<T>?
+ val compilationAssociator: CompilationAssociator<T>?
+ val configure: ((T) -> Unit)?
+}
+
+@ExternalKotlinTargetApi
+fun <T : ExternalKotlinCompilation> ExternalKotlinCompilationDescriptor(
+ configure: ExternalKotlinCompilationDescriptorBuilder<T>.() -> Unit
+): ExternalKotlinCompilationDescriptor<T> {
+ return ExternalKotlinCompilationDescriptorBuilderImpl<T>().also(configure).run {
+ ExternalKotlinCompilationDescriptorImpl(
+ compilationName = compilationName,
+ compileTaskName = compileTaskName,
+ compileAllTaskName = compileAllTaskName,
+ defaultSourceSet = defaultSourceSet,
+ decoratedKotlinCompilationFactory = decoratedKotlinCompilationFactory,
+ friendArtifactResolver = friendArtifactResolver,
+ compilationAssociator = compilationAssociator,
+ configure = this.configure
+ )
+ }
+}
+
+@ExternalKotlinTargetApi
+interface ExternalKotlinCompilationDescriptorBuilder<T : ExternalKotlinCompilation> {
+ var compilationName: String
+ var compileTaskName: String?
+ var compileAllTaskName: String?
+ var defaultSourceSet: KotlinSourceSet
+ var decoratedKotlinCompilationFactory: DecoratedKotlinCompilationFactory<T>
+ var friendArtifactResolver: FriendArtifactResolver<T>?
+ var compilationAssociator: CompilationAssociator<T>?
+ var configure: ((T) -> Unit)?
+ fun configure(action: (T) -> Unit) = apply {
+ val configure = this.configure
+ if (configure == null) this.configure = action
+ else this.configure = { configure(it); action(it) }
+ }
+}
+
+@ExternalKotlinTargetApi
+private class ExternalKotlinCompilationDescriptorBuilderImpl<T : ExternalKotlinCompilation> :
+ ExternalKotlinCompilationDescriptorBuilder<T> {
+ override var compilationName: String by Delegates.notNull()
+ override var compileTaskName: String? = null
+ override var compileAllTaskName: String? = null
+ override var defaultSourceSet: KotlinSourceSet by Delegates.notNull()
+ override var decoratedKotlinCompilationFactory: DecoratedKotlinCompilationFactory<T> by Delegates.notNull()
+ override var friendArtifactResolver: FriendArtifactResolver<T>? = null
+ override var compilationAssociator: CompilationAssociator<T>? = null
+ override var configure: ((T) -> Unit)? = null
+}
+
+@ExternalKotlinTargetApi
+private data class ExternalKotlinCompilationDescriptorImpl<T : ExternalKotlinCompilation>(
+ override val compilationName: String,
+ override val compileTaskName: String?,
+ override val compileAllTaskName: String?,
+ override val defaultSourceSet: KotlinSourceSet,
+ override val decoratedKotlinCompilationFactory: DecoratedKotlinCompilationFactory<T>,
+ override val friendArtifactResolver: FriendArtifactResolver<T>?,
+ override val compilationAssociator: CompilationAssociator<T>?,
+ override val configure: ((T) -> Unit)?
+) : ExternalKotlinCompilationDescriptor<T>
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTarget.kt
new file mode 100644
index 0000000..7b4d710
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTarget.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.mpp.external
+
+import org.gradle.api.Action
+import org.gradle.api.NamedDomainObjectContainer
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.attributes.AttributeContainer
+import org.gradle.api.component.SoftwareComponent
+import org.gradle.api.publish.maven.MavenPublication
+import org.gradle.api.tasks.TaskProvider
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.KotlinTargetComponent
+import org.jetbrains.kotlin.gradle.plugin.mpp.buildAdhocComponentsFromKotlinVariants
+
+@ExternalKotlinTargetApi
+class ExternalKotlinTarget internal constructor(
+ override val project: Project,
+ override val targetName: String,
+ override val platformType: KotlinPlatformType,
+ val defaultConfiguration: Configuration,
+ val apiElementsConfiguration: Configuration,
+ val runtimeElementsConfiguration: Configuration,
+ override val publishable: Boolean,
+ internal val kotlinComponents: Set<KotlinTargetComponent>,
+ private val artifactsTaskLocator: ArtifactsTaskLocator,
+) : KotlinTarget {
+
+ fun interface ArtifactsTaskLocator {
+ fun locate(target: ExternalKotlinTarget): TaskProvider<out Task>
+ }
+
+ val kotlin = project.multiplatformExtension
+
+ override val useDisambiguationClassifierAsSourceSetNamePrefix: Boolean = true
+
+ override val overrideDisambiguationClassifierOnIdeImport: String? = null
+
+ val artifactsTask: TaskProvider<out Task> by lazy {
+ artifactsTaskLocator.locate(this)
+ }
+
+ override val artifactsTaskName: String
+ get() = artifactsTask.name
+
+ override val defaultConfigurationName: String
+ get() = defaultConfiguration.name
+
+ override val apiElementsConfigurationName: String
+ get() = apiElementsConfiguration.name
+
+ override val runtimeElementsConfigurationName: String
+ get() = runtimeElementsConfiguration.name
+
+ override val components: Set<SoftwareComponent> by lazy {
+ project.buildAdhocComponentsFromKotlinVariants(kotlinComponents)
+ }
+
+ override val compilations: NamedDomainObjectContainer<ExternalKotlinCompilation> by lazy {
+ project.container(ExternalKotlinCompilation::class.java)
+ }
+
+ override fun mavenPublication(action: Action<MavenPublication>) {
+ TODO("Not yet implemented")
+ }
+
+ override val preset: Nothing? = null
+
+ override fun getAttributes(): AttributeContainer {
+ TODO("Not yet implemented")
+ }
+
+ internal fun onCreated() {
+ artifactsTask
+ components
+ }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetDescriptor.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetDescriptor.kt
new file mode 100644
index 0000000..47707de
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetDescriptor.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.mpp.external
+
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetDescriptor.DecoratedExternalTargetFactory
+import kotlin.properties.Delegates
+
+@ExternalKotlinTargetApi
+interface ExternalKotlinTargetDescriptor<T : DecoratedExternalKotlinTarget> {
+
+ fun interface DecoratedExternalTargetFactory<T : DecoratedExternalKotlinTarget> {
+ fun create(target: ExternalKotlinTarget): T
+ }
+
+ val targetName: String
+ val platformType: KotlinPlatformType
+ val decoratedExternalTargetFactory: DecoratedExternalTargetFactory<T>
+ val configure: ((T) -> Unit)?
+}
+
+@ExternalKotlinTargetApi
+interface ExternalKotlinTargetDescriptorBuilder<T : DecoratedExternalKotlinTarget> {
+ var targetName: String
+ var platformType: KotlinPlatformType
+ var decoratedExternalTargetFactory: DecoratedExternalTargetFactory<T>
+ var configure: ((T) -> Unit)?
+ fun configure(action: (T) -> Unit) = apply {
+ val configure = this.configure
+ if (configure == null) this.configure = action
+ else this.configure = { configure(it); action(it) }
+ }
+}
+
+@ExternalKotlinTargetApi
+fun <T : DecoratedExternalKotlinTarget> ExternalKotlinTargetDescriptor(
+ configure: ExternalKotlinTargetDescriptorBuilder<T>.() -> Unit
+): ExternalKotlinTargetDescriptor<T> {
+ return ExternalKotlinTargetDescriptorBuilderImpl<T>().also(configure).build()
+}
+
+@ExternalKotlinTargetApi
+private class ExternalKotlinTargetDescriptorBuilderImpl<T : DecoratedExternalKotlinTarget> : ExternalKotlinTargetDescriptorBuilder<T> {
+ override var targetName: String by Delegates.notNull()
+ override var platformType: KotlinPlatformType by Delegates.notNull()
+ override var decoratedExternalTargetFactory: DecoratedExternalTargetFactory<T> by Delegates.notNull()
+ override var configure: ((T) -> Unit)? = null
+
+ fun build(): ExternalKotlinTargetDescriptorImpl<T> = ExternalKotlinTargetDescriptorImpl(
+ targetName = targetName,
+ platformType = platformType,
+ decoratedExternalTargetFactory = decoratedExternalTargetFactory,
+ configure = configure
+ )
+}
+
+@ExternalKotlinTargetApi
+private data class ExternalKotlinTargetDescriptorImpl<T : DecoratedExternalKotlinTarget>(
+ override val targetName: String,
+ override val platformType: KotlinPlatformType,
+ override val decoratedExternalTargetFactory: DecoratedExternalTargetFactory<T>,
+ override val configure: ((T) -> Unit)?
+) : ExternalKotlinTargetDescriptor<T>
+
+
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinCompilation.kt
new file mode 100644
index 0000000..d11f31b
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinCompilation.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.mpp.external
+
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor
+import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationAssociator
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationFriendPathsResolver
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationAssociator
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetsContainer
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory.KotlinCompilationTaskNamesContainerFactory
+import org.jetbrains.kotlin.gradle.plugin.mpp.decoratedInstance
+import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilation.Delegate
+import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
+import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
+
+@ExternalKotlinTargetApi
+fun <T : ExternalKotlinCompilation> DecoratedExternalKotlinTarget.createCompilation(
+ descriptor: ExternalKotlinCompilationDescriptor<T>
+): T {
+ val compilationImplFactory = KotlinCompilationImplFactory(
+ compilerOptionsFactory = when (platformType) {
+ KotlinPlatformType.common -> KotlinMultiplatformCommonCompilerOptionsFactory
+ KotlinPlatformType.jvm -> KotlinJvmCompilerOptionsFactory
+ KotlinPlatformType.androidJvm -> KotlinJvmCompilerOptionsFactory
+ KotlinPlatformType.js -> KotlinJsCompilerOptionsFactory
+ KotlinPlatformType.native -> KotlinNativeCompilerOptionsFactory
+ KotlinPlatformType.wasm -> KotlinMultiplatformCommonCompilerOptionsFactory
+ },
+ compilationSourceSetsContainerFactory = { _, _ -> KotlinCompilationSourceSetsContainer(descriptor.defaultSourceSet) },
+ compilationTaskNamesContainerFactory = KotlinCompilationTaskNamesContainerFactory { target, compilationName ->
+ val default = DefaultKotlinCompilationTaskNamesContainerFactory.create(target, compilationName)
+ default.copy(
+ compileTaskName = descriptor.compileTaskName ?: default.compileTaskName,
+ compileAllTaskName = descriptor.compileAllTaskName ?: default.compileAllTaskName
+ )
+ },
+ compilationAssociator = descriptor.compilationAssociator?.let { declaredAssociator ->
+ @Suppress("unchecked_cast")
+ KotlinCompilationAssociator { _, first, second ->
+ declaredAssociator.associate(first.decoratedInstance as T, second.decoratedInstance as ExternalKotlinCompilation)
+ }
+ } ?: DefaultKotlinCompilationAssociator,
+ compilationFriendPathsResolver = DefaultKotlinCompilationFriendPathsResolver(
+ DefaultKotlinCompilationFriendPathsResolver.FriendArtifactResolver.composite(
+ DefaultKotlinCompilationFriendPathsResolver.DefaultFriendArtifactResolver,
+ descriptor.friendArtifactResolver?.let { declaredResolver ->
+ DefaultKotlinCompilationFriendPathsResolver.FriendArtifactResolver { compilation ->
+ @Suppress("unchecked_cast")
+ declaredResolver.resolveFriendPaths(compilation as T)
+ }
+ }
+ )
+ )
+ )
+
+ val compilationImpl = compilationImplFactory.create(this, descriptor.compilationName)
+ val decoratedCompilation = descriptor.decoratedKotlinCompilationFactory.create(Delegate(compilationImpl))
+ descriptor.configure?.invoke(decoratedCompilation)
+ this.delegate.compilations.add(decoratedCompilation)
+
+
+ val tasksProvider = KotlinTasksProvider()
+ val compilationInfo = KotlinCompilationInfo(decoratedCompilation)
+
+ val config = KotlinCompileConfig(compilationInfo)
+ config.configureTask { task ->
+ task.useModuleDetection.value(true).disallowChanges()
+ task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/debug"))
+ }
+
+ Kotlin2JvmSourceSetProcessor(tasksProvider, compilationInfo).run()
+ project.logger.quiet("Registered: ${compilationInfo.compileKotlinTaskName}")
+
+ return decoratedCompilation
+}
+
+@ExternalKotlinTargetApi
+fun <T : ExternalKotlinCompilation> DecoratedExternalKotlinTarget.createCompilation(
+ descriptor: ExternalKotlinCompilationDescriptorBuilder<T>.() -> Unit
+): T {
+ return createCompilation(ExternalKotlinCompilationDescriptor(descriptor))
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt
new file mode 100644
index 0000000..0c55646
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/createExternalKotlinTarget.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExternalKotlinTargetApi::class)
+
+package org.jetbrains.kotlin.gradle.plugin.mpp.external
+
+import org.gradle.jvm.tasks.Jar
+import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
+import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
+
+@ExternalKotlinTargetApi
+fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExternalKotlinTarget(
+ descriptor: ExternalKotlinTargetDescriptor<T>
+): T {
+ val defaultConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "default"))
+ val apiElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "apiElements"))
+ val runtimeElementsConfiguration = project.configurations.maybeCreate(lowerCamelCaseName(descriptor.targetName, "runtimeElements"))
+ val artifactsTaskLocator = ExternalKotlinTarget.ArtifactsTaskLocator { target ->
+ target.project.locateOrRegisterTask<Jar>(lowerCamelCaseName(descriptor.targetName, "jar"))
+ }
+
+ val target = ExternalKotlinTarget(
+ project = project,
+ targetName = descriptor.targetName,
+ platformType = descriptor.platformType,
+ defaultConfiguration = defaultConfiguration,
+ apiElementsConfiguration = apiElementsConfiguration,
+ runtimeElementsConfiguration = runtimeElementsConfiguration,
+ publishable = true,
+ kotlinComponents = emptySet(),
+ artifactsTaskLocator = artifactsTaskLocator
+ )
+
+ val decorated = descriptor.decoratedExternalTargetFactory.create(target)
+ target.onCreated()
+ descriptor.configure?.invoke(decorated)
+ targets.add(decorated)
+ return decorated
+}
+
+@ExternalKotlinTargetApi
+fun <T : DecoratedExternalKotlinTarget> KotlinMultiplatformExtension.createExternalKotlinTarget(
+ descriptor: ExternalKotlinTargetDescriptorBuilder<T>.() -> Unit
+): T {
+ return createExternalKotlinTarget(ExternalKotlinTargetDescriptor(descriptor))
+}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/buildProject.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/buildProject.kt
index dac5e8f..a6a1189 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/buildProject.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/buildProject.kt
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle
+import com.android.build.api.dsl.ApplicationExtension
import com.android.build.gradle.LibraryExtension
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
@@ -13,7 +14,6 @@
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.kpm.applyKpmPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
-import org.jetbrains.kotlin.gradle.plugin.extraProperties
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
fun buildProject(
@@ -51,8 +51,13 @@
fun Project.androidLibrary(code: LibraryExtension.() -> Unit) {
plugins.findPlugin("com.android.library") ?: plugins.apply("com.android.library")
- val androidExtension = project.extensions.findByName("android") as? LibraryExtension
- ?: throw IllegalStateException("Android library extension is missing in project")
+ val androidExtension = project.extensions.getByName("android") as LibraryExtension
+ androidExtension.code()
+}
+
+fun Project.androidApplication(code: ApplicationExtension.() -> Unit) {
+ plugins.findPlugin("com.android.application") ?: plugins.apply("com.android.application")
+ val androidExtension = project.extensions.getByName("android") as ApplicationExtension
androidExtension.code()
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/externalTargetApi/ExternalAndroidTargetPrototypeSmokeTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/externalTargetApi/ExternalAndroidTargetPrototypeSmokeTest.kt
new file mode 100644
index 0000000..92c9f61
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/externalTargetApi/ExternalAndroidTargetPrototypeSmokeTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("FunctionName")
+
+package org.jetbrains.kotlin.gradle.externalTargetApi
+
+import org.jetbrains.kotlin.gradle.android.androidTargetPrototype
+import org.jetbrains.kotlin.gradle.androidApplication
+import org.jetbrains.kotlin.gradle.buildProjectWithMPP
+import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
+import org.junit.Test
+import kotlin.test.assertEquals
+
+class ExternalAndroidTargetPrototypeSmokeTest {
+
+ @Test
+ fun `apply prototype - evaluate - compilations exist`() {
+ val project = buildProjectWithMPP()
+ project.androidApplication { compileSdk = 31 }
+ val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
+ project.evaluate()
+
+ assertEquals(
+ setOf("main", "unitTest", "instrumentedTest"),
+ androidTargetPrototype.compilations.map { it.name }.toSet()
+ )
+ }
+
+ @Test
+ fun `apply prototype - evaluate - configurations can be resolved`() {
+ val project = buildProjectWithMPP()
+ project.androidApplication { compileSdk = 31 }
+
+ val androidTargetPrototype = project.multiplatformExtension.androidTargetPrototype()
+ project.evaluate()
+
+ androidTargetPrototype.compilations.all { compilation ->
+ compilation.compileDependencyFiles.files
+ compilation.runtimeDependencyFiles?.files
+ }
+ }
+}
\ No newline at end of file