diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractCompilationDetails.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractCompilationDetails.kt new file mode 100644 index 00000000000..ab8a2986279 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractCompilationDetails.kt @@ -0,0 +1,44 @@ +/* + * 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.mpp + +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.sources.internal +import org.jetbrains.kotlin.gradle.utils.MutableObservableSet +import org.jetbrains.kotlin.gradle.utils.ObservableSet + +abstract class AbstractCompilationDetails : CompilationDetails { + private val directlyIncludedKotlinSourceSetsImpl: MutableObservableSet by lazy { + MutableObservableSet(defaultSourceSet) + } + + final override val directlyIncludedKotlinSourceSets: ObservableSet + get() = directlyIncludedKotlinSourceSetsImpl + + private val kotlinSourceSetsClosureImpl: MutableObservableSet by lazy { + MutableObservableSet().also { set -> + defaultSourceSet.internal.withDependsOnClosure.forAll(set::add) + } + } + + final override val kotlinSourceSetsClosure: ObservableSet + get() = kotlinSourceSetsClosureImpl + + final override fun source(sourceSet: KotlinSourceSet) { + directlyIncludedKotlinSourceSetsImpl.add(sourceSet) + sourceSet.internal.withDependsOnClosure.forAll { withDependsOn -> + kotlinSourceSetsClosureImpl.add(withDependsOn) + } + + whenSourceSetAdded(sourceSet) + } + + /** + * Called after [sourceSet] added the [sourceSet] to its respective ObservableSet's + */ + protected open fun whenSourceSetAdded(sourceSet: KotlinSourceSet) = Unit +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinCompilation.kt index b7220fcaad5..0443084c99d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/AbstractKotlinCompilation.kt @@ -16,14 +16,14 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData -import org.jetbrains.kotlin.gradle.plugin.sources.withDependsOnClosure import org.jetbrains.kotlin.gradle.tasks.locateTask +import org.jetbrains.kotlin.gradle.utils.ObservableSet import org.jetbrains.kotlin.project.model.LanguageSettings import java.util.* abstract class AbstractKotlinCompilation( internal open val compilationDetails: CompilationDetails, -) : KotlinCompilation, +) : InternalKotlinCompilation, HasKotlinDependencies, KotlinCompilationData { @@ -62,12 +62,11 @@ abstract class AbstractKotlinCompilation( get() = compilationDetails.compileDependencyFilesHolder.dependencyFiles set(value) { compilationDetails.compileDependencyFilesHolder.dependencyFiles = value } - final override val kotlinSourceSets: MutableSet get() = - when (val details = compilationDetails) { - is DefaultCompilationDetails -> details.directlyIncludedKotlinSourceSets // mutable in that subtype - // TODO deprecate mutability of this set. We shouldn't allow mutating it directly anyway; - else -> details.directlyIncludedKotlinSourceSets.toMutableSet() - } + final override val kotlinSourceSets: ObservableSet + get() = compilationDetails.directlyIncludedKotlinSourceSets + + override val allKotlinSourceSets: ObservableSet + get() = compilationDetails.kotlinSourceSetsClosure final override val defaultSourceSetName: String get() = compilationDetails.defaultSourceSetName @@ -112,9 +111,6 @@ abstract class AbstractKotlinCompilation( override val owner: KotlinTarget get() = target - override val allKotlinSourceSets: Set - get() = compilationDetails.directlyIncludedKotlinSourceSets.withDependsOnClosure - override val relatedConfigurationNames: List get() = compilationDetails.kotlinDependenciesHolder.relatedConfigurationNames + compileDependencyConfigurationName } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt index 4c23f1c1d33..b0105d81ac9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CompilationDetails.kt @@ -50,7 +50,8 @@ interface CompilationDetails { fun source(sourceSet: KotlinSourceSet) - val directlyIncludedKotlinSourceSets: Set + val directlyIncludedKotlinSourceSets: ObservableSet + val kotlinSourceSetsClosure: ObservableSet val defaultSourceSetName: String @Suppress("UNCHECKED_CAST") @@ -72,7 +73,7 @@ open class DefaultCompilationDetails( final override val target: KotlinTarget, final override val compilationPurpose: String, createKotlinOptions: DefaultCompilationDetails<*>.() -> T -) : CompilationDetails, KotlinCompilationData { +) : AbstractCompilationDetails(), KotlinCompilationData { override val kotlinOptions: T by lazy { createKotlinOptions() } @@ -104,8 +105,6 @@ open class DefaultCompilationDetails( ) ) - override val directlyIncludedKotlinSourceSets: MutableSet = mutableSetOf() - override val defaultSourceSetName: String get() = lowerCamelCaseName( target.disambiguationClassifier.takeIf { target !is KotlinMetadataTarget }, @@ -263,12 +262,9 @@ open class DefaultCompilationDetails( } } - - override fun source(sourceSet: KotlinSourceSet) { - if (directlyIncludedKotlinSourceSets.add(sourceSet)) { - sourceSet.internal.withDependsOnClosure.forAll { inWithDependsOnClosure -> - addExactSourceSetEagerly(inWithDependsOnClosure) - } + override fun whenSourceSetAdded(sourceSet: KotlinSourceSet) { + sourceSet.internal.withDependsOnClosure.forAll { inWithDependsOnClosure -> + addExactSourceSetEagerly(inWithDependsOnClosure) } } @@ -280,7 +276,12 @@ open class DefaultCompilationDetails( addAsCommonSources ) { sourceSet.kotlin } + + private val sourceSetsAddedEagerly = hashSetOf() + internal fun addExactSourceSetEagerly(sourceSet: KotlinSourceSet) { + if (!sourceSetsAddedEagerly.add(sourceSet)) return + with(target.project) { //TODO possibly issue with forced instantiation addSourcesToCompileTask( @@ -399,7 +400,7 @@ internal open class SharedNativeCompilationDetails( internal open class MetadataMappedCompilationDetails( override val target: KotlinMetadataTarget, final override val compilationData: AbstractKotlinFragmentMetadataCompilationData -) : CompilationDetails { +) : AbstractCompilationDetails() { override val compileDependencyFilesHolder: GradleKpmDependencyFilesHolder = GradleKpmDependencyFilesHolder.ofMetadataCompilationDependencies(compilationData) @@ -413,7 +414,7 @@ internal open class MetadataMappedCompilationDetails( override val associateCompilations: Set> get() = emptySet() - override fun source(sourceSet: KotlinSourceSet) { + override fun whenSourceSetAdded(sourceSet: KotlinSourceSet) { throw UnsupportedOperationException("metadata compilations have predefined sources") } @@ -422,9 +423,6 @@ internal open class MetadataMappedCompilationDetails( it.underlyingFragment == compilationData.fragment } - override val directlyIncludedKotlinSourceSets: MutableSet - get() = Collections.unmodifiableSet(hashSetOf(underlyingSourceSet)) - override val defaultSourceSetName: String get() = underlyingSourceSet.name } @@ -432,7 +430,7 @@ internal open class MetadataMappedCompilationDetails( internal open class VariantMappedCompilationDetails( open val variant: GradleKpmVariantInternal, override val target: KotlinTarget -) : CompilationDetails { +) : AbstractCompilationDetails() { @Suppress("UNCHECKED_CAST") override val compilationData: KotlinCompilationData @@ -441,7 +439,7 @@ internal open class VariantMappedCompilationDetails( override val defaultSourceSetName: String get() = variant.unambiguousNameInProject - override fun source(sourceSet: KotlinSourceSet) { + override fun whenSourceSetAdded(sourceSet: KotlinSourceSet) { compilation.defaultSourceSet.dependsOn(sourceSet) } @@ -462,8 +460,6 @@ internal open class VariantMappedCompilationDetails( override val kotlinDependenciesHolder: HasKotlinDependencies get() = variant - override val directlyIncludedKotlinSourceSets: Set - get() = compilation.defaultSourceSet.dependsOn } internal open class VariantMappedCompilationDetailsWithRuntime( diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/InternalKotlinCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/InternalKotlinCompilation.kt new file mode 100644 index 00000000000..30e28abd3be --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/InternalKotlinCompilation.kt @@ -0,0 +1,21 @@ +/* + * 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.mpp + +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.utils.ObservableSet + +internal interface InternalKotlinCompilation : KotlinCompilation { + override val kotlinSourceSets: ObservableSet + override val allKotlinSourceSets: ObservableSet +} + +internal val KotlinCompilation.internal: InternalKotlinCompilation + get() = (this as? InternalKotlinCompilation) ?: throw IllegalArgumentException( + "KotlinCompilation($name) ${this::class} does not implement ${InternalKotlinCompilation::class}" + ) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index 34a9c702d32..8b731f4f7f3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -454,7 +454,7 @@ class KotlinMetadataTargetConfigurator : } val resolvedMetadataFilesProviders = lazy { - val transformationTaskHolders = sourceSet.withDependsOnClosure.mapNotNull { hierarchySourceSet -> + val transformationTaskHolders = sourceSet.internal.withDependsOnClosure.mapNotNull { hierarchySourceSet -> project.locateTask(transformGranularMetadataTaskName(hierarchySourceSet.name)) } transformationTaskHolders.map { SourceSetResolvedMetadataProvider(it) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/InternalKotlinCompilationTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/InternalKotlinCompilationTest.kt new file mode 100644 index 00000000000..8a7b0452d29 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/mpp/InternalKotlinCompilationTest.kt @@ -0,0 +1,29 @@ +/* + * 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.mpp + +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.InternalKotlinCompilation +import org.jetbrains.kotlin.gradle.reflections +import org.junit.Test +import kotlin.reflect.full.isSubclassOf +import kotlin.test.fail + +class InternalKotlinSourceSetTest { + @Test + fun `test - all implementations of KotlinCompilation - implement InternalKotlinCompilation`() { + val subtypesOfKotlinSourceSet = reflections.getSubTypesOf(KotlinCompilation::class.java) + subtypesOfKotlinSourceSet + .filter { subtype -> !subtype.isInterface } + .forEach { implementation -> + if (!implementation.kotlin.isSubclassOf(InternalKotlinCompilation::class)) { + fail("$implementation does not implement ${InternalKotlinCompilation::class}") + } + } + } +}