Check the MPP model for unused Kotlin source sets, warn when found

Issue #KT-26963 Fixed
This commit is contained in:
Sergey Igushkin
2018-10-17 20:29:15 +03:00
parent 44d9ff6c71
commit 91b435b4bc
3 changed files with 94 additions and 9 deletions
@@ -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
)
}
}
}
@@ -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()}"
internal val KotlinTarget.defaultArtifactId get() = "${project.name}-${name.toLowerCase()}"
internal fun compilationsBySourceSet(project: Project): Map<KotlinSourceSet, Set<KotlinCompilation>> =
HashMap<KotlinSourceSet, MutableSet<KotlinCompilation>>().also { result ->
for (target in project.multiplatformExtension!!.targets) {
for (compilation in target.compilations) {
for (sourceSet in compilation.allKotlinSourceSets) {
result.getOrPut(sourceSet) { mutableSetOf() }.add(compilation)
}
}
}
}
@@ -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<KotlinSourceSet>) {
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())
}
}
}
}