[KLIB tool] Introduce a new CLI command: "dump-abi"

With this command it is possble to make an ABI snapshot of any IR-ful library

^KT-62340
This commit is contained in:
Dmitriy Dolovov
2024-01-29 13:53:08 +01:00
committed by Space Team
parent 9d6266d6ad
commit ca0c2f1405
2 changed files with 65 additions and 9 deletions
+1
View File
@@ -23,4 +23,5 @@ dependencies {
implementation project(":kotlin-stdlib") implementation project(":kotlin-stdlib")
implementation project(path: ':kotlin-native:backend.native', configuration: 'cli_bcApiElements') implementation project(path: ':kotlin-native:backend.native', configuration: 'cli_bcApiElements')
implementation project(":kotlin-native:utilities:basic-utils") implementation project(":kotlin-native:utilities:basic-utils")
implementation project(":kotlin-util-klib-abi")
} }
@@ -32,13 +32,13 @@ import org.jetbrains.kotlin.ir.util.DumpIrTreeOptions
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
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.file.File
import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.library.resolverByName import org.jetbrains.kotlin.konan.library.resolverByName
import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.util.DependencyDirectories import org.jetbrains.kotlin.konan.util.DependencyDirectories
import org.jetbrains.kotlin.konan.util.KonanHomeProvider import org.jetbrains.kotlin.konan.util.KonanHomeProvider
import org.jetbrains.kotlin.library.* import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.abi.*
import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
import org.jetbrains.kotlin.library.metadata.kotlinLibrary import org.jetbrains.kotlin.library.metadata.kotlinLibrary
@@ -48,7 +48,9 @@ import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.util.Logger import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.util.removeSuffixIfPresent import org.jetbrains.kotlin.util.removeSuffixIfPresent
import java.io.File
import kotlin.system.exitProcess import kotlin.system.exitProcess
import org.jetbrains.kotlin.konan.file.File as KFile
private val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer) private val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer)
@@ -63,6 +65,9 @@ fun printUsage() {
Install the library to the local repository. Install the library to the local repository.
remove [DEPRECATED] Local KLIB repositories to be dropped soon. See https://youtrack.jetbrains.com/issue/KT-61098 remove [DEPRECATED] Local KLIB repositories to be dropped soon. See https://youtrack.jetbrains.com/issue/KT-61098
Remove the library from the local repository. Remove the library from the local repository.
dump-abi Dump the ABI snapshot of the library. Each line in the snapshot corresponds exactly to one
declaration. Whenever an ABI-incompatible change happens to a declaration, this should
be visible in the corresponding line of the snapshot.
dump-ir Dump the intermediate representation (IR) of all declarations in the library. The output of this dump-ir Dump the intermediate representation (IR) of all declarations in the library. The output of this
command is intended to be used for debugging purposes only. command is intended to be used for debugging purposes only.
dump-ir-signatures Dump IR signatures of all non-private declarations in the library and all non-private declarations dump-ir-signatures Dump IR signatures of all non-private declarations in the library and all non-private declarations
@@ -171,7 +176,7 @@ object KlibToolLogger : Logger, IrMessageLogger {
} }
} }
val defaultRepository = File(DependencyDirectories.localKonanDir.resolve("klib").absolutePath) val defaultRepository = KFile(DependencyDirectories.localKonanDir.resolve("klib").absolutePath)
open class ModuleDeserializer(val library: ByteArray) { open class ModuleDeserializer(val library: ByteArray) {
protected val moduleHeader: KlibMetadataProtoBuf.Header protected val moduleHeader: KlibMetadataProtoBuf.Header
@@ -202,7 +207,7 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val repository = requestedRepository?.let { val repository = requestedRepository?.let {
klibRepoDeprecationWarning.logOnceIfNecessary() // Due to use of "-repository" option. klibRepoDeprecationWarning.logOnceIfNecessary() // Due to use of "-repository" option.
File(it) KFile(it)
} ?: defaultRepository } ?: defaultRepository
fun info() { fun info() {
@@ -214,7 +219,7 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val moduleName = ModuleDeserializer(library.moduleHeaderData).moduleName val moduleName = ModuleDeserializer(library.moduleHeaderData).moduleName
println("") println("")
println("Resolved to: ${library.libraryName.File().absolutePath}") println("Resolved to: ${KFile(library.libraryName).absolutePath}")
println("Module name: $moduleName") println("Module name: $moduleName")
println("ABI version: $headerAbiVersion") println("ABI version: $headerAbiVersion")
println("Compiler version: $headerCompilerVersion") println("Compiler version: $headerCompilerVersion")
@@ -235,10 +240,10 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
repository.mkdirs() repository.mkdirs()
} }
val libraryTrueName = File(libraryNameOrPath).name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) val libraryTrueName = KFile(libraryNameOrPath).name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)
val library = libraryInCurrentDir(libraryNameOrPath) val library = libraryInCurrentDir(libraryNameOrPath)
val installLibDir = File(repository, libraryTrueName) val installLibDir = KFile(repository, libraryTrueName)
if (installLibDir.exists) installLibDir.deleteRecursively() if (installLibDir.exists) installLibDir.deleteRecursively()
@@ -335,6 +340,55 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
output.append(irFragment.dump(DumpIrTreeOptions(printSignatures = printSignatures))) output.append(irFragment.dump(DumpIrTreeOptions(printSignatures = printSignatures)))
} }
@OptIn(ExperimentalLibraryAbiReader::class)
fun dumpAbi(output: Appendable, signatureVersion: KotlinIrSignatureVersion?) {
val library = libraryInCurrentDir(libraryNameOrPath)
checkLibraryHasIr(library)
val abiSignatureVersion = if (signatureVersion != null) {
signatureVersion.checkSupportedInLibrary(library)
val abiSignatureVersion = AbiSignatureVersion.resolveByVersionNumber(signatureVersion.number)
if (!abiSignatureVersion.isSupportedByAbiReader)
logError(
"Signature version ${signatureVersion.number} is not supported by the KLIB ABI reader." +
" Supported versions: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}"
)
abiSignatureVersion
} else {
val versionsSupportedByAbiReader: Map<Int, AbiSignatureVersion> = AbiSignatureVersion.allSupportedByAbiReader
.associateBy { it.versionNumber }
val abiSignatureVersion = library.versions.irSignatureVersions
.map { it.number }
.sortedDescending()
.firstNotNullOfOrNull { versionsSupportedByAbiReader[it] }
if (abiSignatureVersion == null)
logError(
"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:" +
" ${library.versions.irSignatureVersions.joinToString { it.number.toString() }}" +
". Supported versions by the KLIB ABI reader: ${AbiSignatureVersion.allSupportedByAbiReader.joinToString { it.versionNumber.toString() }}"
)
abiSignatureVersion
}
LibraryAbiRenderer.render(
libraryAbi = LibraryAbiReader.readAbiInfo(File(library.libraryFile.absolutePath)),
output = output,
settings = AbiRenderingSettings(
renderedSignatureVersion = abiSignatureVersion,
renderManifest = false,
renderDeclarations = true,
indentationString = " ",
)
)
}
fun contents(output: Appendable, printSignatures: Boolean, signatureVersion: KotlinIrSignatureVersion?) { fun contents(output: Appendable, printSignatures: Boolean, signatureVersion: KotlinIrSignatureVersion?) {
logWarning("\"contents\" has been renamed to \"dump-metadata\". Please, use new command name.") logWarning("\"contents\" has been renamed to \"dump-metadata\". Please, use new command name.")
dumpMetadata(output, printSignatures, signatureVersion) dumpMetadata(output, printSignatures, signatureVersion)
@@ -414,12 +468,12 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val currentLanguageVersion = LanguageVersion.LATEST_STABLE val currentLanguageVersion = LanguageVersion.LATEST_STABLE
val currentApiVersion = ApiVersion.LATEST_STABLE val currentApiVersion = ApiVersion.LATEST_STABLE
fun libraryInRepo(repository: File, name: String) = fun libraryInRepo(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), skipCurrentDir = true, logger = KlibToolLogger).resolve(name) resolverByName(listOf(repository.absolutePath), skipCurrentDir = true, logger = KlibToolLogger).resolve(name)
fun libraryInCurrentDir(name: String) = resolverByName(emptyList(), logger = KlibToolLogger).resolve(name) fun libraryInCurrentDir(name: String) = resolverByName(emptyList(), logger = KlibToolLogger).resolve(name)
fun libraryInRepoOrCurrentDir(repository: File, name: String) = fun libraryInRepoOrCurrentDir(repository: KFile, name: String) =
resolverByName(listOf(repository.absolutePath), logger = KlibToolLogger).resolve(name) resolverByName(listOf(repository.absolutePath), logger = KlibToolLogger).resolve(name)
private enum class KnownOption(val option: String) { private enum class KnownOption(val option: String) {
@@ -428,7 +482,7 @@ private enum class KnownOption(val option: String) {
SIGNATURE_VERSION("-signature-version"); SIGNATURE_VERSION("-signature-version");
companion object { companion object {
fun parseOrNull(option: String): KnownOption? = values().firstOrNull { it.option == option } fun parseOrNull(option: String): KnownOption? = entries.firstOrNull { it.option == option }
} }
} }
@@ -443,6 +497,7 @@ fun main(args: Array<String>) {
val library = Library(command.library, repository) val library = Library(command.library, repository)
when (command.verb) { when (command.verb) {
"dump-abi" -> library.dumpAbi(System.out, signatureVersion)
"dump-ir" -> library.dumpIr(System.out, printSignatures, signatureVersion) "dump-ir" -> library.dumpIr(System.out, printSignatures, signatureVersion)
"dump-ir-signatures" -> library.dumpIrSignatures(System.out, signatureVersion) "dump-ir-signatures" -> library.dumpIrSignatures(System.out, signatureVersion)
"dump-metadata" -> library.dumpMetadata(System.out, printSignatures, signatureVersion) "dump-metadata" -> library.dumpMetadata(System.out, printSignatures, signatureVersion)