[Gradle] Remove KotlinOutputDependency
The IdeBinaryDependencyResolver will filter respective files by using a simpler check: testing if the files do reside within the build dir of the current project. ^KT-60937 Verification Pending
This commit is contained in:
committed by
Space Team
parent
aa0f0c8cb4
commit
d978640c42
-109
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
+4
-9
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.gradle.idea.tcs.*
|
||||
import org.jetbrains.kotlin.gradle.idea.tcs.extras.artifactsClasspath
|
||||
import org.jetbrains.kotlin.gradle.idea.tcs.extras.isOpaqueFileDependency
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinOutputDependencyIdentifier
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.ide.*
|
||||
@@ -172,11 +171,13 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
is OpaqueComponentArtifactIdentifier -> {
|
||||
/* Such dependencies *would* require implementing a resolver */
|
||||
/* Files within the build directory still require a custom resolver */
|
||||
if (artifact.file.absoluteFile.startsWith(sourceSet.project.buildDir.absoluteFile)) return@mapNotNull null
|
||||
|
||||
IdeaKotlinResolvedBinaryDependency(
|
||||
binaryType = binaryType, coordinates = IdeaKotlinBinaryCoordinates(
|
||||
group = "<file>",
|
||||
module = componentId.file.relativeOrAbsolute(sourceSet.project.rootDir),
|
||||
module = artifact.file.relativeOrAbsolute(sourceSet.project.rootDir),
|
||||
version = null,
|
||||
sourceSetName = null
|
||||
),
|
||||
@@ -186,12 +187,6 @@ class IdeBinaryDependencyResolver @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Such dependencies (e.g. associate compilations, cinterops, ...)
|
||||
Will have dedicated resolvers implemented
|
||||
*/
|
||||
is KotlinOutputDependencyIdentifier -> null
|
||||
|
||||
else -> {
|
||||
logger.warn("Unhandled componentId: ${componentId.javaClass}")
|
||||
null
|
||||
|
||||
+5
-2
@@ -19,6 +19,9 @@ internal constructor(compilation: KotlinCompilationImpl) : AbstractKotlinCompila
|
||||
final override val runtimeDependencyConfigurationName: String
|
||||
get() = compilation.runtimeDependencyConfigurationName ?: error("$compilation: Missing 'runtimeDependencyConfigurationName'")
|
||||
|
||||
final override var runtimeDependencyFiles: FileCollection =
|
||||
compilation.runtimeDependencyFiles ?: error("$compilation: Missing 'runtimeDependencyFiles'")
|
||||
final override var runtimeDependencyFiles: FileCollection
|
||||
get() = compilation.runtimeDependencyFiles ?: error("$compilation: Missing 'runtimeDependencyFiles'")
|
||||
set(value) {
|
||||
compilation.runtimeDependencyFiles = value
|
||||
}
|
||||
}
|
||||
+3
@@ -21,6 +21,9 @@ internal interface InternalKotlinCompilation<out T : KotlinCommonOptions> : Kotl
|
||||
override val kotlinSourceSets: ObservableSet<KotlinSourceSet>
|
||||
override val allKotlinSourceSets: ObservableSet<KotlinSourceSet>
|
||||
|
||||
val associatedCompilations: ObservableSet<KotlinCompilation<*>>
|
||||
val allAssociatedCompilations: ObservableSet<KotlinCompilation<*>>
|
||||
|
||||
val configurations: KotlinCompilationConfigurationsContainer
|
||||
val friendPaths: Iterable<FileCollection>
|
||||
val processResourcesTaskName: String?
|
||||
|
||||
+27
-28
@@ -7,16 +7,17 @@ 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.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
|
||||
import org.jetbrains.kotlin.gradle.plugin.launch
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.Future
|
||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||
import org.jetbrains.kotlin.gradle.utils.future
|
||||
import org.jetbrains.kotlin.gradle.utils.listProperty
|
||||
|
||||
|
||||
internal fun interface KotlinCompilationAssociator {
|
||||
@@ -28,36 +29,35 @@ internal object DefaultKotlinCompilationAssociator : KotlinCompilationAssociator
|
||||
val project = target.project
|
||||
|
||||
/*
|
||||
we add dependencies to compileDependencyConfiguration ('compileClasspath' usually) and runtimeDependency
|
||||
('runtimeClasspath') instead of modifying respective api/implementation/compileOnly/runtimeOnly configs
|
||||
This associator has two jobs
|
||||
1) It will add the output of the 'main' compilation as compile & runtime dependency to the 'auxiliary' compilation
|
||||
2) It will add all 'declared dependencies' present on 'main' to 'auxiliary'
|
||||
|
||||
This is needed because api/implementation/compileOnly/runtimeOnly are used in IDE Import and will leak
|
||||
to dependencies of IDE modules. But they are not needed here, because IDE resolution works inherently
|
||||
transitively and symbols from associated compilation will be resolved from source sets of associated
|
||||
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)
|
||||
For 1)
|
||||
This is necessary so that all symbols that are declared/produced in 'main' are available in 'auxiliary'.
|
||||
We use the 'compileOnlyConfiguration' and 'runtimeOnlyConfigurationName' to add the respective classes.
|
||||
Note (a): This 'associate' function will be called for 'all' associated compilations (full transitive closure)
|
||||
Note (b): It is important that the compiled output of 'main' is prioritised in the compile path order:
|
||||
We therefore ensure that the files are added to the front of the compile path.
|
||||
|
||||
This is necessary as other binaries might leak into the compile path which contain the same symbols but
|
||||
are not marked as 'friend'. We ensure that associate dependencies are resolved first
|
||||
|
||||
For 2)
|
||||
This is an agreed upon convention: 'test' is able to see all dependencies declared for 'main'
|
||||
As described in 1b: It needs to be taken care of, that the dependencies are ordered after the output of 'main'
|
||||
*/
|
||||
project.dependencies.add(
|
||||
auxiliary.compileOnlyConfigurationName,
|
||||
KotlinOutputDependency(KotlinOutputDependencyIdentifier.AssociateCompilation(main), main.output.classesDirs)
|
||||
)
|
||||
|
||||
project.dependencies.add(
|
||||
auxiliary.runtimeOnlyConfigurationName,
|
||||
KotlinOutputDependency(KotlinOutputDependencyIdentifier.AssociateCompilation(main), main.output.allOutputs)
|
||||
)
|
||||
|
||||
project.dependencies.add(auxiliary.compileOnlyConfigurationName, main.output.classesDirs)
|
||||
project.dependencies.add(auxiliary.runtimeOnlyConfigurationName, 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,
|
||||
KotlinOutputDependency(
|
||||
KotlinOutputDependencyIdentifier.AssociateCompilation(main),
|
||||
project.filesProvider { main.defaultSourceSet.kotlin.classesDirectory.orNull?.asFile }
|
||||
)
|
||||
project.filesProvider { main.defaultSourceSet.kotlin.classesDirectory.orNull?.asFile }
|
||||
)
|
||||
|
||||
// Adding declared dependencies
|
||||
auxiliary.compileDependencyConfigurationName.addAllDependenciesFromOtherConfigurations(
|
||||
project,
|
||||
main.apiConfigurationName,
|
||||
@@ -76,7 +76,6 @@ internal object DefaultKotlinCompilationAssociator : KotlinCompilationAssociator
|
||||
|
||||
internal object KotlinNativeCompilationAssociator : KotlinCompilationAssociator {
|
||||
override fun associate(target: KotlinTarget, auxiliary: InternalKotlinCompilation<*>, main: InternalKotlinCompilation<*>) {
|
||||
|
||||
auxiliary.compileDependencyFiles +=
|
||||
main.output.classesDirs + target.project.filesProvider { main.compileDependencyFiles }
|
||||
|
||||
|
||||
+21
-7
@@ -22,12 +22,13 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.utils.MutableObservableSetImpl
|
||||
import org.jetbrains.kotlin.gradle.utils.ObservableSet
|
||||
import org.jetbrains.kotlin.tooling.core.MutableExtras
|
||||
import org.jetbrains.kotlin.tooling.core.mutableExtrasOf
|
||||
|
||||
internal class KotlinCompilationImpl constructor(
|
||||
private val params: Params
|
||||
private val params: Params,
|
||||
) : InternalKotlinCompilation<KotlinCommonOptions> {
|
||||
|
||||
//region Params
|
||||
@@ -44,7 +45,7 @@ internal class KotlinCompilationImpl constructor(
|
||||
val kotlinOptions: KotlinCommonOptions,
|
||||
val compilationAssociator: KotlinCompilationAssociator,
|
||||
val compilationFriendPathsResolver: KotlinCompilationFriendPathsResolver,
|
||||
val compilationSourceSetInclusion: KotlinCompilationSourceSetInclusion
|
||||
val compilationSourceSetInclusion: KotlinCompilationSourceSetInclusion,
|
||||
)
|
||||
|
||||
//endregion
|
||||
@@ -182,24 +183,37 @@ internal class KotlinCompilationImpl constructor(
|
||||
|
||||
// endregion
|
||||
|
||||
private val associateWithImpl = mutableSetOf<KotlinCompilation<*>>()
|
||||
|
||||
private val associatedCompilationsImpl = MutableObservableSetImpl<KotlinCompilation<*>>()
|
||||
|
||||
private val allAssociatedCompilationsImpl = MutableObservableSetImpl<KotlinCompilation<*>>()
|
||||
|
||||
override val associatedCompilations: ObservableSet<KotlinCompilation<*>>
|
||||
get() = associatedCompilationsImpl
|
||||
|
||||
override val allAssociatedCompilations: ObservableSet<KotlinCompilation<*>>
|
||||
get() = allAssociatedCompilationsImpl
|
||||
|
||||
override val associateWith: List<KotlinCompilation<*>>
|
||||
get() = associateWithImpl.toList()
|
||||
get() = associatedCompilationsImpl.toList()
|
||||
|
||||
override fun associateWith(other: KotlinCompilation<*>) {
|
||||
require(other.target == target) { "Only associations between compilations of a single target are supported" }
|
||||
if (!associateWithImpl.add(other)) return
|
||||
params.compilationAssociator.associate(target, this, other.internal)
|
||||
if (!associatedCompilationsImpl.add(other)) return
|
||||
if (!allAssociatedCompilationsImpl.add(other)) return
|
||||
other.internal.allAssociatedCompilations.forAll { compilation -> allAssociatedCompilationsImpl.add(compilation) }
|
||||
}
|
||||
|
||||
|
||||
//region final init
|
||||
|
||||
init {
|
||||
sourceSets.allKotlinSourceSets.forAll { sourceSet ->
|
||||
params.compilationSourceSetInclusion.include(this, sourceSet)
|
||||
}
|
||||
|
||||
allAssociatedCompilations.forAll { compilation ->
|
||||
params.compilationAssociator.associate(target, this, compilation.internal)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
+2
-6
@@ -158,11 +158,7 @@ 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,
|
||||
KotlinOutputDependency(KotlinOutputDependencyIdentifier.CInterop(interop.identifier), interopOutput)
|
||||
)
|
||||
compileDependencyFiles += interopOutput
|
||||
if (isMain()) {
|
||||
// Add interop library to special CInteropApiElements configuration
|
||||
createCInteropApiElementsKlibArtifact(compilation.target, interop, interopTask)
|
||||
@@ -180,7 +176,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget> : AbstractKotl
|
||||
// IDE doesn't see this library in module dependencies. So we have to manually add
|
||||
// main interop libraries in dependencies of the default test compilation.
|
||||
target.compilations.findByName(TEST_COMPILATION_NAME)?.let { testCompilation ->
|
||||
project.dependencies.add(testCompilation.compileDependencyConfigurationName, interopOutput)
|
||||
testCompilation.compileDependencyFiles += interopOutput
|
||||
testCompilation.cinterops.all {
|
||||
it.dependencyFiles += interopOutput
|
||||
}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.unitTests
|
||||
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal
|
||||
import org.jetbrains.kotlin.gradle.util.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class AssociateCompilationsTest {
|
||||
|
||||
@Test
|
||||
fun `test - associatedCompilations and allAssociatedCompilations sets`() {
|
||||
val project = buildProjectWithMPP()
|
||||
val kotlin = project.multiplatformExtension
|
||||
kotlin.jvm()
|
||||
|
||||
val main = kotlin.jvm().compilations.main
|
||||
val test = kotlin.jvm().compilations.test
|
||||
val bar = kotlin.jvm().compilations.create("bar")
|
||||
val foo = kotlin.jvm().compilations.create("foo")
|
||||
|
||||
// Check: foo is not associated with anything
|
||||
assertEquals(emptySet(), foo.internal.associatedCompilations.toSet())
|
||||
assertEquals(emptySet(), foo.internal.allAssociatedCompilations.toSet())
|
||||
|
||||
// Check: foo associates with test (which is already associated with main by default)
|
||||
foo.associateWith(test)
|
||||
assertEquals(setOf(test), foo.internal.associatedCompilations.toSet())
|
||||
assertEquals(setOf(main, test), foo.internal.allAssociatedCompilations.toSet())
|
||||
|
||||
// Check: associate main with bar (should be reflected in foo transitively)
|
||||
main.associateWith(bar)
|
||||
assertEquals(setOf(test), foo.internal.associatedCompilations.toSet())
|
||||
assertEquals(setOf(main, test, bar), foo.internal.allAssociatedCompilations.toSet())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - associate compilation dependency files are first in compile path`() {
|
||||
val project = buildProject { enableDefaultStdlibDependency(false) }
|
||||
val kotlin = project.applyMultiplatformPlugin()
|
||||
val jvm = kotlin.jvm()
|
||||
val foo = jvm.compilations.create("foo")
|
||||
|
||||
foo.defaultSourceSet.dependencies {
|
||||
implementation(project.files("bar.jar"))
|
||||
}
|
||||
|
||||
foo.associateWith(jvm.compilations.main)
|
||||
|
||||
assertEquals(
|
||||
listOf(jvm.compilations.main.output.classesDirs, project.files("bar.jar")).flatten(),
|
||||
foo.compileDependencyFiles.files.toList(),
|
||||
"Expected 'main classesDirs' to be listed before file dependency 'bar.jar'"
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user