[Commonizer] Re-organize commonizer CLI

This commit is contained in:
Dmitriy Dolovov
2019-12-13 19:29:19 +03:00
parent 805ac2d307
commit f18aa3e0be
8 changed files with 423 additions and 340 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ dependencies {
val runCommonizer by tasks.registering(NoDebugJavaExec::class) {
classpath(sourceSets.main.get().runtimeClasspath)
main = "org.jetbrains.kotlin.descriptors.commonizer.CliKt"
main = "org.jetbrains.kotlin.descriptors.commonizer.cli.NativeDistributionCommonizerKt"
}
sourceSets {
@@ -1,339 +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.backend.common.serialization.DescriptorTable
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.common.serialization.metadata.metadataVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.ModuleForCommonization.DeserializedModule
import org.jetbrains.kotlin.descriptors.commonizer.ModuleForCommonization.SyntheticModule
import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.ir.util.UniqId
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.library.KonanFactories.DefaultDeserializedDescriptorFactory
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.impl.BaseWriterImpl
import org.jetbrains.kotlin.library.impl.KoltinLibraryWriterImpl
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl.Companion.FORWARD_DECLARATIONS_MODULE_NAME
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import java.io.File
import kotlin.system.exitProcess
import kotlin.time.ExperimentalTime
import kotlin.time.MonoClock
import org.jetbrains.kotlin.konan.file.File as KFile
@ExperimentalTime
fun main(args: Array<String>) {
if (args.isEmpty()) printUsageAndExit()
val parsedArgs = parseArgs(args)
val repository = parsedArgs["-repository"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("repository not specified")
if (!repository.isDirectory) printErrorAndExit("repository does not exist: $repository")
val targets = with(HostManager()) {
val targetNames = parsedArgs["-target"]?.toSet() ?: printUsageAndExit("no targets specified")
targetNames.map { targetName ->
targets[targetName] ?: printUsageAndExit("unknown target name: $targetName")
}
}
val destination = parsedArgs["-output"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("output not specified")
when {
!destination.exists() -> destination.mkdirs()
!destination.isDirectory -> printErrorAndExit("output already exists: $destination")
destination.walkTopDown().any { it != destination } -> printErrorAndExit("output is not empty: $destination")
}
val startMark = MonoClock.markNow()
val modulesByTargets = loadModules(repository, targets)
val loadDuration = startMark.elapsedNow()
println("Loaded lazy (uninitialized) libraries in $loadDuration")
val result = commonize(modulesByTargets)
val commonizationDuration = startMark.elapsedNow() - loadDuration
println("Commonization performed in $commonizationDuration")
saveModules(modulesByTargets, destination, result)
val totalDuration = startMark.elapsedNow()
val writeDuration = totalDuration - commonizationDuration - loadDuration
println("Written libraries in $writeDuration")
println("TOTAL: $totalDuration")
println("Done.")
println()
}
private fun parseArgs(args: Array<String>): Map<String, List<String>> {
val commandLine = mutableMapOf<String, MutableList<String>>()
for (index in args.indices step 2) {
val key = args[index]
if (key[0] != '-') printUsageAndExit("Expected a flag with initial dash: $key")
if (index + 1 == args.size) printUsageAndExit("Expected a value after $key")
val value = args[index + 1]
commandLine.computeIfAbsent(key) { mutableListOf() }.add(value)
}
return commandLine
}
private fun printErrorAndExit(errorMessage: String): Nothing {
println("Error: $errorMessage")
println()
exitProcess(1)
}
private fun printUsageAndExit(errorMessage: String? = null): Nothing {
if (errorMessage != null) {
println("Error: $errorMessage")
println()
}
println("Usage: commonizer <options>")
println("where possible options include:")
println("\t-repository <path>\tWork with the specified Kotlin/Native repository")
println("\t-target <name>\t\tAdd hardware target to commonization")
println("\t-output <path>\t\tDestination of commonized KLIBs")
println()
exitProcess(if (errorMessage != null) 1 else 0)
}
private fun loadModules(repository: File, targets: List<KonanTarget>): Map<InputTarget, List<ModuleForCommonization>> {
val stdlibPath = repository.resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME))
val stdlib = loadLibrary(stdlibPath)
val librariesByTargets = targets.map { target ->
val platformLibsPath = repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
.resolve(target.name)
val platformLibs = platformLibsPath.takeIf { it.isDirectory }
?.listFiles()
?.takeIf { it.isNotEmpty() }
?.map { loadLibrary(it) }
?: printErrorAndExit("no platform libraries found for target $target in $platformLibsPath")
InputTarget(target.name, target) to platformLibs
}.toMap()
return librariesByTargets.mapValues { (target, libraries) ->
val storageManager = LockBasedStorageManager("Target $target")
val rawStdlibModule = DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns(
library = stdlib,
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
storageManager = storageManager,
packageAccessHandler = null
)
val otherModules = libraries.map { library ->
val rawModule = DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns(
library = library,
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
storageManager = storageManager,
builtIns = rawStdlibModule.builtIns,
packageAccessHandler = null
)
val data = SensitiveManifestData.readFrom(library)
DeserializedModule(rawModule, data, File(library.libraryFile.path))
}
val rawForwardDeclarationsModule =
createKotlinNativeForwardDeclarationsModule(
storageManager = storageManager,
builtIns = rawStdlibModule.builtIns
)
val onlyDeserializedModules = listOf(rawStdlibModule) + otherModules.map { it.module }
val allModules = onlyDeserializedModules + rawForwardDeclarationsModule
onlyDeserializedModules.forEach { it.setDependencies(allModules) }
val stdlibModule = DeserializedModule(rawStdlibModule, SensitiveManifestData.readFrom(stdlib), File(stdlib.libraryFile.path))
val forwardDeclarationsModule = SyntheticModule(rawForwardDeclarationsModule)
listOf(stdlibModule) + otherModules + forwardDeclarationsModule
}
}
private fun loadLibrary(location: File): KotlinLibrary {
if (!location.isDirectory)
printErrorAndExit("library not found: $location")
val library = createKotlinLibrary(KFile(location.path))
if (library.versions.metadataVersion == null)
printErrorAndExit("library does not have metadata version specified in manifest: $location")
val metadataVersion = library.metadataVersion
if (metadataVersion?.isCompatible() != true)
printErrorAndExit(
"""
library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location,
please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE}
""".trimIndent()
)
return library
}
private fun commonize(modulesByTargets: Map<InputTarget, List<ModuleForCommonization>>): CommonizationPerformed {
val parameters = CommonizationParameters().apply {
modulesByTargets.forEach { (target, modules) ->
addTarget(target, modules.map { it.module })
}
}
val result = runCommonization(parameters)
return when (result) {
is NothingToCommonize -> printUsageAndExit("too few targets specified: ${modulesByTargets.keys}")
is CommonizationPerformed -> result
}
}
private fun saveModules(
originalModulesByTargets: Map<InputTarget, List<ModuleForCommonization>>,
destination: File,
result: CommonizationPerformed
) {
// optimization: stdlib effectively remains the same across all Kotlin/Native targets,
// so it can be just copied to the new destination without running serializer
val stdlibOrigin = originalModulesByTargets.values.asSequence()
.flatten()
.filterIsInstance<DeserializedModule>()
.map { it.location }
.first { it.endsWith(KONAN_STDLIB_NAME) }
val stdlibDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(KONAN_STDLIB_NAME)
stdlibOrigin.copyRecursively(stdlibDestination)
val originalModulesManifestData = originalModulesByTargets.mapValues { (_, modules) ->
modules.asSequence()
.filterIsInstance<DeserializedModule>()
.filter { !it.location.endsWith(KONAN_STDLIB_NAME) }
.associate { it.module.name to it.data }
}
val serializer = KlibMetadataMonolithicSerializer(
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
metadataVersion = KlibMetadataVersion.INSTANCE,
descriptorTable = EmptyDescriptorTable,
skipExpects = false
)
val stdlibName = Name.special("<$KONAN_STDLIB_NAME>")
fun serializeTarget(target: Target) {
val libsDestination: File
val newModulesManifestData: Map<Name, SensitiveManifestData>
when (target) {
is InputTarget -> {
libsDestination = destination.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(target.konanTarget!!.name)
newModulesManifestData = originalModulesManifestData.getValue(target)
}
is OutputTarget -> {
libsDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
newModulesManifestData = originalModulesManifestData.values.first() // just take the first one
}
}
val newModules = result.modulesByTargets.getValue(target)
for (newModule in newModules) {
val libraryName = newModule.name
if (libraryName == stdlibName || libraryName == FORWARD_DECLARATIONS_MODULE_NAME) continue
val metadata = serializer.serializeModule(newModule)
val manifestData = newModulesManifestData.getValue(newModule.name)
val libraryDestination = libsDestination.resolve(libraryName.asString().trimStart('<').trimEnd('>'))
writeLibrary(metadata, manifestData, libraryDestination)
}
}
result.concreteTargets.forEach(::serializeTarget)
result.commonTarget.let(::serializeTarget)
}
private sealed class ModuleForCommonization(val module: ModuleDescriptorImpl) {
class DeserializedModule(
module: ModuleDescriptorImpl,
val data: SensitiveManifestData,
val location: File
) : ModuleForCommonization(module)
class SyntheticModule(module: ModuleDescriptorImpl) : ModuleForCommonization(module)
}
private data class SensitiveManifestData(
val uniqueName: String,
val versions: KotlinLibraryVersioning,
val dependencies: List<String>,
val isInterop: Boolean,
val packageFqName: String?,
val exportForwardDeclarations: List<String>
) {
fun applyTo(library: BaseWriterImpl) {
library.manifestProperties[KLIB_PROPERTY_UNIQUE_NAME] = uniqueName
// note: versions can't be added here
fun addOptionalProperty(name: String, condition: Boolean, value: () -> String) =
if (condition)
library.manifestProperties[name] = value()
else
library.manifestProperties.remove(name)
addOptionalProperty(KLIB_PROPERTY_DEPENDS, dependencies.isNotEmpty()) { dependencies.joinToString(separator = " ") }
addOptionalProperty(KLIB_PROPERTY_INTEROP, isInterop) { "true" }
addOptionalProperty(KLIB_PROPERTY_PACKAGE, packageFqName != null) { packageFqName!! }
addOptionalProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS, exportForwardDeclarations.isNotEmpty()) {
exportForwardDeclarations.joinToString(separator = " ")
}
}
companion object {
fun readFrom(library: KotlinLibrary) = SensitiveManifestData(
uniqueName = library.uniqueName,
versions = library.versions,
dependencies = library.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true),
isInterop = library.isInterop,
packageFqName = library.packageFqName,
exportForwardDeclarations = library.exportForwardDeclarations
)
}
}
private object EmptyDescriptorTable : DescriptorTable {
private const val DEFAULT_UNIQ_ID_INDEX = 0L
override fun put(descriptor: DeclarationDescriptor, uniqId: UniqId) = error("unsupported")
override fun get(descriptor: DeclarationDescriptor): Long = DEFAULT_UNIQ_ID_INDEX
}
private fun writeLibrary(
metadata: SerializedMetadata,
manifestData: SensitiveManifestData,
destination: File
) {
val library = KoltinLibraryWriterImpl(KFile(destination.path), manifestData.uniqueName, manifestData.versions, nopack = true)
library.addMetadata(metadata)
manifestData.applyTo(library.base as BaseWriterImpl)
library.commit()
}
@@ -0,0 +1,30 @@
/*
* 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.cli
import kotlin.system.exitProcess
internal fun parseArgs(
args: Array<String>,
printUsageAndExit: (String) -> Nothing
): Map<String, List<String>> {
val commandLine = mutableMapOf<String, MutableList<String>>()
for (index in args.indices step 2) {
val key = args[index]
if (key[0] != '-') printUsageAndExit("Expected a flag with initial dash: $key")
if (index + 1 == args.size) printUsageAndExit("Expected a value after $key")
val value = args[index + 1]
commandLine.computeIfAbsent(key) { mutableListOf() }.add(value)
}
return commandLine
}
internal fun printErrorAndExit(errorMessage: String): Nothing {
println("Error: $errorMessage")
println()
exitProcess(1)
}
@@ -0,0 +1,54 @@
/*
* 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.cli
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeDistributionCommonizer
import org.jetbrains.kotlin.konan.target.HostManager
import java.io.File
import kotlin.system.exitProcess
import kotlin.time.ExperimentalTime
@ExperimentalTime
fun main(args: Array<String>) {
if (args.isEmpty()) printUsageAndExit()
val parsedArgs = parseArgs(args, ::printUsageAndExit)
val repository = parsedArgs["-repository"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("repository not specified")
val targets = with(HostManager()) {
val targetNames = parsedArgs["-target"]?.toSet() ?: printUsageAndExit("no targets specified")
targetNames.map { targetName ->
targets[targetName] ?: printUsageAndExit("unknown target name: $targetName")
}
}
val destination = parsedArgs["-output"]?.firstOrNull()?.let(::File) ?: printUsageAndExit("output not specified")
NativeDistributionCommonizer(
repository = repository,
targets = targets,
destination = destination,
handleError = ::printErrorAndExit,
log = ::println
).run()
}
private fun printUsageAndExit(errorMessage: String? = null): Nothing {
if (errorMessage != null) {
println("Error: $errorMessage")
println()
}
println("Usage: commonizer <options>")
println("where possible options include:")
println("\t-repository <path>\tWork with the specified Kotlin/Native repository")
println("\t-target <name>\t\tAdd hardware target to commonization")
println("\t-output <path>\t\tDestination of commonized KLIBs")
println()
exitProcess(if (errorMessage != null) 1 else 0)
}
@@ -0,0 +1,253 @@
/*
* 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.konan
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.common.serialization.metadata.metadataVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.commonizer.*
import org.jetbrains.kotlin.descriptors.commonizer.Target
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeModuleForCommonization.DeserializedModule
import org.jetbrains.kotlin.descriptors.commonizer.konan.NativeModuleForCommonization.SyntheticModule
import org.jetbrains.kotlin.descriptors.commonizer.utils.EmptyDescriptorTable
import org.jetbrains.kotlin.descriptors.commonizer.utils.createKotlinNativeForwardDeclarationsModule
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.impl.BaseWriterImpl
import org.jetbrains.kotlin.library.impl.KoltinLibraryWriterImpl
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import java.io.File
import kotlin.time.ExperimentalTime
import kotlin.time.MonoClock
import org.jetbrains.kotlin.konan.file.File as KFile
class NativeDistributionCommonizer(
private val repository: File,
private val targets: List<KonanTarget>,
private val destination: File,
private val handleError: (String) -> Nothing,
private val log: (String) -> Unit
) {
@ExperimentalTime
fun run() {
checkPreconditions()
val startMark = MonoClock.markNow()
val modulesByTargets = loadModules(repository, targets)
val loadDuration = startMark.elapsedNow()
log("Loaded lazy (uninitialized) libraries in $loadDuration")
val result = commonize(modulesByTargets)
val commonizationDuration = startMark.elapsedNow() - loadDuration
log("Commonization performed in $commonizationDuration")
saveModules(modulesByTargets, destination, result)
val totalDuration = startMark.elapsedNow()
val writeDuration = totalDuration - commonizationDuration - loadDuration
log("Written libraries in $writeDuration")
log("TOTAL: $totalDuration")
log("Done.")
log("")
}
private fun checkPreconditions() {
if (!repository.isDirectory)
handleError("repository does not exist: $repository")
when (targets.size) {
0 -> handleError("no targets specified")
1 -> handleError("too few targets specified: $targets")
}
when {
!destination.exists() -> destination.mkdirs()
!destination.isDirectory -> handleError("output already exists: $destination")
destination.walkTopDown().any { it != destination } -> handleError("output is not empty: $destination")
}
}
private fun loadModules(repository: File, targets: List<KonanTarget>): Map<InputTarget, List<NativeModuleForCommonization>> {
val stdlibPath = repository.resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME))
val stdlib = loadLibrary(stdlibPath)
val librariesByTargets = targets.map { target ->
val platformLibsPath = repository.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
.resolve(target.name)
val platformLibs = platformLibsPath.takeIf { it.isDirectory }
?.listFiles()
?.takeIf { it.isNotEmpty() }
?.map { loadLibrary(it) }
?: handleError("no platform libraries found for target $target in $platformLibsPath")
InputTarget(target.name, target) to platformLibs
}.toMap()
return librariesByTargets.mapValues { (target, libraries) ->
val storageManager = LockBasedStorageManager("Target $target")
val rawStdlibModule = KonanFactories.DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns(
library = stdlib,
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
storageManager = storageManager,
packageAccessHandler = null
)
val otherModules = libraries.map { library ->
val rawModule = KonanFactories.DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns(
library = library,
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
storageManager = storageManager,
builtIns = rawStdlibModule.builtIns,
packageAccessHandler = null
)
val data = NativeSensitiveManifestData.readFrom(library)
DeserializedModule(rawModule, data, File(library.libraryFile.path))
}
val rawForwardDeclarationsModule =
createKotlinNativeForwardDeclarationsModule(
storageManager = storageManager,
builtIns = rawStdlibModule.builtIns
)
val onlyDeserializedModules = listOf(rawStdlibModule) + otherModules.map { it.module }
val allModules = onlyDeserializedModules + rawForwardDeclarationsModule
onlyDeserializedModules.forEach { it.setDependencies(allModules) }
val stdlibModule = DeserializedModule(
rawStdlibModule,
NativeSensitiveManifestData.readFrom(stdlib),
File(stdlib.libraryFile.path)
)
val forwardDeclarationsModule = SyntheticModule(rawForwardDeclarationsModule)
listOf(stdlibModule) + otherModules + forwardDeclarationsModule
}
}
private fun loadLibrary(location: File): KotlinLibrary {
if (!location.isDirectory)
handleError("library not found: $location")
val library = createKotlinLibrary(KFile(location.path))
if (library.versions.metadataVersion == null)
handleError("library does not have metadata version specified in manifest: $location")
val metadataVersion = library.metadataVersion
if (metadataVersion?.isCompatible() != true)
handleError(
"""
library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location,
please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE}
""".trimIndent()
)
return library
}
private fun commonize(modulesByTargets: Map<InputTarget, List<NativeModuleForCommonization>>): CommonizationPerformed {
val parameters = CommonizationParameters().apply {
modulesByTargets.forEach { (target, modules) ->
addTarget(target, modules.map { it.module })
}
}
val result = runCommonization(parameters)
return when (result) {
is NothingToCommonize -> handleError("too few targets specified: ${modulesByTargets.keys}")
is CommonizationPerformed -> result
}
}
private fun saveModules(
originalModulesByTargets: Map<InputTarget, List<NativeModuleForCommonization>>,
destination: File,
result: CommonizationPerformed
) {
// optimization: stdlib effectively remains the same across all Kotlin/Native targets,
// so it can be just copied to the new destination without running serializer
val stdlibOrigin = originalModulesByTargets.values.asSequence()
.flatten()
.filterIsInstance<DeserializedModule>()
.map { it.location }
.first { it.endsWith(KONAN_STDLIB_NAME) }
val stdlibDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR).resolve(KONAN_STDLIB_NAME)
stdlibOrigin.copyRecursively(stdlibDestination)
val originalModulesManifestData = originalModulesByTargets.mapValues { (_, modules) ->
modules.asSequence()
.filterIsInstance<DeserializedModule>()
.filter { !it.location.endsWith(KONAN_STDLIB_NAME) }
.associate { it.module.name to it.data }
}
val serializer = KlibMetadataMonolithicSerializer(
languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
metadataVersion = KlibMetadataVersion.INSTANCE,
descriptorTable = EmptyDescriptorTable,
skipExpects = false
)
val stdlibName = Name.special("<$KONAN_STDLIB_NAME>")
fun serializeTarget(target: Target) {
val libsDestination: File
val newModulesManifestData: Map<Name, NativeSensitiveManifestData>
when (target) {
is InputTarget -> {
libsDestination = destination.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR).resolve(target.konanTarget!!.name)
newModulesManifestData = originalModulesManifestData.getValue(target)
}
is OutputTarget -> {
libsDestination = destination.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
newModulesManifestData = originalModulesManifestData.values.first() // just take the first one
}
}
val newModules = result.modulesByTargets.getValue(target)
for (newModule in newModules) {
val libraryName = newModule.name
if (libraryName == stdlibName || libraryName == KlibResolvedModuleDescriptorsFactoryImpl.FORWARD_DECLARATIONS_MODULE_NAME) continue
val metadata = serializer.serializeModule(newModule)
val manifestData = newModulesManifestData.getValue(newModule.name)
val libraryDestination = libsDestination.resolve(libraryName.asString().trimStart('<').trimEnd('>'))
writeLibrary(metadata, manifestData, libraryDestination)
}
}
result.concreteTargets.forEach(::serializeTarget)
result.commonTarget.let(::serializeTarget)
}
private fun writeLibrary(
metadata: SerializedMetadata,
manifestData: NativeSensitiveManifestData,
destination: File
) {
val library = KoltinLibraryWriterImpl(KFile(destination.path), manifestData.uniqueName, manifestData.versions, nopack = true)
library.addMetadata(metadata)
manifestData.applyTo(library.base as BaseWriterImpl)
library.commit()
}
}
@@ -0,0 +1,19 @@
/*
* 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.konan
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import java.io.File
internal sealed class NativeModuleForCommonization(val module: ModuleDescriptorImpl) {
class DeserializedModule(
module: ModuleDescriptorImpl,
val data: NativeSensitiveManifestData,
val location: File
) : NativeModuleForCommonization(module)
class SyntheticModule(module: ModuleDescriptorImpl) : NativeModuleForCommonization(module)
}
@@ -0,0 +1,49 @@
/*
* 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.konan
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.impl.BaseWriterImpl
internal data class NativeSensitiveManifestData(
val uniqueName: String,
val versions: KotlinLibraryVersioning,
val dependencies: List<String>,
val isInterop: Boolean,
val packageFqName: String?,
val exportForwardDeclarations: List<String>
) {
fun applyTo(library: BaseWriterImpl) {
library.manifestProperties[KLIB_PROPERTY_UNIQUE_NAME] = uniqueName
// note: versions can't be added here
fun addOptionalProperty(name: String, condition: Boolean, value: () -> String) =
if (condition)
library.manifestProperties[name] = value()
else
library.manifestProperties.remove(name)
addOptionalProperty(KLIB_PROPERTY_DEPENDS, dependencies.isNotEmpty()) { dependencies.joinToString(separator = " ") }
addOptionalProperty(KLIB_PROPERTY_INTEROP, isInterop) { "true" }
addOptionalProperty(KLIB_PROPERTY_PACKAGE, packageFqName != null) { packageFqName!! }
addOptionalProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS, exportForwardDeclarations.isNotEmpty()) {
exportForwardDeclarations.joinToString(separator = " ")
}
}
companion object {
fun readFrom(library: KotlinLibrary) = NativeSensitiveManifestData(
uniqueName = library.uniqueName,
versions = library.versions,
dependencies = library.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true),
isInterop = library.isInterop,
packageFqName = library.packageFqName,
exportForwardDeclarations = library.exportForwardDeclarations
)
}
}
@@ -0,0 +1,17 @@
/*
* 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.utils
import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.util.UniqId
internal object EmptyDescriptorTable : DescriptorTable {
private const val DEFAULT_UNIQ_ID_INDEX = 0L
override fun put(descriptor: DeclarationDescriptor, uniqId: UniqId) = error("unsupported")
override fun get(descriptor: DeclarationDescriptor): Long = DEFAULT_UNIQ_ID_INDEX
}