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
This commit is contained in:
Yahor Berdnikau
2021-07-15 15:52:35 +02:00
committed by teamcityserver
parent f3116cb64a
commit caa6b630ab
5 changed files with 95 additions and 33 deletions
@@ -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
}
}
}
@@ -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 {
@@ -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."
}
@@ -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
}
@@ -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"
)