diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts index 3f29e9e4b21..d466c86aa06 100644 --- a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts @@ -65,6 +65,7 @@ dependencies { commonCompileOnly(project(":kotlin-tooling-metadata")) commonImplementation(project(":kotlin-gradle-plugin-idea")) + commonImplementation(project(":kotlin-gradle-plugin-idea-proto")) commonImplementation(project(":kotlin-util-klib")) commonImplementation(project(":native:kotlin-klib-commonizer-api")) commonImplementation(project(":kotlin-project-model")) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilder.kt index cedadb45e13..0c7e625212a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilder.kt @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.gradle.kpm.idea import org.jetbrains.kotlin.compilerRunner.konanHome import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKpmProjectModelBuilder.FragmentConstraint +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializationExtension +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializationExtensionBuilder +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.native import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragment @@ -13,10 +16,10 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.containingVariants import org.jetbrains.kotlin.project.model.KpmVariant import java.io.File -internal interface IdeaKpmProjectModelBuildingContext { +internal interface IdeaKpmProjectBuildingContext { val dependencyResolver: IdeaKpmDependencyResolver - companion object Empty : IdeaKpmProjectModelBuildingContext { + companion object Empty : IdeaKpmProjectBuildingContext { override val dependencyResolver: IdeaKpmDependencyResolver = IdeaKpmDependencyResolver.Empty } } @@ -124,12 +127,19 @@ interface IdeaKpmProjectModelBuilder { constraint: FragmentConstraint ) - fun buildIdeaKpmProjectModel(): IdeaKpmProject + @ExternalVariantApi + fun registerExtrasSerializationExtension( + extension: IdeaKpmExtrasSerializationExtension + ) + + fun buildSerializationContext(): IdeaKpmSerializationContext + + fun buildIdeaKpmProject(): IdeaKpmProject companion object } -internal fun IdeaKpmProjectModelBuildingContext.IdeaKpmProject(extension: KotlinPm20ProjectExtension): IdeaKpmProject { +internal fun IdeaKpmProjectBuildingContext.IdeaKpmProject(extension: KotlinPm20ProjectExtension): IdeaKpmProject { return IdeaKpmProjectImpl( gradlePluginVersion = extension.project.getKotlinPluginVersion(), coreLibrariesVersion = extension.coreLibrariesVersion, @@ -154,3 +164,10 @@ infix fun FragmentConstraint.and( operator fun FragmentConstraint.not() = FragmentConstraint { fragment -> this@not(fragment).not() } + +@ExternalVariantApi +fun IdeaKpmProjectModelBuilder.registerExtrasSerializationExtension( + builder: IdeaKpmExtrasSerializationExtensionBuilder.() -> Unit +) { + registerExtrasSerializationExtension(IdeaKpmExtrasSerializationExtension(builder)) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilderImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilderImpl.kt index d17994d49d8..455bba25a2e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilderImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmProjectModelBuilderImpl.kt @@ -11,13 +11,20 @@ import org.gradle.api.Project import org.gradle.tooling.provider.model.ToolingModelBuilder import org.jetbrains.kotlin.gradle.kpm.external.ExternalVariantApi import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKpmProjectModelBuilder.* +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializationExtension +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension +import org.jetbrains.kotlin.kpm.idea.proto.toByteArray import org.jetbrains.kotlin.tooling.core.UnsafeApi internal class IdeaKpmProjectModelBuilderImpl @UnsafeApi("Use factory methods instead") constructor( private val extension: KotlinPm20ProjectExtension, ) : ToolingModelBuilder, IdeaKpmProjectModelBuilder { + private inner class IdeaKpmBuildingContextImpl : IdeaKpmProjectBuildingContext { + override val dependencyResolver = createDependencyResolver() + } + private data class RegisteredDependencyResolver( val resolver: IdeaKpmDependencyResolver, val constraint: FragmentConstraint, @@ -39,6 +46,7 @@ internal class IdeaKpmProjectModelBuilderImpl @UnsafeApi("Use factory methods in private val registeredDependencyResolvers = mutableListOf() private val registeredDependencyTransformers = mutableListOf() private val registeredDependencyEffects = mutableListOf() + private val registeredExtrasSerializationExtensions = mutableListOf() override fun registerDependencyResolver( resolver: IdeaKpmDependencyResolver, @@ -70,20 +78,37 @@ internal class IdeaKpmProjectModelBuilderImpl @UnsafeApi("Use factory methods in ) } - override fun buildIdeaKpmProjectModel(): IdeaKpmProject { - return Context().IdeaKpmProject(extension) + override fun registerExtrasSerializationExtension( + extension: IdeaKpmExtrasSerializationExtension + ) { + registeredExtrasSerializationExtensions.add(extension) } + override fun buildSerializationContext(): IdeaKpmSerializationContext { + return IdeaKpmSerializationContext( + logger = extension.project.logger, + extrasSerializationExtensions = registeredExtrasSerializationExtensions.toList() + ) + } + + override fun buildIdeaKpmProject(): IdeaKpmProject = + IdeaKpmBuildingContextImpl().IdeaKpmProject(extension) + + override fun canBuild(modelName: String): Boolean = - modelName == IdeaKpmProject::class.java.name + modelName == IdeaKpmProject::class.java.name || modelName == IdeaKpmProjectContainer::class.java.name - override fun buildAll(modelName: String, project: Project): IdeaKpmProject { + override fun buildAll(modelName: String, project: Project): Any { check(project === extension.project) { "Expected project ${extension.project.path}, found ${project.path}" } - return buildIdeaKpmProjectModel() - } - private inner class Context : IdeaKpmProjectModelBuildingContext { - override val dependencyResolver = createDependencyResolver() + return when (modelName) { + IdeaKpmProject::class.java.name -> buildIdeaKpmProject() + IdeaKpmProjectContainer::class.java.name -> IdeaKpmProjectContainer( + buildIdeaKpmProject().toByteArray(buildSerializationContext()) + ) + + else -> throw IllegalArgumentException("Unexpected modelName: $modelName") + } } private fun createDependencyResolver(): IdeaKpmDependencyResolver { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmSerializationContextImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmSerializationContextImpl.kt new file mode 100644 index 00000000000..5306ac2f9ab --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/IdeaKpmSerializationContextImpl.kt @@ -0,0 +1,61 @@ +/* + * 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.idea + +import org.gradle.api.logging.Logger +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializationExtension +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializer +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationLogger +import org.jetbrains.kotlin.tooling.core.Extras + +internal fun IdeaKpmSerializationContext( + logger: Logger, extrasSerializationExtensions: List +): IdeaKpmSerializationContext { + return IdeaKpmSerializationContextImpl( + extrasSerializationExtension = IdeaKpmCompositeExtrasSerializationExtension(logger, extrasSerializationExtensions), + logger = IdeaKpmSerializationLoggerImpl(logger) + ) +} + +private class IdeaKpmSerializationContextImpl( + override val extrasSerializationExtension: IdeaKpmExtrasSerializationExtension, + override val logger: IdeaKpmSerializationLogger +) : IdeaKpmSerializationContext + +/* Simple composite implementation, reporting conflicting extensions */ +private class IdeaKpmCompositeExtrasSerializationExtension( + private val logger: Logger, + private val extensions: List +) : IdeaKpmExtrasSerializationExtension { + override fun serializer(key: Extras.Key): IdeaKpmExtrasSerializer? { + val serializers = extensions.mapNotNull { it.serializer(key) } + + if (serializers.size == 1) { + return serializers.single() + } + + if (serializers.size > 1) { + logger.error("Conflicting serializers found for Extras.Key $key: $serializers") + return null + } + + return null + } +} + +/* Simple Gradle logger based implementation */ +private class IdeaKpmSerializationLoggerImpl( + private val logger: Logger, +) : IdeaKpmSerializationLogger { + override fun report(severity: IdeaKpmSerializationLogger.Severity, message: String, cause: Throwable?) { + val text = "[KPM] Serialization: $message" + when (severity) { + IdeaKpmSerializationLogger.Severity.WARNING -> logger.warn(text, cause) + IdeaKpmSerializationLogger.Severity.ERROR -> logger.error(text, cause) + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmFragmentBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmFragmentBuilder.kt index cd2983245b3..025a190ce19 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmFragmentBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmFragmentBuilder.kt @@ -8,12 +8,12 @@ package org.jetbrains.kotlin.gradle.kpm.idea import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* import org.jetbrains.kotlin.tooling.core.emptyExtras -internal fun IdeaKpmProjectModelBuildingContext.IdeaKpmFragment(fragment: GradleKpmFragment): IdeaKpmFragment { +internal fun IdeaKpmProjectBuildingContext.IdeaKpmFragment(fragment: GradleKpmFragment): IdeaKpmFragment { return if (fragment is GradleKpmVariant) buildIdeaKpmVariant(fragment) else buildIdeaKpmFragment(fragment) } -private fun IdeaKpmProjectModelBuildingContext.buildIdeaKpmFragment(fragment: GradleKpmFragment): IdeaKpmFragment { +private fun IdeaKpmProjectBuildingContext.buildIdeaKpmFragment(fragment: GradleKpmFragment): IdeaKpmFragment { return IdeaKpmFragmentImpl( coordinates = IdeaKpmFragmentCoordinates(fragment), platforms = fragment.containingVariants.map { variant -> IdeaKpmPlatform(variant) }.toSet(), @@ -25,7 +25,7 @@ private fun IdeaKpmProjectModelBuildingContext.buildIdeaKpmFragment(fragment: Gr ) } -private fun IdeaKpmProjectModelBuildingContext.buildIdeaKpmVariant(variant: GradleKpmVariant): IdeaKpmVariant { +private fun IdeaKpmProjectBuildingContext.buildIdeaKpmVariant(variant: GradleKpmVariant): IdeaKpmVariant { return IdeaKpmVariantImpl( fragment = buildIdeaKpmFragment(variant), platform = IdeaKpmPlatform(variant), diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmModuleBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmModuleBuilder.kt index 46009ed3a6c..0fca688ebcc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmModuleBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmModuleBuilder.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.gradle.kpm.idea import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule -internal fun IdeaKpmProjectModelBuildingContext.IdeaKpmModule(module: GradleKpmModule): IdeaKpmModule { +internal fun IdeaKpmProjectBuildingContext.IdeaKpmModule(module: GradleKpmModule): IdeaKpmModule { return IdeaKpmModuleImpl( coordinates = IdeaKpmModuleCoordinates(module), fragments = module.fragments.toList().map { fragment -> IdeaKpmFragment(fragment) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmPlatformBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmPlatformBuilder.kt index 00622facc25..ec3e841bd51 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmPlatformBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/ideaKpmPlatformBuilder.kt @@ -14,7 +14,7 @@ import org.jetbrains.kotlin.konan.target.HostManager @Suppress("unused") /* Receiver acts as scope, or key to that function */ -internal fun IdeaKpmProjectModelBuildingContext.IdeaKpmPlatform(variant: GradleKpmVariant): IdeaKpmPlatform { +internal fun IdeaKpmProjectBuildingContext.IdeaKpmPlatform(variant: GradleKpmVariant): IdeaKpmPlatform { when (variant) { is GradleKpmJvmVariant -> return IdeaKpmJvmPlatformImpl(variant.compilationData.kotlinOptions.jvmTarget ?: JvmTarget.DEFAULT.name) is GradleKpmNativeVariantInternal -> return IdeaKpmNativePlatformImpl(variant.konanTarget.name) diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/kpm/AbstractKpmExtensionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/kpm/AbstractKpmExtensionTest.kt index bb75df57601..7939ca243c4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/kpm/AbstractKpmExtensionTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/kpm/AbstractKpmExtensionTest.kt @@ -28,5 +28,5 @@ fun Project.applyKpmPlugin(configure: KotlinPm20ProjectExtension.() -> Unit = {} } fun KotlinPm20ProjectExtension.buildIdeaKpmProjectModel(): IdeaKpmProject { - return ideaKpmProjectModelBuilder.buildIdeaKpmProjectModel() + return ideaKpmProjectModelBuilder.buildIdeaKpmProject() }