[Gradle] Implement 'findMetadataCompilation' in safe way

(replacing old getMetadataCompilationsForSourceSet)

^KT-34662 Verification Pending
This commit is contained in:
Sebastian Sellmair
2023-03-10 18:12:28 +01:00
committed by Space Team
parent 4c653a4297
commit b6a215d681
5 changed files with 82 additions and 28 deletions
@@ -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<Stage> = values.first()..stage
fun until(stage: Stage): Set<Stage> {
return upTo(stage.previousOrNull ?: return emptySet())
@@ -368,9 +370,9 @@ private class KotlinPluginLifecycleImpl(override val project: Project) : KotlinP
private val enqueuedActions: Map<Stage, ArrayDeque<KotlinPluginLifecycle.() -> 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<Property<*>, WeakReference<LifecycleAwareProperty<*>>>()
@@ -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<out Jar>
) = 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<KotlinSourceSet> = 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<Unit> 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<*>?
}
@@ -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)
}
}
}
}
@@ -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
}
}
@@ -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<T> {
fun getOrThrow(): T
}
internal interface CompletableFuture<T> : Future<T> {
fun complete(value: T)
}
internal fun CompletableFuture<Unit>.complete() = complete(Unit)
internal inline fun <Receiver, reified T> lazyFuture(
name: String? = null, noinline block: suspend Receiver.() -> T
): ExtrasLazyProperty<Receiver, Future<T>> where Receiver : HasMutableExtras, Receiver : HasProject {
@@ -55,8 +60,10 @@ internal inline fun <Receiver, reified T> lazyFuture(
}
}
internal fun <T> Project.future(block: suspend Project.() -> T): Future<T> = kotlinPluginLifecycle.future { block() }
@OptIn(ExperimentalCoroutinesApi::class)
internal fun <T> Project.future(block: suspend Project.() -> T): Future<T> {
internal fun <T> KotlinPluginLifecycle.future(block: suspend () -> T): Future<T> {
val deferred = CompletableDeferred<T>()
launch {
@@ -70,8 +77,30 @@ internal fun <T> Project.future(block: suspend Project.() -> T): Future<T> {
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 <T> CompletableFuture(): CompletableFuture<T> {
val deferred = CompletableDeferred<T>()
return object : CompletableFuture<T> {
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"
)
}
}
}