From b6a215d681695a2fe0cc798308966c5675de447f Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 10 Mar 2023 18:12:28 +0100 Subject: [PATCH] [Gradle] Implement 'findMetadataCompilation' in safe way (replacing old getMetadataCompilationsForSourceSet) ^KT-34662 Verification Pending --- .../gradle/plugin/KotlinPluginLifecycle.kt | 8 ++-- .../KotlinMetadataTargetConfigurator.kt | 37 +++++++++++++++---- .../targets/native/KotlinNativeTarget.kt | 24 ++++++------ .../KotlinNativePlatformDependencies.kt | 6 +-- .../jetbrains/kotlin/gradle/utils/Future.kt | 35 ++++++++++++++++-- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt index a0e2c5dae37..bcbb7399d44 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginLifecycle.kt @@ -315,6 +315,8 @@ internal interface KotlinPluginLifecycle { companion object { val values = values().toList() + val first = values.first() + val last = values.last() fun upTo(stage: Stage): Set = values.first()..stage fun until(stage: Stage): Set { return upTo(stage.previousOrNull ?: return emptySet()) @@ -368,9 +370,9 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP private val enqueuedActions: Map Unit>> = Stage.values().associateWith { ArrayDeque() } - private var loopRunning = AtomicBoolean(false) - private var isStarted = AtomicBoolean(false) - private var isFinished = AtomicBoolean(false) + private val loopRunning = AtomicBoolean(false) + private val isStarted = AtomicBoolean(false) + private val isFinished = AtomicBoolean(false) private val properties = WeakHashMap, WeakReference>>() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index eb5fbe8f636..773f0cefdf5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -17,19 +17,28 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.Zip import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget -import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.pm20ExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.GradleKpmModule import org.jetbrains.kotlin.gradle.plugin.sources.* import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService -import org.jetbrains.kotlin.gradle.targets.native.internal.* -import org.jetbrains.kotlin.gradle.tasks.* +import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropMetadataDependencyClasspath +import org.jetbrains.kotlin.gradle.targets.native.internal.includeCommonizedCInteropMetadata +import org.jetbrains.kotlin.gradle.targets.native.internal.sharedCommonizerTarget +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.* import org.jetbrains.kotlin.gradle.utils.filesProvider import org.jetbrains.kotlin.gradle.utils.getResolvedArtifactsCompat import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics +import org.jetbrains.kotlin.tooling.core.UnsafeApi +import org.jetbrains.kotlin.tooling.core.extrasLazyProperty internal const val COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME = "commonMainMetadataElements" @@ -175,7 +184,7 @@ class KotlinMetadataTargetConfigurator : private fun createMetadataCompilationsForCommonSourceSets( target: KotlinMetadataTarget, allMetadataJar: TaskProvider - ) = target.project.launchInStage(KotlinPluginLifecycle.Stage.FinaliseCompilations) { + ) = target.project.launchInRequiredStage(KotlinPluginLifecycle.Stage.FinaliseCompilations) { // Do this after all targets are configured by the user build script val publishedCommonSourceSets: Set = getCommonSourceSetsForMetadataCompilation(project) @@ -199,6 +208,8 @@ class KotlinMetadataTargetConfigurator : sourceSetsWithMetadataCompilations.values.forEach { compilation -> exportDependenciesForPublishing(compilation) } + + target.metadataCompilationsCreated.complete() } private fun isMetadataCompilationSupported(sourceSet: KotlinSourceSet): Boolean { @@ -358,7 +369,7 @@ class KotlinMetadataTargetConfigurator : private val KotlinSourceSet.dependsOnClassesDirs: FileCollection get() = project.filesProvider { internal.dependsOnClosure.mapNotNull { hierarchySourceSet -> - val compilation = project.getMetadataCompilationForSourceSet(hierarchySourceSet) ?: return@mapNotNull null + val compilation = project.future { findMetadataCompilation(hierarchySourceSet) }.getOrThrow() ?: return@mapNotNull null compilation.output.classesDirs } } @@ -491,6 +502,16 @@ internal fun Project.filesWithUnpackedArchives(from: FileCollection, extensions: } }).builtBy(from) -internal fun Project.getMetadataCompilationForSourceSet(sourceSet: KotlinSourceSet): KotlinMetadataCompilation<*>? { - return multiplatformExtension.metadata().compilations.findByName(sourceSet.name) +private val KotlinMetadataTarget.metadataCompilationsCreated: CompletableFuture by extrasLazyProperty("metadataCompilationsCreated") { + CompletableFuture() +} + +internal suspend fun KotlinMetadataTarget.awaitMetadataCompilationsCreated() { + metadataCompilationsCreated.await() +} + +internal suspend fun Project.findMetadataCompilation(sourceSet: KotlinSourceSet): KotlinMetadataCompilation<*>? { + val metadataTarget = multiplatformExtension.metadata() as KotlinMetadataTarget + metadataTarget.awaitMetadataCompilationsCreated() + return metadataTarget.compilations.findByName(sourceSet.name) as KotlinMetadataCompilation<*>? } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt index 04439d88320..285fcb783fe 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt @@ -56,7 +56,7 @@ abstract class KotlinNativeTarget @Inject constructor( // NB: another usage context for the host-specific metadata may be added to this set below val mutableUsageContexts = createUsageContexts(mainCompilation).toMutableSet() - project.whenEvaluated { + project.launchInStage(KotlinPluginLifecycle.Stage.AfterFinaliseDsl) { val hostSpecificSourceSets = getHostSpecificSourceSets(project) .intersect(mainCompilation.allKotlinSourceSets) @@ -70,18 +70,20 @@ abstract class KotlinNativeTarget @Inject constructor( val publishable = this@KotlinNativeTarget.publishable metadataJar.onlyIf { publishable } - val metadataCompilations = hostSpecificSourceSets.mapNotNull { - project.getMetadataCompilationForSourceSet(it) - } - - metadataCompilations.forEach { compilation -> - metadataJar.from(project.filesWithUnpackedArchives(compilation.output.allOutputs, setOf("klib"))) { spec -> - spec.into(compilation.name) + launch { + val metadataCompilations = hostSpecificSourceSets.mapNotNull { + project.findMetadataCompilation(it) } - metadataJar.dependsOn(compilation.output.classesDirs) - if (compilation is KotlinSharedNativeCompilation) { - project.includeCommonizedCInteropMetadata(metadataJar, compilation) + metadataCompilations.forEach { compilation -> + metadataJar.from(project.filesWithUnpackedArchives(compilation.output.allOutputs, setOf("klib"))) { spec -> + spec.into(compilation.name) + } + metadataJar.dependsOn(compilation.output.classesDirs) + + if (compilation is KotlinSharedNativeCompilation) { + project.includeCommonizedCInteropMetadata(metadataJar, compilation) + } } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt index fc1c7523e87..0f962547d1d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.gradle.plugin.ide.ideaImportDependsOn import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations import org.jetbrains.kotlin.gradle.plugin.sources.internal -import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet +import org.jetbrains.kotlin.gradle.targets.metadata.findMetadataCompilation import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.internal.MissingNativeStdlibWarning.showMissingNativeStdlibWarning import org.jetbrains.kotlin.gradle.utils.filesProvider @@ -89,11 +89,11 @@ private fun NativeDistributionCommonizerTask.getCommonizedPlatformLibrariesFor(t return project.filesProvider { targetOutputDirectory.listLibraryFiles() }.builtBy(this) } -private fun Project.addDependencies( +private suspend fun Project.addDependencies( sourceSet: KotlinSourceSet, libraries: FileCollection, isCompilationDependency: Boolean = true, isIdeDependency: Boolean = true ) { if (isCompilationDependency) { - getMetadataCompilationForSourceSet(sourceSet)?.let { compilation -> + findMetadataCompilation(sourceSet)?.let { compilation -> compilation.compileDependencyFiles += libraries } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt index f3191bbb8ed..af64af9fd0f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/Future.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.gradle.plugin.HasProject import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.IllegalLifecycleException import org.jetbrains.kotlin.gradle.plugin.kotlinPluginLifecycle -import org.jetbrains.kotlin.gradle.plugin.launch import org.jetbrains.kotlin.tooling.core.ExtrasLazyProperty import org.jetbrains.kotlin.tooling.core.HasMutableExtras import org.jetbrains.kotlin.tooling.core.extrasLazyProperty @@ -47,6 +46,12 @@ internal interface Future { fun getOrThrow(): T } +internal interface CompletableFuture : Future { + fun complete(value: T) +} + +internal fun CompletableFuture.complete() = complete(Unit) + internal inline fun lazyFuture( name: String? = null, noinline block: suspend Receiver.() -> T ): ExtrasLazyProperty> where Receiver : HasMutableExtras, Receiver : HasProject { @@ -55,8 +60,10 @@ internal inline fun lazyFuture( } } +internal fun Project.future(block: suspend Project.() -> T): Future = kotlinPluginLifecycle.future { block() } + @OptIn(ExperimentalCoroutinesApi::class) -internal fun Project.future(block: suspend Project.() -> T): Future { +internal fun KotlinPluginLifecycle.future(block: suspend () -> T): Future { val deferred = CompletableDeferred() launch { @@ -70,8 +77,30 @@ internal fun Project.future(block: suspend Project.() -> T): Future { override fun getOrThrow(): T { return if (deferred.isCompleted) deferred.getCompleted() else throw IllegalLifecycleException( - "Future was not completed yet. Stage: '${kotlinPluginLifecycle.stage}'" + "Future was not completed yet. Stage: '${stage}'" ) } } } + +@OptIn(ExperimentalCoroutinesApi::class) +internal fun CompletableFuture(): CompletableFuture { + val deferred = CompletableDeferred() + + + return object : CompletableFuture { + override fun complete(value: T) { + deferred.complete(value) + } + + override suspend fun await(): T { + return deferred.await() + } + + override fun getOrThrow(): T { + return if (deferred.isCompleted) deferred.getCompleted() else throw IllegalLifecycleException( + "Future was not completed yet" + ) + } + } +} \ No newline at end of file