[KLIB tool] Introduce KlibToolOutput for capturing stdout and errors/warnings

With `KlibToolOutput` it's possible to forward the output of each
KLIB tool's command to either system stdout/stderr streams or to
some buffers which then can be read by tests.

^KT-62340
This commit is contained in:
Dmitriy Dolovov
2024-02-16 18:25:46 +01:00
committed by Space Team
parent 5b7c11ae39
commit 9878c1fa16
11 changed files with 220 additions and 154 deletions
@@ -40,7 +40,7 @@ internal class DeclarationPrinter(
private fun Printer.printPlain(header: String, signature: String? = null, suffix: String? = null) { private fun Printer.printPlain(header: String, signature: String? = null, suffix: String? = null) {
if (signature != null) println(signature) if (signature != null) println(signature)
println(if (suffix != null ) header + suffix else header) println(if (suffix != null) header + suffix else header)
} }
private inner class PrinterVisitor : DeclarationDescriptorVisitorEmptyBodies<Unit, Unit>() { private inner class PrinterVisitor : DeclarationDescriptorVisitorEmptyBodies<Unit, Unit>() {
@@ -5,15 +5,14 @@ import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerDesc
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.util.IdSignatureRenderer
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
internal class DefaultKlibSignatureRenderer( internal class DefaultKlibSignatureRenderer(
signatureVersion: KotlinIrSignatureVersion?, private val individualSignatureRenderer: IdSignatureRenderer,
private val prefix: String? = null, private val prefix: String? = null,
) : KlibSignatureRenderer { ) : KlibSignatureRenderer {
private val idSignaturer = KonanIdSignaturer(KonanManglerDesc) private val idSignaturer = KonanIdSignaturer(KonanManglerDesc)
private val individualSignatureRenderer = signatureVersion.getMostSuitableSignatureRenderer()
override fun render(descriptor: DeclarationDescriptor): String? { override fun render(descriptor: DeclarationDescriptor): String? {
val idSignature = if (descriptor is ClassDescriptor && descriptor.kind == ClassKind.ENUM_ENTRY) { val idSignature = if (descriptor is ClassDescriptor && descriptor.kind == ClassKind.ENUM_ENTRY) {
@@ -6,11 +6,10 @@
package org.jetbrains.kotlin.cli.klib package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.IdSignatureRenderer
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
internal class IrSignaturesRenderer(private val output: Appendable, signatureVersion: KotlinIrSignatureVersion?) { internal class IrSignaturesRenderer(private val output: Appendable, private val individualSignatureRenderer: IdSignatureRenderer) {
private val individualSignatureRenderer = signatureVersion.getMostSuitableSignatureRenderer()
fun render(signatures: IrSignaturesExtractor.Signatures) { fun render(signatures: IrSignaturesExtractor.Signatures) {
header("Declared signatures: ${signatures.declaredSignatures.size}") header("Declared signatures: ${signatures.declaredSignatures.size}")
@@ -6,34 +6,37 @@
package org.jetbrains.kotlin.cli.klib package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.library.KotlinIrSignatureVersion import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
import kotlin.system.exitProcess
internal class KlibToolArgumentsParser { internal class KlibToolArgumentsParser(private val output: KlibToolOutput) {
fun parseArguments(rawArgs: Array<String>): KlibToolArguments { fun parseArguments(rawArgs: Array<String>): KlibToolArguments? {
if (rawArgs.size < 2) { if (rawArgs.size < 2) {
printUsage() printUsage()
exitProcess(0) return null
} }
val extraArgs: Map<ExtraOption, List<String>> = parseOptions(rawArgs.drop(2).toTypedArray<String>()) val extraArgs: Map<ExtraOption, List<String>> = parseOptions(rawArgs.drop(2).toTypedArray<String>())
.entries ?.entries
.mapNotNull { (option, values) -> ?.mapNotNull { (option, values) ->
val knownOption = ExtraOption.parseOrNull(option) val knownOption = ExtraOption.parseOrNull(option)
if (knownOption == null) { if (knownOption == null) {
logWarning("Unrecognized command-line argument: $option") output.logWarning("Unrecognized command-line argument: $option")
return@mapNotNull null return@mapNotNull null
} }
knownOption to values knownOption to values
}.toMap() }?.toMap()
?: return null
val signatureVersion = extraArgs[ExtraOption.SIGNATURE_VERSION]?.last()?.let { rawSignatureVersion -> val signatureVersion = extraArgs[ExtraOption.SIGNATURE_VERSION]?.last()?.let { rawSignatureVersion ->
rawSignatureVersion.toIntOrNull()?.let(::KotlinIrSignatureVersion) rawSignatureVersion.toIntOrNull()?.let(::KotlinIrSignatureVersion) ?: run {
?: logError("Invalid signature version: $rawSignatureVersion") output.logError("Invalid signature version: $rawSignatureVersion")
return null
}
} }
if (signatureVersion != null && signatureVersion !in KotlinIrSignatureVersion.CURRENTLY_SUPPORTED_VERSIONS) if (signatureVersion != null && signatureVersion !in KotlinIrSignatureVersion.CURRENTLY_SUPPORTED_VERSIONS) {
logError("Unsupported signature version: ${signatureVersion.number}") output.logError("Unsupported signature version: ${signatureVersion.number}")
return null
}
return KlibToolArguments( return KlibToolArguments(
commandName = rawArgs[0], commandName = rawArgs[0],
@@ -45,15 +48,17 @@ internal class KlibToolArgumentsParser {
) )
} }
private fun parseOptions(args: Array<String>): Map<String, List<String>> { private fun parseOptions(args: Array<String>): Map<String, List<String>>? {
val options = mutableMapOf<String, MutableList<String>>() val options = mutableMapOf<String, MutableList<String>>()
for (index in args.indices step 2) { for (index in args.indices step 2) {
val key = args[index] val key = args[index]
if (key[0] != '-') { if (key[0] != '-') {
logError("Expected a flag with initial dash: $key") output.logError("Expected a flag with initial dash: $key")
return null
} }
if (index + 1 == args.size) { if (index + 1 == args.size) {
logError("Expected an value after $key") output.logError("Expected an value after $key")
return null
} }
val value = listOf(args[index + 1]) val value = listOf(args[index + 1])
options[key]?.addAll(value) ?: options.put(key, value.toMutableList()) options[key]?.addAll(value) ?: options.put(key, value.toMutableList())
@@ -62,7 +67,7 @@ internal class KlibToolArgumentsParser {
} }
private fun printUsage() { private fun printUsage() {
println( output.stderr.appendLine(
""" """
Usage: klib <command> <library> [<option>] Usage: klib <command> <library> [<option>]
@@ -18,12 +18,14 @@ import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.library.KotlinAbiVersion import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
internal class KlibToolIrLinker( internal class KlibToolIrLinker(
output: KlibToolOutput,
module: ModuleDescriptor, module: ModuleDescriptor,
irBuiltIns: IrBuiltIns, irBuiltIns: IrBuiltIns,
symbolTable: SymbolTable symbolTable: SymbolTable
) : KotlinIrLinker(module, KlibToolLogger, irBuiltIns, symbolTable, exportedDependencies = emptyList()) { ) : KotlinIrLinker(module, KlibToolLogger(output), irBuiltIns, symbolTable, exportedDependencies = emptyList()) {
override val fakeOverrideBuilder = IrLinkerFakeOverrideProvider( override val fakeOverrideBuilder = IrLinkerFakeOverrideProvider(
linker = this, linker = this,
symbolTable = symbolTable, symbolTable = symbolTable,
@@ -35,7 +37,7 @@ internal class KlibToolIrLinker(
override val returnUnboundSymbolsIfSignatureNotFound get() = true override val returnUnboundSymbolsIfSignatureNotFound get() = true
override val translationPluginContext get() = TODO("Not needed for ir dumping") override val translationPluginContext get() = shouldNotBeCalled()
override fun createModuleDeserializer( override fun createModuleDeserializer(
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
@@ -7,15 +7,22 @@ package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.util.Logger import org.jetbrains.kotlin.util.Logger
import kotlin.system.exitProcess
internal object KlibToolLogger : Logger, IrMessageLogger { internal class KlibToolLogger(private val output: KlibToolOutput) : Logger, IrMessageLogger {
override fun log(message: String) = println(message) override fun log(message: String) {
override fun warning(message: String) = logWarning(message) output.logInfo(message)
override fun error(message: String) = logWarning(message) }
override fun warning(message: String) {
output.logWarning(message)
}
override fun error(message: String) {
output.logError(message)
}
@Deprecated(Logger.FATAL_DEPRECATION_MESSAGE, ReplaceWith(Logger.FATAL_REPLACEMENT)) @Deprecated(Logger.FATAL_DEPRECATION_MESSAGE, ReplaceWith(Logger.FATAL_REPLACEMENT))
override fun fatal(message: String) = logError(message, withStacktrace = true) override fun fatal(message: String) = throw IllegalStateException("error: $message")
override fun report(severity: IrMessageLogger.Severity, message: String, location: IrMessageLogger.Location?) { override fun report(severity: IrMessageLogger.Severity, message: String, location: IrMessageLogger.Location?) {
when (severity) { when (severity) {
@@ -25,16 +32,3 @@ internal object KlibToolLogger : Logger, IrMessageLogger {
} }
} }
} }
internal fun logWarning(text: String) {
println("warning: $text")
}
internal fun logError(text: String, withStacktrace: Boolean = false): Nothing {
if (withStacktrace)
error("error: $text")
else {
System.err.println("error: $text")
exitProcess(1)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2024 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.cli.klib
/**
* TODO: Consider using [org.jetbrains.kotlin.cli.common.messages.MessageCollector] instead of this class.
* Note: The [org.jetbrains.kotlin.cli.common.messages.MessageCollector] is designed to track an individual short messages
* logged at different levels, but is not supposed to collect plain output (such as, for example, IR dump).
*/
internal class KlibToolOutput(
stdout: Appendable,
val stderr: Appendable
) : Appendable by stdout {
var hasErrors: Boolean = false
private set
internal fun logInfo(text: String) {
stderr.append("info: ").appendLine(text)
}
internal fun logWarning(text: String) {
stderr.append("warning: ").appendLine(text)
}
internal fun logError(text: String) {
hasErrors = true
stderr.append("error: ").appendLine(text)
}
internal fun logErrorWithStackTrace(t: Throwable) {
hasErrors = true
stderr.appendLine(t.stackTraceToString())
}
}
@@ -22,21 +22,16 @@ import org.jetbrains.kotlin.kotlinp.Printer
import org.jetbrains.kotlin.kotlinp.Settings import org.jetbrains.kotlin.kotlinp.Settings
import org.jetbrains.kotlin.kotlinp.klib.* import org.jetbrains.kotlin.kotlinp.klib.*
import org.jetbrains.kotlin.kotlinp.klib.TypeArgumentId.VarianceId import org.jetbrains.kotlin.kotlinp.klib.TypeArgumentId.VarianceId
import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addToStdlib.runIf
internal class KotlinpBasedMetadataDumper( internal class KotlinpBasedMetadataDumper(
output: Appendable, private val output: KlibToolOutput,
printSignatures: Boolean, private val signatureRenderer: IdSignatureRenderer?, // `null` means no signatures should be rendered.
signatureVersion: KotlinIrSignatureVersion?
) { ) {
// `null` means no signatures should be rendered.
private val signatureRenderer = runIf(printSignatures) { signatureVersion.getMostSuitableSignatureRenderer() }
private val printer = Printer(output) private val printer = Printer(output)
@@ -102,7 +97,7 @@ internal class KotlinpBasedMetadataDumper(
private fun prepareSignatureComputer(library: KotlinLibrary, moduleMetadata: KlibModuleMetadata): ExternalSignatureComputer? { private fun prepareSignatureComputer(library: KotlinLibrary, moduleMetadata: KlibModuleMetadata): ExternalSignatureComputer? {
val signatureCollector = SignaturesCollector(signatureRenderer ?: return null) val signatureCollector = SignaturesCollector(signatureRenderer ?: return null)
val moduleDescriptor = ModuleDescriptorLoader.load(library) val moduleDescriptor = ModuleDescriptorLoader(output).load(library)
moduleDescriptor.accept(signatureCollector, Unit) moduleDescriptor.accept(signatureCollector, Unit)
return ExternalSignatureComputer(moduleMetadata, signatureCollector.signatures::get) return ExternalSignatureComputer(moduleMetadata, signatureCollector.signatures::get)
@@ -19,7 +19,9 @@ import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.KotlinNativePaths import org.jetbrains.kotlin.utils.KotlinNativePaths
internal object ModuleDescriptorLoader { internal class ModuleDescriptorLoader(output: KlibToolOutput) {
private val logger = KlibToolLogger(output)
fun load(library: KotlinLibrary): ModuleDescriptorImpl { fun load(library: KotlinLibrary): ModuleDescriptorImpl {
val storageManager = LockBasedStorageManager("klib") val storageManager = LockBasedStorageManager("klib")
@@ -31,7 +33,7 @@ internal object ModuleDescriptorLoader {
emptyList(), emptyList(),
distributionKlib = Distribution(KotlinNativePaths.homePath.absolutePath).klib, distributionKlib = Distribution(KotlinNativePaths.homePath.absolutePath).klib,
skipCurrentDir = true, skipCurrentDir = true,
logger = KlibToolLogger logger = logger
) )
resolver.defaultLinks(noStdLib = false, noDefaultLibs = true, noEndorsedLibs = true).mapTo(defaultModules) { resolver.defaultLinks(noStdLib = false, noDefaultLibs = true, noEndorsedLibs = true).mapTo(defaultModules) {
KlibFactories.DefaultDeserializedDescriptorFactory.createDescriptor(it, languageVersionSettings, storageManager, module.builtIns, null) KlibFactories.DefaultDeserializedDescriptorFactory.createDescriptor(it, languageVersionSettings, storageManager, module.builtIns, null)
@@ -45,7 +47,9 @@ internal object ModuleDescriptorLoader {
return module return module
} }
val languageVersionSettings = LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE) companion object {
val languageVersionSettings = LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE)
private val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer) private val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer)
}
} }
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerDesc
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.util.DumpIrTreeOptions import org.jetbrains.kotlin.ir.util.DumpIrTreeOptions
import org.jetbrains.kotlin.ir.util.IdSignatureRenderer
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.KonanLibrary
@@ -27,25 +28,27 @@ import org.jetbrains.kotlin.library.unpackZippedKonanLibraryTo
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.util.removeSuffixIfPresent import org.jetbrains.kotlin.util.removeSuffixIfPresent
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.File import java.io.File
import kotlin.system.exitProcess
import org.jetbrains.kotlin.konan.file.File as KFile import org.jetbrains.kotlin.konan.file.File as KFile
private val defaultRepository = KFile(DependencyDirectories.localKonanDir.resolve("klib").absolutePath) private val defaultRepository = KFile(DependencyDirectories.localKonanDir.resolve("klib").absolutePath)
private class KlibRepoDeprecationWarning { private class KlibRepoDeprecationWarning(private val output: KlibToolOutput) {
private var alreadyLogged = false private var alreadyLogged = false
fun logOnceIfNecessary() { fun logOnceIfNecessary() {
if (!alreadyLogged) { if (!alreadyLogged) {
alreadyLogged = true alreadyLogged = true
logWarning("Local KLIB repositories to be dropped soon. See https://youtrack.jetbrains.com/issue/KT-61098") output.logWarning("Local KLIB repositories to be dropped soon. See https://youtrack.jetbrains.com/issue/KT-61098")
} }
} }
} }
internal class KlibToolCommand(private val args: KlibToolArguments) { internal class KlibToolCommand(private val output: KlibToolOutput, private val args: KlibToolArguments) {
private val klibRepoDeprecationWarning = KlibRepoDeprecationWarning() private val klibRepoDeprecationWarning = KlibRepoDeprecationWarning(output)
private val repository = args.repository?.let { private val repository = args.repository?.let {
klibRepoDeprecationWarning.logOnceIfNecessary() // Due to use of "-repository" option. klibRepoDeprecationWarning.logOnceIfNecessary() // Due to use of "-repository" option.
@@ -60,17 +63,16 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
val headerMetadataVersion = library.versions.metadataVersion val headerMetadataVersion = library.versions.metadataVersion
val moduleName = parseModuleHeader(library.moduleHeaderData).moduleName val moduleName = parseModuleHeader(library.moduleHeaderData).moduleName
println("") output.appendLine()
println("Resolved to: ${KFile(library.libraryName).absolutePath}") output.appendLine("Resolved to: ${KFile(library.libraryName).absolutePath}")
println("Module name: $moduleName") output.appendLine("Module name: $moduleName")
println("ABI version: $headerAbiVersion") output.appendLine("ABI version: $headerAbiVersion")
println("Compiler version: $headerCompilerVersion") output.appendLine("Compiler version: $headerCompilerVersion")
println("Library version: $headerLibraryVersion") output.appendLine("Library version: $headerLibraryVersion")
println("Metadata version: $headerMetadataVersion") output.appendLine("Metadata version: $headerMetadataVersion")
if (library is KonanLibrary) { if (library is KonanLibrary) {
val targets = library.targetList.joinToString(", ") output.appendLine("Available targets: ${library.targetList.joinToString()}")
print("Available targets: $targets\n")
} }
} }
@@ -78,7 +80,7 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
klibRepoDeprecationWarning.logOnceIfNecessary() klibRepoDeprecationWarning.logOnceIfNecessary()
if (!repository.exists) { if (!repository.exists) {
logWarning("Repository does not exist: $repository. Creating...") output.logWarning("Repository does not exist: $repository. Creating...")
repository.mkdirs() repository.mkdirs()
} }
@@ -95,36 +97,35 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
fun remove() { fun remove() {
klibRepoDeprecationWarning.logOnceIfNecessary() klibRepoDeprecationWarning.logOnceIfNecessary()
if (!repository.exists) logError("Repository does not exist: $repository") if (!repository.exists) {
output.logError("Repository does not exist: $repository")
val library = try { return
libraryInRepo(repository, args.libraryNameOrPath) }
} catch (e: Throwable) {
println(e.message) runCatching {
null libraryInRepo(repository, args.libraryNameOrPath).libraryFile.deleteRecursively()
} }
library?.libraryFile?.deleteRecursively()
} }
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
fun dumpIr(output: Appendable) { fun dumpIr() {
val library = libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath) val library = libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath)
checkLibraryHasIr(library)
if (!checkLibraryHasIr(library)) return
if (args.signatureVersion != null && args.signatureVersion != KotlinIrSignatureVersion.V2) { if (args.signatureVersion != null && args.signatureVersion != KotlinIrSignatureVersion.V2) {
// TODO: support passing any signature version through `DumpIrTreeOptions`, KT-62828 // TODO: support passing any signature version through `DumpIrTreeOptions`, KT-62828
logWarning("using a non-default signature version in \"dump-ir\" is not supported yet") output.logWarning("using a non-default signature version in \"dump-ir\" is not supported yet")
} }
val module = ModuleDescriptorLoader.load(library) val module = ModuleDescriptorLoader(output).load(library)
val idSignaturer = KonanIdSignaturer(KonanManglerDesc) val idSignaturer = KonanIdSignaturer(KonanManglerDesc)
val symbolTable = SymbolTable(idSignaturer, IrFactoryImpl) val symbolTable = SymbolTable(idSignaturer, IrFactoryImpl)
val typeTranslator = TypeTranslatorImpl(symbolTable, ModuleDescriptorLoader.languageVersionSettings, module) val typeTranslator = TypeTranslatorImpl(symbolTable, ModuleDescriptorLoader.languageVersionSettings, module)
val irBuiltIns = IrBuiltInsOverDescriptors(module.builtIns, typeTranslator, symbolTable) val irBuiltIns = IrBuiltInsOverDescriptors(module.builtIns, typeTranslator, symbolTable)
val linker = KlibToolIrLinker(module, irBuiltIns, symbolTable) val linker = KlibToolIrLinker(output, module, irBuiltIns, symbolTable)
module.allDependencyModules.forEach { module.allDependencyModules.forEach {
linker.deserializeOnlyHeaderModule(it, it.kotlinLibrary) linker.deserializeOnlyHeaderModule(it, it.kotlinLibrary)
linker.resolveModuleDeserializer(it, null).init() linker.resolveModuleDeserializer(it, null).init()
@@ -137,19 +138,22 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
} }
@OptIn(ExperimentalLibraryAbiReader::class) @OptIn(ExperimentalLibraryAbiReader::class)
fun dumpAbi(output: Appendable) { fun dumpAbi() {
val library = libraryInCurrentDir(args.libraryNameOrPath) val library = libraryInCurrentDir(args.libraryNameOrPath)
checkLibraryHasIr(library)
if (!checkLibraryHasIr(library)) return
val abiSignatureVersion = args.signatureVersion?.let { signatureVersion -> val abiSignatureVersion = args.signatureVersion?.let { signatureVersion ->
signatureVersion.checkSupportedInLibrary(library) if (!signatureVersion.checkSupportedInLibrary(library)) return
val abiSignatureVersion = AbiSignatureVersion.resolveByVersionNumber(signatureVersion.number) val abiSignatureVersion = AbiSignatureVersion.resolveByVersionNumber(signatureVersion.number)
if (!abiSignatureVersion.isSupportedByAbiReader) if (!abiSignatureVersion.isSupportedByAbiReader) {
logError( output.logError(
"Signature version ${signatureVersion.number} is not supported by the KLIB ABI reader." + "Signature version ${signatureVersion.number} is not supported by the KLIB ABI reader." +
" Supported versions: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}" " Supported versions: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}"
) )
return
}
abiSignatureVersion abiSignatureVersion
} ?: run { } ?: run {
@@ -161,13 +165,15 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
.sortedDescending() .sortedDescending()
.firstNotNullOfOrNull { versionsSupportedByAbiReader[it] } .firstNotNullOfOrNull { versionsSupportedByAbiReader[it] }
if (abiSignatureVersion == null) if (abiSignatureVersion == null) {
logError( output.logError(
"There is no signature version that would be both supported in library ${library.libraryFile}" + "There is no signature version that would be both supported in library ${library.libraryFile}" +
" and by the KLIB ABI reader. Supported versions in the library:" + " and by the KLIB ABI reader. Supported versions in the library:" +
" ${library.versions.irSignatureVersions.joinToString { it.number.toString() }}" + " ${library.versions.irSignatureVersions.joinToString { it.number.toString() }}" +
". Supported versions by the KLIB ABI reader: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}" ". Supported versions by the KLIB ABI reader: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}"
) )
return
}
abiSignatureVersion abiSignatureVersion
} }
@@ -186,79 +192,119 @@ internal class KlibToolCommand(private val args: KlibToolArguments) {
} }
// TODO: This command is deprecated. Drop it after 2.0. KT-65380 // TODO: This command is deprecated. Drop it after 2.0. KT-65380
fun contents(output: Appendable) { fun contents() {
logWarning("\"contents\" has been renamed to \"dump-metadata\". Please, use new command name.") output.logWarning("\"contents\" has been renamed to \"dump-metadata\". Please, use new command name.")
val module = ModuleDescriptorLoader.load(libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath))
val signatureRenderer = if (args.printSignatures) val idSignatureRenderer = args.signatureVersion.getMostSuitableSignatureRenderer() ?: return
DefaultKlibSignatureRenderer(args.signatureVersion, "// Signature: ")
val module = ModuleDescriptorLoader(output).load(libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath))
val klibSignatureRenderer = if (args.printSignatures)
DefaultKlibSignatureRenderer(idSignatureRenderer, "// Signature: ")
else else
KlibSignatureRenderer.NO_SIGNATURE KlibSignatureRenderer.NO_SIGNATURE
val printer = DeclarationPrinter(output, signatureRenderer) val printer = DeclarationPrinter(output, klibSignatureRenderer)
printer.print(module) printer.print(module)
} }
fun dumpMetadata(output: Appendable) { fun dumpMetadata() {
KotlinpBasedMetadataDumper(output, args.printSignatures, args.signatureVersion).dumpLibrary(libraryInCurrentDir(args.libraryNameOrPath), args.testMode) val idSignatureRenderer: IdSignatureRenderer? = runIf(args.printSignatures) {
args.signatureVersion.getMostSuitableSignatureRenderer() ?: return
}
KotlinpBasedMetadataDumper(output, idSignatureRenderer).dumpLibrary(libraryInCurrentDir(args.libraryNameOrPath), args.testMode)
} }
fun signatures(output: Appendable) { fun signatures() {
logWarning("\"signatures\" has been renamed to \"dump-metadata-signatures\". Please, use new command name.") output.logWarning("\"signatures\" has been renamed to \"dump-metadata-signatures\". Please, use new command name.")
dumpMetadataSignatures(output) dumpMetadataSignatures()
} }
fun dumpMetadataSignatures(output: Appendable) { fun dumpMetadataSignatures() {
val module = ModuleDescriptorLoader.load(libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath))
// Don't call `checkSupportedInLibrary()` - the signatures are anyway generated on the fly. // Don't call `checkSupportedInLibrary()` - the signatures are anyway generated on the fly.
val printer = SignaturePrinter(output, DefaultKlibSignatureRenderer(args.signatureVersion))
val idSignatureRenderer = args.signatureVersion.getMostSuitableSignatureRenderer() ?: return
val module = ModuleDescriptorLoader(output).load(libraryInRepoOrCurrentDir(repository, args.libraryNameOrPath))
val printer = SignaturePrinter(output, DefaultKlibSignatureRenderer(idSignatureRenderer))
printer.print(module) printer.print(module)
} }
fun dumpIrSignatures(output: Appendable) { fun dumpIrSignatures() {
val library = libraryInCurrentDir(args.libraryNameOrPath) val library = libraryInCurrentDir(args.libraryNameOrPath)
checkLibraryHasIr(library)
args.signatureVersion?.checkSupportedInLibrary(library) if (!checkLibraryHasIr(library) || !args.signatureVersion.checkSupportedInLibrary(library)) return
val idSignatureRenderer = args.signatureVersion.getMostSuitableSignatureRenderer() ?: return
val signatures = IrSignaturesExtractor(library).extract() val signatures = IrSignaturesExtractor(library).extract()
IrSignaturesRenderer(output, args.signatureVersion).render(signatures) IrSignaturesRenderer(output, idSignatureRenderer).render(signatures)
} }
private fun checkLibraryHasIr(library: KotlinLibrary) { private fun checkLibraryHasIr(library: KotlinLibrary): Boolean {
if (!library.hasIr) logError("Library ${library.libraryFile} is an IR-less library") if (!library.hasIr) {
output.logError("Library ${library.libraryFile} is an IR-less library")
return false
}
return true
} }
private fun KotlinIrSignatureVersion.checkSupportedInLibrary(library: KotlinLibrary) { private fun KotlinIrSignatureVersion?.checkSupportedInLibrary(library: KotlinLibrary): Boolean {
val supportedSignatureVersions = library.versions.irSignatureVersions if (this != null) {
if (this !in supportedSignatureVersions) val supportedSignatureVersions = library.versions.irSignatureVersions
logError("Signature version ${this.number} is not supported in library ${library.libraryFile}." + if (this !in supportedSignatureVersions) {
" Supported versions: ${supportedSignatureVersions.joinToString { it.number.toString() }}") output.logError("Signature version ${this.number} is not supported in library ${library.libraryFile}." +
" Supported versions: ${supportedSignatureVersions.joinToString { it.number.toString() }}")
return false
}
}
return true
} }
private fun KotlinIrSignatureVersion?.getMostSuitableSignatureRenderer(): IdSignatureRenderer? = when (this) {
KotlinIrSignatureVersion.V1 -> IdSignatureRenderer.LEGACY
null, KotlinIrSignatureVersion.V2 -> IdSignatureRenderer.DEFAULT
else -> {
output.logError("Unsupported signature version: $number")
null
}
}
private fun libraryInRepo(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), skipCurrentDir = true, logger = KlibToolLogger(output)).resolve(name)
private fun libraryInCurrentDir(name: String) = resolverByName(emptyList(), logger = KlibToolLogger(output)).resolve(name)
private fun libraryInRepoOrCurrentDir(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), logger = KlibToolLogger(output)).resolve(name)
} }
private fun libraryInRepo(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), skipCurrentDir = true, logger = KlibToolLogger).resolve(name)
private fun libraryInCurrentDir(name: String) = resolverByName(emptyList(), logger = KlibToolLogger).resolve(name)
private fun libraryInRepoOrCurrentDir(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), logger = KlibToolLogger).resolve(name)
fun main(rawArgs: Array<String>) { fun main(rawArgs: Array<String>) {
val args = KlibToolArgumentsParser().parseArguments(rawArgs) val output = KlibToolOutput(stdout = System.out, stderr = System.err)
val command = KlibToolCommand(args)
when (args.commandName) { val args = KlibToolArgumentsParser(output).parseArguments(rawArgs)
"dump-abi" -> command.dumpAbi(System.out) if (args != null) {
"dump-ir" -> command.dumpIr(System.out) val command = KlibToolCommand(output, args)
"dump-ir-signatures" -> command.dumpIrSignatures(System.out)
"dump-metadata" -> command.dumpMetadata(System.out) try {
"dump-metadata-signatures" -> command.dumpMetadataSignatures(System.out) when (args.commandName) {
"contents" -> command.contents(System.out) "dump-abi" -> command.dumpAbi()
"signatures" -> command.signatures(System.out) "dump-ir" -> command.dumpIr()
"info" -> command.info() "dump-ir-signatures" -> command.dumpIrSignatures()
"install" -> command.install() "dump-metadata" -> command.dumpMetadata()
"remove" -> command.remove() "dump-metadata-signatures" -> command.dumpMetadataSignatures()
else -> logError("Unknown command: ${args.commandName}") "contents" -> command.contents()
"signatures" -> command.signatures()
"info" -> command.info()
"install" -> command.install()
"remove" -> command.remove()
else -> output.logError("Unknown command: ${args.commandName}")
}
} catch (t: Throwable) {
output.logErrorWithStackTrace(t)
}
} }
if (output.hasErrors)
exitProcess(1)
} }
@@ -1,15 +0,0 @@
/*
* Copyright 2010-2023 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.cli.klib
import org.jetbrains.kotlin.ir.util.IdSignatureRenderer
import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
internal fun KotlinIrSignatureVersion?.getMostSuitableSignatureRenderer() = when (this) {
KotlinIrSignatureVersion.V1 -> IdSignatureRenderer.LEGACY
null, KotlinIrSignatureVersion.V2 -> IdSignatureRenderer.DEFAULT
else -> error("Unsupported signature version: $number")
}