diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index 6ec900f4f01..7b515934b94 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin +import org.jetbrains.kotlin.gradle.plugin.mpp.UnusedSourceSetsChecker import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks import org.jetbrains.kotlin.gradle.util.checkBytecodeContains @@ -1062,4 +1063,34 @@ class NewMultiplatformIT : BaseGradleIT() { assertFileExists("foo/2.txt") } } + + @Test + fun testUnusedSourceSetsReport() = with(Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")) { + setupWorkingDir() + + gradleBuildScript().appendText("\nkotlin { sourceSets { foo { } } }") + + build { + assertSuccessful() + assertContains(UnusedSourceSetsChecker.WARNING_PREFIX_ONE, UnusedSourceSetsChecker.WARNING_INTRO) + } + + gradleBuildScript().appendText("\nkotlin { sourceSets { bar { dependsOn foo } } }") + + build { + assertSuccessful() + assertContains(UnusedSourceSetsChecker.WARNING_PREFIX_MANY, UnusedSourceSetsChecker.WARNING_INTRO) + } + + gradleBuildScript().appendText("\nkotlin { sourceSets { jvm6Main { dependsOn bar } } }") + + build { + assertSuccessful() + assertNotContains( + UnusedSourceSetsChecker.WARNING_PREFIX_ONE, + UnusedSourceSetsChecker.WARNING_PREFIX_MANY, + UnusedSourceSetsChecker.WARNING_INTRO + ) + } + } } \ 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 074bbaeecd5..8556d5af0a7 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 @@ -94,6 +94,8 @@ class KotlinMultiplatformPlugin( // propagate compiler plugin options to the source set language settings setupCompilerPluginOptions(project) + + UnusedSourceSetsChecker.checkSourceSets(project) } private fun setupCompilerPluginOptions(project: Project) { @@ -104,14 +106,7 @@ class KotlinMultiplatformPlugin( .compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) val primaryCompilationsBySourceSet by lazy { // don't evaluate eagerly: Android targets are not created at this point - val allCompilationsForSourceSets = - project.multiplatformExtension!!.targets - .flatMap { target -> - target.compilations.flatMap { compilation -> - compilation.allKotlinSourceSets.map { it to compilation } - } - } - .groupBy(keySelector = { (sourceSet, _) -> sourceSet }, valueTransform = { (_, compilation) -> compilation }) + val allCompilationsForSourceSets = compilationsBySourceSet(project) allCompilationsForSourceSets.mapValues { (_, compilations) -> // choose one primary compilation when (compilations.size) { @@ -305,4 +300,15 @@ class KotlinMultiplatformPlugin( } } -internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}" \ No newline at end of file +internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}" + +internal fun compilationsBySourceSet(project: Project): Map> = + HashMap>().also { result -> + for (target in project.multiplatformExtension!!.targets) { + for (compilation in target.compilations) { + for (sourceSet in compilation.allKotlinSourceSets) { + result.getOrPut(sourceSet) { mutableSetOf() }.add(compilation) + } + } + } + } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt new file mode 100644 index 00000000000..9504b222b3c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet + +object UnusedSourceSetsChecker { + const val WARNING_PREFIX_ONE = + "The Kotlin source set" + + const val WARNING_PREFIX_MANY = + "The following Kotlin source sets were" + + const val WARNING_INTRO = "configured but not added to any Kotlin compilation" + + const val WARNING_BOTTOM_LINE = + "You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.\n" + + "See https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#connecting-source-sets" + + private fun reportUnusedSourceSets(project: Project, sourceSets: Set) { + require(sourceSets.isNotEmpty()) + val message = when (sourceSets.size) { + 1 -> "$WARNING_PREFIX_ONE ${sourceSets.single().name} was $WARNING_INTRO. $WARNING_BOTTOM_LINE" + else -> { + val list = sourceSets.joinToString("\n", "\n", "\n") { " * ${it.name}" } + "$WARNING_PREFIX_MANY $WARNING_INTRO:$list$WARNING_BOTTOM_LINE" + } + } + project.logger.warn("\n" + message) // make sure the message stands out + } + + fun checkSourceSets(project: Project) { + project.afterEvaluate { + val compilationsBySourceSet = compilationsBySourceSet(project) + val unusedSourceSets = project.kotlinExtension.sourceSets.filter { + compilationsBySourceSet[it]?.isEmpty() ?: true + } + if (unusedSourceSets.isNotEmpty()) { + reportUnusedSourceSets(project, unusedSourceSets.toSet()) + } + } + } +} \ No newline at end of file