From 94fd5c2884316508f3488ad02bf97f87194c239c Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 23 Aug 2019 16:26:36 +0300 Subject: [PATCH] Implement source sets visibility inference via associate compilations Add non-public API for the IDE to query additional visible source sets for each source set. Implement visibility inference via associate compilation links. Implement visibility requirements for source sets and requirements satisfaction checks in Gradle build. --- .../kotlin/gradle/plugin/KotlinSourceSet.kt | 3 + .../plugin/mpp/KotlinMultiplatformPlugin.kt | 11 +- .../plugin/sources/DefaultKotlinSourceSet.kt | 16 +- .../sources/SourceSetsVisibilityInference.kt | 156 +++++++++ .../SourceSetVisibilityInferenceTest.kt | 322 ++++++++++++++++++ 5 files changed, 503 insertions(+), 5 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetsVisibilityInference.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/SourceSetVisibilityInferenceTest.kt 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