[Gradle][KT-53342] Introduce 'invokeWhenCreated' API to configure objects when created
This aims to make it possible and sane to configure
Android SourceSets that get created in AGP's afterEvaluate phase.
SourceSets that contain flavor and buildTypes will not be available
right away. Using regular Gradle APIs like
```
sourceSets.matching { it.name == "..." }.configure {}
```
are not sufficient, since misconfigurations here will stay
unnoticed, whilst this 'invokeWhenCreated' API ensures
that the object is available at least before the buildscripts
'afterEvluate'
This commit is contained in:
committed by
Space
parent
9634e64322
commit
3b19cf5831
+15
-1
@@ -115,12 +115,26 @@ abstract class KotlinTopLevelExtension(internal val project: Project) : KotlinTo
|
||||
explicitApi = ExplicitApiMode.Warning
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to configure objects that are not yet created, or will be created in
|
||||
* 'afterEvaluate' (e.g. typically Android source sets containing flavors and buildTypes)
|
||||
*
|
||||
* Will fail project evaluation if the domain object is not created before 'afterEvaluate' listeners in the buildscript.
|
||||
*
|
||||
* @param configure: Called inline, if the value is already present. Called once the domain object is created.
|
||||
*/
|
||||
@ExperimentalKotlinGradlePluginApi
|
||||
fun <T : Named> NamedDomainObjectContainer<T>.invokeWhenCreated(name: String, configure: T.() -> Unit) {
|
||||
val invoked = AtomicBoolean(false)
|
||||
matching { it.name == name }.all { value -> if (!invoked.getAndSet(true)) value.configure() }
|
||||
project.whenEvaluated {
|
||||
if (!invoked.getAndSet(true)) named(name).configure(configure)
|
||||
if (!invoked.getAndSet(true)) {
|
||||
/*
|
||||
Expected to fail, since the listener was not called.
|
||||
Letting Gradle fail here will result in a more natural error message
|
||||
*/
|
||||
named(name).configure(configure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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
|
||||
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.UnknownDomainObjectException
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.tooling.core.withLinearClosure
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFails
|
||||
import kotlin.test.fail
|
||||
|
||||
class InvokeWhenCreatedTest {
|
||||
|
||||
data class Value(private val name: String) : Named {
|
||||
override fun getName(): String = name
|
||||
override fun toString(): String = name
|
||||
}
|
||||
|
||||
private val project = buildProjectWithJvm()
|
||||
private val container = project.objects.domainObjectContainer(Value::class.java) { name -> Value(name) }
|
||||
|
||||
@Test
|
||||
fun `test - values is already present - is called inline`() {
|
||||
var invocations = 0
|
||||
|
||||
container.create("x")
|
||||
project.kotlinExtension.apply {
|
||||
container.invokeWhenCreated("x") { invocations++; assertEquals("x", name) }
|
||||
assertEquals(1, invocations, "Expected 'invokeWhenCreated' to be called inline, once")
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
assertEquals(1, invocations, "Expected 'invokeWhenCreated' to be called only once")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - value will be created later`() {
|
||||
var invocations = 0
|
||||
|
||||
project.kotlinExtension.apply {
|
||||
container.invokeWhenCreated("x") { invocations++; assertEquals("x", name) }
|
||||
assertEquals(0, invocations, "Expected 'invokeWhenCreated' to be not called")
|
||||
}
|
||||
|
||||
container.create("y")
|
||||
assertEquals(0, invocations, "Expected 'invokeWhenCreated' to be not called")
|
||||
|
||||
container.create("x")
|
||||
assertEquals(1, invocations, "Expected 'invokeWhenCreated' to be called once")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - value is missing - evaluate will fail`() {
|
||||
project.kotlinExtension.apply {
|
||||
container.invokeWhenCreated("x") {
|
||||
fail("Called 'invokeWhenCreated' unexpectedly")
|
||||
}
|
||||
}
|
||||
|
||||
val causes = assertFails { project.evaluate() }.withLinearClosure { it.cause }
|
||||
assertEquals<Class<*>>(UnknownDomainObjectException::class.java, causes.last().javaClass)
|
||||
}
|
||||
}
|
||||
+6
@@ -11,6 +11,7 @@ import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformJvmPlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
|
||||
fun buildProject(
|
||||
@@ -35,6 +36,11 @@ fun buildProjectWithKPM(code: Project.() -> Unit= {}) = buildProject {
|
||||
code()
|
||||
}
|
||||
|
||||
fun buildProjectWithJvm(code: Project.() -> Unit = {}) = buildProject {
|
||||
project.plugins.apply(KotlinPlatformJvmPlugin::class.java)
|
||||
code()
|
||||
}
|
||||
|
||||
fun Project.kotlin(code: KotlinMultiplatformExtension.() -> Unit) {
|
||||
val kotlin = project.kotlinExtension as KotlinMultiplatformExtension
|
||||
kotlin.code()
|
||||
|
||||
Reference in New Issue
Block a user