diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinDependencyCoordinates.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinDependencyCoordinates.kt index 85c373a9972..940f52761d9 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinDependencyCoordinates.kt +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/main/kotlin/org/jetbrains/kotlin/gradle/idea/tcs/IdeaKotlinDependencyCoordinates.kt @@ -49,6 +49,10 @@ data class IdeaKotlinProjectArtifactCoordinates( val artifactFile: File ) : IdeaKotlinDependencyCoordinates { + override fun toString(): String { + return "$project${File.pathSeparator}$artifactFile" + } + internal companion object { const val serialVersionUID = 0L } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeDependencyResolver.kt index 0228d61123b..eb0fc29b93e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeDependencyResolver.kt @@ -5,8 +5,12 @@ package org.jetbrains.kotlin.gradle.plugin.ide +import org.gradle.api.artifacts.result.ResolvedArtifactResult import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.tooling.core.extrasKeyOf +import org.jetbrains.kotlin.tooling.core.getValue +import org.jetbrains.kotlin.tooling.core.setValue fun interface IdeDependencyResolver { fun resolve(sourceSet: KotlinSourceSet): Set @@ -14,6 +18,11 @@ fun interface IdeDependencyResolver { object Empty : IdeDependencyResolver { override fun resolve(sourceSet: KotlinSourceSet): Set = emptySet() } + + companion object { + var IdeaKotlinDependency.resolvedBy: IdeDependencyResolver? by extrasKeyOf("resolvedBy") + var IdeaKotlinDependency.gradleArtifact: ResolvedArtifactResult? by extrasKeyOf("gradleArtifact") + } } fun IdeDependencyResolver( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt index 7d4b386cb6b..309cb2ca50b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeMultiplatformImportImpl.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinSerializationContext import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependency import org.jetbrains.kotlin.gradle.kpm.idea.IdeaSerializationContext import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver.Companion.resolvedBy import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.* import org.jetbrains.kotlin.gradle.plugin.ide.IdeMultiplatformImport.Companion.logger import org.jetbrains.kotlin.tooling.core.Extras @@ -161,6 +162,7 @@ internal class IdeMultiplatformImportImpl( return runCatching { resolver.resolve(sourceSet) } .onFailure { error -> reportError(sourceSet, error) } .onSuccess { dependencies -> reportSuccess(sourceSet, dependencies) } + .onSuccess { dependencies -> attachResolvedByExtra(dependencies) } .getOrNull().orEmpty() } @@ -172,5 +174,12 @@ internal class IdeMultiplatformImportImpl( if (!logger.isDebugEnabled) return logger.debug("${resolver::class.java.name} resolved on ${IdeaKotlinSourceCoordinates(sourceSet)}: $dependencies") } + + private fun attachResolvedByExtra(dependencies: Iterable) { + dependencies.forEach { dependency -> + if (dependency.resolvedBy == null) dependency.resolvedBy = resolver + } + } } + } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeResolveDependenciesTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeResolveDependenciesTask.kt new file mode 100644 index 00000000000..8f1fdc6cc55 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/IdeResolveDependenciesTask.kt @@ -0,0 +1,96 @@ +/* + * 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 + +import com.google.gson.* +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinDependencyCoordinates +import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask +import org.jetbrains.kotlin.gradle.utils.notCompatibleWithConfigurationCacheCompat +import org.jetbrains.kotlin.tooling.core.Extras +import java.io.File +import java.lang.reflect.Type + +internal fun Project.locateOrRegisterIdeResolveDependenciesTask(): TaskProvider { + return locateOrRegisterTask("resolveIdeDependencies") { task -> + task.description = "Debugging/Diagnosing task that will resolve dependencies for the IDE" + task.group = "ide" + task.notCompatibleWithConfigurationCacheCompat("Just a debugging util") + } +} + +/** + * Task intended to be use for debugging/diagnosing purposes. + * This will invoke the [IdeMultiplatformImport] to resolve all dependencies (like the IDE would). + * Outputs are written as json and protobufs + */ +internal open class IdeResolveDependenciesTask : DefaultTask() { + + @TaskAction + fun resolveDependencies() { + val outputDirectory = project.buildDir.resolve("ide/dependencies") + outputDirectory.deleteRecursively() + val gson = GsonBuilder().setLenient().setPrettyPrinting() + .registerTypeHierarchyAdapter(IdeDependencyResolver::class.java, IdeDependencyResolverAdapter) + .registerTypeHierarchyAdapter(Extras::class.java, ExtrasAdapter) + .registerTypeHierarchyAdapter(IdeaKotlinDependencyCoordinates::class.java, IdeaKotlinDependencyCoordinatesAdapter) + .registerTypeAdapter(File::class.java, FileAdapter(project)) + .create() + + val extension = project.kotlinExtension + extension.sourceSets.forEach { sourceSet -> + val dependencies = project.kotlinIdeMultiplatformImport.resolveDependencies(sourceSet) + val jsonOutput = outputDirectory.resolve("json/${sourceSet.name}.json") + jsonOutput.parentFile.mkdirs() + jsonOutput.writeText(gson.toJson(dependencies)) + + project.kotlinIdeMultiplatformImport.serialize(dependencies).forEachIndexed { index, proto -> + val protoOutput = outputDirectory.resolve("proto/${sourceSet.name}/$index.bin") + protoOutput.parentFile.mkdirs() + protoOutput.writeBytes(proto) + } + } + } + + private object IdeDependencyResolverAdapter : JsonSerializer { + override fun serialize(src: IdeDependencyResolver, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + return JsonPrimitive(src.javaClass.name) + } + } + + private object IdeaKotlinDependencyCoordinatesAdapter : JsonSerializer { + override fun serialize(src: IdeaKotlinDependencyCoordinates, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement { + return JsonPrimitive(src.toString()) + } + } + + private object ExtrasAdapter : JsonSerializer { + override fun serialize(src: Extras, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + return JsonObject().apply { + src.entries.forEach { entry -> + val valueElement = runCatching { context.serialize(entry.value) }.getOrElse { JsonPrimitive(entry.value.toString()) } + add(entry.key.stableString, valueElement) + } + } + } + } + + private class FileAdapter(private val project: Project) : JsonSerializer { + override fun serialize(src: File, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement { + return if (src.startsWith(project.projectDir)) { + JsonPrimitive(src.relativeTo(project.projectDir).path) + } else if (src.startsWith(project.rootDir)) { + JsonPrimitive(src.relativeTo(project.rootDir).path) + } else { + JsonPrimitive(src.path) + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt index 866337efb5c..2ec97dee99d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdePlatformDependencyResolver.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.idea.tcs.* import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver +import org.jetbrains.kotlin.gradle.plugin.ide.IdeDependencyResolver.Companion.gradleArtifact import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinBinaryCoordinates import org.jetbrains.kotlin.gradle.plugin.ide.IdeaKotlinProjectCoordinates import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation @@ -119,7 +120,7 @@ internal class IdePlatformDependencyResolver( logger.warn("Unhandled componentId: ${componentId.javaClass}") null } - } + }?.also { dependency -> dependency.gradleArtifact = artifact } }.toSet() return resolvedDependencies + unresolvedDependencies diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeTransformedMetadataDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeTransformedMetadataDependencyResolver.kt index 8dba1962a5d..972236d367a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeTransformedMetadataDependencyResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/ide/dependencyResolvers/IdeTransformedMetadataDependencyResolver.kt @@ -10,7 +10,6 @@ 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 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 @@ -26,7 +25,6 @@ internal object IdeTransformedMetadataDependencyResolver : IdeDependencyResolver private fun resolve(sourceSet: KotlinSourceSet, resolution: ChooseVisibleSourceSets): Iterable { val metadataProvider = resolution.metadataProvider as? ArtifactMetadataProvider ?: return emptySet() - return metadataProvider.read { artifactContent -> resolution.allVisibleSourceSetNames.mapNotNull { visibleSourceSet -> val sourceSetContent = artifactContent.findSourceSet(visibleSourceSet) ?: return@mapNotNull null diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index b14514e1680..c09bc91bfcd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -18,6 +18,8 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.internal.customizeKotlinDependencies import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.plugin.ide.IdeResolveDependenciesTask +import org.jetbrains.kotlin.gradle.plugin.ide.locateOrRegisterIdeResolveDependenciesTask import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin.Companion.sourceSetFreeCompilerArgsPropertyName import org.jetbrains.kotlin.gradle.plugin.mpp.internal.handleHierarchicalStructureFlagsMigration import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* @@ -34,6 +36,7 @@ import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinWasmTargetPreset import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.registerKotlinArtifactsExtension import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool +import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.* @@ -76,6 +79,8 @@ class KotlinMultiplatformPlugin : Plugin { project.pluginManager.apply(ScriptingGradleSubplugin::class.java) exportProjectStructureMetadataForOtherBuilds(kotlinMultiplatformExtension) + + project.locateOrRegisterIdeResolveDependenciesTask() } private fun exportProjectStructureMetadataForOtherBuilds(