[Gradle] Implement 'KotlinOutputDependency' to retain information about kotlin produced dependencies

^KT-60612 In Progress
This commit is contained in:
Sebastian Sellmair
2023-07-24 14:26:38 +02:00
committed by Space Team
parent 973c5770b8
commit 6c4ce81237
4 changed files with 140 additions and 15 deletions
@@ -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<File> {
return files.files
}
override fun resolve(transitive: Boolean): MutableSet<File> {
return files.files
}
override fun getFiles(): FileCollection {
return files
}
}
@@ -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 }
)
)
@@ -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<T : KotlinNativeTarget> : AbstractKotl
// FIXME support creating interop tasks for PM20
private fun Project.createCInteropTasks(
compilation: KotlinNativeCompilation,
cinterops: NamedDomainObjectCollection<DefaultCInteropSettings>
cinterops: NamedDomainObjectCollection<DefaultCInteropSettings>,
) {
val compilationInfo = KotlinCompilationInfo(compilation)
cinterops.all { interop ->
@@ -161,7 +162,10 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : 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<T : KotlinNativeTarget> : AbstractKotl
internal fun createKlibCompilationTask(
compilationInfo: KotlinCompilationInfo,
konanTarget: KonanTarget
konanTarget: KonanTarget,
): TaskProvider<KotlinNativeCompile> {
val project = compilationInfo.project
val ext = project.topLevelExtension
@@ -455,7 +459,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : 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<T : KotlinNativeTarget> : AbstractKotl
internal fun createRegularKlibArtifact(
compilation: KotlinCompilationInfo,
konanTarget: KonanTarget,
compileTask: TaskProvider<out KotlinNativeCompile>
compileTask: TaskProvider<out KotlinNativeCompile>,
) = createKlibArtifact(compilation, konanTarget, compileTask.map { it.outputFile.get() }, null, compileTask)
private fun createKlibArtifact(
@@ -517,7 +521,8 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
abstract class KotlinNativeTargetWithTestsConfigurator<
TargetType : KotlinNativeTargetWithTests<TestRunType>,
TestRunType : KotlinNativeBinaryTestRun,
TaskType : KotlinNativeTest>(
TaskType : KotlinNativeTest,
>(
) : KotlinNativeTargetConfigurator<TargetType>(),
KotlinTargetWithTestsConfigurator<TestRunType, TargetType> {
@@ -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) }
}
@@ -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)
}