From caa6b630ab4e6a2f83ddfbd22674ca1bb7bdeca5 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 15 Jul 2021 15:52:35 +0200 Subject: [PATCH] Apply 'kotlin-android' plugin dynamically. Only apply when one of android plugins are also applied to the project. If none of AGP plugins are applied and 'kotlin-android' is - exception in 'afterEvaluate {..}' will be thrown. ^KT-46626 Fixed --- .../AbstractKotlinAndroidGradleTests.kt | 35 +++++++++ .../testProject/simpleProject/build.gradle | 2 - .../kotlin/gradle/plugin/KotlinPlugin.kt | 75 ++++++++++++------- .../android/KotlinAndroidTargetPreset.kt | 4 +- .../kotlin/gradle/utils/androidPluginIds.kt | 12 ++- 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index b207e3c7631..2ca9fa46b78 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -953,4 +953,39 @@ fun getSomething() = 10 assertContainsRegex(kotlinJvmTarget16Regex) } } + + @Test + fun shouldAllowToApplyPluginWhenAndroidPluginIsMissing() { + with(Project("simpleProject", minLogLevel = LogLevel.WARN)) { + setupWorkingDir() + + gradleBuildScript().modify { + it.lines().joinToString( + separator = "\n", + transform = jvmToAndroidModifier() + ) + } + gradleSettingsScript().modify { + it.lines().joinToString( + separator = "\n", + transform = jvmToAndroidModifier(true) + ) + } + + build("tasks") { + assertFailed() + assertContains("'kotlin-android' plugin requires one of the Android Gradle plugins.") + } + } + } + + private fun jvmToAndroidModifier( + appendKotlinVersion: Boolean = false + ): (String) -> CharSequence = { line -> + if (line.contains("id \"org.jetbrains.kotlin.jvm\"")) { + " id \"org.jetbrains.kotlin.android\"" + if (appendKotlinVersion) " version \"${'$'}kotlin_version\"" else "" + } else { + line + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle index e8765086e13..ffda2c07b45 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/simpleProject/build.gradle @@ -16,8 +16,6 @@ dependencies { implementation 'com.google.guava:guava:12.0' deployImplementation 'com.google.guava:guava:12.0' testImplementation 'org.testng:testng:6.8' - implementation "org.jetbrains.kotlin:kotlin-stdlib" - deployImplementation "org.jetbrains.kotlin:kotlin-stdlib" } test { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 01c8ccb439d..3af8ea9e04c 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.plugin import com.android.build.gradle.* +import com.android.build.gradle.BasePlugin import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.SourceKind @@ -18,10 +19,7 @@ import org.gradle.api.file.Directory import org.gradle.api.file.FileCollection import org.gradle.api.file.SourceDirectorySet import org.gradle.api.logging.Logging -import org.gradle.api.plugins.InvalidPluginException -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginConvention -import org.gradle.api.plugins.MavenPluginConvention +import org.gradle.api.plugins.* import org.gradle.api.provider.Provider import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPom @@ -689,18 +687,18 @@ internal open class KotlinAndroidPlugin( override fun apply(project: Project) { checkGradleCompatibility() - val androidTarget = KotlinAndroidTarget("", project) - (project.kotlinExtension as KotlinAndroidProjectExtension).target = androidTarget - - applyToTarget(androidTarget) - - applyUserDefinedAttributes(androidTarget) - - customizeKotlinDependencies(project) - - registry.register(KotlinModelBuilder(project.getKotlinPluginVersion(), androidTarget)) - - project.whenEvaluated { project.components.addAll(androidTarget.components) } + project.dynamicallyApplyWhenAndroidPluginIsApplied( + { + KotlinAndroidTarget("", project).also { + (project.kotlinExtension as KotlinAndroidProjectExtension).target = it + } + } + ) { androidTarget -> + applyUserDefinedAttributes(androidTarget) + customizeKotlinDependencies(project) + registry.register(KotlinModelBuilder(project.getKotlinPluginVersion(), androidTarget)) + project.whenEvaluated { project.components.addAll(androidTarget.components) } + } } companion object { @@ -721,8 +719,32 @@ internal open class KotlinAndroidPlugin( return Android25ProjectHandler(kotlinTools) } - fun applyToTarget(kotlinTarget: KotlinAndroidTarget) { - androidTargetHandler().configureTarget(kotlinTarget) + internal fun Project.dynamicallyApplyWhenAndroidPluginIsApplied( + kotlinAndroidTargetProvider: () -> KotlinAndroidTarget, + additionalConfiguration: (KotlinAndroidTarget) -> Unit = {} + ) { + var wasConfigured = false + + androidPluginIds.forEach { pluginId -> + plugins.withId(pluginId) { + wasConfigured = true + val target = kotlinAndroidTargetProvider() + androidTargetHandler().configureTarget(target) + additionalConfiguration(target) + } + } + + afterEvaluate { + if (!wasConfigured) { + throw GradleException( + """ + |'kotlin-android' plugin requires one of the Android Gradle plugins. + |Please apply one of the following plugins to '${project.path}' project: + |${androidPluginIds.joinToString(prefix = "- ", separator = "\n\t- ")} + """.trimMargin() + ) + } + } } } } @@ -778,14 +800,13 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool kotlinOptions.noJdk = true ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions) - val plugin by lazy { - androidPluginIds.asSequence() - .mapNotNull { project.plugins.findPlugin(it) as? BasePlugin<*> } - .firstOrNull() - ?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " + - "plugins to be applied to the project:\n\t" + - androidPluginIds.joinToString("\n\t") { "* $it" }) - } + val plugin = androidPluginIds + .asSequence() + .mapNotNull { project.plugins.findPlugin(it) as? BasePlugin<*> } + .firstOrNull() + ?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " + + "plugins to be applied to the project:\n\t" + + androidPluginIds.joinToString("\n\t") { "* $it" }) project.forEachVariant { variant -> val variantName = getVariantName(variant) @@ -938,7 +959,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool it.parentKotlinOptionsImpl.set(rootKotlinOptions) // store kotlin classes in separate directory. They will serve as class-path to java compiler - it.destinationDirectory.set(project.layout.buildDirectory.dir( "tmp/kotlin-classes/$variantDataName")) + it.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/$variantDataName")) it.description = "Compiles the $variantDataName kotlin." } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt index fcde11e7756..367c32d0937 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/android/KotlinAndroidTargetPreset.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPlugin +import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPlugin.Companion.dynamicallyApplyWhenAndroidPluginIsApplied import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset class KotlinAndroidTargetPreset( @@ -22,7 +22,7 @@ class KotlinAndroidTargetPreset( preset = this@KotlinAndroidTargetPreset } - KotlinAndroidPlugin.applyToTarget(result) + project.dynamicallyApplyWhenAndroidPluginIsApplied({ result }) return result } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/androidPluginIds.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/androidPluginIds.kt index 93902bd610b..acc8ebedc85 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/androidPluginIds.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/utils/androidPluginIds.kt @@ -6,6 +6,14 @@ package org.jetbrains.kotlin.gradle.utils internal val androidPluginIds = listOf( - "android", "com.android.application", "android-library", "com.android.library", - "com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp" + "com.android.application", + "com.android.library", + "com.android.dynamic-feature", + "com.android.asset-pack", + "com.android.asset-pack-bundle", + "com.android.lint", + "com.android.test", + // Deprecated android plugins + "com.android.instantapp", + "com.android.feature" )