[Gradle] Move 'whenEvaluated' util function into 'utils' package

This commit is contained in:
Sebastian Sellmair
2023-10-16 15:31:08 +02:00
committed by Space Team
parent 6240e2eddb
commit ca79860a71
26 changed files with 114 additions and 118 deletions
@@ -31,71 +31,6 @@ import kotlin.test.*
@GradleTestVersions(minVersion = G_7_1)
@AndroidTestVersions(minVersion = AGP_70)
class KotlinAndroidMppIT : KGPBaseTest() {
@DisplayName("KT-50736: whenEvaluated waits for AGP being applied later")
@GradleAndroidTest
fun testAfterEvaluateOrdering(
gradleVersion: GradleVersion,
agpVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
) {
project(
"AndroidProject",
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = agpVersion),
buildJdk = jdkVersion.location
) {
subProject("Lib").buildGradle.writeText(
//language=Gradle
"""
buildscript {
repositories {
mavenLocal()
google()
gradlePluginPortal()
}
dependencies {
classpath "com.android.tools.build:gradle:${'$'}android_tools_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}kotlin_version"
}
}
plugins {
id 'org.jetbrains.kotlin.multiplatform'
}
class MyAction implements kotlin.jvm.functions.Function1<Project, Void> {
Void invoke (Project p) {
println("compilations: " + p.kotlin.targets.getByName("android").compilations.names)
}
}
org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginKt.whenEvaluated(project, new MyAction ())
apply plugin : "android-library"
android {
compileSdkVersion 22
namespace 'org.jetbrains.kotlin.gradle.test.android.libalfa'
}
kotlin { android("android") { } }
""".trimIndent()
)
build("help") {
val reportedCompilations = output.lines()
.single { it.contains("compilations: ") }
.substringAfter("compilations: ")
.removeSurrounding("[", "]")
.split(", ")
.toSet()
assertEquals(
setOf("debug", "debugAndroidTest", "debugUnitTest", "release", "releaseUnitTest"),
reportedCompilations
)
}
}
}
@DisplayName("KotlinToolingMetadataArtifact is bundled into apk")
@GradleAndroidTest
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.configuration.*
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import java.io.ByteArrayOutputStream
import java.io.File
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.tasks.InspectClassesForMultiModuleIC
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.archivePathCompatible
const val PLUGIN_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerPluginClasspath"
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.Kotlin2JsCompileConfig
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import java.io.File
internal class Kotlin2JsSourceSetProcessor(
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinJvmPlugin.Companion.configureCom
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.applyUserDefinedAttributes
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.androidPluginIds
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.configuration.Kotlin2JsCompileConfig
import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinJsIrLinkConfig
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.filesProvider
internal class KotlinJsIrSourceSetProcessor(
@@ -27,9 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnosticOncePerBuild
import org.jetbrains.kotlin.gradle.plugin.internal.JavaSourceSetsAccessor
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.utils.androidPluginIds
import org.jetbrains.kotlin.gradle.utils.getOrPut
import java.util.concurrent.atomic.AtomicBoolean
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
abstract class KotlinPlatformPluginBase(protected val platformName: String) : Plugin<Project> {
companion object {
@@ -200,48 +198,6 @@ open class KotlinPlatformImplementationPluginBase(platformName: String) : Kotlin
}
}
internal fun <T> Project.whenEvaluated(fn: Project.() -> T) {
if (state.executed) {
fn()
return
}
/** If there's already an Android plugin applied, just dispatch the action to `afterEvaluate`, it gets executed after AGP's actions */
if (androidPluginIds.any { pluginManager.hasPlugin(it) }) {
afterEvaluate { fn() }
return
}
val isDispatchedAfterAndroid = AtomicBoolean(false)
/**
* This queue holds all actions submitted to `whenEvaluated` in this project, waiting for one of the Android plugins to be applied.
* After (and if) an Android plugin gets applied, we dispatch all the actions in the queue to `afterEvaluate`, so that they are
* executed after what AGP scheduled to `afterEvaluate`. There are different Android plugins, so actions in the queue also need to check
* if it's the first Android plugin, using `isDispatched` (each has its own instance).
*/
val afterAndroidDispatchQueue = project.extensions.extraProperties.getOrPut("org.jetbrains.kotlin.whenEvaluated") {
val queue = mutableListOf<() -> Unit>()
// Trigger the actions on any plugin applied; the actions themselves ensure that they only dispatch the fn once.
androidPluginIds.forEach { id ->
pluginManager.withPlugin(id) { queue.forEach { it() } }
}
queue
}
afterAndroidDispatchQueue.add {
if (!isDispatchedAfterAndroid.getAndSet(true)) {
afterEvaluate { fn() }
}
}
afterEvaluate {
/** If no Android plugin was loaded, then the action was not dispatched, and we can freely execute it now */
if (!isDispatchedAfterAndroid.getAndSet(true)) {
fn()
}
}
}
private const val KOTLIN_12X_MPP_DEPRECATION_SUPPRESS_FLAG = "kotlin.internal.mpp12x.deprecation.suppress"
internal fun warnAboutKotlin12xMppDeprecation(project: Project) {
@@ -9,6 +9,7 @@ import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.CoroutineStart.Default
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.CoroutineStart.Undispatched
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.ProjectConfigurationResult
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.CompletableFuture
import org.jetbrains.kotlin.gradle.utils.failures
import java.util.concurrent.atomic.AtomicBoolean
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
/**
* This class encapsulated logic which should be invoked during not before the script evaluation is ready and
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.MetadataDependencyResolution.ChooseVisibleSourceSets.MetadataProvider.ArtifactMetadataProvider
import org.jetbrains.kotlin.gradle.plugin.sources.internal
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.*
import java.util.*
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.targets.native.createFatFrameworks
import org.jetbrains.kotlin.gradle.targets.native.internal.setupCInteropCommonizedCInteropApiElementsConfigurations
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.registerKotlinArtifactsExtension
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget.*
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
internal fun configurePublishingWithMavenPublish(project: Project) = project.pluginManager.withPlugin("maven-publish") {
if (project.kotlinPropertiesProvider.createDefaultMultiplatformPublications) {
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.gradle.tasks.configuration.KotlinCompileConfig
import org.jetbrains.kotlin.gradle.tasks.thisTaskProvider
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.tooling.includeKotlinToolingMetadataInApk
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.*
import java.io.File
import java.io.ObjectInputStream
@@ -9,7 +9,6 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootExtension.Companion.EXTENSION_NAME
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetType
import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.npm
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
open class KotlinJvmTargetConfigurator :
KotlinOnlyTargetConfigurator<KotlinJvmCompilation, KotlinJvmTarget>(true),
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.sharedCommonizerTarge
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.tooling.core.extrasLazyProperty
@@ -42,9 +42,9 @@ import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.XcodeUtils
import org.jetbrains.kotlin.gradle.utils.copyAttributes
import org.jetbrains.kotlin.gradle.utils.named
import org.jetbrains.kotlin.gradle.utils.newInstance
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.DeprecatedTargetPresetApi
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.android.internal.InternalKotlinTargetPreset
import org.jetbrains.kotlin.gradle.targets.native.internal.*
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.gradle.utils.setupNativeCompiler
import org.jetbrains.kotlin.konan.target.HostManager
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.apple.*
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleSdk
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.AppleTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.FrameworkCopy.Companion.dsymFile
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.native.cocoapods.CocoapodsPluginDiagnostics
import org.jetbrains.kotlin.gradle.targets.native.cocoapods.KotlinArtifactsPodspecExtension
import org.jetbrains.kotlin.gradle.targets.native.cocoapods.kotlinArtifactsPodspecExtension
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPro
import org.jetbrains.kotlin.gradle.plugin.await
import org.jetbrains.kotlin.gradle.plugin.ide.Idea222Api
import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import java.io.File
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.plugin.launchInStage
/**
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2023 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.
*/
package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import java.util.concurrent.atomic.AtomicBoolean
internal fun <T> Project.whenEvaluated(fn: Project.() -> T) {
if (state.executed) {
fn()
return
}
/** If there's already an Android plugin applied, just dispatch the action to `afterEvaluate`, it gets executed after AGP's actions */
if (androidPluginIds.any { pluginManager.hasPlugin(it) }) {
afterEvaluate { fn() }
return
}
val isDispatchedAfterAndroid = AtomicBoolean(false)
/**
* This queue holds all actions submitted to `whenEvaluated` in this project, waiting for one of the Android plugins to be applied.
* After (and if) an Android plugin gets applied, we dispatch all the actions in the queue to `afterEvaluate`, so that they are
* executed after what AGP scheduled to `afterEvaluate`. There are different Android plugins, so actions in the queue also need to check
* if it's the first Android plugin, using `isDispatched` (each has its own instance).
*/
val afterAndroidDispatchQueue = project.extensions.extraProperties.getOrPut("org.jetbrains.kotlin.whenEvaluated") {
val queue = mutableListOf<() -> Unit>()
// Trigger the actions on any plugin applied; the actions themselves ensure that they only dispatch the fn once.
androidPluginIds.forEach { id ->
pluginManager.withPlugin(id) { queue.forEach { it() } }
}
queue
}
afterAndroidDispatchQueue.add {
if (!isDispatchedAfterAndroid.getAndSet(true)) {
afterEvaluate { fn() }
}
}
afterEvaluate {
/** If no Android plugin was loaded, then the action was not dispatched, and we can freely execute it now */
if (!isDispatchedAfterAndroid.getAndSet(true)) {
fn()
}
}
}
@@ -11,7 +11,7 @@ import com.android.build.gradle.LibraryExtension
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.testfixtures.ProjectBuilder
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import org.jetbrains.kotlin.gradle.util.applyMultiplatformPlugin
import org.junit.Test
import kotlin.test.BeforeTest
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2023 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.unitTests
import com.android.build.api.variant.AndroidComponentsExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.util.androidLibrary
import org.jetbrains.kotlin.gradle.util.applyMultiplatformPlugin
import org.jetbrains.kotlin.gradle.util.buildProject
import org.jetbrains.kotlin.gradle.utils.getByType
import org.jetbrains.kotlin.gradle.utils.whenEvaluated
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.test.Test
import kotlin.test.assertFalse
class WhenEvaluatedTest {
@Test
fun `test - whenEvaluated - waits for Android plugins afterEvaluate`() {
val project = buildProject()
project.androidLibrary { compileSdk = 33 }
project.applyMultiplatformPlugin()
project.multiplatformExtension.androidTarget()
val onVariantsCalled = AtomicBoolean(false)
val onWhenEvaluatedCalled = AtomicBoolean(false)
project.extensions.getByType<AndroidComponentsExtension<*, *, *>>().onVariants {
assertFalse(onVariantsCalled.getAndSet(true))
assertFalse(onWhenEvaluatedCalled.get(), "whenEvaluated was called before 'onVariants'")
}
project.whenEvaluated {
assertFalse(onVariantsCalled.get(), "whenEvaluated was called after 'onVariants'")
assertFalse(onWhenEvaluatedCalled.getAndSet(true))
}
}
}