From a218ad92be6b4d8a5f4ffbcca68466a80740de63 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 23 Jun 2022 16:51:58 +0200 Subject: [PATCH] Fix false-positive on checking unused sourcesets in MPP+Android project AGP could create 'AndroidSourceSet' that does not participate in any compilation. For this 'AndroidSourceSet's plugin should mark corresponding KotlinSourceSet as orphan and exclude from check. Unfortunately filtering them on KotlinSourceSet does not work, as AGP adds sourcesets into variants lazily. And MPP requires all SourceSets to be created eagerly. This change excludes all KotlinSourceSets created for Android compilation from such check. ^KT-48436 Fixed --- .../kotlin/gradle/NewMultiplatformIT.kt | 20 ++++++++++++++++++- .../kotlin/gradle/plugin/gradleUtils.kt | 4 ++++ .../plugin/mpp/UnusedSourceSetsChecker.kt | 11 +++++++--- .../mpp/syncKotlinAndAndroidSourceSets.kt | 11 +++++++++- 4 files changed, 41 insertions(+), 5 deletions(-) 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 217218029de..58a3817bf49 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 @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.library.KLIB_PROPERTY_SHORT_NAME import org.jetbrains.kotlin.library.KLIB_PROPERTY_UNIQUE_NAME import org.junit.Assert -import org.junit.Ignore import org.junit.Test import java.util.* import java.util.jar.JarFile @@ -1624,6 +1623,25 @@ class NewMultiplatformIT : BaseGradleIT() { } } + // https://youtrack.jetbrains.com/issue/KT-48436 + @Test + fun testUnusedSourceSetsReportAndroid() = with(Project("new-mpp-android", gradleVersion)) { + setupWorkingDir() + + build( + "assembleDebug", + // https://issuetracker.google.com/issues/152187160 + options = defaultBuildOptions().copy(androidGradlePluginVersion = AGPVersion.v4_2_0) + ) { + assertSuccessful() + assertNotContains( + UnusedSourceSetsChecker.WARNING_PREFIX_ONE, + UnusedSourceSetsChecker.WARNING_PREFIX_MANY, + UnusedSourceSetsChecker.WARNING_INTRO + ) + } + } + @Test fun testIncrementalCompilation() = with(Project("new-mpp-jvm-js-ic", gradleVersion)) { build("build") { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt index f96a960f604..c8af17ec882 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/gradleUtils.kt @@ -20,6 +20,7 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.internal.HasConvention import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.ExtraPropertiesExtension @Deprecated("Conventions are deprecated in Gradle") internal inline fun Any.addConvention(name: String, plugin: T) { @@ -42,3 +43,6 @@ internal inline fun Any.addExtension(name: String, extension: internal inline fun Any.getExtension(name: String): T? = (this as ExtensionAware).extensions.getByName(name) as T? + +inline val Any.extraProperties: ExtraPropertiesExtension + get() = (this as ExtensionAware).extensions.extraProperties diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt index 15a2cb5c1e3..9162e97e5f9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UnusedSourceSetsChecker.kt @@ -8,6 +8,8 @@ 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 +import org.jetbrains.kotlin.gradle.plugin.extraProperties +import org.jetbrains.kotlin.gradle.plugin.whenEvaluated object UnusedSourceSetsChecker { const val WARNING_PREFIX_ONE = @@ -35,11 +37,14 @@ object UnusedSourceSetsChecker { } fun checkSourceSets(project: Project) { - // TODO once Android compilations are configured eagerly, move this to afterEvaluate { ... } instead of taskGraph.whenReady { ... } - project.gradle.taskGraph.whenReady { _ -> + project.whenEvaluated { val compilationsBySourceSet = CompilationSourceSetUtil.compilationsBySourceSets(project) val unusedSourceSets = project.kotlinExtension.sourceSets.filter { - compilationsBySourceSet[it]?.isEmpty() ?: true + // Ignoring Android source sets + val isAndroidSourceSet = it.extraProperties.has(IS_ANDROID_SOURCE_SET) && + (it.extraProperties[IS_ANDROID_SOURCE_SET] as Boolean) + + !isAndroidSourceSet && compilationsBySourceSet[it]?.isEmpty() ?: true } if (unusedSourceSets.isNotEmpty()) { reportUnusedSourceSets(project, unusedSourceSets.toSet()) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt index 6781d2ffda7..3e3f59ba643 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/syncKotlinAndAndroidSourceSets.kt @@ -16,13 +16,22 @@ import org.jetbrains.kotlin.gradle.plugin.AbstractAndroidProjectHandler.Companio import org.jetbrains.kotlin.gradle.plugin.addConvention import org.jetbrains.kotlin.gradle.plugin.sources.KotlinSourceSetFactory.Companion.defaultSourceFolder +internal const val IS_ANDROID_SOURCE_SET = "android-source-set" + internal fun syncKotlinAndAndroidSourceSets(target: KotlinAndroidTarget) { val project = target.project val android = project.extensions.getByName("android") as BaseExtension + val kotlinExtension = project.kotlinExtension + val createdKotlinSourceSets = mutableSetOf() android.sourceSets.all { androidSourceSet -> val kotlinSourceSetName = kotlinSourceSetNameForAndroidSourceSet(target, androidSourceSet.name) - val kotlinSourceSet = project.kotlinExtension.sourceSets.maybeCreate(kotlinSourceSetName) + createdKotlinSourceSets.add(kotlinSourceSetName) + val kotlinSourceSet = kotlinExtension.sourceSets.maybeCreate(kotlinSourceSetName) + + // Marking KotlinSource set is created for Android compilation to ignore it in UnusedSourceSetChecker + kotlinSourceSet.extraProperties.set(IS_ANDROID_SOURCE_SET, true) + androidSourceSet.addKotlinSources(kotlinSourceSet) createDefaultDependsOnEdges(target, kotlinSourceSet, androidSourceSet)