From e8004a40342b384d22650a375db98e23ebe3ddb3 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Wed, 26 Jul 2023 15:18:35 +0200 Subject: [PATCH] [Gradle] Implement AndroidGradlePluginIsMissing diagnostics ^KT-60693 Verification Pending --- .../diagnostics/KotlinToolingDiagnostics.kt | 21 ++++++++++++ .../android/KotlinAndroidTargetPreset.kt | 32 ++++++++++--------- .../AndroidGradlePluginIsMissingTest.kt | 30 +++++++++++++++++ ...otlinSourceSetTreeDependsOnMismatchTest.kt | 2 +- .../AndroidGradlePluginIsMissing.txt | 13 ++++++++ 5 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/AndroidGradlePluginIsMissingTest.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/AndroidGradlePluginIsMissing.txt diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index 8515734162e..58768262539 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -237,6 +237,27 @@ object KotlinToolingDiagnostics { ) } + object AndroidGradlePluginIsMissing : ToolingDiagnosticFactory(FATAL) { + operator fun invoke(trace: Throwable? = null) = build( + """ + The Android target requires a 'Android Gradle Plugin' to be applied to the project. + + plugins { + kotlin("multiplatform") + + /* Android Gradle Plugin missing */ + id("com.android.library") /* <- Android Gradle Plugin for libraries */ + id("com.android.application") <* <- Android Gradle Plugin for applications */ + } + + kotlin { + androidTarget() /* <- requires Android Gradle Plugin to be applied */ + } + """.trimIndent(), + throwable = trace + ) + } + object NoKotlinTargetsDeclared : ToolingDiagnosticFactory(ERROR) { operator fun invoke(projectName: String, projectPath: String) = build( """ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt index 9258f1fc8d7..32398c8625a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt @@ -9,6 +9,10 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPlugin.Companion.dynamicallyApplyWhenAndroidPluginIsApplied import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.AndroidGradlePluginIsMissing +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic +import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull import javax.inject.Inject @@ -19,25 +23,23 @@ abstract class KotlinAndroidTargetPreset @Inject constructor( override fun getName(): String = PRESET_NAME override fun createTarget(name: String): KotlinAndroidTarget { - val result = project.objects.newInstance( - KotlinAndroidTarget::class.java, - name, - project - ).apply { + + /* + Android Gradle Plugin is required: + Creating target will fail with Linkage Error instead + + Could not create an instance of type org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget. + > Could not generate a decorated class for type KotlinAndroidTarget. + > com/android/build/gradle/api/BaseVariant + */ + project.findAppliedAndroidPluginIdOrNull() ?: project.reportDiagnostic(AndroidGradlePluginIsMissing(Throwable())) + + return project.objects.newInstance(KotlinAndroidTarget::class.java, name, project).apply { preset = this@KotlinAndroidTargetPreset - targetUnderConstruction = this + project.dynamicallyApplyWhenAndroidPluginIsApplied({ this }) } - - project.dynamicallyApplyWhenAndroidPluginIsApplied({ result }) - - targetUnderConstruction = null - - return result } - /** This is a way to check if there's an Android target being configured now despite it not being added to the `kotlin.targets` yet */ - internal var targetUnderConstruction: KotlinAndroidTarget? = null - companion object { const val PRESET_NAME = "android" } diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/AndroidGradlePluginIsMissingTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/AndroidGradlePluginIsMissingTest.kt new file mode 100644 index 00000000000..5c8b05fd0ec --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/AndroidGradlePluginIsMissingTest.kt @@ -0,0 +1,30 @@ +/* + * 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. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.kotlin.gradle.unitTests.diagnosticsTests + +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.util.* +import kotlin.test.Test +import kotlin.test.assertFails + +class AndroidGradlePluginIsMissingTest { + @Test + fun `test - android plugin is not applied`() { + val project = buildProjectWithMPP() + assertFails { project.multiplatformExtension.androidTarget() } + project.checkDiagnostics("AndroidGradlePluginIsMissing") + } + + @Test + fun `test - android application plugin is applied`() { + val project = buildProjectWithMPP() + project.androidApplication { compileSdk = 33 } + project.multiplatformExtension.androidTarget() + project.assertNoDiagnostics() + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/KotlinSourceSetTreeDependsOnMismatchTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/KotlinSourceSetTreeDependsOnMismatchTest.kt index 9518176791a..9f34451baff 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/KotlinSourceSetTreeDependsOnMismatchTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/KotlinSourceSetTreeDependsOnMismatchTest.kt @@ -18,9 +18,9 @@ class KotlinSourceSetTreeDependsOnMismatchTest { private fun checkDiagnostics(configure: KotlinMultiplatformExtension.() -> Unit): List { val project = buildProjectWithMPP { kotlin { + project.androidApplication { compileSdk = 32 } applyDefaultHierarchyTemplate() androidTarget() - project.androidApplication { compileSdk = 32 } iosX64(); iosArm64(); iosSimulatorArm64() macosX64(); macosArm64() diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/AndroidGradlePluginIsMissing.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/AndroidGradlePluginIsMissing.txt new file mode 100644 index 00000000000..3060f8af0f7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/AndroidGradlePluginIsMissing.txt @@ -0,0 +1,13 @@ +[AndroidGradlePluginIsMissing | FATAL] The Android target requires a 'Android Gradle Plugin' to be applied to the project. + +plugins { + kotlin("multiplatform") + + /* Android Gradle Plugin missing */ + id("com.android.library") /* <- Android Gradle Plugin for libraries */ + id("com.android.application") <* <- Android Gradle Plugin for applications */ +} + +kotlin { + androidTarget() /* <- requires Android Gradle Plugin to be applied */ +}