diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt new file mode 100644 index 00000000000..5ea6a28131f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainer.kt @@ -0,0 +1,34 @@ +/* + * 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.kpm + +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.Serializable + +interface KotlinHasExternalModel { + val external: KotlinExternalModelContainer +} + +abstract class KotlinExternalModelContainer internal constructor() : Serializable { + abstract operator fun contains(key: KotlinExternalModelKey): Boolean + abstract operator fun get(key: KotlinExternalModelKey): T? + fun getOrThrow(key: KotlinExternalModelKey): T = get(key) + ?: throw NoSuchElementException("Missing external model for ${key.id}") + + @InternalKotlinGradlePluginApi + companion object { + fun mutable(): KotlinMutableExternalModelContainer = KotlinMutableExternalModelContainerImpl() + } + + object Empty : KotlinExternalModelContainer() { + override fun contains(key: KotlinExternalModelKey): Boolean = false + override fun get(key: KotlinExternalModelKey): T? = null + } +} + +abstract class KotlinMutableExternalModelContainer internal constructor() : KotlinExternalModelContainer() { + abstract operator fun set(key: KotlinExternalModelKey, value: T) +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.kt new file mode 100644 index 00000000000..5c527e4a401 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelContainerImpl.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.kpm + +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.Serializable + +internal class KotlinMutableExternalModelContainerImpl : KotlinMutableExternalModelContainer(), Serializable { + private val values = mutableMapOf, Any>() + + @Synchronized + override fun set(key: KotlinExternalModelKey, value: T) { + values[key] = value + } + + @Synchronized + override fun contains(key: KotlinExternalModelKey): Boolean { + return key in values + } + + @Synchronized + @Suppress("unchecked_cast") + override fun get(key: KotlinExternalModelKey): T? { + return values[key]?.let { it as T } + } + + @Synchronized + private fun writeReplace(): Any { + val serializedValues = values.filterKeys { it.serializer != null } + .mapValues { (key, value) -> + @Suppress("unchecked_cast") + val serializer = checkNotNull(key.serializer) as KotlinExternalModelSerializer + serializer.serialize(value) + }.mapKeys { (key, _) -> key.id } + .toMutableMap() + + return SerializedKotlinExternalModelContainer(serializedValues) + } +} + +private class SerializedKotlinExternalModelContainer( + private val serializedValues: MutableMap, ByteArray> +) : KotlinExternalModelContainer(), Serializable { + + private val deserializedValues = mutableMapOf, Any>() + + @Synchronized + override fun contains(key: KotlinExternalModelKey): Boolean { + return key.id in deserializedValues || key.id in serializedValues + } + + @Synchronized + @Suppress("unchecked_cast") + override fun get(key: KotlinExternalModelKey): T? { + deserializedValues[key.id]?.let { return it as T } + val serializedValue = serializedValues.remove(key.id) ?: return null + val deserializedValue = key.serializer?.deserialize(serializedValue) ?: return null + deserializedValues[key.id] = deserializedValue + return deserializedValue + } + + @InternalKotlinGradlePluginApi + companion object { + private const val serialVersionUID = 0L + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.kt new file mode 100644 index 00000000000..1756d0e7ce5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelId.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.kpm + +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.Serializable + +inline fun KotlinExternalModelId(disambiguationName: String? = null): KotlinExternalModelId { + return KotlinExternalModelId(KotlinExternalModelType(), disambiguationName) +} + +class KotlinExternalModelId @PublishedApi internal constructor( + internal val type: KotlinExternalModelType, + private val disambiguationName: String? = null +) : Serializable { + override fun toString(): String { + return if (disambiguationName == null) type.toString() + else "$disambiguationName:$type" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as KotlinExternalModelId<*> + if (type != other.type) return false + if (disambiguationName != other.disambiguationName) return false + return true + } + + override fun hashCode(): Int { + var result = type.hashCode() + result = 31 * result + (disambiguationName?.hashCode() ?: 0) + return result + } + + @InternalKotlinGradlePluginApi + companion object { + private const val serialVersionUID = 0L + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt new file mode 100644 index 00000000000..1e95bde24cb --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelKey.kt @@ -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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.kpm + +inline fun KotlinExternalModelKey( + disambiguationName: String? = null, serializer: KotlinExternalModelSerializer? = null +): KotlinExternalModelKey = KotlinExternalModelKey(KotlinExternalModelId(disambiguationName), serializer) + +inline fun KotlinExternalModelKey(serializer: KotlinExternalModelSerializer): KotlinExternalModelKey = + KotlinExternalModelKey(KotlinExternalModelId(), serializer) + +class KotlinExternalModelKey @PublishedApi internal constructor( + internal val id: KotlinExternalModelId, + internal val serializer: KotlinExternalModelSerializer? = null, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as KotlinExternalModelKey<*> + + if (id != other.id) return false + if (serializer != other.serializer) return false + + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + (serializer?.hashCode() ?: 0) + return result + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt new file mode 100644 index 00000000000..4cd401bffae --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelSerializer.kt @@ -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.kpm + +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.* + +interface KotlinExternalModelSerializer { + fun serialize(value: T): ByteArray + fun deserialize(data: ByteArray): T + + @InternalKotlinGradlePluginApi + companion object { + inline fun serializable(): KotlinExternalModelSerializer = + KotlinSerializableExternalModelSerializer(T::class.java) + } +} + +@PublishedApi +internal class KotlinSerializableExternalModelSerializer( + private val clazz: Class +) : KotlinExternalModelSerializer { + override fun serialize(value: T): ByteArray { + return ByteArrayOutputStream().run { + ObjectOutputStream(this).use { stream -> stream.writeObject(value) } + toByteArray() + } + } + + override fun deserialize(data: ByteArray): T { + return ObjectInputStream(ByteArrayInputStream(data)).use { stream -> + clazz.cast(stream.readObject()) + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt new file mode 100644 index 00000000000..280535241d5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/KotlinExternalModelType.kt @@ -0,0 +1,43 @@ +/* + * 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.kpm + +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.Serializable +import kotlin.reflect.KClass +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +@Suppress("FunctionName") +inline fun KotlinExternalModelType(): KotlinExternalModelType { + return KotlinExternalModelType(externalModelTypeSignature(typeOf())) +} + +class KotlinExternalModelType +@PublishedApi internal constructor(private val signature: String) : Serializable { + override fun equals(other: Any?): Boolean { + if (other !is KotlinExternalModelType<*>) return false + if (other.signature != this.signature) return false + return true + } + + override fun hashCode(): Int = 31 * signature.hashCode() + override fun toString(): String = "ExternalModelType($signature)" + + @InternalKotlinGradlePluginApi + companion object { + private const val serialVersionUID = 0L + } +} + +@PublishedApi +internal fun externalModelTypeSignature(type: KType): String { + require(!type.isMarkedNullable) { "Unexpected nullable type. Found $type" } + require(type.arguments.isEmpty()) { "Parameterized types are not supported. Found $type" } + val classifier = requireNotNull(type.classifier) { "Expected classifier. Found $type" } + val clazz = (classifier as? KClass<*>) ?: throw IllegalArgumentException("Expected KClass classifier. Found $classifier") + return clazz.qualifiedName ?: throw IllegalArgumentException("Missing qualifiedName in $type") +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt index 917ceb93d45..3edc81b14ae 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragment.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.kpm.idea +import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer import java.io.Serializable interface IdeaKotlinFragment : Serializable { @@ -14,6 +15,7 @@ interface IdeaKotlinFragment : Serializable { val directRefinesDependencies: Collection val sourceDirectories: Collection val resourceDirectories: Collection + val external: KotlinExternalModelContainer } @InternalKotlinGradlePluginApi @@ -23,7 +25,8 @@ data class IdeaKotlinFragmentImpl( override val dependencies: Collection, override val directRefinesDependencies: Collection, override val sourceDirectories: Collection, - override val resourceDirectories: Collection + override val resourceDirectories: Collection, + override val external: KotlinExternalModelContainer ) : IdeaKotlinFragment { @InternalKotlinGradlePluginApi diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts index 325e43c0519..b88351dd96b 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/build.gradle.kts @@ -7,6 +7,7 @@ plugins { dependencies { implementation(gradleKotlinDsl()) compileOnly(project(":kotlin-gradle-plugin")) + compileOnly(project(":kotlin-gradle-plugin-idea")) compileOnly("com.android.tools.build:gradle:7.0.0") } diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt new file mode 100644 index 00000000000..bcbf6fc7059 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/AndroidDsl.kt @@ -0,0 +1,22 @@ +/* + * 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(InternalKotlinGradlePluginApi::class) + +package org.jetbrains.kotlin.gradle.android + +import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey +import org.jetbrains.kotlin.gradle.kpm.idea.InternalKotlinGradlePluginApi +import java.io.File +import java.io.Serializable + + +internal val androidDslKey = KotlinExternalModelKey() + +class AndroidDsl : Serializable { + var compileSdk = 0 + var isMinifyEnabled: Boolean = false + var androidManifest: File? = null +} diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt index 7002f7230ae..adf18f39b70 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/KotlinAndroidVariantFactory.kt @@ -78,4 +78,9 @@ fun KotlinGradleModule.createKotlinAndroidVariant(androidVariant: BaseVariant) { androidVariant.getCompileClasspath(mainBytecodeKey), project.getAndroidRuntimeJars() ) + + val androidDsl = AndroidDsl() + androidDsl.androidManifest = project.file("AndroidManifest.xml") + androidDsl.compileSdk = 23 + androidCommon.external[androidDslKey] = androidDsl } diff --git a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt index c0c35b8e887..d6051d4b502 100644 --- a/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt +++ b/libraries/tools/kotlin-gradle-plugin-kpm-android/src/main/kotlin/org/jetbrains/kotlin/gradle/android/androidCommonFragment.kt @@ -6,9 +6,10 @@ package org.jetbrains.kotlin.gradle.android import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragmentInternal import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleModule -val KotlinGradleModule.androidCommon: KotlinGradleFragment - get() = fragments.findByName("android") ?: fragments.create("android") { fragment -> +val KotlinGradleModule.androidCommon: KotlinGradleFragmentInternal + get() = (fragments.findByName("android") ?: fragments.create("android") { fragment -> fragment.refines(common) - } + }) as KotlinGradleFragmentInternal diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt index 120d9d30a98..2e1272520dd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKotlinFragmentBuilder.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.gradle.kpm.idea +import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer +import org.jetbrains.kotlin.gradle.kpm.KotlinHasExternalModel import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleFragment internal fun KotlinGradleFragment.toIdeaKotlinFragment( @@ -21,7 +23,8 @@ internal fun KotlinGradleFragment.toIdeaKotlinFragment( sourceDirectories = kotlinSourceRoots.sourceDirectories.files.toList().map { file -> IdeaKotlinSourceDirectoryImpl(file) }, - resourceDirectories = emptyList() + resourceDirectories = emptyList(), + external = (this as? KotlinHasExternalModel)?.external ?: KotlinExternalModelContainer.Empty ) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalModelContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalModelContainer.kt new file mode 100644 index 00000000000..d2504cec74c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/external/ExternalModelContainer.kt @@ -0,0 +1,17 @@ +/* + * 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 kotlin.reflect.KType + + +interface ExternalModelContainer { + +} + +interface MutableExternalModelContainer { + +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt index 815e9511c56..c1da76de502 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/KotlinGradleFragment.kt @@ -12,6 +12,9 @@ import org.gradle.api.file.SourceDirectorySet import org.gradle.api.provider.Provider import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies +import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelContainer +import org.jetbrains.kotlin.gradle.kpm.KotlinHasExternalModel +import org.jetbrains.kotlin.gradle.kpm.KotlinMutableExternalModelContainer import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler @@ -31,7 +34,9 @@ open class KotlinGradleFragmentInternal @Inject constructor( final override val containingModule: KotlinGradleModule, final override val fragmentName: String, dependencyConfigurations: KotlinFragmentDependencyConfigurations -) : KotlinGradleFragment, KotlinFragmentDependencyConfigurations by dependencyConfigurations { +) : KotlinGradleFragment, + KotlinFragmentDependencyConfigurations by dependencyConfigurations, + KotlinHasExternalModel { final override fun getName(): String = fragmentName @@ -43,6 +48,8 @@ open class KotlinGradleFragmentInternal @Inject constructor( // FIXME check for consistency override val languageSettings: LanguageSettingsBuilder = DefaultLanguageSettingsBuilder() + override val external: KotlinMutableExternalModelContainer = KotlinExternalModelContainer.mutable() + override fun refines(other: KotlinGradleFragment) { checkCanRefine(other) refines(containingModule.fragments.named(other.name))