From b064493c7b4ce50077d85b8b324f3ab0ce8a1cee Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Mon, 13 Mar 2023 14:48:00 +0100 Subject: [PATCH] [Gradle] Implement CInteropCommonizerDependent factories as safe suspend functions ^KT-34662 Verification Pending --- ...latformProjectConfigurationHealthChecks.kt | 6 +- .../mpp/GranularMetadataTransformation.kt | 14 +++-- .../plugin/mpp/ProjectMetadataProviderImpl.kt | 4 +- .../AbstractCInteropCommonizerTask.kt | 8 ++- ...pCommonizerCompositeMetadataJarBundling.kt | 7 ++- .../CInteropCommonizerDependencies.kt | 16 ++++-- .../internal/CInteropCommonizerDependent.kt | 12 ++-- .../native/internal/CInteropCommonizerTask.kt | 40 ++++++------- ...yCInteropCommonizerTaskOutputForIdeTask.kt | 4 +- .../jetbrains/kotlin/gradle/utils/Future.kt | 56 ++++++++----------- .../unitTests/CInteropCommonizerTaskTest.kt | 34 +++++------ .../gradle/util/MultiplatformExtensionTest.kt | 16 +++--- 12 files changed, 109 insertions(+), 108 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt index c29dfe273f5..b743115e2a8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinMultiplatformProjectConfigurationHealthChecks.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDep import org.jetbrains.kotlin.gradle.targets.native.internal.from import org.jetbrains.kotlin.gradle.targets.native.internal.isAllowCommonizer import org.jetbrains.kotlin.gradle.utils.findAppliedAndroidPluginIdOrNull +import org.jetbrains.kotlin.gradle.utils.future import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheck private class KotlinMultiplatformProjectConfigurationException(message: String) : Exception(message) @@ -77,7 +78,10 @@ internal fun Project.runDisabledCInteropCommonizationOnHmppProjectConfigurationH val sharedCompilationsWithInterops = multiplatformExtension.targets.flatMap { it.compilations } .filterIsInstance() - .mapNotNull { compilation -> compilation to (CInteropCommonizerDependent.from(compilation) ?: return@mapNotNull null) } + .mapNotNull { compilation -> + val cinteropDependent = future { CInteropCommonizerDependent.from(compilation) }.getOrThrow() ?: return@mapNotNull null + compilation to cinteropDependent + } .toMap() val affectedCompilations = sharedCompilationsWithInterops.keys diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt index 7981c5830cd..3143f78b9a4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/GranularMetadataTransformation.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.extraProperties 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.LazyResolvedConfiguration +import org.jetbrains.kotlin.gradle.utils.future import org.jetbrains.kotlin.gradle.utils.getOrPut import java.util.* @@ -143,7 +144,7 @@ internal class GranularMetadataTransformation( params.resolvedMetadataConfiguration .root .dependencies - .filter { !it.isConstraint} + .filter { !it.isConstraint } .filterIsInstance() ) } @@ -332,15 +333,18 @@ internal val ResolvedComponentResult.currentBuildProjectIdOrNull } } -private val Project.allProjectsData: Map get() = rootProject - .extraProperties - .getOrPut("all${GranularMetadataTransformation.ProjectData::class.java.simpleName}") { collectAllProjectsData() } +private val Project.allProjectsData: Map + get() = rootProject + .extraProperties + .getOrPut("all${GranularMetadataTransformation.ProjectData::class.java.simpleName}") { + future { collectAllProjectsData() }.getOrThrow() + } private fun Project.collectAllProjectsData(): Map { return rootProject.allprojects.associateBy { it.path }.mapValues { (path, subProject) -> GranularMetadataTransformation.ProjectData( path = path, - sourceSetMetadataOutputsProvider = { subProject.collectSourceSetMetadataOutputs() }, + sourceSetMetadataOutputsProvider = future { subProject.collectSourceSetMetadataOutputs() }::getOrThrow, moduleIdProvider = { ModuleIds.idOfRootModule(subProject) } ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/ProjectMetadataProviderImpl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/ProjectMetadataProviderImpl.kt index 0965e93d58b..7827a3e1f5c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/ProjectMetadataProviderImpl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/ProjectMetadataProviderImpl.kt @@ -48,7 +48,7 @@ private class ProjectMetadataProviderImpl( } } -internal fun Project.collectSourceSetMetadataOutputs(): Map { +internal suspend fun Project.collectSourceSetMetadataOutputs(): Map { val multiplatformExtension = multiplatformExtensionOrNull ?: return emptyMap() val sourceSetMetadata = multiplatformExtension.sourceSetsMetadataOutputs() @@ -82,7 +82,7 @@ private fun KotlinMultiplatformExtension.sourceSetsMetadataOutputs(): Map ): Map { val taskForCLI = project.commonizeCInteropTask ?: return emptyMap() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AbstractCInteropCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AbstractCInteropCommonizerTask.kt index 55071e529fd..2b6e8038259 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AbstractCInteropCommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AbstractCInteropCommonizerTask.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.base64Hash import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength import org.jetbrains.kotlin.commonizer.identityString import org.jetbrains.kotlin.gradle.utils.changing +import org.jetbrains.kotlin.gradle.utils.future import org.jetbrains.kotlin.gradle.utils.outputFilesProvider import java.io.File @@ -20,7 +21,7 @@ internal abstract class AbstractCInteropCommonizerTask : DefaultTask() { @get:OutputDirectory abstract val outputDirectory: File - internal abstract fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? + internal abstract suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? } internal fun AbstractCInteropCommonizerTask.outputDirectory(group: CInteropCommonizerGroup): File { @@ -35,14 +36,15 @@ internal fun AbstractCInteropCommonizerTask.outputDirectory(group: CInteropCommo internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection { return outputFilesProvider { - val outputDirectory = commonizedOutputDirectory(dependent) ?: return@outputFilesProvider emptySet() + val outputDirectory = project.future { commonizedOutputDirectory(dependent) }.getOrThrow() + ?: return@outputFilesProvider emptySet() project.providers.changing { outputDirectory.listFiles().orEmpty().toSet() } } } -internal fun AbstractCInteropCommonizerTask.commonizedOutputDirectory(dependent: CInteropCommonizerDependent): File? { +internal suspend fun AbstractCInteropCommonizerTask.commonizedOutputDirectory(dependent: CInteropCommonizerDependent): File? { val group = findInteropsGroup(dependent) ?: return null return CommonizerOutputFileLayout .resolveCommonizedDirectory(outputDirectory(group), dependent.target) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerCompositeMetadataJarBundling.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerCompositeMetadataJarBundling.kt index 5abbd77c781..1613e267282 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerCompositeMetadataJarBundling.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerCompositeMetadataJarBundling.kt @@ -9,16 +9,19 @@ import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Zip import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.launch import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerCompositeMetadataJarBundling.cinteropMetadataDirectoryPath internal fun Project.includeCommonizedCInteropMetadata( metadataKlib: TaskProvider, compilation: KotlinSharedNativeCompilation ) { - metadataKlib.configure { jar -> includeCommonizedCInteropMetadata(jar, compilation) } + metadataKlib.configure { jar -> + launch { includeCommonizedCInteropMetadata(jar, compilation) } + } } -internal fun Project.includeCommonizedCInteropMetadata(metadataKlib: Zip, compilation: KotlinSharedNativeCompilation) { +internal suspend fun Project.includeCommonizedCInteropMetadata(metadataKlib: Zip, compilation: KotlinSharedNativeCompilation) { val commonizerTask = commonizeCInteropTask?.get() ?: return val commonizerDependencyToken = CInteropCommonizerDependent.from(compilation) ?: return val outputDirectory = commonizerTask.commonizedOutputDirectory(commonizerDependencyToken) ?: return diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt index e684c7d9802..f6741b92b22 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.internal import org.jetbrains.kotlin.gradle.utils.filesProvider +import org.jetbrains.kotlin.gradle.utils.future import java.io.File internal fun Project.setupCInteropCommonizerDependencies() { @@ -33,7 +34,8 @@ private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilatio val cinteropCommonizerTask = project.commonizeCInteropTask ?: return compilation.compileDependencyFiles += filesProvider { - val cinteropCommonizerDependent = CInteropCommonizerDependent.from(compilation) ?: return@filesProvider emptySet() + val cinteropCommonizerDependent = future { CInteropCommonizerDependent.from(compilation) }.getOrThrow() + ?: return@filesProvider emptySet() cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent) } } @@ -51,12 +53,14 @@ internal fun Project.cinteropCommonizerDependencies(sourceSet: DefaultKotlinSour val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return project.files() return filesProvider { - val directlyDependent = CInteropCommonizerDependent.from(sourceSet) - val associateDependent = CInteropCommonizerDependent.fromAssociateCompilations(sourceSet) + future { + val directlyDependent = CInteropCommonizerDependent.from(sourceSet) + val associateDependent = CInteropCommonizerDependent.fromAssociateCompilations(sourceSet) - listOfNotNull(directlyDependent, associateDependent).map { cinteropCommonizerDependent -> - cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent) - } + listOfNotNull(directlyDependent, associateDependent).map { cinteropCommonizerDependent -> + cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent) + } + }.getOrThrow() } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt index da95b4b1be4..d3a14d2a085 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt @@ -72,24 +72,24 @@ internal fun CInteropCommonizerDependent.Factory.from( return CInteropCommonizerDependent(target, scopes, interops) } -internal fun CInteropCommonizerDependent.Factory.from(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? { +internal suspend fun CInteropCommonizerDependent.Factory.from(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? { return from( - compilation.commonizerTarget.getOrThrow() as? SharedCommonizerTarget ?: return null, + compilation.commonizerTarget.await() as? SharedCommonizerTarget ?: return null, compilation.getImplicitlyDependingNativeCompilations() ) } -internal fun CInteropCommonizerDependent.Factory.from(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { +internal suspend fun CInteropCommonizerDependent.Factory.from(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { return from( - target = sourceSet.commonizerTarget.getOrThrow() as? SharedCommonizerTarget ?: return null, + target = sourceSet.commonizerTarget.await() as? SharedCommonizerTarget ?: return null, compilations = sourceSet.internal.compilations .filterIsInstance().toSet() ) } -internal fun CInteropCommonizerDependent.Factory.fromAssociateCompilations(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { +internal suspend fun CInteropCommonizerDependent.Factory.fromAssociateCompilations(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { return from( - target = sourceSet.commonizerTarget.getOrThrow() as? SharedCommonizerTarget ?: return null, + target = sourceSet.commonizerTarget.await() as? SharedCommonizerTarget ?: return null, compilations = sourceSet.internal.compilations .filterIsInstance() .flatMap { compilation -> compilation.associateWithClosure } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt index fd5e07d4118..9fd3b480497 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerTask.kt @@ -27,9 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.sources.withDependsOnClosure import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask.CInteropGist import org.jetbrains.kotlin.gradle.tasks.CInteropProcess -import org.jetbrains.kotlin.gradle.utils.chainedFinalizeValueOnRead -import org.jetbrains.kotlin.gradle.utils.listProperty -import org.jetbrains.kotlin.gradle.utils.property +import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File import javax.inject.Inject @@ -123,14 +121,14 @@ internal open class CInteropCommonizerTask * For Gradle Configuration Cache support the Group-to-Dependencies relation should be pre-cached. * It is used during execution phase. */ - private val groupedCommonizerDependencies: Map> by lazy { - val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@lazy emptyMap() + private val groupedCommonizerDependencies: Future>> = project.future { + val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@future emptyMap() val sourceSetsByTarget = multiplatformExtension.sourceSets.groupBy { sourceSet -> sourceSet.commonizerTarget.getOrThrow() } val sourceSetsByGroup = multiplatformExtension.sourceSets.groupBy { sourceSet -> CInteropCommonizerDependent.from(sourceSet)?.let { findInteropsGroup(it) } } - getAllInteropsGroups().associateWith { group -> + allInteropGroups.await().associateWith { group -> (group.targets + group.targets.allLeaves()).map { target -> val externalDependencyFiles: List = when (target) { is LeafCommonizerTarget -> { @@ -165,7 +163,7 @@ internal open class CInteropCommonizerTask @get:Classpath protected val commonizerDependenciesClasspath: FileCollection get() = project.files( - groupedCommonizerDependencies.values.flatten().map { it.dependencies } + groupedCommonizerDependencies.getOrThrow().values.flatten().map { it.dependencies } ) @get:Nested @@ -174,7 +172,7 @@ internal open class CInteropCommonizerTask @get:OutputDirectories val allOutputDirectories: Set - get() = getAllInteropsGroups().map { outputDirectory(it) }.toSet() + get() = allInteropGroups.getOrThrow().map { outputDirectory(it) }.toSet() fun from(vararg tasks: CInteropProcess) = from( tasks.toList() @@ -192,7 +190,7 @@ internal open class CInteropCommonizerTask @TaskAction protected fun commonizeCInteropLibraries() { - getAllInteropsGroups().forEach(::commonize) + allInteropGroups.getOrThrow().forEach(::commonize) } private fun commonize(group: CInteropCommonizerGroup) { @@ -217,7 +215,7 @@ internal open class CInteropCommonizerTask } private fun getCInteropCommonizerGroupDependencies(group: CInteropCommonizerGroup): Set { - val dependencies = groupedCommonizerDependencies[group] + val dependencies = groupedCommonizerDependencies.getOrThrow()[group] ?.flatMap { (target, dependencies) -> dependencies.files .filter { file -> file.exists() && (file.isDirectory || file.extension == "klib") } @@ -229,15 +227,15 @@ internal open class CInteropCommonizerTask return dependencies } - @Nested - internal fun getAllInteropsGroups(): Set { - val dependents = allDependents + @get:Internal + internal val allInteropGroups: Future> = project.future { + val dependents = allDependents.await() val allScopeSets = dependents.map { it.scopes }.toSet() val rootScopeSets = allScopeSets.filter { scopeSet -> allScopeSets.none { otherScopeSet -> otherScopeSet != scopeSet && otherScopeSet.containsAll(scopeSet) } } - return rootScopeSets.map { scopeSet -> + rootScopeSets.map { scopeSet -> val dependentsForScopes = dependents.filter { dependent -> scopeSet.containsAll(dependent.scopes) } @@ -249,8 +247,12 @@ internal open class CInteropCommonizerTask }.toSet() } - override fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? { - val suitableGroups = getAllInteropsGroups().filter { group -> + @get:Nested + @Suppress("unused") // UP-TO-DATE check + protected val allInteropGroupsOrThrow get() = allInteropGroups.getOrThrow() + + override suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? { + val suitableGroups = allInteropGroups.await().filter { group -> group.interops.containsAll(dependent.interops) && group.targets.contains(dependent.target) } @@ -261,8 +263,8 @@ internal open class CInteropCommonizerTask return suitableGroups.firstOrNull() } - private val allDependents: Set by lazy { - val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@lazy emptySet() + private val allDependents: Future> = project.future { + val multiplatformExtension = project.multiplatformExtensionOrNull ?: return@future emptySet() val fromSharedNativeCompilations = multiplatformExtension .targets.flatMap { target -> target.compilations } @@ -278,7 +280,7 @@ internal open class CInteropCommonizerTask .mapNotNull { sourceSet -> CInteropCommonizerDependent.fromAssociateCompilations(sourceSet) } .toSet() - return@lazy (fromSharedNativeCompilations + fromSourceSets + fromSourceSetsAssociateCompilations) + (fromSharedNativeCompilations + fromSourceSets + fromSourceSetsAssociateCompilations) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt index d29215584b6..c66f07c0a6c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt @@ -26,14 +26,14 @@ internal open class CopyCommonizeCInteropForIdeTask : AbstractCInteropCommonizer override val outputDirectory: File = project.rootDir.resolve(".gradle/kotlin/commonizer") .resolve(project.path.removePrefix(":").replace(":", "/")) - override fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? { + override suspend fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? { return commonizeCInteropTask.get().findInteropsGroup(dependent) } @TaskAction protected fun copy() { outputDirectory.mkdirs() - for (group in commonizeCInteropTask.get().getAllInteropsGroups()) { + for (group in commonizeCInteropTask.get().allInteropGroups.getOrThrow()) { val source = commonizeCInteropTask.get().outputDirectory(group) if (!source.exists()) continue val target = outputDirectory(group) 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 af64af9fd0f..3a68b596d14 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 @@ -16,6 +16,8 @@ import org.jetbrains.kotlin.gradle.plugin.kotlinPluginLifecycle import org.jetbrains.kotlin.tooling.core.ExtrasLazyProperty import org.jetbrains.kotlin.tooling.core.HasMutableExtras import org.jetbrains.kotlin.tooling.core.extrasLazyProperty +import java.io.Serializable +import kotlin.reflect.KProperty /** * See [KotlinPluginLifecycle]: @@ -62,45 +64,31 @@ internal inline fun lazyFuture( internal fun Project.future(block: suspend Project.() -> T): Future = kotlinPluginLifecycle.future { block() } -@OptIn(ExperimentalCoroutinesApi::class) internal fun KotlinPluginLifecycle.future(block: suspend () -> T): Future { - val deferred = CompletableDeferred() - - launch { - deferred.completeWith(runCatching { block() }) - } - - return object : Future { - 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. Stage: '${stage}'" - ) - } + return FutureImpl(CompletableDeferred()).also { future -> + launch { future.completeWith(runCatching { block() }) } } } -@OptIn(ExperimentalCoroutinesApi::class) internal fun CompletableFuture(): CompletableFuture { - val deferred = CompletableDeferred() + return FutureImpl() +} +@OptIn(ExperimentalCoroutinesApi::class) +private class FutureImpl(private val deferred: CompletableDeferred = CompletableDeferred()) : CompletableFuture, Serializable { + fun completeWith(result: Result) = deferred.completeWith(result) - 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" - ) - } + override fun complete(value: T) { + deferred.complete(value) } -} \ No newline at end of file + + 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" + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CInteropCommonizerTaskTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CInteropCommonizerTaskTest.kt index 012e2c7cc51..6c83798ea75 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CInteropCommonizerTaskTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/CInteropCommonizerTaskTest.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerGroup import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask +import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest import org.jetbrains.kotlin.konan.target.KonanTarget.* import kotlin.test.* @@ -30,7 +31,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { } @Test - fun `nativeMain linux macos`() { + fun `nativeMain linux macos`() = project.runLifecycleAwareTest { val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName") val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName") @@ -43,9 +44,8 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { linuxMain.dependsOn(nativeMain) macosMain.dependsOn(nativeMain) - project.evaluate() - val groups = task.getAllInteropsGroups() + val groups = task.allInteropGroups.await() assertEquals(1, groups.size, "Expected only one InteropsGroup") assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain) @@ -60,7 +60,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { } @Test - fun `nativeMain linux macos (no macos interop defined)`() { + fun `nativeMain linux macos (no macos interop defined)`() = project.runLifecycleAwareTest { kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName") kotlin.macosX64("macos") @@ -73,8 +73,6 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { linuxMain.dependsOn(nativeMain) macosMain.dependsOn(nativeMain) - project.evaluate() - assertNull( findCInteropCommonizerDependent(nativeMain), "Expected no CInteropCommonizerTarget from nativeMain, since one target has not defined any cinterop" @@ -87,7 +85,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { } @Test - fun `nativeMain iosMain linux macos iosX64 iosArm64`() { + fun `nativeMain iosMain linux macos iosX64 iosArm64`() = project.runLifecycleAwareTest { val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier val iosX64Interop = kotlin.iosX64("iosX64").compilations.getByName("main").cinterops.create("anyInteropName").identifier @@ -108,10 +106,8 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { iosX64Main.dependsOn(iosMain) iosArm64Main.dependsOn(iosMain) - project.evaluate() - assertEquals( - 1, task.getAllInteropsGroups().size, + 1, task.allInteropGroups.await().size, "Expected exactly one InteropsGroup for task" ) @@ -144,7 +140,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { private fun `nativeTest nativeMain linux macos`( nativeTestDependsOnNativeMain: Boolean - ) { + ) = project.runLifecycleAwareTest { val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier @@ -169,10 +165,8 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { nativeTest.dependsOn(nativeMain) } - project.evaluate() - assertEquals( - 1, task.getAllInteropsGroups().size, + 1, task.allInteropGroups.await().size, "Expected exactly 1 'SharedInteropsGroup' for task" ) @@ -209,7 +203,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { private fun `nativeTest nativeMain linux macos - test compilation defines custom cinterop`( nativeTestDependsOnNativeMain: Boolean - ) { + ) = project.runLifecycleAwareTest { val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier kotlin.linuxX64("linux").compilations.getByName("test").cinterops.create("anyOtherName").identifier @@ -235,10 +229,9 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { nativeTest.dependsOn(nativeMain) } - project.evaluate() assertEquals( - 1, task.getAllInteropsGroups().size, + 1, task.allInteropGroups.await().size, "Expected exactly 1 'SharedInteropsGroup' for task" ) @@ -271,7 +264,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { `hierarchical project`(testSourceSetsDependOnMainSourceSets = false) } - private fun `hierarchical project`(testSourceSetsDependOnMainSourceSets: Boolean) { + private fun `hierarchical project`(testSourceSetsDependOnMainSourceSets: Boolean) = project.runLifecycleAwareTest { /* Define targets */ val linux = kotlin.linuxX64("linux") val macos = kotlin.macosX64("macos") @@ -368,14 +361,13 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { target.compilations.getByName("test").defaultSourceSet.dependsOn(nativeTest) } - project.evaluate() assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain) assertCInteropDependentEqualsForSourceSetAndCompilation(unixMain) assertCInteropDependentEqualsForSourceSetAndCompilation(appleMain) assertCInteropDependentEqualsForSourceSetAndCompilation(iosMain) - val groups = task.getAllInteropsGroups() + val groups = task.allInteropGroups.await() assertEquals(2, groups.size, "Expected exactly two interop groups: main and test") val nativeCommonizerTarget = SharedCommonizerTarget(nativeTargets.map { it.konanTarget }) @@ -462,7 +454,7 @@ class CInteropCommonizerTaskTest : MultiplatformExtensionTest() { } } - private fun assertCInteropDependentEqualsForSourceSetAndCompilation(sourceSet: KotlinSourceSet) { + private suspend fun assertCInteropDependentEqualsForSourceSetAndCompilation(sourceSet: KotlinSourceSet) { assertEquals( expectCInteropCommonizerDependent(sourceSet), expectCInteropCommonizerDependent(expectSharedNativeCompilation(sourceSet)), diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/MultiplatformExtensionTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/MultiplatformExtensionTest.kt index 2a8c9b918b1..2fd92fd2765 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/MultiplatformExtensionTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/util/MultiplatformExtensionTest.kt @@ -15,11 +15,13 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.targets.metadata.findMetadataCompilation import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDependent import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropIdentifier import org.jetbrains.kotlin.gradle.targets.native.internal.from import kotlin.test.BeforeTest import kotlin.test.assertNotNull +import kotlin.test.fail abstract class MultiplatformExtensionTest { @@ -39,29 +41,29 @@ abstract class MultiplatformExtensionTest { project.enableCInteropCommonization() } - internal fun expectCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent { + internal suspend fun expectCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent { return assertNotNull( CInteropCommonizerDependent.from(compilation), "Can't find SharedInterops for ${compilation.name} compilation" ) } - internal fun expectCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent { + internal suspend fun expectCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent { return assertNotNull( CInteropCommonizerDependent.from(sourceSet), "Can't find SharedInterops for ${sourceSet.name} source set" ) } - internal fun findCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? { + internal suspend fun findCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? { return CInteropCommonizerDependent.from(compilation) } - internal fun findCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { + internal suspend fun findCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? { return CInteropCommonizerDependent.from(sourceSet) } - internal fun expectSharedNativeCompilation(sourceSet: KotlinSourceSet): KotlinSharedNativeCompilation { - return kotlin.targets.flatMap { it.compilations }.filterIsInstance() - .single { it.defaultSourceSet == sourceSet } + internal suspend fun expectSharedNativeCompilation(sourceSet: KotlinSourceSet): KotlinSharedNativeCompilation { + val compilation = project.findMetadataCompilation(sourceSet) ?: fail("Missing metadata compilation for $sourceSet") + return assertIsInstance(compilation) } internal fun KotlinNativeTarget.mainCinteropIdentifier(name: String): CInteropIdentifier {