[Gradle] Implement SourceSetTreeClassifier API

- This new API will be configurable for 'External Kotlin Targets'
- This change also moves targetHierarchy.android {} into the androidTarget
to avoid potential confusion around the KGP managed AndroidTarget vs
the Google implementation.

KT-58710
This commit is contained in:
Sebastian Sellmair
2023-05-16 11:52:10 +02:00
committed by Space Team
parent dbfa8d4c29
commit ff7e4f7986
17 changed files with 501 additions and 200 deletions
@@ -142,16 +142,6 @@ public abstract interface class org/jetbrains/kotlin/gradle/dsl/KaptJavacOption
public abstract fun option (Ljava/lang/Object;Ljava/lang/Object;)V
}
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinAndroidTargetHierarchyDsl {
public abstract fun getInstrumentedTest ()Lorg/jetbrains/kotlin/gradle/dsl/KotlinAndroidVariantHierarchyDsl;
public abstract fun getMain ()Lorg/jetbrains/kotlin/gradle/dsl/KotlinAndroidVariantHierarchyDsl;
public abstract fun getUnitTest ()Lorg/jetbrains/kotlin/gradle/dsl/KotlinAndroidVariantHierarchyDsl;
}
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinAndroidVariantHierarchyDsl {
public abstract fun getSourceSetTree ()Lorg/gradle/api/provider/Property;
}
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinArtifact : org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware {
public abstract fun getArtifactName ()Ljava/lang/String;
public abstract fun getModules ()Ljava/util/Set;
@@ -530,11 +520,9 @@ public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinNativeXCFr
}
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl {
public abstract fun android (Lkotlin/jvm/functions/Function1;)V
public abstract fun apply (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptor;Lkotlin/jvm/functions/Function1;)V
public abstract fun custom (Lkotlin/jvm/functions/Function1;)V
public abstract fun default (Lkotlin/jvm/functions/Function1;)V
public abstract fun getAndroid ()Lorg/jetbrains/kotlin/gradle/dsl/KotlinAndroidTargetHierarchyDsl;
}
public final class org/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl$DefaultImpls {
@@ -1105,6 +1093,7 @@ public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinTargetH
public abstract fun withAndroidNativeArm64 ()V
public abstract fun withAndroidNativeX64 ()V
public abstract fun withAndroidNativeX86 ()V
public abstract fun withAndroidTarget ()V
public abstract fun withApple ()V
public abstract fun withCompilations (Lkotlin/jvm/functions/Function1;)V
public abstract fun withIos ()V
@@ -170,72 +170,4 @@ interface KotlinTargetHierarchyDsl {
* ```
*/
fun custom(describe: KotlinTargetHierarchyBuilder.Root.() -> Unit)
/**
* Configure Android specific settings within the context of [KotlinTargetHierarchy].
* The difference between Android and other targets is that the build author is free to choose
* the names of compilations, whereas Android is using predefined SourceSet names.
*
* ### Default dependsOn edges
* By default, Kotlin Multiplatform will set the following default dependsOn edges:
* - `androidMain` -> `commonMain`
* - `androidUnitTest` -> `commonTest`
*
* In this default setup, SourceSets like `androidInstrumentedTest` will *not* dependOn `commonTest`.
* This API can be used to change the default behavior
*
* #### Example 1: Setting up androidInstrumentedTest -> commonTest
* This can be done by putting the 'androidInstrumentedTest' variants into the 'test' [SourceSetTree]:
* ```kotlin
* targetHierarchy.android {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* ```
*
* #### Example 2: Setting up androidInstrumentedTest -> commonTest and removing 'unitTests' from the 'test' [SourceSetTree]
* ```kotlin
* targetHierarchy.android {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* unitTest.sourceSetTree.set(SourceSetTree.unitTest) // ! <- Anything *other* than 'test'
* }
* ```
*/
@ExperimentalKotlinGradlePluginApi
fun android(configure: KotlinAndroidTargetHierarchyDsl.() -> Unit)
/**
* See [android]
*/
@ExperimentalKotlinGradlePluginApi
val android: KotlinAndroidTargetHierarchyDsl
}
@ExperimentalKotlinGradlePluginApi
interface KotlinAndroidTargetHierarchyDsl {
val main: KotlinAndroidVariantHierarchyDsl
val unitTest: KotlinAndroidVariantHierarchyDsl
val instrumentedTest: KotlinAndroidVariantHierarchyDsl
}
@ExperimentalKotlinGradlePluginApi
interface KotlinAndroidVariantHierarchyDsl {
/**
* Configures under which [SourceSetTree] the currently configured Android Variant shall be placed.
* e.g.
*
* ```kotlin
* kotlin {
* targetHierarchy.android {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* }
* ```
*
* Will ensure that all android instrumented tests (androidInstrumentedTest, androidInstrumentedTestDebug, ...)
* will be placed into the 'test' SourceSet tree (with 'commonTest' as root)
*
* See [KotlinTargetHierarchyDsl.android]
*/
val sourceSetTree: Property<SourceSetTree>
}
@@ -231,6 +231,7 @@ public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/external/
public abstract fun getConfigure ()Lkotlin/jvm/functions/Function1;
public abstract fun getDefaultSourceSet ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;
public abstract fun getFriendArtifactResolver ()Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$FriendArtifactResolver;
public abstract fun getSourceSetTreeClassifier ()Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier;
}
public abstract interface class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$CompilationAssociator {
@@ -254,6 +255,7 @@ public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotli
public final fun getCompileTaskName ()Ljava/lang/String;
public final fun getDefaultSourceSet ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;
public final fun getFriendArtifactResolver ()Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$FriendArtifactResolver;
public final fun getSourceSetTreeClassifier ()Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier;
public final fun setCompilationAssociator (Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$CompilationAssociator;)V
public final fun setCompilationFactory (Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$CompilationFactory;)V
public final fun setCompilationName (Ljava/lang/String;)V
@@ -261,6 +263,7 @@ public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotli
public final fun setCompileTaskName (Ljava/lang/String;)V
public final fun setDefaultSourceSet (Lorg/jetbrains/kotlin/gradle/plugin/KotlinSourceSet;)V
public final fun setFriendArtifactResolver (Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptor$FriendArtifactResolver;)V
public final fun setSourceSetTreeClassifier (Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier;)V
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinCompilationDescriptorKt {
@@ -312,3 +315,44 @@ public final class org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotli
public static final fun ExternalKotlinTargetDescriptor (Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalKotlinTargetDescriptor;
}
public abstract class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Default : org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
public static final field INSTANCE Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Default;
public fun toString ()Ljava/lang/String;
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Name : org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
public fun <init> (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Name;
public static synthetic fun copy$default (Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Name;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Name;
public fun equals (Ljava/lang/Object;)Z
public final fun getName ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$None : org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
public static final field INSTANCE Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$None;
public fun toString ()Ljava/lang/String;
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Property : org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
public fun <init> (Lorg/gradle/api/provider/Property;)V
public final fun getProperty ()Lorg/gradle/api/provider/Property;
public fun toString ()Ljava/lang/String;
}
public final class org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Value : org/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier {
public fun <init> (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchy$SourceSetTree;)V
public final fun component1 ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchy$SourceSetTree;
public final fun copy (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchy$SourceSetTree;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Value;
public static synthetic fun copy$default (Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Value;Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchy$SourceSetTree;ILjava/lang/Object;)Lorg/jetbrains/kotlin/gradle/plugin/mpp/targetHierarchy/SourceSetTreeClassifier$Value;
public fun equals (Ljava/lang/Object;)Z
public final fun getTree ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchy$SourceSetTree;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@@ -53,7 +53,7 @@ abstract class KotlinMultiplatformExtension(project: Project) :
}
internal val internalKotlinTargetHierarchy by lazy {
KotlinTargetHierarchyDslImpl(project.objects, targets, sourceSets)
KotlinTargetHierarchyDslImpl(targets, sourceSets)
}
@ExperimentalKotlinGradlePluginApi
@@ -76,13 +76,13 @@ abstract class KotlinMultiplatformExtension(project: Project) :
fun <T : KotlinTarget> targetFromPreset(
preset: KotlinTargetPreset<T>,
name: String = preset.name,
configure: T.() -> Unit = { }
configure: T.() -> Unit = { },
): T = configureOrCreate(name, preset, configure)
fun <T : KotlinTarget> targetFromPreset(
preset: KotlinTargetPreset<T>,
name: String,
configure: Action<T>
configure: Action<T>,
) = targetFromPreset(preset, name) { configure.execute(this) }
fun <T : KotlinTarget> targetFromPreset(preset: KotlinTargetPreset<T>) = targetFromPreset(preset, preset.name) { }
@@ -100,37 +100,37 @@ interface TargetsFromPresetExtension : NamedDomainObjectCollection<KotlinTarget>
fun <T : KotlinTarget> fromPreset(
preset: KotlinTargetPreset<T>,
name: String,
configureAction: T.() -> Unit = {}
configureAction: T.() -> Unit = {},
): T
fun <T : KotlinTarget> fromPreset(
preset: KotlinTargetPreset<T>,
name: String
name: String,
): T = fromPreset(preset, name, {})
fun <T : KotlinTarget> fromPreset(
preset: KotlinTargetPreset<T>,
name: String,
configureAction: Action<T>
configureAction: Action<T>,
): T
}
internal abstract class DefaultTargetsFromPresetExtension @Inject constructor(
private val targetsContainer: () -> KotlinTargetsContainerWithPresets,
val targets: NamedDomainObjectCollection<KotlinTarget>
val targets: NamedDomainObjectCollection<KotlinTarget>,
) : TargetsFromPresetExtension,
NamedDomainObjectCollection<KotlinTarget> by targets {
override fun <T : KotlinTarget> fromPreset(
preset: KotlinTargetPreset<T>,
name: String,
configureAction: T.() -> Unit
configureAction: T.() -> Unit,
): T = targetsContainer().configureOrCreate(name, preset, configureAction)
override fun <T : KotlinTarget> fromPreset(
preset: KotlinTargetPreset<T>,
name: String,
configureAction: Action<T>
configureAction: Action<T>,
) = fromPreset(preset, name) {
configureAction.execute(this)
}
@@ -142,7 +142,7 @@ internal fun KotlinTarget.isProducedFromPreset(kotlinTargetPreset: KotlinTargetP
internal fun <T : KotlinTarget> KotlinTargetsContainerWithPresets.configureOrCreate(
targetName: String,
targetPreset: KotlinTargetPreset<T>,
configure: T.() -> Unit
configure: T.() -> Unit,
): T {
val existingTarget = targets.findByName(targetName)
when {
@@ -172,7 +172,7 @@ internal fun <T : KotlinTarget> KotlinTargetsContainerWithPresets.configureOrCre
internal fun KotlinTargetsContainerWithPresets.configureOrCreateAndroidTargetAndReportDeprecation(
targetName: String,
configure: KotlinAndroidTarget.() -> Unit
configure: KotlinAndroidTarget.() -> Unit,
): KotlinAndroidTarget {
val targetPreset = presets.getByName("android") as KotlinAndroidTargetPreset
val result = configureOrCreate(targetName, targetPreset, configure)
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companio
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.addBuildListenerForXcode
import org.jetbrains.kotlin.gradle.plugin.mpp.internal.runDeprecationDiagnostics
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.orNull
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements
import org.jetbrains.kotlin.gradle.plugin.sources.internal
@@ -85,7 +86,7 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
}
private fun exportProjectStructureMetadataForOtherBuilds(
extension: KotlinMultiplatformExtension
extension: KotlinMultiplatformExtension,
) {
GlobalProjectStructureMetadataStorage.registerProjectStructureMetadata(extension.project) {
extension.kotlinProjectStructureMetadata
@@ -168,22 +169,24 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
private fun configureSourceSets(project: Project) = with(project.multiplatformExtension) {
val production = sourceSets.create(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
val test = sourceSets.create(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
/* Create 'commonMain' and 'commonTest' SourceSets */
sourceSets.create(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
sourceSets.create(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
/* Create default 'dependsOn' to commonMain/commonTest (or even common{SourceSetTree}) */
targets.all { target ->
project.launchInStage(KotlinPluginLifecycle.Stage.FinaliseRefinesEdges) {
/* Only setup default refines edges when no KotlinTargetHierarchy was applied */
if (project.multiplatformExtension.internalKotlinTargetHierarchy.appliedDescriptors.isNotEmpty()) return@launchInStage
target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.let { mainCompilation ->
mainCompilation.defaultSourceSet.takeIf { it != production }?.dependsOn(production)
}
target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME)?.let { testCompilation ->
testCompilation.defaultSourceSet.takeIf { it != test }?.dependsOn(test)
target.compilations.forEach { compilation ->
val sourceSetTree = KotlinTargetHierarchy.SourceSetTree.orNull(compilation) ?: return@forEach
val commonSourceSet = sourceSets.findByName(lowerCamelCaseName("common", sourceSetTree.name)) ?: return@forEach
compilation.defaultSourceSet.dependsOn(commonSourceSet)
}
}
/* Report the platform to tbe build stats service */
val targetName = if (target is KotlinNativeTarget)
target.konanTarget.name
else
@@ -277,7 +280,7 @@ private fun sourcesJarTask(
project: Project,
sourceSets: Future<Map<String, Iterable<File>>>,
taskNamePrefix: String,
artifactNameAppendix: String
artifactNameAppendix: String,
): TaskProvider<Jar> =
sourcesJarTaskNamed(lowerCamelCaseName(taskNamePrefix, "sourcesJar"), taskNamePrefix, project, sourceSets, artifactNameAppendix)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.DecoratedKotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.*
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.SourceSetTreeClassifier
import kotlin.properties.Delegates
/**
@@ -54,6 +55,7 @@ interface ExternalKotlinCompilationDescriptor<T : DecoratedExternalKotlinCompila
val compilationFactory: CompilationFactory<T>
val friendArtifactResolver: FriendArtifactResolver<T>?
val compilationAssociator: CompilationAssociator<T>?
val sourceSetTreeClassifier: SourceSetTreeClassifier
val configure: ((T) -> Unit)?
}
@@ -70,6 +72,7 @@ fun <T : DecoratedExternalKotlinCompilation> ExternalKotlinCompilationDescriptor
compilationFactory = compilationFactory,
friendArtifactResolver = friendArtifactResolver,
compilationAssociator = compilationAssociator,
sourceSetTreeClassifier = sourceSetTreeClassifier,
configure = this.configure
)
}
@@ -84,6 +87,8 @@ class ExternalKotlinCompilationDescriptorBuilder<T : DecoratedExternalKotlinComp
var compilationFactory: CompilationFactory<T> by Delegates.notNull()
var friendArtifactResolver: FriendArtifactResolver<T>? = null
var compilationAssociator: CompilationAssociator<T>? = null
var sourceSetTreeClassifier: SourceSetTreeClassifier = SourceSetTreeClassifier.Default
internal var configure: ((T) -> Unit)? = null
fun configure(action: (T) -> Unit) = apply {
@@ -101,5 +106,6 @@ private data class ExternalKotlinCompilationDescriptorImpl<T : DecoratedExternal
override val compilationFactory: CompilationFactory<T>,
override val friendArtifactResolver: FriendArtifactResolver<T>?,
override val compilationAssociator: CompilationAssociator<T>?,
override val sourceSetTreeClassifier: SourceSetTreeClassifier,
override val configure: ((T) -> Unit)?,
) : ExternalKotlinCompilationDescriptor<T>
@@ -12,9 +12,9 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompi
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.DecoratedExternalKotlinCompilation.Delegate
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.sourceSetTreeClassifier
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
/**
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
*/
@ExternalKotlinTargetApi
fun <T : DecoratedExternalKotlinCompilation> DecoratedExternalKotlinTarget.createCompilation(
descriptor: ExternalKotlinCompilationDescriptor<T>
descriptor: ExternalKotlinCompilationDescriptor<T>,
): T {
val compilationImplFactory = KotlinCompilationImplFactory(
compilerOptionsFactory = when (platformType) {
@@ -42,7 +42,7 @@ fun <T : DecoratedExternalKotlinCompilation> DecoratedExternalKotlinTarget.creat
KotlinPlatformType.wasm -> KotlinMultiplatformCommonCompilerOptionsFactory
},
compilationSourceSetsContainerFactory = { _, _ -> KotlinCompilationSourceSetsContainer(descriptor.defaultSourceSet) },
compilationTaskNamesContainerFactory = KotlinCompilationTaskNamesContainerFactory { target, compilationName ->
compilationTaskNamesContainerFactory = { target, compilationName ->
val default = DefaultKotlinCompilationTaskNamesContainerFactory.create(target, compilationName)
default.copy(
compileTaskName = descriptor.compileTaskName ?: default.compileTaskName,
@@ -70,6 +70,7 @@ fun <T : DecoratedExternalKotlinCompilation> DecoratedExternalKotlinTarget.creat
val compilationImpl = compilationImplFactory.create(this, descriptor.compilationName)
val decoratedCompilation = descriptor.compilationFactory.create(Delegate(compilationImpl))
decoratedCompilation.sourceSetTreeClassifier = descriptor.sourceSetTreeClassifier
descriptor.configure?.invoke(decoratedCompilation)
this.delegate.compilations.add(decoratedCompilation)
@@ -83,13 +84,13 @@ fun <T : DecoratedExternalKotlinCompilation> DecoratedExternalKotlinTarget.creat
*/
@ExternalKotlinTargetApi
fun <T : DecoratedExternalKotlinCompilation> DecoratedExternalKotlinTarget.createCompilation(
descriptor: ExternalKotlinCompilationDescriptorBuilder<T>.() -> Unit
descriptor: ExternalKotlinCompilationDescriptorBuilder<T>.() -> Unit,
): T {
return createCompilation(ExternalKotlinCompilationDescriptor(descriptor))
}
private fun DecoratedExternalKotlinTarget.setupCompileTask(
compilation: DecoratedExternalKotlinCompilation
compilation: DecoratedExternalKotlinCompilation,
) {
val tasksProvider = KotlinTasksProvider()
val compilationInfo = KotlinCompilationInfo(compilation)
@@ -1,43 +0,0 @@
/*
* Copyright 2010-2023 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.targetHierarchy
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy.SourceSetTree
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
import org.jetbrains.kotlin.gradle.plugin.sources.android.type
internal suspend fun SourceSetTree.Companion.orNull(compilation: KotlinCompilation<*>): SourceSetTree? =
when (compilation) {
is KotlinJvmAndroidCompilation -> orNull(compilation.target, compilation.androidVariant.type)
else -> when (compilation.name) {
"main" -> main
"test" -> test
else -> SourceSetTree(compilation.name)
}
}
internal suspend fun SourceSetTree.Companion.orNull(
target: KotlinAndroidTarget,
variantType: AndroidVariantType
): SourceSetTree? {
val multiplatform = target.project.multiplatformExtensionOrNull ?: return null
return when (variantType) {
AndroidVariantType.Main ->
multiplatform.targetHierarchy.android.main.sourceSetTree.awaitFinalValue() ?: main
AndroidVariantType.UnitTest ->
multiplatform.targetHierarchy.android.unitTest.sourceSetTree.awaitFinalValue() ?: test
AndroidVariantType.InstrumentedTest ->
multiplatform.targetHierarchy.android.instrumentedTest.sourceSetTree.awaitFinalValue() ?: instrumentedTest
AndroidVariantType.Unknown -> null
}
}
@@ -61,17 +61,17 @@ private class KotlinTargetHierarchyBuilderRootImpl(
override fun sourceSetTrees(vararg tree: KotlinTargetHierarchy.SourceSetTree) {
builder.modules = tree.toHashSet()
builder.sourceSetTrees = tree.toHashSet()
}
override fun withSourceSetTree(vararg tree: KotlinTargetHierarchy.SourceSetTree) {
builder.modules = builder.modules.orEmpty().plus(tree)
builder.sourceSetTrees = builder.sourceSetTrees.orEmpty().plus(tree)
}
override fun excludeSourceSetTree(vararg tree: KotlinTargetHierarchy.SourceSetTree) {
val modules = tree.toHashSet()
if (modules.isEmpty()) return
builder.modules = builder.modules.orEmpty() - modules
builder.sourceSetTrees = builder.sourceSetTrees.orEmpty() - modules
}
}
@@ -84,7 +84,7 @@ private class KotlinTargetHierarchyBuilderImpl(
val children = mutableSetOf<KotlinTargetHierarchyBuilderImpl>()
val childrenClosure get() = closure { it.children }
var modules: Set<KotlinTargetHierarchy.SourceSetTree>? = null
var sourceSetTrees: Set<KotlinTargetHierarchy.SourceSetTree>? = null
private var includePredicate: ((KotlinCompilation<*>) -> Boolean) = { false }
private var excludePredicate: ((KotlinCompilation<*>) -> Boolean) = { false }
@@ -104,9 +104,9 @@ private class KotlinTargetHierarchyBuilderImpl(
}
suspend fun contains(compilation: KotlinCompilation<*>): Boolean {
modules?.let { modules ->
val module = KotlinTargetHierarchy.SourceSetTree.orNull(compilation) ?: return false
if (module !in modules) return false
sourceSetTrees?.let { sourceSetTrees ->
val sourceSetTree = KotlinTargetHierarchy.SourceSetTree.orNull(compilation) ?: return false
if (sourceSetTree !in sourceSetTrees) return false
}
/* Return eagerly, when compilation is explicitly excluded */
@@ -7,16 +7,13 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy
import org.gradle.api.DomainObjectCollection
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidTargetHierarchyDsl
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidVariantHierarchyDsl
import org.jetbrains.kotlin.gradle.dsl.KotlinTargetHierarchyDsl
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.property
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyBuilder
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyDescriptor
internal class KotlinTargetHierarchyDslImpl(
objects: ObjectFactory,
private val targets: DomainObjectCollection<KotlinTarget>,
private val sourceSets: NamedDomainObjectContainer<KotlinSourceSet>,
) : KotlinTargetHierarchyDsl {
@@ -24,8 +21,6 @@ internal class KotlinTargetHierarchyDslImpl(
private val _appliedDescriptors = mutableListOf<KotlinTargetHierarchyDescriptor>()
val appliedDescriptors: List<KotlinTargetHierarchyDescriptor> get() = _appliedDescriptors
override val android: KotlinAndroidTargetHierarchyDsl = KotlinAndroidTargetHierarchyDslImpl(objects)
override fun apply(
hierarchyDescriptor: KotlinTargetHierarchyDescriptor,
describeExtension: (KotlinTargetHierarchyBuilder.Root.() -> Unit)?,
@@ -42,21 +37,8 @@ internal class KotlinTargetHierarchyDslImpl(
override fun custom(describe: KotlinTargetHierarchyBuilder.Root.() -> Unit) {
apply(KotlinTargetHierarchyDescriptor(describe))
}
override fun android(configure: KotlinAndroidTargetHierarchyDsl.() -> Unit) {
android.configure()
}
}
private fun KotlinTargetHierarchyDescriptor.extendIfNotNull(describe: (KotlinTargetHierarchyBuilder.Root.() -> Unit)?) =
if (describe == null) this else extend(describe)
internal class KotlinAndroidTargetHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidTargetHierarchyDsl {
override val main: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val unitTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val instrumentedTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
}
internal class KotlinAndroidVariantHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidVariantHierarchyDsl {
override val sourceSetTree: Property<KotlinTargetHierarchy.SourceSetTree> = objects.property()
}
@@ -0,0 +1,120 @@
/*
* Copyright 2010-2023 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.targetHierarchy
import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy.SourceSetTree
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilationFactory
import org.jetbrains.kotlin.tooling.core.extrasLazyProperty
/**
* Classifier providing the corresponding [SourceSetTree] associated with any given [KotlinCompilation]
*
* ### Example: Overwriting 'test' compilations [SourceSetTreeClassifier]:
* Consider the following setup:
* ```kotlin
* kotlin {
* val testCompilation = jvm().compilations.getByName("test")
* }
* ```
*
* In this example, we know that the 'jvm/test' compilation will have a 'jvmTest' SourceSet which
* will depend on 'commonTest' and therefore is part of the 'test' [SourceSetTree].
*
* When another [SourceSetTreeClassifier] is specified, this behaviour is changed.
* Using the External Kotlin Target API:
* ```kotlin
* myTarget.createCompilation {
* compilationName = "test"
* sourceSetTreeClassifier = SourceSetTreeClassifier.Name("unitTest")
* }
* ```
*
* This will create a compilation called 'test' which however will be considered part of the 'unitTest' SourceSetTree.
* The SourceSet of this 'jvm/test' compilation will still be called 'jvmTest' but since its part of the 'unitTest [SourceSetTree],
* there will not be a dependsOn edge to 'commonTest', but (if present) 'commonUnitTest'
*/
@ExternalKotlinTargetApi
sealed class SourceSetTreeClassifier {
/**
* Default Classifier: The name of the compilation will be used to infer the [SourceSetTree]:
* 'main' compilations will be part of [SourceSetTree.main]
* 'test' compilations will be part of [SourceSetTree.test]
* ...
*/
@ExternalKotlinTargetApi
object Default : SourceSetTreeClassifier() {
override fun toString(): String = "Default"
}
/**
* Indicates that the given compilations is not part of any 'named' [SourceSetTree].
* Neither [KotlinTargetHierarchy] will be applied nor default dependsOn edges shall be set.
*/
@ExternalKotlinTargetApi
object None : SourceSetTreeClassifier() {
override fun toString(): String = "None"
}
/**
* Predefined [SourceSetTree] using the [tree] specified.
*/
@ExternalKotlinTargetApi
data class Value(val tree: SourceSetTree) : SourceSetTreeClassifier()
/**
* Predefined [SourceSetTree] using the [name] specified
*/
@ExternalKotlinTargetApi
data class Name(val name: String) : SourceSetTreeClassifier()
/**
* Wrapper around [org.gradle.api.provider.Property] of a given [SourceSetTree] in order to
* make the [SourceSetTree] configurable.
*/
@ExternalKotlinTargetApi
class Property(val property: org.gradle.api.provider.Property<SourceSetTree>) : SourceSetTreeClassifier() {
override fun toString(): String {
return property.toString()
}
}
internal suspend fun classify(compilation: KotlinCompilation<*>): SourceSetTree? {
return when (this) {
is Default -> SourceSetTree(compilation.name)
is Property -> property.awaitFinalValue()
is Value -> tree
is Name -> SourceSetTree(name)
is None -> null
}
}
}
/**
* Returns the classifier configured for a given compilation.
* This is writable, as Android requires overwriting of this default behaviour.
*
* - The KGP maintained target will set the classifier within the [KotlinJvmAndroidCompilationFactory]
* - The external Android target will set this classifier within the 'createCompilation'
*
* It is therefore safe to access this value as soon as a compilation is provided
*/
internal var KotlinCompilation<*>.sourceSetTreeClassifier: SourceSetTreeClassifier by extrasLazyProperty {
SourceSetTreeClassifier.Default
}
/**
* Returns the [SourceSetTree] of a given [KotlinCompilation]:
* Uses the [sourceSetTreeClassifier] under the hood.
* See [SourceSetTreeClassifier]
*/
internal suspend fun SourceSetTree.Companion.orNull(compilation: KotlinCompilation<*>) =
compilation.sourceSetTreeClassifier.classify(compilation)
@@ -11,10 +11,9 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.launchInStage
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.orNull
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
@@ -38,7 +37,13 @@ internal object MultiplatformLayoutV2DependsOnConfigurator : KotlinAndroidSource
return@launchInStage
}
val sourceSetTree = KotlinTargetHierarchy.SourceSetTree.orNull(target, variantType) ?: return@launchInStage
val sourceSetTree = when (variantType) {
AndroidVariantType.Main -> target.targetHierarchy.main.sourceSetTree.awaitFinalValue()
AndroidVariantType.UnitTest -> target.targetHierarchy.unitTest.sourceSetTree.awaitFinalValue()
AndroidVariantType.InstrumentedTest -> target.targetHierarchy.instrumentedTest.sourceSetTree.awaitFinalValue()
AndroidVariantType.Unknown -> null
} ?: return@launchInStage
val commonSourceSetName = lowerCamelCaseName("common", sourceSetTree.name)
val commonSourceSet = target.project.kotlinExtension.sourceSets.findByName(commonSourceSetName) ?: return@launchInStage
kotlinSourceSet.dependsOn(commonSourceSet)
@@ -7,14 +7,13 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import com.android.build.gradle.api.BaseVariant
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeContainer
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy.SourceSetTree
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.forAllAndroidVariants
@@ -26,7 +25,7 @@ import javax.inject.Inject
abstract class KotlinAndroidTarget @Inject constructor(
final override val targetName: String,
project: Project
project: Project,
) : AbstractKotlinTarget(project) {
final override val disambiguationClassifier: String = targetName
@@ -37,6 +36,109 @@ abstract class KotlinAndroidTarget @Inject constructor(
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(KotlinJvmAndroidCompilation::class.java)
/**
* Configure Android specific settings within the context of [KotlinTargetHierarchy].
* The difference between Android and other targets is that the build author is free to choose
* the names of compilations, whereas Android is using predefined SourceSet names.
*
* ### Default dependsOn edges
* By default, Kotlin Multiplatform will set the following default dependsOn edges:
* - `androidMain` -> `commonMain`
* - `androidUnitTest` -> `commonTest`
*
* In this default setup, SourceSets like `androidInstrumentedTest` will *not* dependOn `commonTest`.
* This API can be used to change the default behavior
*
* #### Example 1: Setting up androidInstrumentedTest -> commonTest
* This can be done by putting the 'androidInstrumentedTest' variants into the 'test' [SourceSetTree]:
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* ```
*
* #### Example 2: Setting up androidInstrumentedTest -> commonTest and removing 'unitTests' from the 'test' [SourceSetTree]
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* unitTest.sourceSetTree.set(SourceSetTree.unitTest) // ! <- Anything *other* than 'test'
* }
* ```
*/
@ExperimentalKotlinGradlePluginApi
val targetHierarchy: KotlinAndroidTargetHierarchyDsl = KotlinAndroidTargetHierarchyDslImpl(project.objects).apply {
main.sourceSetTree.convention(SourceSetTree.main)
unitTest.sourceSetTree.convention(SourceSetTree.test)
instrumentedTest.sourceSetTree.convention(SourceSetTree.instrumentedTest)
}
/**
* Configure Android specific settings within the context of [KotlinTargetHierarchy].
* The difference between Android and other targets is that the build author is free to choose
* the names of compilations, whereas Android is using predefined SourceSet names.
*
* ### Default dependsOn edges
* By default, Kotlin Multiplatform will set the following default dependsOn edges:
* - `androidMain` -> `commonMain`
* - `androidUnitTest` -> `commonTest`
*
* In this default setup, SourceSets like `androidInstrumentedTest` will *not* dependOn `commonTest`.
* This API can be used to change the default behavior
*
* #### Example 1: Setting up androidInstrumentedTest -> commonTest
* This can be done by putting the 'androidInstrumentedTest' variants into the 'test' [SourceSetTree]:
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* ```
*
* #### Example 2: Setting up androidInstrumentedTest -> commonTest and removing 'unitTests' from the 'test' [SourceSetTree]
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* unitTest.sourceSetTree.set(SourceSetTree.unitTest) // ! <- Anything *other* than 'test'
* }
* ```
*/
@ExperimentalKotlinGradlePluginApi
fun targetHierarchy(configure: Action<KotlinAndroidTargetHierarchyDsl>) {
configure.execute(targetHierarchy)
}
/**
* Configure Android specific settings within the context of [KotlinTargetHierarchy].
* The difference between Android and other targets is that the build author is free to choose
* the names of compilations, whereas Android is using predefined SourceSet names.
*
* ### Default dependsOn edges
* By default, Kotlin Multiplatform will set the following default dependsOn edges:
* - `androidMain` -> `commonMain`
* - `androidUnitTest` -> `commonTest`
*
* In this default setup, SourceSets like `androidInstrumentedTest` will *not* dependOn `commonTest`.
* This API can be used to change the default behavior
*
* #### Example 1: Setting up androidInstrumentedTest -> commonTest
* This can be done by putting the 'androidInstrumentedTest' variants into the 'test' [SourceSetTree]:
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* ```
*
* #### Example 2: Setting up androidInstrumentedTest -> commonTest and removing 'unitTests' from the 'test' [SourceSetTree]
* ```kotlin
* androidTarget().targetHierarchy {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* unitTest.sourceSetTree.set(SourceSetTree.unitTest) // ! <- Anything *other* than 'test'
* }
* ```
*/
@ExperimentalKotlinGradlePluginApi
fun targetHierarchy(configure: KotlinAndroidTargetHierarchyDsl.() -> Unit) {
targetHierarchy.configure()
}
/** Names of the Android library variants that should be published from the target's project within the default publications which are
* set up if the `maven-publish` Gradle plugin is applied.
@@ -241,7 +343,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
private fun createSourcesElementsIfNeeded(
variantName: String,
apiElementsConfigurationName: String,
sourcesElementsConfigurationName: String
sourcesElementsConfigurationName: String,
): Configuration {
val existingConfiguration = project.configurations.findByName(sourcesElementsConfigurationName)
if (existingConfiguration != null) return existingConfiguration
@@ -261,7 +363,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
/** We filter this variant out as it is never requested on the consumer side, while keeping it leads to ambiguity between Android and
* JVM variants due to non-nesting sets of unmatched attributes. */
private fun filterOutAndroidVariantAttribute(
attribute: Attribute<*>
attribute: Attribute<*>,
): Boolean =
attribute.name != "com.android.build.gradle.internal.attributes.VariantAttr" &&
attribute.name != "com.android.build.api.attributes.VariantAttr"
@@ -269,7 +371,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
private fun filterOutAndroidBuildTypeAttribute(
it: Attribute<*>,
valueString: String,
isSinglePublishedVariant: Boolean
isSinglePublishedVariant: Boolean,
) = when {
PropertiesProvider(project).keepAndroidBuildTypeAttribute -> true
it.name != "com.android.build.api.attributes.BuildTypeAttr" -> true
@@ -281,6 +383,7 @@ abstract class KotlinAndroidTarget @Inject constructor(
}
private fun filterOutAndroidAgpVersionAttribute(
attribute: Attribute<*>
attribute: Attribute<*>,
): Boolean = attribute.name != "com.android.build.api.attributes.AgpVersionAttr"
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2023 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("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.KotlinTargetHierarchyDsl
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy.SourceSetTree
import org.jetbrains.kotlin.gradle.utils.property
@ExperimentalKotlinGradlePluginApi
interface KotlinAndroidTargetHierarchyDsl {
val main: KotlinAndroidVariantHierarchyDsl
val unitTest: KotlinAndroidVariantHierarchyDsl
val instrumentedTest: KotlinAndroidVariantHierarchyDsl
}
@ExperimentalKotlinGradlePluginApi
interface KotlinAndroidVariantHierarchyDsl {
/**
* Configures under which [SourceSetTree] the currently configured Android Variant shall be placed.
* e.g.
*
* ```kotlin
* kotlin {
* targetHierarchy.android {
* instrumentedTest.sourceSetTree.set(SourceSetTree.test)
* }
* }
* ```
*
* Will ensure that all android instrumented tests (androidInstrumentedTest, androidInstrumentedTestDebug, ...)
* will be placed into the 'test' SourceSet tree (with 'commonTest' as root)
*
* See [KotlinTargetHierarchyDsl.android]
*/
val sourceSetTree: Property<SourceSetTree>
}
internal class KotlinAndroidTargetHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidTargetHierarchyDsl {
override val main: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val unitTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
override val instrumentedTest: KotlinAndroidVariantHierarchyDsl = KotlinAndroidVariantHierarchyDslImpl(objects)
}
internal class KotlinAndroidVariantHierarchyDslImpl(objects: ObjectFactory) : KotlinAndroidVariantHierarchyDsl {
override val sourceSetTree: Property<SourceSetTree> = objects.property()
}
@@ -12,10 +12,15 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinAndroidCompi
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.AndroidCompilationSourceSetsContainerFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinJvmCompilerOptionsFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.SourceSetTreeClassifier
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.SourceSetTreeClassifier.Property
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.sourceSetTreeClassifier
import org.jetbrains.kotlin.gradle.plugin.sources.android.AndroidVariantType
import org.jetbrains.kotlin.gradle.plugin.sources.android.type
class KotlinJvmAndroidCompilationFactory internal constructor(
override val target: KotlinAndroidTarget,
private val variant: BaseVariant
private val variant: BaseVariant,
) : KotlinCompilationFactory<KotlinJvmAndroidCompilation> {
override val itemClass: Class<KotlinJvmAndroidCompilation>
@@ -34,6 +39,17 @@ class KotlinJvmAndroidCompilationFactory internal constructor(
)
override fun create(name: String): KotlinJvmAndroidCompilation {
return project.objects.newInstance(itemClass, compilationImplFactory.create(target, name), variant)
return project.objects.newInstance(itemClass, compilationImplFactory.create(target, name), variant).also { compilation ->
configureSourceSetTreeClassifier(compilation)
}
}
private fun configureSourceSetTreeClassifier(compilation: KotlinJvmAndroidCompilation) {
compilation.sourceSetTreeClassifier = when (variant.type) {
AndroidVariantType.Main -> Property(target.targetHierarchy.main.sourceSetTree)
AndroidVariantType.UnitTest -> Property(target.targetHierarchy.unitTest.sourceSetTree)
AndroidVariantType.InstrumentedTest -> Property(target.targetHierarchy.instrumentedTest.sourceSetTree)
AndroidVariantType.Unknown -> SourceSetTreeClassifier.None
}
}
}
@@ -0,0 +1,92 @@
/*
* Copyright 2010-2023 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", "DuplicatedCode")
package org.jetbrains.kotlin.gradle.unitTests
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchy.SourceSetTree
import org.jetbrains.kotlin.gradle.plugin.launchInStage
import org.jetbrains.kotlin.gradle.plugin.mpp.external.*
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinCompilationDescriptor.CompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.external.ExternalKotlinTargetDescriptor.TargetFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.SourceSetTreeClassifier
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.orNull
import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP
import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest
import org.jetbrains.kotlin.gradle.utils.property
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class ExternalKotlinTargetApiTests {
class FakeTarget(delegate: Delegate) : DecoratedExternalKotlinTarget(delegate)
class FakeCompilation(delegate: Delegate) : DecoratedExternalKotlinCompilation(delegate)
val project = buildProjectWithMPP()
val kotlin = project.multiplatformExtension
private fun ExternalKotlinTargetDescriptorBuilder<FakeTarget>.defaults() {
targetName = "fake"
platformType = KotlinPlatformType.jvm
targetFactory = TargetFactory(::FakeTarget)
}
private fun ExternalKotlinCompilationDescriptorBuilder<FakeCompilation>.defaults() {
compilationName = "fake"
compilationFactory = CompilationFactory(::FakeCompilation)
defaultSourceSet = kotlin.sourceSets.maybeCreate("fake")
}
@Test
fun `test - sourceSetClassifier - default`() = buildProjectWithMPP().runLifecycleAwareTest {
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
val compilation = target.createCompilation<FakeCompilation> { defaults() }
assertEquals(SourceSetTree("fake"), SourceSetTree.orNull(compilation))
}
@Test
fun `test - sourceSetClassifier - custom name`() = buildProjectWithMPP().runLifecycleAwareTest {
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
val compilation = target.createCompilation<FakeCompilation> {
defaults()
sourceSetTreeClassifier = SourceSetTreeClassifier.Name("mySourceSetTree")
}
assertEquals(SourceSetTree("mySourceSetTree"), SourceSetTree.orNull(compilation))
}
@Test
fun `test - sourceSetClassifier - custom property`() = buildProjectWithMPP().runLifecycleAwareTest {
val myProperty = project.objects.property<SourceSetTree>()
val nullProperty = project.objects.property<SourceSetTree>()
val target = kotlin.createExternalKotlinTarget<FakeTarget> { defaults() }
val mainCompilation = target.createCompilation<FakeCompilation> {
defaults()
sourceSetTreeClassifier = SourceSetTreeClassifier.Property(myProperty)
}
val auxCompilation = target.createCompilation<FakeCompilation>() {
compilationName = "aux"
compilationFactory = CompilationFactory(::FakeCompilation)
defaultSourceSet = kotlin.sourceSets.create("aux")
sourceSetTreeClassifier = SourceSetTreeClassifier.Property(nullProperty)
}
launchInStage(KotlinPluginLifecycle.Stage.FinaliseDsl) {
myProperty.set(SourceSetTree.main)
}
assertEquals(SourceSetTree.main, SourceSetTree.orNull(mainCompilation))
assertNull(SourceSetTree.orNull(auxCompilation))
}
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.AfterFinal
import org.jetbrains.kotlin.gradle.plugin.awaitFinalValue
import org.jetbrains.kotlin.gradle.plugin.currentKotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.kotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.KotlinAndroidVariantHierarchyDslImpl
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidVariantHierarchyDslImpl
import org.jetbrains.kotlin.gradle.util.*
import org.junit.Test
import kotlin.test.assertEquals
@@ -38,7 +38,7 @@ class KotlinAndroidTargetHierarchyDsl {
afterEvaluate { dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("x")) }
dsl.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("-set-before-after-evaluate-"))
assertEquals("x", dsl.sourceSetTree.awaitFinalValue()?.name)
assertEquals(KotlinPluginLifecycle.Stage.FinaliseDsl, currentKotlinPluginLifecycle().stage)
assertEquals(KotlinPluginLifecycle.Stage.AfterFinaliseDsl, currentKotlinPluginLifecycle().stage)
}
@Test
@@ -51,9 +51,7 @@ class KotlinAndroidTargetHierarchyDsl {
val kotlin = project.multiplatformExtension
project.runLifecycleAwareTest {
kotlin.androidTarget()
kotlin.targetHierarchy.android {
kotlin.androidTarget().targetHierarchy {
unitTest.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree.test)
instrumentedTest.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree.test)
}
@@ -75,9 +73,7 @@ class KotlinAndroidTargetHierarchyDsl {
val kotlin = project.multiplatformExtension
project.runLifecycleAwareTest {
kotlin.androidTarget()
kotlin.targetHierarchy.android {
kotlin.androidTarget().targetHierarchy {
unitTest.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("xxx"))
instrumentedTest.sourceSetTree.set(KotlinTargetHierarchy.SourceSetTree("yyy"))
}