diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSet.kt index 39abf4dc4c0..5216c648049 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinSourceSet.kt @@ -42,5 +42,8 @@ interface KotlinSourceSet : Named, HasKotlinDependencies { val customSourceFilesExtensions: Iterable // lazy iterable expected + val requiresVisibilityOf: Set + fun requiresVisibilityOf(other: KotlinSourceSet) + fun addCustomSourceFilesExtensions(extensions: List) {} } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index d2c7a216938..234f5b14c56 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -21,12 +21,13 @@ import org.gradle.api.publish.maven.tasks.AbstractPublishToMaven import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.jvm.tasks.Jar import org.gradle.util.ConfigureUtil -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.dsl.configureOrCreate import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder +import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequirements import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.utils.* @@ -91,8 +92,6 @@ class KotlinMultiplatformPlugin( setupCompilerPluginOptions(project) project.pluginManager.apply(ScriptingGradleSubplugin::class.java) - - UnusedSourceSetsChecker.checkSourceSets(project) } private fun setupCompilerPluginOptions(project: Project) { @@ -256,6 +255,12 @@ class KotlinMultiplatformPlugin( sourceSets.findByName(testCompilation.defaultSourceSetName)?.dependsOn(test) } } + + UnusedSourceSetsChecker.checkSourceSets(project) + + project.whenEvaluated { + checkSourceSetVisibilityRequirements(project) + } } companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt index b18c6a46380..401cc6fd6a6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultKotlinSourceSet.kt @@ -16,9 +16,8 @@ import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder -import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultKotlinDependencyHandler +import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.GranularMetadataTransformation -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName @@ -119,6 +118,15 @@ class DefaultKotlinSourceSet( internal val dependencyTransformations: MutableMap = mutableMapOf() + private val _requiresVisibilityOf = mutableSetOf() + + override val requiresVisibilityOf: MutableSet + get() = Collections.unmodifiableSet(_requiresVisibilityOf) + + override fun requiresVisibilityOf(other: KotlinSourceSet) { + requiresVisibilityOf += other + } + //region IDE import for Granular source sets metadata data class MetadataDependencyTransformation( @@ -141,6 +149,10 @@ class DefaultKotlinSourceSet( return getDependenciesTransformation(scope) } + @Suppress("unused") // Used in IDE import + fun getAdditionalVisibleSourceSets(): Set = + getVisibleSourceSetsFromAssociateCompilations(project, this) + internal fun getDependenciesTransformation(scope: KotlinDependencyScope): Iterable { val metadataDependencyResolutionByModule = dependencyTransformations[scope]?.metadataDependencyResolutions diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetsVisibilityInference.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetsVisibilityInference.kt new file mode 100644 index 00000000000..5b412224af8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetsVisibilityInference.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2010-2019 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.sources + +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil +import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure + +fun getSourceSetsFromAssociatedCompilations(fromCompilation: KotlinCompilation<*>): Map, Set> = + fromCompilation.associateWithTransitiveClosure.associate { it to it.allKotlinSourceSets } + +fun getVisibleSourceSetsFromAssociateCompilations( + project: Project, + sourceSet: KotlinSourceSet +) = getVisibleSourceSetsFromAssociateCompilations( + CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(sourceSet) +) + +internal fun getVisibleSourceSetsFromAssociateCompilations( + participatesInCompilations: Set> +): Set { + val visibleInCompilations = participatesInCompilations.map { + val sourceSetsInAssociatedCompilations = getSourceSetsFromAssociatedCompilations(it) + when (sourceSetsInAssociatedCompilations.size) { + 0 -> emptySet() + 1 -> sourceSetsInAssociatedCompilations.values.single() + else -> mutableSetOf().apply { + for ((_, sourceSets) in sourceSetsInAssociatedCompilations) { + addAll(sourceSets) + } + } + } + } + + // Intersect the sets of source sets from the compilations: + return when (visibleInCompilations.size) { + 0 -> emptySet() + 1 -> visibleInCompilations.single() + else -> visibleInCompilations.first().toMutableSet().apply { + visibleInCompilations.subList(1, visibleInCompilations.size).forEach { retainAll(it) } + } + } +} + +class UnsatisfiedSourceSetVisibilityException( + val sourceSet: KotlinSourceSet, + val compilations: Set>, + val visibleSourceSets: Set, + val requiredButNotVisible: Set +) : GradleException() { + + override val message: String? + get() = buildString { + fun singularOrPlural(collection: Collection<*>, singular: String, plural: String = singular + "s") = + if (collection.size == 1) singular else plural + + fun compilationWithTarget(compilation: KotlinCompilation<*>) = "${compilation.name} (target ${compilation.target.name})" + + append( + "The source set ${sourceSet.name} requires visibility of the " + + singularOrPlural(requiredButNotVisible, "source set", "source sets:") + " " + + "${requiredButNotVisible.joinToString()}. " + + "This requirement was not satisfied.\n\n" + ) + + append("${sourceSet.name} takes part in the ${singularOrPlural(compilations, "compilation")}:\n") + + fun appendCompilationRecursively(compilation: KotlinCompilation<*>, depth: Int) { + val isAssociatedCompilation = depth > 0 + + val sourceSetsInAssociatedCompilations = + getSourceSetsFromAssociatedCompilations(compilation) + val allKotlinSourceSets = compilation.allKotlinSourceSets + + val indent = " ".repeat(depth + 1) + + val prefix = if (isAssociatedCompilation) + "$indent- ${"indirectly ".takeIf { depth > 1 }.orEmpty()}associated with" + else + "$indent-" + + append("$prefix ${compilationWithTarget(compilation)}") + + append( + if (isAssociatedCompilation) + ", which compiles" + + singularOrPlural(allKotlinSourceSets, "source set ", "source sets: ") + + allKotlinSourceSets.joinToString { it.name } + + "\n" + else "\n" + ) + + compilation.associateWith.forEach { appendCompilationRecursively(it, depth + 1) } + + if (!isAssociatedCompilation) { + val missingRequiredSourceSets = requiredButNotVisible.filter { missingSourceSet -> + sourceSetsInAssociatedCompilations.values.none { missingSourceSet in it } + } + + if (missingRequiredSourceSets.isEmpty()) { + append("${indent}The compilation ${compilationWithTarget(compilation)} requires no changes.\n") + } else { + append( + "${indent}To ensure the required visibility, the compilation " + compilationWithTarget(compilation) + + "must have a direct or indirect associate that compiles the source " + + singularOrPlural(missingRequiredSourceSets, "set ", "sets: ") + + missingRequiredSourceSets.joinToString() + "\n" + ) + } + } + } + + compilations.forEach { + appendCompilationRecursively(it, 0) + append("\n") + } + } +} + +fun checkSourceSetVisibilityRequirements( + project: Project +) = checkSourceSetVisibilityRequirements( + project.kotlinExtension.sourceSets, + CompilationSourceSetUtil.compilationsBySourceSets(project) +) + +internal fun checkSourceSetVisibilityRequirements( + sourceSets: Iterable, + compilationsBySourceSet: Map>> +) { + sourceSets.forEach { sourceSet -> + val requiredVisibility = sourceSet.requiresVisibilityOf + val inferredVisibility = + getVisibleSourceSetsFromAssociateCompilations(compilationsBySourceSet[sourceSet].orEmpty()) + + val requiredButNotVisible = requiredVisibility - inferredVisibility //TODO minus dependsOn? + + if (requiredButNotVisible.isNotEmpty()) { + val compilations = compilationsBySourceSet.getValue(sourceSet) + + throw UnsatisfiedSourceSetVisibilityException( + sourceSet, + compilations, + inferredVisibility, + requiredButNotVisible + ) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetVisibilityInferenceTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetVisibilityInferenceTest.kt new file mode 100644 index 00000000000..afd28475149 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetVisibilityInferenceTest.kt @@ -0,0 +1,322 @@ +/* + * Copyright 2010-2019 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.sources + +import groovy.lang.Closure +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.file.FileCollection +import org.gradle.api.file.SourceDirectorySet +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile +import org.jetbrains.kotlin.gradle.plugin.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class SourceSetVisibilityTest { + @Test + fun testBasicSuccessful(): Unit = with(SourceSetCompilationDsl()) { + val commonMain = sourceSet("commonMain") + val jvmMain = sourceSet("jvmMain", commonMain) + + val commonTest = sourceSet("commonTest") { + requiresVisibilityOf(commonMain) + } + val jvmTest = sourceSet("jvmTest", commonTest) { + requiresVisibilityOf(jvmMain) + } + + val jvmMainCompilation = compilation("jvm/main", jvmMain) + compilation("jvm/test", jvmTest, jvmMainCompilation) + + checkInferredSourceSetsVisibility(jvmTest.name, commonMain.name, jvmMain.name) + + // should not throw exception + checkSourceSetVisibilityRequirements(sourceSets, compilationsBySourceSets) + } + + @Test + fun testFailureWithNoAssociation(): Unit = with(SourceSetCompilationDsl()) { + val commonMain = sourceSet("commonMain") + val jvmMain = sourceSet("jvmMain", commonMain) + + val commonTest = sourceSet("commonTest") { + requiresVisibilityOf(commonMain) + } + val jvmTest = sourceSet("jvmTest", commonTest) { + requiresVisibilityOf(jvmMain) + } + + compilation("jvm/main", jvmMain) + val jvmTestCompilation = compilation("jvm/test", jvmTest) // note: no association with jvmMainCompilation + + checkInferredSourceSetsVisibility("jvmTest", *arrayOf()) + + assertFailsWith { + checkSourceSetVisibilityRequirements(setOf(jvmTest), compilationsBySourceSets) + }.apply { + assertEquals(jvmTest, sourceSet) + assertEquals(emptySet(), visibleSourceSets) + assertEquals(setOf(jvmMain), requiredButNotVisible) + assertEquals(setOf(jvmTestCompilation), compilations) + } + + assertFailsWith { + checkSourceSetVisibilityRequirements(setOf(commonTest), compilationsBySourceSets) + }.apply { + assertEquals(commonTest, sourceSet) + assertEquals(emptySet(), visibleSourceSets) + assertEquals(setOf(commonMain), requiredButNotVisible) + assertEquals(setOf(jvmTestCompilation), compilations) + } + } + + private fun SourceSetCompilationDsl.checkInferredSourceSetsVisibility( + forSourceSetName: String, + vararg expectedVisibleSourceSetNames: String + ) = assertEquals( + setOf(*expectedVisibleSourceSetNames), + getVisibleSourceSetsFromAssociateCompilations( + compilationsBySourceSets.getValue(sourceSetsByName.getValue(forSourceSetName)) + ).mapTo(mutableSetOf()) { it.name } + ) + + @Test + fun testInferenceForHierarchy() = with(SourceSetCompilationDsl()) { + for (suffix in listOf("Main", "Test")) { + val common = sourceSet("common$suffix") + val jvmAndJs = sourceSet("jvmAndJs$suffix", common) { + sourceSetsByName["jvmAndJsMain"]?.let(this::requiresVisibilityOf) + } + val linuxAndJs = sourceSet("linuxAndJs$suffix", common) { + sourceSetsByName["linuxAndJsMain"]?.let(this::requiresVisibilityOf) + } + val jvm = sourceSet("jvm$suffix", common, jvmAndJs) { + sourceSetsByName["jvmMain"]?.let(this::requiresVisibilityOf) + } + val linux = sourceSet("linux$suffix", common, linuxAndJs) { + sourceSetsByName["linuxMain"]?.let(this::requiresVisibilityOf) + } + val js = sourceSet("js$suffix", common, jvmAndJs, linuxAndJs) { + sourceSetsByName["jsMain"]?.let(this::requiresVisibilityOf) + } + + compilation("jvm / $suffix", jvm, *listOfNotNull(compilationsByName["jvm / Main"]).toTypedArray()) + compilation("linux / $suffix", linux, *listOfNotNull(compilationsByName["linux / Main"]).toTypedArray()) + compilation("js / $suffix", js, *listOfNotNull(compilationsByName["js / Main"]).toTypedArray()) + } + + checkInferredSourceSetsVisibility("commonMain", *arrayOf()) + checkInferredSourceSetsVisibility("jvmMain", *arrayOf()) + checkInferredSourceSetsVisibility("jvmAndJsMain", *arrayOf()) + + checkInferredSourceSetsVisibility("commonTest", "commonMain") + checkInferredSourceSetsVisibility("jvmAndJsTest", "commonMain", "jvmAndJsMain") + checkInferredSourceSetsVisibility("linuxAndJsTest", "commonMain", "linuxAndJsMain") + checkInferredSourceSetsVisibility("jvmTest", "commonMain", "jvmAndJsMain", "jvmMain") + + checkSourceSetVisibilityRequirements(sourceSets, compilationsBySourceSets) + } + + @Test + fun testInferenceThroughIndirectAssociation(): Unit = with(SourceSetCompilationDsl()) { + for ((previousSuffix, suffix) in listOf(null, "Main", "Test", "IntegrationTest").zipWithNext()) { + val common = sourceSet("common$suffix") { + if (previousSuffix != null) requiresVisibilityOf(sourceSetsByName.getValue("common$previousSuffix")) + } + val jvm = sourceSet("jvm$suffix", common) { + if (previousSuffix != null) requiresVisibilityOf(sourceSetsByName.getValue("jvm$previousSuffix")) + } + val js = sourceSet("js$suffix", common) { + if (previousSuffix != null) requiresVisibilityOf(sourceSetsByName.getValue("js$previousSuffix")) + } + + compilation("jvm / $suffix", jvm, *listOfNotNull(compilationsByName["jvm / $previousSuffix"]).toTypedArray()) + compilation("js / $suffix", js, *listOfNotNull(compilationsByName["js / $previousSuffix"]).toTypedArray()) + } + + checkInferredSourceSetsVisibility("commonIntegrationTest", "commonMain", "commonTest") + checkInferredSourceSetsVisibility("jvmIntegrationTest", "commonMain", "jvmMain", "commonTest", "jvmTest") + + checkSourceSetVisibilityRequirements(sourceSets, compilationsBySourceSets) + + // Now break visibility between *Test and *Main: + + compilationsByName.getValue("jvm / Test").associateWith.clear() + compilationsByName.getValue("js / Test").associateWith.clear() + val commonIntegrationTest = sourceSetsByName.getValue("commonIntegrationTest") + commonIntegrationTest.requiresVisibilityOf(sourceSetsByName.getValue("commonMain")) + + assertFailsWith { + checkSourceSetVisibilityRequirements(setOf(commonIntegrationTest), compilationsBySourceSets) + }.apply { + assertEquals(commonIntegrationTest, this.sourceSet) + assertEquals(setOf("commonTest"), visibleSourceSets.map { it.name }.toSet()) + assertEquals(setOf("commonMain"), requiredButNotVisible.map { it.name }.toSet()) + assertEquals(setOf("jvm / IntegrationTest", "js / IntegrationTest"), compilations.map { it.name }.toSet()) + } + } + + @Test + fun testFailureWithPlatformSpecificRequirement(): Unit = with(SourceSetCompilationDsl()) { + val commonMain = sourceSet("commonMain") + val jvmMain = sourceSet("jvmMain", commonMain) + val jsMain = sourceSet("jvmMain", commonMain) + + val commonTest = sourceSet("commonTest") { + requiresVisibilityOf(jvmMain) // <- invalid requirement! + } + val jvmTest = sourceSet("jvmTest", commonTest) { + requiresVisibilityOf(jvmMain) + } + val jsTest = sourceSet("jsTest", commonTest) { + requiresVisibilityOf(jsMain) + } + + val jvmMainCompilation = compilation("jvm / main", jvmMain) + val jsMainCompilation = compilation("js / main", jsMain) + val jvmTestCompilation = compilation("jvm / test", jvmTest, jvmMainCompilation) + val jsTestCompilation = compilation("js / test", jsTest, jsMainCompilation) + + assertFailsWith { + checkSourceSetVisibilityRequirements(setOf(commonTest), compilationsBySourceSets) + }.apply { + assertEquals(commonTest, this.sourceSet) + assertEquals(setOf(commonMain), visibleSourceSets) + assertEquals(setOf(jvmMain), requiredButNotVisible) + assertEquals(setOf(jvmTestCompilation, jsTestCompilation), compilations) + } + } +} + +class SourceSetCompilationDsl { + val compilations: Set get() = compilationsByName.values.toSet() + val sourceSets: Set get() = sourceSetsByName.values.toSet() + + val sourceSetsByName = mutableMapOf() + val compilationsByName = mutableMapOf() + + val compilationsBySourceSets: Map>> + get() = mutableMapOf>>().apply { + compilations.forEach { compilation -> + compilation.allKotlinSourceSets.filterIsInstance().forEach { sourceSet -> + getOrPut(sourceSet) { mutableSetOf() }.add(compilation) + } + } + } + + fun sourceSet( + name: String, + vararg dependsOn: MockKotlinSourceSet, + configure: MockKotlinSourceSet.() -> Unit = { } + ) = MockKotlinSourceSet(name).apply { + dependsOn.forEach { dependsOn(it) } + configure() + sourceSetsByName[name] = this + } + + fun compilation( + name: String, + defaultSourceSet: KotlinSourceSet, + vararg associateWith: MockKotlinCompilation, + configure: MockKotlinCompilation.() -> Unit = { } + ) = MockKotlinCompilation(name, defaultSourceSet).apply { + associateWith.forEach { associateWith(it) } + configure() + compilationsByName[name] = this + } +} + +class MockKotlinSourceSet(private val name: String) : KotlinSourceSet { + override fun getName(): String = name + + override val dependsOn: MutableSet = mutableSetOf() + + override val requiresVisibilityOf: MutableSet = mutableSetOf() + + override fun dependsOn(other: KotlinSourceSet) { + dependsOn += other + } + + override fun requiresVisibilityOf(other: KotlinSourceSet) { + requiresVisibilityOf += other + } + + //region Not implemented + override val kotlin: SourceDirectorySet get() = throw UnsupportedOperationException() + + override fun kotlin(configureClosure: Closure): SourceDirectorySet = throw UnsupportedOperationException() + override val resources: SourceDirectorySet get() = throw UnsupportedOperationException() + override val languageSettings: LanguageSettingsBuilder get() = throw UnsupportedOperationException() + override fun languageSettings(configureClosure: Closure): LanguageSettingsBuilder = languageSettings + override val apiMetadataConfigurationName: String get() = throw UnsupportedOperationException() + override val implementationMetadataConfigurationName: String get() = throw UnsupportedOperationException() + override val compileOnlyMetadataConfigurationName: String get() = throw UnsupportedOperationException() + override val runtimeOnlyMetadataConfigurationName: String get() = throw UnsupportedOperationException() + override val customSourceFilesExtensions: Iterable get() = throw UnsupportedOperationException() + override fun dependencies(configure: KotlinDependencyHandler.() -> Unit) = throw UnsupportedOperationException() + override fun dependencies(configureClosure: Closure) = throw UnsupportedOperationException() + override val apiConfigurationName: String get() = throw UnsupportedOperationException() + override val implementationConfigurationName: String get() = throw UnsupportedOperationException() + override val compileOnlyConfigurationName: String get() = throw UnsupportedOperationException() + override val runtimeOnlyConfigurationName: String get() = throw UnsupportedOperationException() + //endregion +} + +class MockKotlinCompilation( + override val compilationName: String, + override val defaultSourceSet: KotlinSourceSet +) : KotlinCompilation { + override val kotlinSourceSets: Set = setOf(defaultSourceSet) + + override val allKotlinSourceSets: Set + get() = mutableSetOf().apply { + fun visit(sourceSet: KotlinSourceSet) { + if (add(sourceSet)) { + sourceSet.dependsOn.forEach(::visit) + } + } + visit(defaultSourceSet) + } + + override fun source(sourceSet: KotlinSourceSet) = defaultSourceSet.dependsOn(sourceSet) + + override fun associateWith(other: KotlinCompilation<*>) { + associateWith += other + } + + override val associateWith: MutableSet> = mutableSetOf() + + override fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit) = defaultSourceSet.run(configure) + + //region Not implemented + override val target: KotlinTarget get() = throw UnsupportedOperationException() + + override fun getAttributes(): AttributeContainer = throw UnsupportedOperationException() + override fun dependencies(configure: KotlinDependencyHandler.() -> Unit) = throw UnsupportedOperationException() + override fun dependencies(configureClosure: Closure) = throw UnsupportedOperationException() + override val apiConfigurationName: String get() = throw UnsupportedOperationException() + override val implementationConfigurationName: String get() = throw UnsupportedOperationException() + override val compileOnlyConfigurationName: String get() = throw UnsupportedOperationException() + override val runtimeOnlyConfigurationName: String get() = throw UnsupportedOperationException() + override fun defaultSourceSet(configure: Closure<*>) = throw UnsupportedOperationException() + override val compileDependencyConfigurationName: String get() = throw UnsupportedOperationException() + override var compileDependencyFiles: FileCollection + get() = throw UnsupportedOperationException() + set(value) = throw UnsupportedOperationException() + override val output: KotlinCompilationOutput get() = throw UnsupportedOperationException() + override val compileKotlinTaskName: String get() = throw UnsupportedOperationException() + override val compileKotlinTask: KotlinCompile get() = throw UnsupportedOperationException() + override val kotlinOptions: KotlinCommonOptions get() = throw UnsupportedOperationException() + override fun kotlinOptions(configure: KotlinCommonOptions.() -> Unit) = throw UnsupportedOperationException() + override fun kotlinOptions(configure: Closure<*>) = throw UnsupportedOperationException() + override fun attributes(configure: AttributeContainer.() -> Unit) = throw UnsupportedOperationException() + override fun attributes(configure: Closure<*>) = throw UnsupportedOperationException() + override val compileAllTaskName: String get() = throw UnsupportedOperationException() + //endregion + + override fun toString(): String = "compilation '${name}'" +} \ No newline at end of file