[Gradle] Implement IdeTransformedMetadataDependencyResolver

KT-54825
This commit is contained in:
Sebastian Sellmair
2022-11-10 14:09:23 +01:00
committed by Space Team
parent 687ccda3e8
commit 9771e57951
8 changed files with 196 additions and 9 deletions
@@ -0,0 +1,52 @@
syntax = "proto3";
package org.jetbrains.kotlin.gradle.idea.proto.generated.tcs;
import "proto_extras.proto";
option java_multiple_files = true;
message IdeaKotlinDependencyProto {
oneof dependency {
IdeaKotlinSourceDependencyProto source_dependency = 1;
IdeaKotlinResolvedBinaryDependencyProto resolved_binary_dependency = 2;
IdeaKotlinUnresolvedBinaryDependencyProto unresolved_binary_dependency = 3;
}
}
message IdeaKotlinSourceDependencyProto {
enum Type {
REGULAR = 0;
FRIEND = 1;
DEPENDS_ON = 2;
}
optional IdeaExtrasProto extras = 1;
optional Type type = 2;
optional IdeaKotlinSourceCoordinatesProto coordinates = 3;
}
message IdeaKotlinUnresolvedBinaryDependencyProto {
optional IdeaExtrasProto extras = 1;
optional IdeaKotlinBinaryCoordinatesProto coordinates = 2;
optional string cause = 3;
}
message IdeaKotlinResolvedBinaryDependencyProto {
optional IdeaExtrasProto extras = 1;
optional IdeaKotlinBinaryCoordinatesProto coordinates = 2;
optional string binary_type = 3;
optional string binary_file = 4;
}
message IdeaKotlinBinaryCoordinatesProto {
optional string group = 1;
optional string module = 2;
optional string version = 3;
optional string source_set_name = 4;
}
message IdeaKotlinSourceCoordinatesProto {
optional string build_id = 1;
optional string project_path = 2;
optional string project_name = 3;
optional string source_set_name = 4;
}
@@ -14,7 +14,7 @@ import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.pm20Extension
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.utils.doNotTrackStateCompat
import org.jetbrains.kotlin.kpm.idea.proto.writeTo
import org.jetbrains.kotlin.gradle.idea.proto.kpm.writeTo
import java.io.ByteArrayOutputStream
import java.io.ObjectOutputStream
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragment
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmFragmentGranularMetadataResolverFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule.Companion.moduleName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.toKpmModuleDependency
import org.jetbrains.kotlin.gradle.plugin.mpp.read
internal class IdeaKpmMetadataBinaryDependencyResolver(
private val fragmentGranularMetadataResolverFactory: GradleKpmFragmentGranularMetadataResolverFactory
@@ -138,6 +138,7 @@ interface IdeMultiplatformImport {
companion object {
val unconstrained = SourceSetConstraint { true }
val isNative = SourceSetConstraint { sourceSet -> isNativeSourceSet(sourceSet) }
val isMetadata = SourceSetConstraint { sourceSet -> sourceSet.internal.compilations.any { it is KotlinMetadataCompilation } }
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaExtrasSerializer
import org.jetbrains.kotlin.gradle.kpm.idea.kotlinDebugKey
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeDependsOnDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeTransformedMetadataDependencyResolver
fun IdeMultiplatformImport(extension: KotlinMultiplatformExtension): IdeMultiplatformImport {
return IdeMultiplatformImportImpl(extension).apply {
@@ -20,6 +21,13 @@ fun IdeMultiplatformImport(extension: KotlinMultiplatformExtension): IdeMultipla
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
)
registerDependencyResolver(
resolver = IdeTransformedMetadataDependencyResolver,
constraint = IdeMultiplatformImport.SourceSetConstraint.isMetadata,
phase = IdeMultiplatformImport.DependencyResolutionPhase.BinaryDependencyResolution,
level = IdeMultiplatformImport.DependencyResolutionLevel.Default
)
registerExtrasSerializationExtension {
register(kotlinDebugKey, IdeaExtrasSerializer.javaIoSerializable())
}
@@ -0,0 +1,64 @@
/*
* 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.ide.dependencyResolvers
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinBinaryCoordinates
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ArtifactMetadataProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinTransformedMetadataLibraryDirectoryForIde
import org.jetbrains.kotlin.gradle.plugin.mpp.read
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope.*
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
internal object IdeTransformedMetadataDependencyResolver : IdeDependencyResolver {
override fun resolve(sourceSet: KotlinSourceSet): Set<IdeaKotlinDependency> {
if (sourceSet !is DefaultKotlinSourceSet) return emptySet()
return sourceSet.dependencyTransformations[API_SCOPE]?.metadataDependencyResolutions?.toList().orEmpty()
.plus(sourceSet.dependencyTransformations[IMPLEMENTATION_SCOPE]?.metadataDependencyResolutions?.toList().orEmpty())
.plus(sourceSet.dependencyTransformations[COMPILE_ONLY_SCOPE]?.metadataDependencyResolutions?.toList().orEmpty())
.filterIsInstance<ChooseVisibleSourceSets>()
.flatMap { resolution -> resolve(sourceSet, resolution) }
.toSet()
}
private fun resolve(sourceSet: KotlinSourceSet, resolution: ChooseVisibleSourceSets): Iterable<IdeaKotlinDependency> {
val metadataProvider = resolution.metadataProvider as? ArtifactMetadataProvider ?: return emptySet()
return metadataProvider.read { artifactContent ->
resolution.allVisibleSourceSetNames.mapNotNull { visibleSourceSet ->
val sourceSetContent = artifactContent.findSourceSet(visibleSourceSet) ?: return@mapNotNull null
val sourceSetMetadataBinary = sourceSetContent.metadataBinary ?: return@mapNotNull null
val metadataLibraryOutputFile = sourceSet.internal.project.kotlinTransformedMetadataLibraryDirectoryForIde
.resolve(sourceSetMetadataBinary.relativeFile)
metadataLibraryOutputFile.parentFile.mkdirs()
if (!metadataLibraryOutputFile.exists()) {
sourceSetMetadataBinary.copyTo(metadataLibraryOutputFile)
if (!metadataLibraryOutputFile.exists()) return@mapNotNull null
}
IdeaKotlinResolvedBinaryDependency(
binaryType = IdeaKotlinDependency.CLASSPATH_BINARY_TYPE,
binaryFile = metadataLibraryOutputFile,
extras = mutableExtrasOf(),
coordinates = IdeaKotlinBinaryCoordinates(
group = metadataProvider.moduleDependencyIdentifier.groupId ?: "",
module = metadataProvider.moduleDependencyIdentifier.moduleId,
version = metadataProvider.moduleDependencyVersion,
sourceSetName = visibleSourceSet
)
)
}
}
}
}
@@ -21,16 +21,15 @@ internal interface CompositeMetadataArtifact {
* Alternatively use the [read] function instead.
*/
fun open(): CompositeMetadataArtifactContent
/**
* Safe shortcut function for opening and reading the content of this artifact.
* The [CompositeMetadataArtifactContent] will be closed after the [action] executed.
*/
fun <T> read(action: (artifactContent: CompositeMetadataArtifactContent) -> T): T {
return open().use(action)
}
}
/**
* Safe shortcut function for opening and reading the content of this artifact.
* The [CompositeMetadataArtifactContent] will be closed after the [action] executed.
*/
internal inline fun <T> CompositeMetadataArtifact.read(action: (artifactContent: CompositeMetadataArtifactContent) -> T): T {
return open().use(action)
}
/**
* This [CompositeMetadataArtifactContent] abstraction provides access into a metadata artifact published by a Multiplatform Library
@@ -0,0 +1,62 @@
/*
* 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.ide
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.assertMatches
import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.binaryCoordinates
import org.jetbrains.kotlin.gradle.kpm.idea.mavenCentralCacheRedirector
import org.jetbrains.kotlin.gradle.plugin.ide.dependencyResolvers.IdeTransformedMetadataDependencyResolver
import org.junit.Test
class IdeTransformedMetadataDependencyResolverTest {
@Test
fun `test - MVIKotlin`() {
val project = buildProject {
enableDefaultStdlibDependency(false)
enableDependencyVerification(false)
applyMultiplatformPlugin()
repositories.mavenCentralCacheRedirector()
}
val kotlin = project.multiplatformExtension
kotlin.targetHierarchy.default()
kotlin.jvm()
kotlin.linuxX64()
kotlin.linuxArm64()
val commonMain = kotlin.sourceSets.getByName("commonMain")
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
commonMain.dependencies {
implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.2")
implementation("com.arkivanov.essenty:lifecycle:0.4.2")
implementation("com.arkivanov.essenty:instance-keeper:0.4.2")
}
project.evaluate()
IdeTransformedMetadataDependencyResolver.resolve(commonMain)
.assertMatches(
binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin:3.0.2:commonMain"),
binaryCoordinates("com.arkivanov.essenty:lifecycle:0.4.2:commonMain"),
binaryCoordinates("com.arkivanov.essenty:instance-keeper:0.4.2:commonMain")
)
IdeTransformedMetadataDependencyResolver.resolve(linuxMain)
.assertMatches(
binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin:3.0.2:commonMain"),
binaryCoordinates("com.arkivanov.mvikotlin:mvikotlin:3.0.2:jsNativeMain"),
binaryCoordinates("com.arkivanov.essenty:lifecycle:0.4.2:commonMain"),
binaryCoordinates("com.arkivanov.essenty:instance-keeper:0.4.2:commonMain")
)
}
}