From 55ef9a6badbe4d2b3e55cf105b79cefcfe1a3a1b Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 18 Nov 2022 14:50:03 +0100 Subject: [PATCH] [Gradle] Only add 'directly included' source sets to metadata compile tasks Contrary to platform compilations, metadata based compile tasks are expected to only compile the directly associated source sets w/o their dependsOn closure. DependsOn source sets will be represented as .klib binary dependencies instead. ^KT-54981 Verification Pending --- .../mpp/KotlinCommonCompilationFactory.kt | 9 ++ .../KotlinCompilationSourceSetInclusion.kt | 59 ++++---- .../factory/KotlinCompilationImplFactory.kt | 2 +- .../native/KotlinNativeCompilationFactory.kt | 6 +- .../KotlinSharedNativeCompilationFactory.kt | 11 +- .../gradle/KotlinCompileTaskSourcesTest.kt | 134 ++++++++++++++++++ 6 files changed, 185 insertions(+), 36 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileTaskSourcesTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilationFactory.kt index d39bc3b9302..da252ced671 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCommonCompilationFactory.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationFriendPathsResolver +import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetInclusion import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetsContainer import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.DefaultKotlinCompilationDependencyConfigurationsFactory import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory @@ -30,6 +31,14 @@ class KotlinCommonCompilationFactory internal constructor( DefaultKotlinCompilationFriendPathsResolver.AdditionalMetadataFriendArtifactResolver ) ), + + /* + Metadata compilation will only include directly added source sets to the compile task + */ + compilationSourceSetInclusion = KotlinCompilationSourceSetInclusion( + addSourcesToCompileTask = KotlinCompilationSourceSetInclusion.AddSourcesWithoutDependsOnClosure() + ), + /* Metadata compilations are created *because* of a pre-existing SourceSet. We therefore can create the container inline diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationSourceSetInclusion.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationSourceSetInclusion.kt index 40ef59b4fb9..706b2ed032e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationSourceSetInclusion.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/KotlinCompilationSourceSetInclusion.kt @@ -19,21 +19,16 @@ import org.jetbrains.kotlin.tooling.core.extrasKeyOf import org.jetbrains.kotlin.tooling.core.getOrPut import java.util.concurrent.Callable -internal interface KotlinCompilationSourceSetInclusion { - fun include(compilation: InternalKotlinCompilation<*>, sourceSet: KotlinSourceSet) -} - -internal class DefaultKotlinCompilationSourceSetInclusion( +internal class KotlinCompilationSourceSetInclusion( private val addSourcesToCompileTask: AddSourcesToCompileTask = DefaultAddSourcesToCompileTask -) : KotlinCompilationSourceSetInclusion { +) { - override fun include(compilation: InternalKotlinCompilation<*>, sourceSet: KotlinSourceSet) { + fun include(compilation: InternalKotlinCompilation<*>, sourceSet: KotlinSourceSet) { /* Check if the source set was already included */ if (!compilation.includedSourceSets.add(sourceSet)) return addSourcesToCompileTask.addSources( - compilation, sourceSet, - addAsCommonSources = lazy { + compilation, sourceSet, addAsCommonSources = lazy { compilation.project.kotlinExtension.sourceSets.any { otherSourceSet -> sourceSet in otherSourceSet.dependsOn } @@ -79,7 +74,7 @@ internal class DefaultKotlinCompilationSourceSetInclusion( * to avoid re-processing of unnecessary source sets! */ private val includedSourceSetsKey = extrasKeyOf>( - DefaultKotlinCompilationSourceSetInclusion::class.java.name + KotlinCompilationSourceSetInclusion::class.java.name ) val InternalKotlinCompilation<*>.includedSourceSets: MutableSet @@ -89,17 +84,18 @@ internal class DefaultKotlinCompilationSourceSetInclusion( fun interface AddSourcesToCompileTask { fun addSources(compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet, addAsCommonSources: Lazy) - - companion object { - fun composite(vararg elements: AddSourcesToCompileTask?): AddSourcesToCompileTask = - CompositeAddSourcesToCompileTask(listOfNotNull(*elements)) - } } - private class CompositeAddSourcesToCompileTask(private val children: List) : AddSourcesToCompileTask { - override fun addSources(compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet, addAsCommonSources: Lazy) { - return children.forEach { child -> child.addSources(compilation, sourceSet, addAsCommonSources) } - } + object DefaultAddSourcesToCompileTask : AddSourcesToCompileTask { + override fun addSources( + compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet, addAsCommonSources: Lazy + ) = addSourcesToKotlinCompileTask( + project = compilation.project, + taskName = compilation.compileKotlinTaskName, + sourceFileExtensions = sourceSet.customSourceFilesExtensions, + addAsCommonSources = addAsCommonSources, + sources = { sourceSet.kotlin } + ) } object NativeAddSourcesToCompileTask : AddSourcesToCompileTask { @@ -116,15 +112,20 @@ internal class DefaultKotlinCompilationSourceSetInclusion( } } - object DefaultAddSourcesToCompileTask : AddSourcesToCompileTask { - override fun addSources( - compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet, addAsCommonSources: Lazy - ) = addSourcesToKotlinCompileTask( - project = compilation.project, - taskName = compilation.compileKotlinTaskName, - sourceFileExtensions = sourceSet.customSourceFilesExtensions, - addAsCommonSources = addAsCommonSources, - sources = { sourceSet.kotlin } - ) + /** + * Will use the [delegate] to add sources to the compile task only if the source set was added directly to the compilation + * (not if the source set is just part of the dependsOn closure). + * + * This is necessary for metadata compilations, where dependsOn source sets will be binary dependencies. + * In contrast: Platform compilations (jvm, android, linuxX64, ...) will add *all* sources to the compile task + * and compiles them together. + */ + class AddSourcesWithoutDependsOnClosure( + private val delegate: AddSourcesToCompileTask = DefaultAddSourcesToCompileTask + ) : AddSourcesToCompileTask { + override fun addSources(compilation: KotlinCompilation<*>, sourceSet: KotlinSourceSet, addAsCommonSources: Lazy) { + if (sourceSet !in compilation.kotlinSourceSets) return + delegate.addSources(compilation, sourceSet, addAsCommonSources) + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationImplFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationImplFactory.kt index 8f6611f6845..2141f53fd1a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationImplFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/compilationImpl/factory/KotlinCompilationImplFactory.kt @@ -30,7 +30,7 @@ internal class KotlinCompilationImplFactory( DefaultKotlinCompilationFriendPathsResolver(), private val compilationSourceSetInclusion: KotlinCompilationSourceSetInclusion = - DefaultKotlinCompilationSourceSetInclusion(), + KotlinCompilationSourceSetInclusion(), private val compilationOutputFactory: KotlinCompilationOutputFactory = DefaultKotlinCompilationOutputFactory, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt index 858a313a41e..82cd7120fe8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt @@ -6,7 +6,7 @@ @file:Suppress("PackageDirectoryMismatch") // Old package for compatibility package org.jetbrains.kotlin.gradle.plugin.mpp -import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationSourceSetInclusion +import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetInclusion import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinNativeCompilationAssociator import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinCompilationImplFactory import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.KotlinNativeCompilerOptionsFactory @@ -26,8 +26,8 @@ open class KotlinNativeCompilationFactory internal constructor( compilationDependencyConfigurationsFactory = NativeKotlinCompilationDependencyConfigurationsFactory, compilerOptionsFactory = KotlinNativeCompilerOptionsFactory, compilationAssociator = KotlinNativeCompilationAssociator, - compilationSourceSetInclusion = DefaultKotlinCompilationSourceSetInclusion( - DefaultKotlinCompilationSourceSetInclusion.NativeAddSourcesToCompileTask + compilationSourceSetInclusion = KotlinCompilationSourceSetInclusion( + KotlinCompilationSourceSetInclusion.NativeAddSourcesToCompileTask ), ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinSharedNativeCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinSharedNativeCompilationFactory.kt index 81031e83f61..4d5586beb53 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinSharedNativeCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinSharedNativeCompilationFactory.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationFriendPathsResolver -import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.DefaultKotlinCompilationSourceSetInclusion +import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetInclusion import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinCompilationSourceSetsContainer import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.KotlinNativeCompilationAssociator import org.jetbrains.kotlin.gradle.plugin.mpp.compilationImpl.factory.DefaultKotlinCompilationDependencyConfigurationsFactory @@ -31,8 +31,13 @@ open class KotlinSharedNativeCompilationFactory internal constructor( compilerOptionsFactory = KotlinNativeCompilerOptionsFactory, compilationAssociator = KotlinNativeCompilationAssociator, - compilationSourceSetInclusion = DefaultKotlinCompilationSourceSetInclusion( - DefaultKotlinCompilationSourceSetInclusion.NativeAddSourcesToCompileTask + /* + Metadata compilation will only include directly added source sets to the compile task + */ + compilationSourceSetInclusion = KotlinCompilationSourceSetInclusion( + addSourcesToCompileTask = KotlinCompilationSourceSetInclusion.AddSourcesWithoutDependsOnClosure( + KotlinCompilationSourceSetInclusion.NativeAddSourcesToCompileTask + ) ), compilationFriendPathsResolver = DefaultKotlinCompilationFriendPathsResolver( diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileTaskSourcesTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileTaskSourcesTest.kt new file mode 100644 index 00000000000..fbe66af2e68 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/KotlinCompileTaskSourcesTest.kt @@ -0,0 +1,134 @@ +/* + * 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 + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCommonCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals + +class KotlinCompileTaskSourcesTest { + + @Test + fun `test - sources - shared native compile - KT-54995`() { + val project = buildProjectWithMPP() + val kotlin = project.multiplatformExtension + kotlin.targetHierarchy.default() + + kotlin.linuxX64() + kotlin.linuxArm64() + kotlin.jvm() + + val commonMainSourceFile = project.file("src/commonMain/kotlin/CommonMain.kt") + val linuxMainSourceFile = project.file("src/linuxMain/kotlin/LinuxMain.kt") + + commonMainSourceFile.parentFile.mkdirs() + linuxMainSourceFile.parentFile.mkdirs() + + commonMainSourceFile.writeText("object CommonMain") + linuxMainSourceFile.writeText("object LinuxMain") + + project.evaluate() + + /* Check sources of linuxMain compilation */ + val linuxMainCompilation = kotlin.metadata().compilations.getByName("linuxMain") as KotlinSharedNativeCompilation + val linuxMainCompileTask = linuxMainCompilation.compileTaskProvider.get() + + assertEquals( + setOf(linuxMainSourceFile).relativeTo(project), + linuxMainCompileTask.sources.files.relativeTo(project) + ) + + /* Check sources of commonMain compilation */ + val commonMainCompilation = kotlin.metadata().compilations.getByName("commonMain") as KotlinCommonCompilation + val commonMainCompileTask = commonMainCompilation.compileTaskProvider.get() as KotlinCompileCommon + + assertEquals( + setOf(commonMainSourceFile).relativeTo(project), + commonMainCompileTask.sources.files.relativeTo(project) + ) + } + + @Test + fun `test - sources - linux and jvm`() { + val project = buildProjectWithMPP() + val kotlin = project.multiplatformExtension + kotlin.targetHierarchy.default { + common { + group("jvmAndLinux") { + anyLinux() + jvm() + } + } + } + + kotlin.linuxArm64() + kotlin.jvm() + + val commonMainSourceFile = project.file("src/commonMain/kotlin/Common.kt") + val jvmAndLinuxMainSourceFile = project.file("src/jvmAndLinuxMain/kotlin/JvmAndLinuxMain.kt") + val linuxArm64MainSourceFile = project.file("src/linuxArm64Main/kotlin/Linux.kt") + val jvmMainSourceFile = project.file("src/jvmMain/kotlin/Jvm.kt") + + commonMainSourceFile.parentFile.mkdirs() + jvmAndLinuxMainSourceFile.parentFile.mkdirs() + linuxArm64MainSourceFile.parentFile.mkdirs() + jvmMainSourceFile.parentFile.mkdirs() + + commonMainSourceFile.writeText("object CommonMain") + jvmAndLinuxMainSourceFile.writeText("object JvmAndLinuxMain") + linuxArm64MainSourceFile.writeText("object Linux") + jvmMainSourceFile.writeText("object Jvm") + + project.evaluate() + + /* Check sources of commonMain compile task */ + val commonMainCompileTask = kotlin.metadata().compilations.getByName("commonMain") + .compileTaskProvider.get() as KotlinCompileCommon + + assertEquals( + setOf(commonMainSourceFile).relativeTo(project), + commonMainCompileTask.sources.files.relativeTo(project) + ) + + /* Check sources of jvmAndLinuxMain compile task */ + val jvmAndLinuxMainCompileTask = kotlin.metadata().compilations.getByName("jvmAndLinuxMain") + .compileTaskProvider.get() as KotlinCompileCommon + + assertEquals( + setOf(jvmAndLinuxMainSourceFile).relativeTo(project), + jvmAndLinuxMainCompileTask.sources.files.relativeTo(project) + ) + + /* Check sources of jvm compile task */ + val jvmMainCompileTask = kotlin.jvm().compilations.getByName("main") + .compileTaskProvider.get() as KotlinJvmCompile + + assertEquals( + setOf(commonMainSourceFile, jvmAndLinuxMainSourceFile, jvmMainSourceFile).relativeTo(project), + jvmMainCompileTask.sources.files.relativeTo(project) + ) + + /* Check sources of linuxArm64 compile task */ + val linuxArm64CompileTask = kotlin.linuxArm64().compilations.getByName("main") + .compileTaskProvider.get() as KotlinNativeCompile + + assertEquals( + setOf(commonMainSourceFile, jvmAndLinuxMainSourceFile, linuxArm64MainSourceFile).relativeTo(project), + linuxArm64CompileTask.sources.files.relativeTo(project) + ) + } + + private fun Set.relativeTo(project: Project): Set = map { it.relativeTo(project.projectDir) }.toSet() +}