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) 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' implementation 'com.google.guava:guava:12.0'
deployImplementation 'com.google.guava:guava:12.0' deployImplementation 'com.google.guava:guava:12.0'
testImplementation 'org.testng:testng:6.8' testImplementation 'org.testng:testng:6.8'
implementation "org.jetbrains.kotlin:kotlin-stdlib"
deployImplementation "org.jetbrains.kotlin:kotlin-stdlib"
} }
test { test {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.plugin package org.jetbrains.kotlin.gradle.plugin
import com.android.build.gradle.* import com.android.build.gradle.*
import com.android.build.gradle.BasePlugin
import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.SourceKind 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.FileCollection
import org.gradle.api.file.SourceDirectorySet import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.api.plugins.InvalidPluginException import org.gradle.api.plugins.*
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.plugins.MavenPluginConvention
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPom import org.gradle.api.publish.maven.MavenPom
@@ -689,18 +687,18 @@ internal open class KotlinAndroidPlugin(
override fun apply(project: Project) { override fun apply(project: Project) {
checkGradleCompatibility() checkGradleCompatibility()
val androidTarget = KotlinAndroidTarget("", project) project.dynamicallyApplyWhenAndroidPluginIsApplied(
(project.kotlinExtension as KotlinAndroidProjectExtension).target = androidTarget {
KotlinAndroidTarget("", project).also {
applyToTarget(androidTarget) (project.kotlinExtension as KotlinAndroidProjectExtension).target = it
}
applyUserDefinedAttributes(androidTarget) }
) { androidTarget ->
customizeKotlinDependencies(project) applyUserDefinedAttributes(androidTarget)
customizeKotlinDependencies(project)
registry.register(KotlinModelBuilder(project.getKotlinPluginVersion(), androidTarget)) registry.register(KotlinModelBuilder(project.getKotlinPluginVersion(), androidTarget))
project.whenEvaluated { project.components.addAll(androidTarget.components) }
project.whenEvaluated { project.components.addAll(androidTarget.components) } }
} }
companion object { companion object {
@@ -721,8 +719,32 @@ internal open class KotlinAndroidPlugin(
return Android25ProjectHandler(kotlinTools) return Android25ProjectHandler(kotlinTools)
} }
fun applyToTarget(kotlinTarget: KotlinAndroidTarget) { internal fun Project.dynamicallyApplyWhenAndroidPluginIsApplied(
androidTargetHandler().configureTarget(kotlinTarget) 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 kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions) ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
val plugin by lazy { val plugin = androidPluginIds
androidPluginIds.asSequence() .asSequence()
.mapNotNull { project.plugins.findPlugin(it) as? BasePlugin<*> } .mapNotNull { project.plugins.findPlugin(it) as? BasePlugin<*> }
.firstOrNull() .firstOrNull()
?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " + ?: throw InvalidPluginException("'kotlin-android' expects one of the Android Gradle " +
"plugins to be applied to the project:\n\t" + "plugins to be applied to the project:\n\t" +
androidPluginIds.joinToString("\n\t") { "* $it" }) androidPluginIds.joinToString("\n\t") { "* $it" })
}
project.forEachVariant { variant -> project.forEachVariant { variant ->
val variantName = getVariantName(variant) val variantName = getVariantName(variant)
@@ -938,7 +959,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
it.parentKotlinOptionsImpl.set(rootKotlinOptions) it.parentKotlinOptionsImpl.set(rootKotlinOptions)
// store kotlin classes in separate directory. They will serve as class-path to java compiler // 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." it.description = "Compiles the $variantDataName kotlin."
} }
@@ -7,7 +7,7 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project 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 import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
class KotlinAndroidTargetPreset( class KotlinAndroidTargetPreset(
@@ -22,7 +22,7 @@ class KotlinAndroidTargetPreset(
preset = this@KotlinAndroidTargetPreset preset = this@KotlinAndroidTargetPreset
} }
KotlinAndroidPlugin.applyToTarget(result) project.dynamicallyApplyWhenAndroidPluginIsApplied({ result })
return result return result
} }
@@ -6,6 +6,14 @@
package org.jetbrains.kotlin.gradle.utils package org.jetbrains.kotlin.gradle.utils
internal val androidPluginIds = listOf( internal val androidPluginIds = listOf(
"android", "com.android.application", "android-library", "com.android.library", "com.android.application",
"com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp" "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"
) )