[KPM] Implement KotlinExternalModelContainer

KT-51262
KT-51220
This commit is contained in:
sebastian.sellmair
2022-02-13 16:11:10 +01:00
committed by Space
parent 55448943af
commit f1655e5f4e
14 changed files with 332 additions and 6 deletions
@@ -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 <T : Any> contains(key: KotlinExternalModelKey<T>): Boolean
abstract operator fun <T : Any> get(key: KotlinExternalModelKey<T>): T?
fun <T : Any> getOrThrow(key: KotlinExternalModelKey<T>): T = get(key)
?: throw NoSuchElementException("Missing external model for ${key.id}")
@InternalKotlinGradlePluginApi
companion object {
fun mutable(): KotlinMutableExternalModelContainer = KotlinMutableExternalModelContainerImpl()
}
object Empty : KotlinExternalModelContainer() {
override fun <T : Any> contains(key: KotlinExternalModelKey<T>): Boolean = false
override fun <T : Any> get(key: KotlinExternalModelKey<T>): T? = null
}
}
abstract class KotlinMutableExternalModelContainer internal constructor() : KotlinExternalModelContainer() {
abstract operator fun <T : Any> set(key: KotlinExternalModelKey<T>, value: T)
}
@@ -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<KotlinExternalModelKey<*>, Any>()
@Synchronized
override fun <T : Any> set(key: KotlinExternalModelKey<T>, value: T) {
values[key] = value
}
@Synchronized
override fun <T : Any> contains(key: KotlinExternalModelKey<T>): Boolean {
return key in values
}
@Synchronized
@Suppress("unchecked_cast")
override fun <T : Any> get(key: KotlinExternalModelKey<T>): 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<Any>
serializer.serialize(value)
}.mapKeys { (key, _) -> key.id }
.toMutableMap()
return SerializedKotlinExternalModelContainer(serializedValues)
}
}
private class SerializedKotlinExternalModelContainer(
private val serializedValues: MutableMap<KotlinExternalModelId<*>, ByteArray>
) : KotlinExternalModelContainer(), Serializable {
private val deserializedValues = mutableMapOf<KotlinExternalModelId<*>, Any>()
@Synchronized
override fun <T : Any> contains(key: KotlinExternalModelKey<T>): Boolean {
return key.id in deserializedValues || key.id in serializedValues
}
@Synchronized
@Suppress("unchecked_cast")
override fun <T : Any> get(key: KotlinExternalModelKey<T>): 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
}
}
@@ -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 <reified T : Any> KotlinExternalModelId(disambiguationName: String? = null): KotlinExternalModelId<T> {
return KotlinExternalModelId(KotlinExternalModelType(), disambiguationName)
}
class KotlinExternalModelId<T : Any> @PublishedApi internal constructor(
internal val type: KotlinExternalModelType<T>,
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
}
}
@@ -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 <reified T : Any> KotlinExternalModelKey(
disambiguationName: String? = null, serializer: KotlinExternalModelSerializer<T>? = null
): KotlinExternalModelKey<T> = KotlinExternalModelKey(KotlinExternalModelId(disambiguationName), serializer)
inline fun <reified T : Any> KotlinExternalModelKey(serializer: KotlinExternalModelSerializer<T>): KotlinExternalModelKey<T> =
KotlinExternalModelKey(KotlinExternalModelId(), serializer)
class KotlinExternalModelKey<T : Any> @PublishedApi internal constructor(
internal val id: KotlinExternalModelId<T>,
internal val serializer: KotlinExternalModelSerializer<T>? = 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
}
}
@@ -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<T : Any> {
fun serialize(value: T): ByteArray
fun deserialize(data: ByteArray): T
@InternalKotlinGradlePluginApi
companion object {
inline fun <reified T : Serializable> serializable(): KotlinExternalModelSerializer<T> =
KotlinSerializableExternalModelSerializer(T::class.java)
}
}
@PublishedApi
internal class KotlinSerializableExternalModelSerializer<T : Serializable>(
private val clazz: Class<T>
) : KotlinExternalModelSerializer<T> {
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())
}
}
}
@@ -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 <reified T : Any> KotlinExternalModelType(): KotlinExternalModelType<T> {
return KotlinExternalModelType(externalModelTypeSignature(typeOf<T>()))
}
class KotlinExternalModelType</* Used as phantom type */ @Suppress("unused") T: Any>
@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")
}
@@ -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<IdeaKotlinFragment>
val sourceDirectories: Collection<IdeaKotlinSourceDirectory>
val resourceDirectories: Collection<IdeaKotlinResourceDirectory>
val external: KotlinExternalModelContainer
}
@InternalKotlinGradlePluginApi
@@ -23,7 +25,8 @@ data class IdeaKotlinFragmentImpl(
override val dependencies: Collection<IdeaKotlinFragmentDependency>,
override val directRefinesDependencies: Collection<IdeaKotlinFragment>,
override val sourceDirectories: Collection<IdeaKotlinSourceDirectory>,
override val resourceDirectories: Collection<IdeaKotlinResourceDirectory>
override val resourceDirectories: Collection<IdeaKotlinResourceDirectory>,
override val external: KotlinExternalModelContainer
) : IdeaKotlinFragment {
@InternalKotlinGradlePluginApi
@@ -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")
}
@@ -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<AndroidDsl>()
class AndroidDsl : Serializable {
var compileSdk = 0
var isMinifyEnabled: Boolean = false
var androidManifest: File? = null
}
@@ -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
}
@@ -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
@@ -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
)
}
}
@@ -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 {
}
@@ -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))