From 6c4ce812379b39365300650b9906d56cb29e1082 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Mon, 24 Jul 2023 14:26:38 +0200 Subject: [PATCH] [Gradle] Implement 'KotlinOutputDependency' to retain information about kotlin produced dependencies ^KT-60612 In Progress --- .../gradle/plugin/KotlinOutputDependency.kt | 109 ++++++++++++++++++ .../KotlinCompilationAssociator.kt | 23 +++- .../native/KotlinNativeTargetConfigurator.kt | 21 ++-- .../kotlin/gradle/utils/providerApiUtils.kt | 2 +- 4 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinOutputDependency.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinOutputDependency.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinOutputDependency.kt new file mode 100644 index 00000000000..ae7d3d73dfa --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinOutputDependency.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2023 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 + +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.FileCollectionDependency +import org.gradle.api.artifacts.component.ComponentIdentifier +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.artifacts.dependencies.SelfResolvingDependencyInternal +import org.gradle.api.tasks.TaskDependency +import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropIdentifier +import java.io.File + +/** + * Identifier used for [KotlinOutputDependency]. + * All use cases where Kotlin needs to add 'file collection based' dependencies shall be modelled + * using [KotlinOutputDependency] where this identifier is able to provide + * further information about 'what kind of dependency' this is used. + * + * This identifier basically contains + * 1) The information that this dependency was added by the Kotlin Gradle Plugin + * 2) The information that the files are locally built + * 3) The information about the semantics of the dependency + */ +internal sealed class KotlinOutputDependencyIdentifier : ComponentIdentifier { + + /** + * Indicates that the dependency is representing the compilation under + * the specified coordinates and that the reason for the dependency is a 'compilation association' + * + * e.g. 'test compilations' are associated with 'main compilations' + */ + internal class AssociateCompilation( + val projectPath: String, val target: String, val compilation: String, + ) : KotlinOutputDependencyIdentifier() { + constructor(compilation: KotlinCompilation<*>) : this( + projectPath = compilation.project.path, + target = compilation.target.targetName, + compilation = compilation.compilationName + ) + } + + /** + * Indicates that the dependency represents the output of the 'cinterop tool' for a single cinterop, + * identified by the specified [identifier] + */ + internal class CInterop( + val identifier: CInteropIdentifier, + ) : KotlinOutputDependencyIdentifier() + + override fun getDisplayName(): String = when (this) { + is AssociateCompilation -> "Associate Compilation: '$projectPath/$target/$compilation'" + is CInterop -> "CInterop ${identifier.uniqueName}" + } + + override fun toString(): String = displayName +} + +/** + * Special 'Dependency' implementation that shall be used to add 'file collection' based dependencies + * to Kotlin entities (such as compilations). This dependency implementation retains information about + * the semantics of the file collection underneath (by providing a [KotlinOutputDependencyIdentifier]) + */ +internal class KotlinOutputDependency internal constructor( + private val identifier: KotlinOutputDependencyIdentifier, + private val files: FileCollection, +) : FileCollectionDependency, SelfResolvingDependencyInternal { + private var reason: String? = null + + override fun getTargetComponentId(): ComponentIdentifier = identifier + override fun getGroup(): String? = null + override fun getName(): String = "unspecified" + override fun getVersion(): String? = null + + override fun contentEquals(dependency: Dependency): Boolean { + if (dependency !is KotlinOutputDependency) return false + return files == dependency.files + } + + override fun copy(): KotlinOutputDependency { + return KotlinOutputDependency(identifier, files) + } + + override fun getReason(): String? = reason + + override fun because(reason: String?) { + this.reason = reason + } + + override fun getBuildDependencies(): TaskDependency { + return files.buildDependencies + } + + override fun resolve(): MutableSet { + return files.files + } + + override fun resolve(transitive: Boolean): MutableSet { + return files.files + } + + override fun getFiles(): FileCollection { + return files + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationAssociator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationAssociator.kt index ba3efa93f2c..25309e67221 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationAssociator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationAssociator.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl import org.gradle.api.Project import org.gradle.api.artifacts.Dependency +import org.jetbrains.kotlin.gradle.plugin.KotlinOutputDependency +import org.jetbrains.kotlin.gradle.plugin.KotlinOutputDependencyIdentifier import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinCompilation @@ -16,6 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.isTest import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.utils.filesProvider + internal fun interface KotlinCompilationAssociator { fun associate(target: KotlinTarget, auxiliary: InternalKotlinCompilation<*>, main: InternalKotlinCompilation<*>) } @@ -34,16 +37,24 @@ internal object DefaultKotlinCompilationAssociator : KotlinCompilationAssociator compilation itself (moreover, direct dependencies are not equivalent to transitive ones because of resolution order - e.g. in case of FQNs clash, so it's even harmful) */ - project.dependencies.add(auxiliary.compileOnlyConfigurationName, project.files({ main.output.classesDirs })) - project.dependencies.add(auxiliary.runtimeOnlyConfigurationName, project.files({ main.output.allOutputs })) + project.dependencies.add( + auxiliary.compileOnlyConfigurationName, + KotlinOutputDependency(KotlinOutputDependencyIdentifier.AssociateCompilation(main), main.output.classesDirs) + ) + + project.dependencies.add( + auxiliary.runtimeOnlyConfigurationName, + KotlinOutputDependency(KotlinOutputDependencyIdentifier.AssociateCompilation(main), main.output.allOutputs) + ) + + // Adding classes that could be produced into non-default destination for JVM target // Check KotlinSourceSetProcessor for details project.dependencies.add( auxiliary.implementationConfigurationName, - project.objects.fileCollection().from( - { - main.defaultSourceSet.kotlin.classesDirectory.orNull?.asFile - } + KotlinOutputDependency( + KotlinOutputDependencyIdentifier.AssociateCompilation(main), + project.filesProvider { main.defaultSourceSet.kotlin.classesDirectory.orNull?.asFile } ) ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 4661fd008fa..6c96a6851fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPI import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationInfo.KPM import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.ReadyForExecution import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_IGNORE_INCORRECT_DEPENDENCIES +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider import org.jetbrains.kotlin.gradle.plugin.internal.artifactTypeAttribute import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XcodeVersionTask @@ -124,7 +125,7 @@ open class KotlinNativeTargetConfigurator : AbstractKotl // FIXME support creating interop tasks for PM20 private fun Project.createCInteropTasks( compilation: KotlinNativeCompilation, - cinterops: NamedDomainObjectCollection + cinterops: NamedDomainObjectCollection, ) { val compilationInfo = KotlinCompilationInfo(compilation) cinterops.all { interop -> @@ -161,7 +162,10 @@ open class KotlinNativeTargetConfigurator : AbstractKotl val interopOutput = project.files(interopTask.map { it.outputFileProvider }) with(compilation) { // Register the interop library as a dependency of the compilation to make IDE happy. - project.dependencies.add(compileDependencyConfigurationName, interopOutput) + project.dependencies.add( + compileDependencyConfigurationName, + KotlinOutputDependency(KotlinOutputDependencyIdentifier.CInterop(interop.identifier), interopOutput) + ) if (isMain()) { // Add interop library to special CInteropApiElements configuration createCInteropApiElementsKlibArtifact(compilation.target, interop, interopTask) @@ -387,7 +391,7 @@ open class KotlinNativeTargetConfigurator : AbstractKotl internal fun createKlibCompilationTask( compilationInfo: KotlinCompilationInfo, - konanTarget: KonanTarget + konanTarget: KonanTarget, ): TaskProvider { val project = compilationInfo.project val ext = project.topLevelExtension @@ -455,7 +459,7 @@ open class KotlinNativeTargetConfigurator : AbstractKotl } private fun Project.klibOutputDirectory( - compilation: KotlinCompilationInfo + compilation: KotlinCompilationInfo, ): File { val targetSubDirectory = compilation.targetDisambiguationClassifier?.let { "$it/" }.orEmpty() return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.compilationName}") @@ -478,7 +482,7 @@ open class KotlinNativeTargetConfigurator : AbstractKotl internal fun createRegularKlibArtifact( compilation: KotlinCompilationInfo, konanTarget: KonanTarget, - compileTask: TaskProvider + compileTask: TaskProvider, ) = createKlibArtifact(compilation, konanTarget, compileTask.map { it.outputFile.get() }, null, compileTask) private fun createKlibArtifact( @@ -517,7 +521,8 @@ open class KotlinNativeTargetConfigurator : AbstractKotl abstract class KotlinNativeTargetWithTestsConfigurator< TargetType : KotlinNativeTargetWithTests, TestRunType : KotlinNativeBinaryTestRun, - TaskType : KotlinNativeTest>( + TaskType : KotlinNativeTest, + >( ) : KotlinNativeTargetConfigurator(), KotlinTargetWithTestsConfigurator { @@ -571,7 +576,7 @@ class KotlinNativeTargetWithHostTestsConfigurator() : override fun createTestRun( name: String, - target: KotlinNativeTargetWithHostTests + target: KotlinNativeTargetWithHostTests, ): KotlinNativeHostTestRun = DefaultHostTestRun(name, target).apply { configureTestRun(target, this) } } @@ -605,7 +610,7 @@ class KotlinNativeTargetWithSimulatorTestsConfigurator : override fun createTestRun( name: String, - target: KotlinNativeTargetWithSimulatorTests + target: KotlinNativeTargetWithSimulatorTests, ): KotlinNativeSimulatorTestRun = DefaultSimulatorTestRun(name, target).apply { configureTestRun(target, this) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt index 3caa023246e..787ace05046 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/providerApiUtils.kt @@ -110,7 +110,7 @@ internal fun Project.newFileProperty(initialize: (() -> File)? = null): RegularF internal fun Project.filesProvider( vararg buildDependencies: Any, - provider: () -> Any + provider: () -> Any? ): ConfigurableFileCollection { return project.files(provider).builtBy(*buildDependencies) }