[Gradle][MPP] Implement initial KotlinTargetHierarchy APIs
^KT-53570 Verification Pending
This commit is contained in:
committed by
Space Team
parent
1fc2575052
commit
c5d6162578
+44
@@ -505,6 +505,17 @@ public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinNativeXCFr
|
||||
public abstract fun targets ([Lorg/jetbrains/kotlin/konan/target/KonanTarget;)V
|
||||
}
|
||||
|
||||
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl {
|
||||
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 final class org/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl$DefaultImpls {
|
||||
public static synthetic fun apply$default (Lorg/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl;Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
|
||||
public static synthetic fun default$default (Lorg/jetbrains/kotlin/gradle/dsl/KotlinTargetHierarchyDsl;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
|
||||
}
|
||||
|
||||
public abstract interface class org/jetbrains/kotlin/gradle/dsl/KotlinTopLevelExtensionConfig {
|
||||
public abstract fun explicitApi ()V
|
||||
public abstract fun explicitApiWarning ()V
|
||||
@@ -1017,6 +1028,39 @@ public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinTargetE
|
||||
public abstract fun getTarget ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;
|
||||
}
|
||||
|
||||
public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyBuilder {
|
||||
public abstract fun common (Lkotlin/jvm/functions/Function1;)V
|
||||
public abstract fun getCompilation ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinCompilation;
|
||||
public abstract fun getTarget ()Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;
|
||||
public abstract fun group (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
|
||||
public abstract fun isAndroidJvm ()Z
|
||||
public abstract fun isAndroidNative ()Z
|
||||
public abstract fun isApple ()Z
|
||||
public abstract fun isIos ()Z
|
||||
public abstract fun isJs ()Z
|
||||
public abstract fun isJsLegacy ()Z
|
||||
public abstract fun isJvm ()Z
|
||||
public abstract fun isLinux ()Z
|
||||
public abstract fun isMacos ()Z
|
||||
public abstract fun isNative ()Z
|
||||
public abstract fun isTvos ()Z
|
||||
public abstract fun isWatchos ()Z
|
||||
public abstract fun isWindows ()Z
|
||||
}
|
||||
|
||||
public final class org/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyBuilder$DefaultImpls {
|
||||
public static fun common (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyBuilder;Lkotlin/jvm/functions/Function1;)V
|
||||
public static synthetic fun group$default (Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
|
||||
}
|
||||
|
||||
public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptor : kotlin/jvm/functions/Function1 {
|
||||
public abstract fun extend (Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptor;
|
||||
}
|
||||
|
||||
public final class org/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptorKt {
|
||||
public static final fun KotlinTargetHierarchyDescriptor (Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinTargetHierarchyDescriptor;
|
||||
}
|
||||
|
||||
public abstract interface class org/jetbrains/kotlin/gradle/plugin/KotlinTargetPreset : org/gradle/api/Named {
|
||||
public abstract fun createTarget (Ljava/lang/String;)Lorg/jetbrains/kotlin/gradle/plugin/KotlinTarget;
|
||||
}
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.dsl
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyBuilder
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyDescriptor
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
interface KotlinTargetHierarchyDsl {
|
||||
fun apply(
|
||||
hierarchyDescriptor: KotlinTargetHierarchyDescriptor,
|
||||
describeExtension: (KotlinTargetHierarchyBuilder.() -> Unit)? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Set's up a 'natural'/'default' hierarchy withing [KotlinTarget]'s in the project.
|
||||
*
|
||||
* #### Example 1
|
||||
*
|
||||
* ```kotlin
|
||||
* kotlin {
|
||||
* targets.hierarchy.default() // <- position of this call is not relevant!
|
||||
*
|
||||
* iosX64()
|
||||
* iosArm64()
|
||||
* linuxX64()
|
||||
* linuxArm64()
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Will create the following SourceSets:
|
||||
* `[iosMain, iosTest, appleMain, appleTest, linuxMain, linuxTest, nativeMain, nativeTest]
|
||||
*
|
||||
*
|
||||
* Hierarchy:
|
||||
* ```
|
||||
* common
|
||||
* |
|
||||
* +-----------------+-------------------+
|
||||
* | |
|
||||
*
|
||||
* native ...
|
||||
*
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* +----------------------+--------------------+-----------------------+
|
||||
* | | | |
|
||||
*
|
||||
* apple linux windows androidNative
|
||||
*
|
||||
* |
|
||||
* +-----------+------------+------------+
|
||||
* | | | |
|
||||
*
|
||||
* macos ios tvos watchos
|
||||
* ```
|
||||
*
|
||||
* #### Example 2: Adding custom groups
|
||||
* Let's imagine we would additionally like to share code between linux and apple (unixLike)
|
||||
*
|
||||
* ```kotlin
|
||||
* kotlin {
|
||||
* targets.hierarchy.default { target ->
|
||||
* if(target.isNative) {
|
||||
* group("native") { // <- we can re-declare already existing groups and connect children to it!
|
||||
* if(target.isLinux || target.isApple) {
|
||||
* group("unixLike")
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param describeExtension: Additional groups can be described to extend the 'default'/'natural' hierarchy:
|
||||
* @see KotlinTargetHierarchyDescriptor.extend
|
||||
*/
|
||||
fun default(describeExtension: (KotlinTargetHierarchyBuilder.() -> Unit)? = null)
|
||||
fun custom(describe: KotlinTargetHierarchyBuilder.() -> Unit)
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
interface KotlinTargetHierarchyBuilder {
|
||||
val target: KotlinTarget
|
||||
val compilation: KotlinCompilation<*>
|
||||
|
||||
val isNative: Boolean
|
||||
val isApple: Boolean
|
||||
val isIos: Boolean
|
||||
val isWatchos: Boolean
|
||||
val isMacos: Boolean
|
||||
val isTvos: Boolean
|
||||
val isWindows: Boolean
|
||||
val isLinux: Boolean
|
||||
val isAndroidNative: Boolean
|
||||
val isJvm: Boolean
|
||||
val isAndroidJvm: Boolean
|
||||
val isJsLegacy: Boolean
|
||||
val isJsIr: Boolean
|
||||
val isJs: Boolean
|
||||
|
||||
fun common(build: KotlinTargetHierarchyBuilder.() -> Unit) = group("common", build)
|
||||
fun group(name: String, build: KotlinTargetHierarchyBuilder.() -> Unit = {})
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
interface KotlinTargetHierarchyDescriptor : (KotlinTargetHierarchyBuilder) -> Unit {
|
||||
fun extend(describe: KotlinTargetHierarchyBuilder.() -> Unit): KotlinTargetHierarchyDescriptor
|
||||
}
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
fun KotlinTargetHierarchyDescriptor(
|
||||
describe: KotlinTargetHierarchyBuilder.() -> Unit
|
||||
): KotlinTargetHierarchyDescriptor {
|
||||
return KotlinTargetHierarchyDescriptorImpl(describe)
|
||||
}
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
private class KotlinTargetHierarchyDescriptorImpl(
|
||||
private val describe: KotlinTargetHierarchyBuilder.() -> Unit
|
||||
) : KotlinTargetHierarchyDescriptor {
|
||||
|
||||
override fun extend(describe: KotlinTargetHierarchyBuilder.() -> Unit): KotlinTargetHierarchyDescriptor {
|
||||
val sourceDescribe = this.describe
|
||||
return KotlinTargetHierarchyDescriptor {
|
||||
sourceDescribe()
|
||||
describe()
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(p1: KotlinTargetHierarchyBuilder) = p1.describe()
|
||||
}
|
||||
+8
-4
@@ -5,15 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.dsl
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.InvalidUserCodeException
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.internal.plugins.DslObject
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.KotlinTargetHierarchyDslImpl
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class KotlinMultiplatformExtension(project: Project) :
|
||||
@@ -53,6 +52,9 @@ abstract class KotlinMultiplatformExtension(project: Project) :
|
||||
configure(presetExtension)
|
||||
}
|
||||
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
val targetHierarchy: KotlinTargetHierarchyDsl get() = KotlinTargetHierarchyDslImpl(targets, sourceSets)
|
||||
|
||||
@Suppress("unused") // DSL
|
||||
val testableTargets: NamedDomainObjectCollection<KotlinTargetWithTests<*, *>>
|
||||
get() = targets.withType(KotlinTargetWithTests::class.java)
|
||||
@@ -180,12 +182,14 @@ internal fun <T : KotlinTarget> KotlinTargetsContainerWithPresets.configureOrCre
|
||||
configure(existingTarget as T)
|
||||
return existingTarget
|
||||
}
|
||||
|
||||
existingTarget == null -> {
|
||||
val newTarget = targetPreset.createTarget(targetName)
|
||||
targets.add(newTarget)
|
||||
configure(newTarget)
|
||||
return newTarget
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw InvalidUserCodeException(
|
||||
"The target '$targetName' already exists, but it was not created with the '${targetPreset.name}' preset. " +
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.targetHierarchy
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
|
||||
internal data class KotlinTargetHierarchy(
|
||||
val node: Node, val children: Set<KotlinTargetHierarchy> = emptySet()
|
||||
) {
|
||||
|
||||
sealed class Node {
|
||||
abstract fun sharedSourceSetName(compilation: KotlinCompilation<*>): String?
|
||||
|
||||
object Root : Node() {
|
||||
override fun sharedSourceSetName(compilation: KotlinCompilation<*>): String? = null
|
||||
}
|
||||
|
||||
data class Group(val name: String) : Node() {
|
||||
override fun sharedSourceSetName(compilation: KotlinCompilation<*>): String {
|
||||
return lowerCamelCaseName(name, compilation.name)
|
||||
}
|
||||
}
|
||||
|
||||
final override fun toString(): String = when (this) {
|
||||
is Group -> name
|
||||
is Root -> "<root>"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
if (children.isEmpty()) return node.toString()
|
||||
return node.toString() + "\n" + children.joinToString("\n").prependIndent("----")
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.targetHierarchy
|
||||
|
||||
import org.gradle.api.DomainObjectCollection
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTargetHierarchyDsl
|
||||
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(
|
||||
private val targets: DomainObjectCollection<KotlinTarget>,
|
||||
private val sourceSets: NamedDomainObjectContainer<KotlinSourceSet>
|
||||
) : KotlinTargetHierarchyDsl {
|
||||
override fun apply(
|
||||
hierarchyDescriptor: KotlinTargetHierarchyDescriptor,
|
||||
describeExtension: (KotlinTargetHierarchyBuilder.() -> Unit)?
|
||||
) {
|
||||
applyKotlinTargetHierarchy(hierarchyDescriptor.extendIfNotNull(describeExtension), targets, sourceSets)
|
||||
}
|
||||
|
||||
override fun default(describeExtension: (KotlinTargetHierarchyBuilder.() -> Unit)?) {
|
||||
apply(naturalKotlinTargetHierarchy, describeExtension)
|
||||
}
|
||||
|
||||
override fun custom(describe: KotlinTargetHierarchyBuilder.() -> Unit) {
|
||||
apply(KotlinTargetHierarchyDescriptor(describe))
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinTargetHierarchyDescriptor.extendIfNotNull(describe: (KotlinTargetHierarchyBuilder.() -> Unit)?) =
|
||||
if (describe == null) this else extend(describe)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.targetHierarchy
|
||||
|
||||
import org.gradle.api.DomainObjectCollection
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
|
||||
internal fun applyKotlinTargetHierarchy(
|
||||
hierarchyDescriptor: KotlinTargetHierarchyDescriptor,
|
||||
targets: DomainObjectCollection<KotlinTarget>,
|
||||
sourceSets: NamedDomainObjectContainer<KotlinSourceSet>
|
||||
) {
|
||||
targets
|
||||
.matching { target -> target.platformType != KotlinPlatformType.common }
|
||||
.all { target ->
|
||||
target.compilations.all { compilation ->
|
||||
val hierarchy = hierarchyDescriptor.buildKotlinTargetHierarchy(compilation)
|
||||
applyKotlinTargetHierarchy(hierarchy, compilation, sourceSets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyKotlinTargetHierarchy(
|
||||
hierarchy: KotlinTargetHierarchy,
|
||||
compilation: KotlinCompilation<*>,
|
||||
sourceSets: NamedDomainObjectContainer<KotlinSourceSet>
|
||||
): KotlinSourceSet? {
|
||||
val sharedSourceSetName = hierarchy.node.sharedSourceSetName(compilation) ?: return null
|
||||
val sharedSourceSet = sourceSets.maybeCreate(sharedSourceSetName)
|
||||
|
||||
hierarchy.children
|
||||
.mapNotNull { childHierarchy -> applyKotlinTargetHierarchy(childHierarchy, compilation, sourceSets) }
|
||||
.forEach { childSourceSet -> childSourceSet.dependsOn(sharedSourceSet) }
|
||||
|
||||
if (hierarchy.children.isEmpty()) {
|
||||
compilation.defaultSourceSet.dependsOn(sharedSourceSet)
|
||||
}
|
||||
|
||||
return sharedSourceSet
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.targetHierarchy
|
||||
|
||||
import org.gradle.api.InvalidUserCodeException
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
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.konan.target.Family
|
||||
import org.jetbrains.kotlin.tooling.core.closure
|
||||
|
||||
internal fun KotlinTargetHierarchyDescriptor.buildKotlinTargetHierarchy(compilation: KotlinCompilation<*>): KotlinTargetHierarchy {
|
||||
val context = KotlinTargetHierarchyBuilderImplContext(compilation)
|
||||
this(context.getOrCreateBuilder(KotlinTargetHierarchy.Node.Root))
|
||||
return context.build(KotlinTargetHierarchy.Node.Root)
|
||||
}
|
||||
|
||||
private class KotlinTargetHierarchyBuilderImplContext(val compilation: KotlinCompilation<*>) {
|
||||
private val builders = hashMapOf<KotlinTargetHierarchy.Node, KotlinTargetHierarchyBuilderImpl>()
|
||||
private val builtValues = hashMapOf<KotlinTargetHierarchy.Node, KotlinTargetHierarchy>()
|
||||
|
||||
fun getOrCreateBuilder(node: KotlinTargetHierarchy.Node): KotlinTargetHierarchyBuilderImpl = builders.getOrPut(node) {
|
||||
KotlinTargetHierarchyBuilderImpl(this, node)
|
||||
}
|
||||
|
||||
fun build(node: KotlinTargetHierarchy.Node): KotlinTargetHierarchy {
|
||||
return builtValues.getOrPut(node) {
|
||||
val builder = getOrCreateBuilder(node)
|
||||
|
||||
/*
|
||||
Keep the hierarchy 'deduplicated'.
|
||||
e.g.
|
||||
if we have two child hierarchies:
|
||||
|
||||
1) a -> b -> c
|
||||
2) b -> c
|
||||
|
||||
then we can remove the duplicated 'sub hierarchy' 2) and only return
|
||||
a -> b -> c
|
||||
|
||||
*/
|
||||
val childrenNodes = builder.children.map { it.node } -
|
||||
builder.children.flatMap { child -> child.childrenClosure }.map { it.node }.toSet()
|
||||
|
||||
KotlinTargetHierarchy(node, childrenNodes.map(this::build).toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinTargetHierarchyBuilderImpl(
|
||||
val context: KotlinTargetHierarchyBuilderImplContext,
|
||||
val node: KotlinTargetHierarchy.Node,
|
||||
) : KotlinTargetHierarchyBuilder {
|
||||
|
||||
override val compilation: KotlinCompilation<*> get() = context.compilation
|
||||
override val target: KotlinTarget get() = compilation.target
|
||||
|
||||
val children = mutableSetOf<KotlinTargetHierarchyBuilderImpl>()
|
||||
val childrenClosure: Set<KotlinTargetHierarchyBuilderImpl> get() = closure { it.children }
|
||||
|
||||
override fun group(name: String, build: KotlinTargetHierarchyBuilder.() -> Unit) {
|
||||
val node = KotlinTargetHierarchy.Node.Group(name)
|
||||
val child = context.getOrCreateBuilder(node)
|
||||
children.add(child)
|
||||
child.build()
|
||||
checkCyclicHierarchy()
|
||||
}
|
||||
|
||||
override val isNative: Boolean get() = target is KotlinNativeTarget
|
||||
override val isApple: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family.isAppleFamily }
|
||||
override val isIos: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.IOS }
|
||||
override val isWatchos: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.WATCHOS }
|
||||
override val isMacos: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.OSX }
|
||||
override val isTvos: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.TVOS }
|
||||
override val isWindows: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.MINGW }
|
||||
override val isLinux: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.LINUX }
|
||||
override val isAndroidNative: Boolean get() = target.let { it is KotlinNativeTarget && it.konanTarget.family == Family.ANDROID }
|
||||
override val isJvm: Boolean get() = target is KotlinJvmTarget
|
||||
override val isAndroidJvm: Boolean get() = target is KotlinAndroidTarget
|
||||
override val isJsLegacy: Boolean get() = target is KotlinJsTarget
|
||||
override val isJsIr: Boolean get() = target is KotlinJsIrTarget
|
||||
override val isJs: Boolean get() = isJsIr || isJsLegacy
|
||||
|
||||
override fun toString(): String {
|
||||
return "KotlinTargetHierarchyBuilder($node)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Cycle Detection: Provide feedback for users when a KotlinTargetHierarchy cycle is declared */
|
||||
|
||||
private fun KotlinTargetHierarchyBuilderImpl.checkCyclicHierarchy(): Nothing? {
|
||||
val stack = mutableListOf(node)
|
||||
val visited = hashSetOf<KotlinTargetHierarchyBuilderImpl>()
|
||||
|
||||
fun checkChild(child: KotlinTargetHierarchyBuilderImpl) {
|
||||
if (!visited.add(child)) return
|
||||
stack += child.node
|
||||
if (this == child) throw CyclicKotlinTargetHierarchyException(stack)
|
||||
child.children.forEach { next -> checkChild(next) }
|
||||
stack -= child.node
|
||||
}
|
||||
|
||||
children.forEach { child -> checkChild(child) }
|
||||
return null
|
||||
}
|
||||
|
||||
internal class CyclicKotlinTargetHierarchyException(val cycle: List<KotlinTargetHierarchy.Node>) : InvalidUserCodeException(
|
||||
"KotlinTargetHierarchy cycle detected: ${cycle.joinToString(" -> ")}"
|
||||
)
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.targetHierarchy
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyDescriptor
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
|
||||
|
||||
internal val naturalKotlinTargetHierarchy = KotlinTargetHierarchyDescriptor {
|
||||
if (!compilation.isMain() && !compilation.isTest()) {
|
||||
/* This hierarchy is only defined for default 'main' and 'test' compilations */
|
||||
return@KotlinTargetHierarchyDescriptor
|
||||
}
|
||||
|
||||
common {
|
||||
if (isNative) {
|
||||
group("native") {
|
||||
if (isApple) {
|
||||
group("apple") {
|
||||
if (isIos) group("ios")
|
||||
if (isTvos) group("tvos")
|
||||
if (isWatchos) group("watchos")
|
||||
if (isMacos) group("macos")
|
||||
}
|
||||
}
|
||||
|
||||
if (isLinux) group("linux")
|
||||
if (isWindows) group("windows")
|
||||
if (isAndroidNative) group("androidNative")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyDescriptor
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.CyclicKotlinTargetHierarchyException
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.KotlinTargetHierarchy
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.KotlinTargetHierarchy.Node
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.targetHierarchy.buildKotlinTargetHierarchy
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertSame
|
||||
|
||||
class KotlinTargetHierarchyDescriptorTest {
|
||||
|
||||
private val project = buildProjectWithMPP()
|
||||
private val kotlin = project.multiplatformExtension
|
||||
|
||||
@Test
|
||||
fun `test - simple descriptor`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor {
|
||||
common {
|
||||
if (target.name == "a") group("groupA")
|
||||
if (target.name == "b") group("groupB")
|
||||
}
|
||||
}
|
||||
|
||||
val targetA = kotlin.linuxX64("a")
|
||||
val targetB = kotlin.linuxArm64("b")
|
||||
val targetC = kotlin.jvm()
|
||||
|
||||
assertEquals(
|
||||
hierarchy {
|
||||
group("common") {
|
||||
group("groupA")
|
||||
}
|
||||
},
|
||||
descriptor.buildKotlinTargetHierarchy(targetA.compilations.main)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
hierarchy {
|
||||
group("common") {
|
||||
group("groupB")
|
||||
}
|
||||
},
|
||||
descriptor.buildKotlinTargetHierarchy(targetB.compilations.main)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
hierarchy { group("common") },
|
||||
descriptor.buildKotlinTargetHierarchy(targetC.compilations.main)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - extend`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor { group("base") }.extend {
|
||||
group("base") {
|
||||
group("extension")
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
hierarchy {
|
||||
group("base") {
|
||||
group("extension")
|
||||
}
|
||||
},
|
||||
descriptor.buildKotlinTargetHierarchy(kotlin.linuxX64().compilations.getByName("main"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - extend - with new root`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor { group("base") }.extend {
|
||||
group("newRoot") {
|
||||
group("base") {
|
||||
group("extension")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
hierarchy {
|
||||
group("newRoot") {
|
||||
group("base") {
|
||||
group("extension")
|
||||
}
|
||||
}
|
||||
},
|
||||
descriptor.buildKotlinTargetHierarchy(kotlin.linuxX64().compilations.main)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - extend - with two new roots and two extensions`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor { group("base") }
|
||||
.extend {
|
||||
group("newRoot1") {
|
||||
group("base") {
|
||||
group("extension1")
|
||||
}
|
||||
}
|
||||
}
|
||||
.extend {
|
||||
group("newRoot2") {
|
||||
group("base") {
|
||||
group("extension2")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val hierarchy = descriptor.buildKotlinTargetHierarchy(kotlin.linuxX64().compilations.main)
|
||||
|
||||
assertEquals(
|
||||
hierarchy {
|
||||
group("newRoot1") {
|
||||
group("base") {
|
||||
group("extension1")
|
||||
group("extension2")
|
||||
}
|
||||
}
|
||||
|
||||
group("newRoot2") {
|
||||
group("base") {
|
||||
group("extension1")
|
||||
group("extension2")
|
||||
}
|
||||
}
|
||||
}, hierarchy
|
||||
)
|
||||
|
||||
fun KotlinTargetHierarchy.collectChildren(): List<KotlinTargetHierarchy> {
|
||||
return children.toList() + children.flatMap { it.collectChildren() }
|
||||
}
|
||||
|
||||
/* Check that all equal hierarchies are even the same instance */
|
||||
val allNodes = hierarchy.collectChildren()
|
||||
allNodes.forEach { node ->
|
||||
val equalNodes = allNodes.filter { otherNode -> otherNode == node }
|
||||
equalNodes.forEach { equalNode ->
|
||||
assertSame(node, equalNode, "Expected equal nodes to be the same instance")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - cycle`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor {
|
||||
group("x") { // decoy 1
|
||||
group("a") {
|
||||
group("xx") // decoy 2
|
||||
|
||||
group("b") {
|
||||
group("xxx") // decoy 3
|
||||
|
||||
group("c") {
|
||||
group("a") {
|
||||
group("xxxx") // decoy 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val cycleStack = assertFailsWith<CyclicKotlinTargetHierarchyException> {
|
||||
descriptor.buildKotlinTargetHierarchy(kotlin.linuxX64().compilations.main)
|
||||
}.cycle
|
||||
|
||||
assertEquals(
|
||||
listOf(Node.Group("a"), Node.Group("b"), Node.Group("c"), Node.Group("a")),
|
||||
cycleStack
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TestHierarchyBuilder(private val node: Node) {
|
||||
private val children = mutableSetOf<TestHierarchyBuilder>()
|
||||
|
||||
fun group(name: String, builder: TestHierarchyBuilder.() -> Unit = {}) {
|
||||
children.add(TestHierarchyBuilder(Node.Group(name)).also(builder))
|
||||
}
|
||||
|
||||
fun build(): KotlinTargetHierarchy = KotlinTargetHierarchy(node, children.map { it.build() }.toSet())
|
||||
}
|
||||
|
||||
private fun hierarchy(build: TestHierarchyBuilder.() -> Unit): KotlinTargetHierarchy {
|
||||
return TestHierarchyBuilder(Node.Root).also(build).build()
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetHierarchyDescriptor
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class KotlinTargetHierarchyDslTest {
|
||||
|
||||
|
||||
private val project = buildProjectWithMPP()
|
||||
private val kotlin = project.multiplatformExtension
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - hierarchy default - targets from all families`() {
|
||||
kotlin.apply {
|
||||
targetHierarchy.default()
|
||||
iosArm32()
|
||||
iosArm64()
|
||||
iosX64()
|
||||
iosSimulatorArm64()
|
||||
|
||||
tvosArm64()
|
||||
tvosX64()
|
||||
|
||||
watchosArm32()
|
||||
watchosArm64()
|
||||
|
||||
macosX64()
|
||||
macosArm64()
|
||||
|
||||
linuxX64()
|
||||
linuxArm32Hfp()
|
||||
|
||||
mingwX64()
|
||||
mingwX86()
|
||||
|
||||
androidNativeArm32()
|
||||
androidNativeArm64()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
stringSetOf(
|
||||
"androidNativeArm32Main",
|
||||
"androidNativeArm64Main",
|
||||
"iosArm32Main",
|
||||
"iosArm64Main",
|
||||
"iosSimulatorArm64Main",
|
||||
"iosX64Main",
|
||||
"linuxArm32HfpMain",
|
||||
"linuxX64Main",
|
||||
"macosArm64Main",
|
||||
"macosX64Main",
|
||||
"mingwX64Main",
|
||||
"mingwX86Main",
|
||||
"nativeMain",
|
||||
"tvosArm64Main",
|
||||
"tvosX64Main",
|
||||
"watchosArm32Main",
|
||||
"watchosArm64Main"
|
||||
),
|
||||
kotlin.dependingSourceSetNames("commonMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf(
|
||||
"androidNativeArm32Test",
|
||||
"androidNativeArm64Test",
|
||||
"iosArm32Test",
|
||||
"iosArm64Test",
|
||||
"iosSimulatorArm64Test",
|
||||
"iosX64Test",
|
||||
"linuxArm32HfpTest",
|
||||
"linuxX64Test",
|
||||
"macosArm64Test",
|
||||
"macosX64Test",
|
||||
"mingwX64Test",
|
||||
"mingwX86Test",
|
||||
"nativeTest",
|
||||
"tvosArm64Test",
|
||||
"tvosX64Test",
|
||||
"watchosArm32Test",
|
||||
"watchosArm64Test"
|
||||
), kotlin.dependingSourceSetNames("commonTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("androidNativeMain", "appleMain", "linuxMain", "windowsMain"),
|
||||
kotlin.dependingSourceSetNames("nativeMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("androidNativeTest", "appleTest", "linuxTest", "windowsTest"),
|
||||
kotlin.dependingSourceSetNames("nativeTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("iosMain", "macosMain", "tvosMain", "watchosMain"),
|
||||
kotlin.dependingSourceSetNames("appleMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("iosTest", "macosTest", "tvosTest", "watchosTest"),
|
||||
kotlin.dependingSourceSetNames("appleTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("iosArm32Main", "iosArm64Main", "iosSimulatorArm64Main", "iosX64Main"),
|
||||
kotlin.dependingSourceSetNames("iosMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("iosArm32Test", "iosArm64Test", "iosSimulatorArm64Test", "iosX64Test"),
|
||||
kotlin.dependingSourceSetNames("iosTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("tvosArm64Main", "tvosX64Main"),
|
||||
kotlin.dependingSourceSetNames("tvosMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("tvosArm64Test", "tvosX64Test"),
|
||||
kotlin.dependingSourceSetNames("tvosTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("watchosArm32Main", "watchosArm64Main"),
|
||||
kotlin.dependingSourceSetNames("watchosMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("watchosArm32Test", "watchosArm64Test"),
|
||||
kotlin.dependingSourceSetNames("watchosTest")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("linuxArm32HfpMain", "linuxX64Main"),
|
||||
kotlin.dependingSourceSetNames("linuxMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("linuxArm32HfpTest", "linuxX64Test"),
|
||||
kotlin.dependingSourceSetNames("linuxTest")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - hierarchy default - only linuxX64`() {
|
||||
kotlin.apply {
|
||||
targetHierarchy.default()
|
||||
kotlin.linuxX64()
|
||||
}
|
||||
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val commonTest = kotlin.sourceSets.getByName("commonTest")
|
||||
val nativeMain = kotlin.sourceSets.getByName("nativeMain")
|
||||
val nativeTest = kotlin.sourceSets.getByName("nativeTest")
|
||||
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
|
||||
val linuxTest = kotlin.sourceSets.getByName("linuxTest")
|
||||
val linuxX64Main = kotlin.sourceSets.getByName("linuxX64Main")
|
||||
val linuxX64Test = kotlin.sourceSets.getByName("linuxX64Test")
|
||||
|
||||
assertEquals(
|
||||
setOf(commonMain, commonTest, nativeMain, nativeTest, linuxMain, linuxTest, linuxX64Main, linuxX64Test),
|
||||
kotlin.sourceSets.toSet()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - hierarchy apply - extend`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor {
|
||||
group("common") {
|
||||
group("base")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.apply {
|
||||
targetHierarchy.apply(descriptor) { group("base") { group("extension") } }
|
||||
linuxX64()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("baseMain", "linuxX64Main"), kotlin.dependingSourceSetNames("commonMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("extensionMain"), kotlin.dependingSourceSetNames("baseMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("linuxX64Main"), kotlin.dependingSourceSetNames("extensionMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf(), kotlin.dependingSourceSetNames("linuxX64Main")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - hierarchy set - extend - with new root`() {
|
||||
val descriptor = KotlinTargetHierarchyDescriptor {
|
||||
group("common") {
|
||||
group("base")
|
||||
}
|
||||
}
|
||||
|
||||
kotlin.apply {
|
||||
targetHierarchy.apply(descriptor) {
|
||||
group("newRoot") {
|
||||
group("base") { group("extension") }
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("baseMain"), kotlin.dependingSourceSetNames("newRootMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("baseMain", "linuxX64Main"), kotlin.dependingSourceSetNames("commonMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("extensionMain"), kotlin.dependingSourceSetNames("baseMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf("linuxX64Main"), kotlin.dependingSourceSetNames("extensionMain")
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
stringSetOf(), kotlin.dependingSourceSetNames("linuxX64Main")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun KotlinMultiplatformExtension.dependingSourceSetNames(sourceSetName: String) =
|
||||
dependingSourceSetNames(sourceSets.getByName(sourceSetName))
|
||||
|
||||
private fun KotlinMultiplatformExtension.dependingSourceSetNames(sourceSet: KotlinSourceSet) =
|
||||
sourceSets.filter { sourceSet in it.dependsOn }.map { it.name }.toStringSet()
|
||||
|
||||
|
||||
/* StringSet: Special Set implementation, which makes it easy to copy and paste after assertions fail */
|
||||
|
||||
private fun stringSetOf(vararg values: String) = StringSet(values.toSet())
|
||||
|
||||
private fun Iterable<String>.toStringSet() = StringSet(this.toSet())
|
||||
|
||||
private data class StringSet(private val set: Set<String>) : Set<String> by set {
|
||||
override fun toString(): String {
|
||||
return "stringSetOf(" + set.joinToString(", ") { "\"$it\"" } + ")"
|
||||
}
|
||||
}
|
||||
+12
@@ -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
|
||||
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
|
||||
val NamedDomainObjectCollection<out KotlinCompilation<*>>.main: KotlinCompilation<*> get() = getByName("main")
|
||||
val NamedDomainObjectCollection<out KotlinCompilation<*>>.test: KotlinCompilation<*> get() = getByName("test")
|
||||
Reference in New Issue
Block a user