[Gradle] Migrate warning about missing Android-target to new infra

This commit is contained in:
Dmitry Savvinov
2023-04-19 17:07:37 +02:00
parent 84fb045d8a
commit dcb51e64df
7 changed files with 114 additions and 89 deletions
@@ -38,37 +38,6 @@ internal fun Project.runMissingKotlinTargetsProjectConfigurationHealthCheck() =
}
}
internal fun Project.runMissingAndroidTargetProjectConfigurationHealthCheck(
warningLogger: (warningMessage: String) -> Unit = project.logger::warn
) = project.runProjectConfigurationHealthCheck check@{
if (project.kotlinPropertiesProvider.ignoreAbsentAndroidMultiplatformTarget) {
return@check
}
val androidPluginId = findAppliedAndroidPluginIdOrNull() ?: return@check
if (findAndroidTarget() != null) return@check
warningLogger(
"""
Missing 'androidTarget()' Kotlin target in multiplatform project ${project.name} (${project.path})'.
The Android Gradle plugin was applied without creating a corresponding 'androidTarget()' Kotlin Target:
```
plugins {
id("$androidPluginId")
kotlin("multiplatform")
}
kotlin {
androidTarget() // <-- please register this Android target
}
```
""".trimIndent()
)
}
internal fun Project.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck(
warningLogger: (warningMessage: String) -> Unit = project.logger::warn
) {
@@ -387,7 +387,6 @@ abstract class AbstractKotlinMultiplatformPluginWrapper : KotlinBasePluginWrappe
get() = KotlinMultiplatformExtension::class
override fun whenBuildEvaluated(project: Project) {
project.runMissingAndroidTargetProjectConfigurationHealthCheck()
project.runMissingKotlinTargetsProjectConfigurationHealthCheck()
project.runDisabledCInteropCommonizationOnHmppProjectConfigurationHealthCheck()
}
@@ -99,7 +99,8 @@ internal interface KotlinGradleProjectChecker {
MissingNativeStdlibChecker,
UnusedSourceSetsChecker,
AndroidSourceSetLayoutV1SourceSetsNotFoundChecker,
TargetsWithAmbiguousConsumableConfigurationsChecker
TargetsWithAmbiguousConsumableConfigurationsChecker,
AndroidPluginWithoutAndroidTargetChecker
)
}
}
@@ -220,4 +220,24 @@ object KotlinToolingDiagnostics {
""".trimIndent()
)
}
object AndroidTargetIsMissing : ToolingDiagnosticFactory(WARNING) {
operator fun invoke(projectName: String, projectPath: String, androidPluginId: String) = build(
"""
Missing 'androidTarget()' Kotlin target in multiplatform project '$projectName ($projectPath)'.
The Android Gradle plugin was applied without creating a corresponding 'android()' Kotlin Target:
```
plugins {
id("$androidPluginId")
kotlin("multiplatform")
}
kotlin {
androidTarget() // <-- please register this Android target
}
```
""".trimIndent()
)
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 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.diagnostics.checkers
import org.jetbrains.kotlin.gradle.plugin.configurationResult
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectChecker
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinGradleProjectCheckerContext
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnosticsCollector
import org.jetbrains.kotlin.gradle.targets.android.findAndroidTarget
import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull
internal object AndroidPluginWithoutAndroidTargetChecker : KotlinGradleProjectChecker {
override suspend fun KotlinGradleProjectCheckerContext.runChecks(collector: KotlinToolingDiagnosticsCollector) {
project.configurationResult.await()
if (multiplatformExtension == null || kotlinPropertiesProvider.ignoreAbsentAndroidMultiplatformTarget) return
val androidPluginId = project.findAppliedAndroidPluginIdOrNull() ?: return
if (project.findAndroidTarget() != null) return
collector.reportOncePerGradleProject(
project,
KotlinToolingDiagnostics.AndroidTargetIsMissing(project.name, project.path, androidPluginId)
)
}
}
@@ -8,84 +8,79 @@
package org.jetbrains.kotlin.gradle.regressionTests
import com.android.build.gradle.LibraryExtension
import org.jetbrains.kotlin.gradle.util.MultiplatformExtensionTest
import org.jetbrains.kotlin.gradle.plugin.runMissingAndroidTargetProjectConfigurationHealthCheck
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.targets.android.findAndroidTarget
import org.jetbrains.kotlin.gradle.util.propertiesExtension
import org.jetbrains.kotlin.gradle.util.*
import org.jetbrains.kotlin.gradle.util.assertNoDiagnostics
import org.junit.Test
import kotlin.test.*
import kotlin.test.assertNull
import kotlin.test.assertSame
class KT41641AbsentAndroidTarget : MultiplatformExtensionTest() {
@Test
fun `test android plugin without android target`() {
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("android-library")
/* Arbitrary minimal Android setup */
val android = project.extensions.getByName("android") as LibraryExtension
android.compileSdk = 31
kotlin.jvm()
defaultSetupWithoutAndroidTarget()
/* Previously failed with 'Collection is empty.' */
assertNull(project.findAndroidTarget())
assertSame(kotlin.androidTarget(), project.findAndroidTarget())
}
@Test
fun `test runMissingAndroidTargetProjectConfigurationHealthCheck`() {
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("android-library")
/* Arbitrary minimal Android setup */
val android = project.extensions.getByName("android") as LibraryExtension
android.compileSdk = 31
kotlin.jvm()
fun `test runMissingAndroidTargetProjectConfigurationHealthCheck - warning`() {
defaultSetupWithoutAndroidTarget()
// Missing android target -> expect warning message
var warningMessage: String? = null
project.runMissingAndroidTargetProjectConfigurationHealthCheck(warningLogger = { warningMessage = it })
assertNotNull(warningMessage, "Expected warning message to be logged")
// Present android target -> expect no warning message anymore
kotlin.androidTarget()
project.runMissingAndroidTargetProjectConfigurationHealthCheck(warningLogger = {
fail("Expected no warning message to be logged. Received: $it")
})
project.evaluate()
project.assertContainsDiagnostic(KotlinToolingDiagnostics.AndroidTargetIsMissing)
}
@Test
fun `test kotlin-mpp-absentAndroidTarget-nowarn flag`() {
fun `test runMissingAndroidTargetProjectConfigurationHealthCheck - no warning`() {
defaultSetupWithoutAndroidTarget()
kotlin.androidTarget()
// Present android target -> expect no warning message anymore
project.evaluate()
project.assertNoDiagnostics()
}
@Test
fun `test kotlin-mpp-absentAndroidTarget-nowarn flag set to true`() {
defaultSetupWithoutAndroidTarget()
project.propertiesExtension["kotlin.mpp.absentAndroidTarget.nowarn"] = "true"
project.evaluate()
project.assertNoDiagnostics()
}
@Test
fun `test kotlin-mpp-absentAndroidTarget-nowarn flag set to false`() {
defaultSetupWithoutAndroidTarget()
project.propertiesExtension["kotlin.mpp.absentAndroidTarget.nowarn"] = null
project.evaluate()
project.assertContainsDiagnostic(KotlinToolingDiagnostics.AndroidTargetIsMissing)
}
@Test
fun `test kotlin-mpp-absentAndroidTarget-nowarn flag set to null`() {
defaultSetupWithoutAndroidTarget()
project.propertiesExtension["kotlin.mpp.absentAndroidTarget.nowarn"] = null
project.evaluate()
project.assertContainsDiagnostic(KotlinToolingDiagnostics.AndroidTargetIsMissing)
}
private fun defaultSetupWithoutAndroidTarget() {
project.plugins.apply("kotlin-multiplatform")
project.plugins.apply("android-library")
/* Arbitrary minimal Android setup */
val android = project.extensions.getByName("android") as LibraryExtension
android.compileSdk = 31
kotlin.jvm()
project.runMissingAndroidTargetProjectConfigurationHealthCheck(warningLogger = {
fail("Expected no warning message to be logged. Received $it")
})
// Test when nowarn is set to false
run {
var warningMessage: String? = null
project.propertiesExtension["kotlin.mpp.absentAndroidTarget.nowarn"] = "false"
project.runMissingAndroidTargetProjectConfigurationHealthCheck(warningLogger = { warningMessage = it })
assertNotNull(warningMessage, "Expected warning message to be logged")
}
// Test when nowarn is set to null
run {
var warningMessage: String? = null
project.propertiesExtension["kotlin.mpp.absentAndroidTarget.nowarn"] = null
project.runMissingAndroidTargetProjectConfigurationHealthCheck(warningLogger = { warningMessage = it })
assertNotNull(warningMessage, "Expected warning message to be logged")
}
project.setMultiplatformAndroidSourceSetLayoutVersion(2)
}
}
@@ -68,15 +68,29 @@ internal fun Project.assertNoDiagnostics() {
)
}
/**
* Checks that diagnostic with [factory.id] is reported. The exact parameters (if any)
* are ignored. If you need to compare the parameters, refer to the overload accepting [ToolingDiagnostic]
*/
internal fun Project.assertContainsDiagnostic(factory: ToolingDiagnosticFactory) {
kotlinToolingDiagnosticsCollector.getDiagnosticsForProject(this).assertContainsDiagnostic(factory)
}
internal fun Project.assertContainsDiagnostic(diagnostic: ToolingDiagnostic) {
kotlinToolingDiagnosticsCollector.getDiagnosticsForProject(this).assertContainsDiagnostic(diagnostic)
}
internal fun Collection<ToolingDiagnostic>.assertContainsDiagnostic(factory: ToolingDiagnosticFactory) {
if (!any { it.id == factory.id }) failDiagnosticNotFound("diagnostic with id ${factory.id} ", this)
}
internal fun Collection<ToolingDiagnostic>.assertContainsDiagnostic(diagnostic: ToolingDiagnostic) {
if (diagnostic !in this) failDiagnosticNotFound("diagnostic $diagnostic\n", this)
}
private fun failDiagnosticNotFound(diagnosticDescription: String, notFoundInCollection: Collection<ToolingDiagnostic>) {
fun Any.withIndent() = this.toString().prependIndent(" ")
if (diagnostic !in this) {
fail("Missing diagnostic\n${diagnostic.withIndent()} \nin:\n${this.render().withIndent()}")
}
fail("Missing ${diagnosticDescription}in:\n${notFoundInCollection.render().withIndent()}")
}
internal fun Project.assertNoDiagnostics(id: String) {
@@ -100,4 +114,3 @@ private val expectedDiagnosticsRoot: Path
get() = resourcesRoot.resolve("expectedDiagnostics")
private fun expectedDiagnosticsFile(projectName: String): File = expectedDiagnosticsRoot.resolve("$projectName.txt").toFile()