Create and configure Android compilations earlier (KT-29964)

As Android variants are created after the project is evaluated,
the build script and plugins might have added some item handlers to the
domain object containers which are yet to be filled.

To ensure that those handlers see properly configured compilations and
their tasks, we need to add a compilations to the container only after
it has been properly configured.

Also, the `forEachVariant` handler is already executed in
`afterEvaluate`, and there seems to be no need to wrap our
code working with variants in `afterEvaluate`.

Issue #KT-29964 Fixed
This commit is contained in:
Sergey Igushkin
2019-02-26 18:27:17 +03:00
parent e06514c945
commit 6fa610156e
4 changed files with 43 additions and 23 deletions
@@ -21,6 +21,8 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio
build("assemble", "compileDebugUnitTestJavaWithJavac", "printCompilerPluginOptions") { build("assemble", "compileDebugUnitTestJavaWithJavac", "printCompilerPluginOptions") {
assertSuccessful() assertSuccessful()
assertContains("KT-29964 OK") // Output from lib/build.gradle
assertTasksExecuted( assertTasksExecuted(
":lib:compileDebugKotlinAndroidLib", ":lib:compileDebugKotlinAndroidLib",
":lib:compileReleaseKotlinAndroidLib", ":lib:compileReleaseKotlinAndroidLib",
@@ -36,6 +36,21 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
} }
kotlin.targets.all {
compilations.all {
// KT-29964: check that Android compilations can be configured with an `all { ... }` handler:
kotlinOptions {
verbose = true
}
compileKotlinTask.doFirst {
if (!compileKotlinTask.kotlinOptions.verbose) {
throw new AssertionError("kotlinOptions were not configured properly")
}
println "KT-29964 OK"
}
}
}
kotlin { kotlin {
sourceSets { sourceSets {
jvmLibMain { jvmLibMain {
@@ -727,44 +727,48 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
kotlinOptions.noJdk = true kotlinOptions.noJdk = true
ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions) ext.addExtension(KOTLIN_OPTIONS_DSL_NAME, kotlinOptions)
project.afterEvaluate { project -> val androidPluginIds = listOf(
forEachVariant(project) { variant -> "android", "com.android.application", "android-library", "com.android.library",
val variantName = getVariantName(variant) "com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp"
val compilation = kotlinAndroidTarget.compilations.create(variantName) )
setUpDependencyResolution(variant, compilation)
}
val androidPluginIds = listOf( val plugin by lazy {
"android", "com.android.application", "android-library", "com.android.library", androidPluginIds.asSequence()
"com.android.test", "com.android.feature", "com.android.dynamic-feature", "com.android.instantapp"
)
val plugin = androidPluginIds.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" })
}
checkAndroidAnnotationProcessorDependencyUsage(project) forEachVariant(project) { variant ->
val variantName = getVariantName(variant)
forEachVariant(project) { // Create the compilation and configure it first, then add to the compilations container. As this code is executed
processVariant( // in afterEvaluate, a user's build script might have already attached item handlers to the compilations container, and those
it, kotlinAndroidTarget, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider // handlers might break when fired on a compilation that is not yet properly configured (e.g. KT-29964):
) kotlinAndroidTarget.compilationFactory.create(variantName).let { compilation ->
setUpDependencyResolution(variant, compilation)
processVariant(variant, compilation, project, ext, plugin, kotlinOptions, kotlinConfigurationTools.kotlinTasksProvider)
@Suppress("UNCHECKED_CAST")
(kotlinAndroidTarget.compilations as NamedDomainObjectCollection<in KotlinJvmAndroidCompilation>).add(compilation)
} }
val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion) val subpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinConfigurationTools.kotlinPluginVersion)
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
applySubplugins(project, compilation, variant, subpluginEnvironment)
}
forEachVariant(project) { variant -> project.whenEvaluated {
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant)) checkAndroidAnnotationProcessorDependencyUsage(project)
applySubplugins(project, compilation, variant, subpluginEnvironment)
}
} }
} }
private fun processVariant( private fun processVariant(
variantData: V, variantData: V,
target: KotlinAndroidTarget, compilation: KotlinJvmAndroidCompilation,
project: Project, project: Project,
androidExt: BaseExtension, androidExt: BaseExtension,
androidPlugin: BasePlugin, androidPlugin: BasePlugin,
@@ -782,7 +786,6 @@ abstract class AbstractAndroidProjectHandler<V>(private val kotlinConfigurationT
return return
} }
val compilation = target.compilations.getByName(variantDataName)
val defaultSourceSet = project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName) val defaultSourceSet = project.kotlinExtension.sourceSets.maybeCreate(compilation.defaultSourceSetName)
val kotlinTaskName = compilation.compileKotlinTaskName val kotlinTaskName = compilation.compileKotlinTaskName
@@ -26,7 +26,7 @@ open class KotlinAndroidTarget(
override val platformType: KotlinPlatformType override val platformType: KotlinPlatformType
get() = KotlinPlatformType.androidJvm get() = KotlinPlatformType.androidJvm
private val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this) internal val compilationFactory = KotlinJvmAndroidCompilationFactory(project, this)
override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> = override val compilations: NamedDomainObjectContainer<out KotlinJvmAndroidCompilation> =
project.container(compilationFactory.itemClass, compilationFactory) project.container(compilationFactory.itemClass, compilationFactory)