diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerParameters.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerParameters.kt index ca1b747521f..52cf89ef12e 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerParameters.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerParameters.kt @@ -13,7 +13,6 @@ class CommonizerParameters( ) { // use linked hash map to preserve order private val _targetProviders = LinkedHashMap() - val targetProviders: List get() = _targetProviders.values.toList() val sharedTarget: SharedTarget get() = SharedTarget(_targetProviders.keys) @@ -31,5 +30,13 @@ class CommonizerParameters( return this } + private var _resultsConsumer: ResultsConsumer? = null + var resultsConsumer: ResultsConsumer + get() = _resultsConsumer ?: error("Results consumer has not been set") + set(value) { + check(_resultsConsumer == null) + _resultsConsumer = value + } + fun hasAnythingToCommonize(): Boolean = _targetProviders.size >= 2 } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerResult.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerResult.kt deleted file mode 100644 index f61af56ce32..00000000000 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/CommonizerResult.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2010-2019 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.descriptors.commonizer - -import org.jetbrains.kotlin.library.SerializedMetadata -import java.io.File - -sealed class CommonizerResult { - object NothingToDo : CommonizerResult() - - class Done( - val modulesByTargets: Map> - ) : CommonizerResult() { - val sharedTarget: SharedTarget by lazy { modulesByTargets.keys.filterIsInstance().single() } - val leafTargets: Set by lazy { modulesByTargets.keys.filterIsInstance().toSet() } - } -} - -sealed class ModuleResult { - abstract val libraryName: String - - class Missing(val originalLocation: File) : ModuleResult() { - override val libraryName: String get() = originalLocation.name - } - - class Commonized(override val libraryName: String, val metadata: SerializedMetadata) : ModuleResult() -} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ResultsConsumer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ResultsConsumer.kt new file mode 100644 index 00000000000..dfa7e9d584d --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ResultsConsumer.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2021 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.descriptors.commonizer + +import org.jetbrains.kotlin.library.SerializedMetadata +import java.io.File + +interface ResultsConsumer { + enum class Status { NOTHING_TO_DO, DONE } + + sealed class ModuleResult { + abstract val libraryName: String + + class Missing(val originalLocation: File) : ModuleResult() { + override val libraryName: String get() = originalLocation.name + } + + class Commonized(override val libraryName: String, val metadata: SerializedMetadata) : ModuleResult() + } + + fun consumeResults(target: CommonizerTarget, moduleResults: Collection) + + fun successfullyFinished(status: Status) +} diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt index 95b33e9c7b4..10e5ef1d21c 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.descriptors.commonizer import kotlinx.metadata.klib.ChunkedKlibModuleFragmentWriteStrategy +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.ModuleResult +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.Status import org.jetbrains.kotlin.descriptors.commonizer.core.CommonizationVisitor import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.CirNode.Companion.dimension @@ -15,9 +17,12 @@ import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager -fun runCommonization(parameters: CommonizerParameters): CommonizerResult { - if (!parameters.hasAnythingToCommonize()) - return CommonizerResult.NothingToDo +fun runCommonization(parameters: CommonizerParameters) { + val resultsConsumer = parameters.resultsConsumer + if (!parameters.hasAnythingToCommonize()) { + resultsConsumer.successfullyFinished(Status.NOTHING_TO_DO) + return + } val storageManager = LockBasedStorageManager("Declarations commonization") @@ -25,7 +30,6 @@ fun runCommonization(parameters: CommonizerParameters): CommonizerResult { val mergedTree = mergeResult.root // build resulting declarations: - val modulesByTargets = LinkedHashMap>() // use linked hash map to preserve order val klibFragmentWriteStrategy = ChunkedKlibModuleFragmentWriteStrategy() for (targetIndex in 0 until mergedTree.dimension) { @@ -39,16 +43,15 @@ fun runCommonization(parameters: CommonizerParameters): CommonizerResult { ModuleResult.Commonized(libraryName, serializedMetadata) } - parameters.progressLogger?.invoke("Built metadata for ${target.prettyCommonizedName(parameters.sharedTarget)}") val missingModules: List = if (target is LeafTarget) mergeResult.missingModuleInfos.getValue(target).map { ModuleResult.Missing(it.originalLocation) } else emptyList() - modulesByTargets[target] = commonizedModules + missingModules + resultsConsumer.consumeResults(target, commonizedModules + missingModules) } - return CommonizerResult.Done(modulesByTargets) + resultsConsumer.successfullyFinished(Status.DONE) } private fun mergeAndCommonize(storageManager: StorageManager, parameters: CommonizerParameters): CirTreeMergeResult { diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt index 1aa657c619e..9a70bf1abbf 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt @@ -14,12 +14,7 @@ import org.jetbrains.kotlin.descriptors.commonizer.utils.ResettableClockMark import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.library.KotlinLibrary -import org.jetbrains.kotlin.library.SerializedMetadata import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy -import org.jetbrains.kotlin.library.impl.BaseWriterImpl -import org.jetbrains.kotlin.library.impl.BuiltInsPlatform -import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter -import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl import org.jetbrains.kotlin.library.resolveSingleFileKlib import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.util.Logger @@ -35,9 +30,7 @@ class NativeDistributionCommonizer( private val statsType: StatsType, private val logger: Logger ) { - enum class StatsType { - RAW, AGGREGATED, NONE - } + enum class StatsType { RAW, AGGREGATED, NONE } private val clockMark = ResettableClockMark() @@ -48,11 +41,8 @@ class NativeDistributionCommonizer( // 1. load libraries val allLibraries = loadLibraries() - // 2. run commonization - val result = commonize(allLibraries) - - // 3. write new libraries - saveModules(allLibraries, result) + // 2. run commonization & write new libraries + commonizeAndSaveResults(allLibraries) logTotal() } @@ -127,7 +117,7 @@ class NativeDistributionCommonizer( return library } - private fun commonize(allLibraries: AllNativeLibraries): CommonizerResult { + private fun commonizeAndSaveResults(allLibraries: AllNativeLibraries) { val statsCollector = when (statsType) { RAW -> RawStatsCollector(targets) AGGREGATED -> AggregatedStatsCollector(targets) @@ -137,6 +127,14 @@ class NativeDistributionCommonizer( val parameters = CommonizerParameters(statsCollector, ::logProgress).apply { val storageManager = LockBasedStorageManager("Commonized modules") + resultsConsumer = NativeDistributionResultsConsumer( + repository = repository, + originalLibraries = allLibraries, + destination = destination, + copyStdlib = copyStdlib, + copyEndorsedLibs = copyEndorsedLibs, + logProgress = ::logProgress + ) dependeeModulesProvider = NativeDistributionStdlibProvider(storageManager, allLibraries.stdlib) allLibraries.librariesByTargets.forEach { (target, librariesToCommonize) -> @@ -154,148 +152,13 @@ class NativeDistributionCommonizer( } } - val result = runCommonization(parameters) + runCommonization(parameters) + statsCollector?.writeTo(FileStatsOutput(destination, statsType.name.toLowerCase())) - - return result - } - - private fun saveModules(originalLibraries: AllNativeLibraries, result: CommonizerResult) { - // optimization: stdlib and endorsed libraries effectively remain the same across all Kotlin/Native targets, - // so they can be just copied to the new destination without running serializer - copyCommonStandardLibraries() - - when (result) { - is CommonizerResult.NothingToDo -> { - // It may happen that all targets to be commonized (or at least all but one target) miss platform libraries. - // In such case commonizer will do nothing and return a special result value 'NothingToCommonize'. - // So, let's just copy platform libraries from the target where they are to the new destination. - originalLibraries.librariesByTargets.forEach { (target, librariesToCommonize) -> - copyTargetAsIs(target, librariesToCommonize.libraries.size) - } - } - - is CommonizerResult.Done -> { - // 'targetsToCopy' are some targets with empty set of platform libraries - val targetsToCopy = originalLibraries.librariesByTargets.keys - result.leafTargets - if (targetsToCopy.isNotEmpty()) { - targetsToCopy.forEach { target -> - val librariesToCommonize = originalLibraries.librariesByTargets.getValue(target) - copyTargetAsIs(target, librariesToCommonize.libraries.size) - } - } - - val targetsToSerialize = result.leafTargets + result.sharedTarget - targetsToSerialize.forEach { target -> - val moduleResults: Collection = result.modulesByTargets.getValue(target) - val prettyTargetName = target.prettyCommonizedName(result.sharedTarget) - - val manifestProvider = when (target) { - is LeafTarget -> originalLibraries.librariesByTargets.getValue(target) - is SharedTarget -> CommonNativeManifestDataProvider(originalLibraries.librariesByTargets.values) - } - - serializeTarget(target, prettyTargetName, moduleResults, manifestProvider) - } - } - } - } - - private fun copyCommonStandardLibraries() { - if (copyStdlib || copyEndorsedLibs) { - repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR) - .resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR) - .listFiles() - ?.filter { it.isDirectory } - ?.let { - if (copyStdlib) { - if (copyEndorsedLibs) it else it.filter { dir -> dir.endsWith(KONAN_STDLIB_NAME) } - } else - it.filter { dir -> !dir.endsWith(KONAN_STDLIB_NAME) } - }?.forEach { libraryOrigin -> - val libraryDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(libraryOrigin.name) - libraryOrigin.copyRecursively(libraryDestination) - } - - val what = listOfNotNull( - "standard library".takeIf { copyStdlib }, - "endorsed libraries".takeIf { copyEndorsedLibs } - ).joinToString(separator = " and ") - - logProgress("Copied $what") - } - } - - private fun copyTargetAsIs(leafTarget: LeafTarget, librariesCount: Int) { - val librariesDestination = leafTarget.librariesDestination - librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy - - val librariesSource = leafTarget.platformLibrariesSource - if (librariesSource.isDirectory) librariesSource.copyRecursively(librariesDestination) - - logProgress("Copied $librariesCount libraries for [${leafTarget.name}]") - } - - private fun serializeTarget( - target: CommonizerTarget, - prettyTargetName: String, - moduleResults: Collection, - manifestProvider: NativeManifestDataProvider - ) { - val librariesDestination = target.librariesDestination - librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy - - for (moduleResult in moduleResults) { - when (moduleResult) { - is ModuleResult.Commonized -> { - val libraryName = moduleResult.libraryName - - val manifestData = manifestProvider.getManifest(libraryName) - val libraryDestination = librariesDestination.resolve(libraryName) - - writeLibrary(moduleResult.metadata, manifestData, libraryDestination) - } - is ModuleResult.Missing -> { - val libraryName = moduleResult.libraryName - val missingModuleLocation = moduleResult.originalLocation - - missingModuleLocation.copyRecursively(librariesDestination.resolve(libraryName)) - } - } - } - - logProgress("Written libraries for $prettyTargetName") - } - - private fun writeLibrary( - metadata: SerializedMetadata, - manifestData: NativeSensitiveManifestData, - destination: File - ) { - val kDestination = KFile(destination.path) - val layout = KotlinLibraryLayoutForWriter(kDestination, kDestination) - val library = KotlinLibraryWriterImpl( - moduleName = manifestData.uniqueName, - versions = manifestData.versions, - builtInsPlatform = BuiltInsPlatform.NATIVE, - nativeTargets = emptyList(), // will be overwritten with NativeSensitiveManifestData.applyTo() below - nopack = true, - shortName = manifestData.shortName, - layout = layout - ) - library.addMetadata(metadata) - manifestData.applyTo(library.base as BaseWriterImpl) - library.commit() } private val LeafTarget.platformLibrariesSource: File get() = repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR) .resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR) .resolve(name) - - private val CommonizerTarget.librariesDestination: File - get() = when (this) { - is LeafTarget -> destination.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(name) - is SharedTarget -> destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR) - } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionResultsConsumer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionResultsConsumer.kt new file mode 100644 index 00000000000..7ed8442a33e --- /dev/null +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionResultsConsumer.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2010-2021 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.descriptors.commonizer.konan + +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.ModuleResult +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.Status +import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget +import org.jetbrains.kotlin.descriptors.commonizer.LeafTarget +import org.jetbrains.kotlin.descriptors.commonizer.SharedTarget +import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMON_LIBS_DIR +import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR +import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR +import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME +import org.jetbrains.kotlin.library.SerializedMetadata +import org.jetbrains.kotlin.library.impl.BaseWriterImpl +import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter +import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl +import java.io.File +import org.jetbrains.kotlin.konan.file.File as KFile + +internal class NativeDistributionResultsConsumer( + private val repository: File, + private val originalLibraries: AllNativeLibraries, + private val destination: File, + private val copyStdlib: Boolean, + private val copyEndorsedLibs: Boolean, + private val logProgress: (String) -> Unit +) : ResultsConsumer { + private val consumedTargets = LinkedHashSet() + private val allLeafTargets = originalLibraries.librariesByTargets.keys + private val sharedTarget = SharedTarget(allLeafTargets) + + override fun consumeResults(target: CommonizerTarget, moduleResults: Collection) { + val added = consumedTargets.add(target) + check(added) + + serializeTarget(target, moduleResults) + } + + override fun successfullyFinished(status: Status) { + // optimization: stdlib and endorsed libraries effectively remain the same across all Kotlin/Native targets, + // so they can be just copied to the new destination without running serializer + copyCommonStandardLibraries() + + when (status) { + Status.NOTHING_TO_DO -> { + // It may happen that all targets to be commonized (or at least all but one target) miss platform libraries. + // In such case commonizer will do nothing and raise a special status value 'NOTHING_TO_DO'. + // So, let's just copy platform libraries from the target where they are to the new destination. + allLeafTargets.forEach(::copyTargetAsIs) + } + Status.DONE -> { + // 'targetsToCopy' are some leaf targets with empty set of platform libraries + val targetsToCopy = allLeafTargets - consumedTargets.filterIsInstance() + targetsToCopy.forEach(::copyTargetAsIs) + } + } + } + + private fun copyCommonStandardLibraries() { + if (copyStdlib || copyEndorsedLibs) { + repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR) + .resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR) + .listFiles() + ?.filter { it.isDirectory } + ?.let { + if (copyStdlib) { + if (copyEndorsedLibs) it else it.filter { dir -> dir.endsWith(KONAN_STDLIB_NAME) } + } else + it.filter { dir -> !dir.endsWith(KONAN_STDLIB_NAME) } + }?.forEach { libraryOrigin -> + val libraryDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(libraryOrigin.name) + libraryOrigin.copyRecursively(libraryDestination) + } + + val what = listOfNotNull( + "standard library".takeIf { copyStdlib }, + "endorsed libraries".takeIf { copyEndorsedLibs } + ).joinToString(separator = " and ") + + logProgress("Copied $what") + } + } + + private fun copyTargetAsIs(leafTarget: LeafTarget) { + val librariesCount = originalLibraries.librariesByTargets.getValue(leafTarget).libraries.size + + val librariesDestination = leafTarget.librariesDestination + librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy + + val librariesSource = leafTarget.platformLibrariesSource + if (librariesSource.isDirectory) librariesSource.copyRecursively(librariesDestination) + + logProgress("Copied $librariesCount libraries for ${leafTarget.prettyName}") + } + + private fun serializeTarget(target: CommonizerTarget, moduleResults: Collection) { + val librariesDestination = target.librariesDestination + librariesDestination.mkdirs() // always create an empty directory even if there is nothing to copy + + val manifestProvider = when (target) { + is LeafTarget -> originalLibraries.librariesByTargets.getValue(target) + is SharedTarget -> CommonNativeManifestDataProvider(originalLibraries.librariesByTargets.values) + } + + for (moduleResult in moduleResults) { + when (moduleResult) { + is ModuleResult.Commonized -> { + val libraryName = moduleResult.libraryName + + val manifestData = manifestProvider.getManifest(libraryName) + val libraryDestination = librariesDestination.resolve(libraryName) + + writeLibrary(moduleResult.metadata, manifestData, libraryDestination) + } + is ModuleResult.Missing -> { + val libraryName = moduleResult.libraryName + val missingModuleLocation = moduleResult.originalLocation + + missingModuleLocation.copyRecursively(librariesDestination.resolve(libraryName)) + } + } + } + + logProgress("Written libraries for ${target.prettyCommonizedName(sharedTarget)}") + } + + private fun writeLibrary( + metadata: SerializedMetadata, + manifestData: NativeSensitiveManifestData, + libraryDestination: File + ) { + val layout = KFile(libraryDestination.path).let { KotlinLibraryLayoutForWriter(it, it) } + val library = KotlinLibraryWriterImpl( + moduleName = manifestData.uniqueName, + versions = manifestData.versions, + builtInsPlatform = BuiltInsPlatform.NATIVE, + nativeTargets = emptyList(), // will be overwritten with NativeSensitiveManifestData.applyTo() below + nopack = true, + shortName = manifestData.shortName, + layout = layout + ) + library.addMetadata(metadata) + manifestData.applyTo(library.base as BaseWriterImpl) + library.commit() + } + + private val LeafTarget.platformLibrariesSource: File + get() = repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR) + .resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR) + .resolve(name) + + private val CommonizerTarget.librariesDestination: File + get() = when (this) { + is LeafTarget -> destination.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(name) + is SharedTarget -> destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR) + } +} diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt index 2da0e3b75c0..b9eb1cf3357 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt @@ -23,6 +23,8 @@ import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.ModuleResult +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.Status import org.jetbrains.kotlin.descriptors.commonizer.SourceModuleRoot.Companion.SHARED_TARGET_NAME import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.ClassCollector import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.FunctionCollector @@ -72,25 +74,26 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() { val sourceModuleRoots: SourceModuleRoots = SourceModuleRoots.load(getTestDataDir()) val analyzedModules: AnalyzedModules = AnalyzedModules.create(sourceModuleRoots, testRootDisposable) - val result: CommonizerResult = runCommonization(analyzedModules.toCommonizationParameters()) - assertCommonizationPerformed(result) + val results = MockResultsConsumer() + runCommonization(analyzedModules.toCommonizerParameters(results)) + assertEquals(Status.DONE, results.status) val sharedTarget: SharedTarget = analyzedModules.sharedTarget - assertEquals(sharedTarget, result.sharedTarget) + assertEquals(sharedTarget, results.sharedTarget) val sharedModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(sharedTarget) val sharedModuleByCommonizer: SerializedMetadata = - (result.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).metadata + (results.modulesByTargets.getValue(sharedTarget).single() as ModuleResult.Commonized).metadata assertModulesAreEqual(sharedModuleAsExpected, sharedModuleByCommonizer, sharedTarget) val leafTargets: Set = analyzedModules.leafTargets - assertEquals(leafTargets, result.leafTargets) + assertEquals(leafTargets, results.leafTargets) for (leafTarget in leafTargets) { val leafTargetModuleAsExpected: SerializedMetadata = analyzedModules.commonizedModules.getValue(leafTarget) val leafTargetModuleByCommonizer: SerializedMetadata = - (result.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).metadata + (results.modulesByTargets.getValue(leafTarget).single() as ModuleResult.Commonized).metadata assertModulesAreEqual(leafTargetModuleAsExpected, leafTargetModuleByCommonizer, leafTarget) } @@ -203,26 +206,24 @@ private class AnalyzedModules( check(allTargets.containsAll(dependeeModules.keys)) } - fun toCommonizationParameters(): CommonizerParameters { - val parameters = CommonizerParameters() + fun toCommonizerParameters(resultsConsumer: ResultsConsumer) = + CommonizerParameters().also { parameters -> + parameters.resultsConsumer = resultsConsumer + parameters.dependeeModulesProvider = dependeeModules[sharedTarget]?.let(MockModulesProvider::create) - leafTargets.forEach { leafTarget -> - val originalModule = originalModules.getValue(leafTarget) + leafTargets.forEach { leafTarget -> + val originalModule = originalModules.getValue(leafTarget) - parameters.addTarget( - TargetProvider( - target = leafTarget, - modulesProvider = MockModulesProvider.create(originalModule), - dependeeModulesProvider = dependeeModules[leafTarget]?.let(MockModulesProvider::create) + parameters.addTarget( + TargetProvider( + target = leafTarget, + modulesProvider = MockModulesProvider.create(originalModule), + dependeeModulesProvider = dependeeModules[leafTarget]?.let(MockModulesProvider::create) + ) ) - ) + } } - parameters.dependeeModulesProvider = dependeeModules[sharedTarget]?.let(MockModulesProvider::create) - - return parameters - } - companion object { fun create( sourceModuleRoots: SourceModuleRoots, diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/CommonizerFacadeTest.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/CommonizerFacadeTest.kt index b4d1e606437..ae516f38f6d 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/CommonizerFacadeTest.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/CommonizerFacadeTest.kt @@ -5,11 +5,14 @@ package org.jetbrains.kotlin.descriptors.commonizer +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.ModuleResult +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.Status +import org.jetbrains.kotlin.descriptors.commonizer.utils.MockResultsConsumer import org.jetbrains.kotlin.descriptors.commonizer.utils.MockModulesProvider -import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed import org.junit.Test import kotlin.contracts.ExperimentalContracts import kotlin.test.assertEquals +import kotlin.test.assertTrue @ExperimentalContracts class CommonizerFacadeTest { @@ -59,26 +62,32 @@ class CommonizerFacadeTest { ) companion object { - private fun Map>.toCommonizationParameters() = CommonizerParameters().also { - forEach { (targetName, moduleNames) -> - it.addTarget( - TargetProvider( - target = LeafTarget(targetName), - modulesProvider = MockModulesProvider.create(moduleNames), - dependeeModulesProvider = null + private fun Map>.toCommonizerParameters(resultsConsumer: ResultsConsumer) = + CommonizerParameters().also { parameters -> + parameters.resultsConsumer = resultsConsumer + + forEach { (targetName, moduleNames) -> + parameters.addTarget( + TargetProvider( + target = LeafTarget(targetName), + modulesProvider = MockModulesProvider.create(moduleNames), + dependeeModulesProvider = null + ) ) - ) + } } - } private fun doTestNothingToCommonize(originalModules: Map>) { - val result = runCommonization(originalModules.toCommonizationParameters()) - assertEquals(CommonizerResult.NothingToDo, result) + val results = MockResultsConsumer() + runCommonization(originalModules.toCommonizerParameters(results)) + assertEquals(results.status, Status.NOTHING_TO_DO) + assertTrue(results.modulesByTargets.isEmpty()) } private fun doTestSuccessfulCommonization(originalModules: Map>) { - val result = runCommonization(originalModules.toCommonizationParameters()) - assertCommonizationPerformed(result) + val results = MockResultsConsumer() + runCommonization(originalModules.toCommonizerParameters(results)) + assertEquals(results.status, Status.DONE) val expectedCommonModuleNames = mutableSetOf() originalModules.values.forEachIndexed { index, moduleNames -> @@ -90,17 +99,17 @@ class CommonizerFacadeTest { assertModulesMatch( expectedCommonizedModuleNames = expectedCommonModuleNames, expectedMissingModuleNames = emptySet(), - actualModuleResults = result.modulesByTargets.getValue(result.sharedTarget) + actualModuleResults = results.modulesByTargets.getValue(results.sharedTarget) ) - result.leafTargets.forEach { target -> + results.leafTargets.forEach { target -> val allModuleNames = originalModules.getValue(target.name).toSet() val expectedMissingModuleNames = allModuleNames - expectedCommonModuleNames assertModulesMatch( expectedCommonizedModuleNames = expectedCommonModuleNames, expectedMissingModuleNames = expectedMissingModuleNames, - actualModuleResults = result.modulesByTargets.getValue(target) + actualModuleResults = results.modulesByTargets.getValue(target) ) } } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt index 1e4846803bb..fbe7dcf82c4 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/assertions.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils import kotlinx.metadata.klib.KlibModuleMetadata -import org.jetbrains.kotlin.descriptors.commonizer.CommonizerResult import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.MetadataDeclarationsComparator.Mismatch @@ -15,7 +14,6 @@ import org.jetbrains.kotlin.descriptors.commonizer.metadata.utils.SerializedMeta import org.jetbrains.kotlin.library.SerializedMetadata import java.io.File import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.contract import kotlin.test.fail fun assertIsDirectory(file: File) { @@ -23,16 +21,6 @@ fun assertIsDirectory(file: File) { fail("Not a directory: $file") } -@ExperimentalContracts -fun assertCommonizationPerformed(result: CommonizerResult) { - contract { - returns() implies (result is CommonizerResult.Done) - } - - if (result !is CommonizerResult.Done) - fail("$result is not instance of ${CommonizerResult.Done::class}") -} - @ExperimentalContracts fun assertModulesAreEqual(reference: SerializedMetadata, generated: SerializedMetadata, target: CommonizerTarget) { val referenceModule = with(reference) { KlibModuleMetadata.read(SerializedMetadataLibraryProvider(module, fragments, fragmentNames)) } diff --git a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/mocks.kt b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/mocks.kt index a5edf1e34ae..c5137ac07c0 100644 --- a/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/mocks.kt +++ b/native/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/utils/mocks.kt @@ -7,7 +7,8 @@ package org.jetbrains.kotlin.descriptors.commonizer.utils import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider +import org.jetbrains.kotlin.descriptors.commonizer.* +import org.jetbrains.kotlin.descriptors.commonizer.ResultsConsumer.ModuleResult import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider.ModuleInfo import org.jetbrains.kotlin.descriptors.commonizer.cir.factory.CirClassFactory import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.* @@ -175,3 +176,23 @@ internal class MockModulesProvider private constructor( ) } } + +internal class MockResultsConsumer : ResultsConsumer { + private val _modulesByTargets = LinkedHashMap>() // use linked hash map to preserve order + val modulesByTargets: Map> + get() = _modulesByTargets + + val sharedTarget: SharedTarget by lazy { modulesByTargets.keys.filterIsInstance().single() } + val leafTargets: Set by lazy { modulesByTargets.keys.filterIsInstance().toSet() } + + lateinit var status: ResultsConsumer.Status + + override fun consumeResults(target: CommonizerTarget, moduleResults: Collection) { + val oldValue = _modulesByTargets.put(target, moduleResults) + check(oldValue == null) + } + + override fun successfullyFinished(status: ResultsConsumer.Status) { + this.status = status + } +}