[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(path: ':kotlin-native:backend.native', configuration: 'cli_bcApiElements')
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.SymbolTable
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.resolverByName
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import org.jetbrains.kotlin.konan.util.KonanHomeProvider
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.abi.*
import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
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.util.Logger
import org.jetbrains.kotlin.util.removeSuffixIfPresent
import java.io.File
import kotlin.system.exitProcess
import org.jetbrains.kotlin.konan.file.File as KFile
private val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer)
@@ -63,6 +65,9 @@ fun printUsage() {
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 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
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
@@ -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) {
protected val moduleHeader: KlibMetadataProtoBuf.Header
@@ -202,7 +207,7 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val repository = requestedRepository?.let {
klibRepoDeprecationWarning.logOnceIfNecessary() // Due to use of "-repository" option.
File(it)
KFile(it)
} ?: defaultRepository
fun info() {
@@ -214,7 +219,7 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val moduleName = ModuleDeserializer(library.moduleHeaderData).moduleName
println("")
println("Resolved to: ${library.libraryName.File().absolutePath}")
println("Resolved to: ${KFile(library.libraryName).absolutePath}")
println("Module name: $moduleName")
println("ABI version: $headerAbiVersion")
println("Compiler version: $headerCompilerVersion")
@@ -235,10 +240,10 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
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 installLibDir = File(repository, libraryTrueName)
val installLibDir = KFile(repository, libraryTrueName)
if (installLibDir.exists) installLibDir.deleteRecursively()
@@ -335,6 +340,55 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
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?) {
logWarning("\"contents\" has been renamed to \"dump-metadata\". Please, use new command name.")
dumpMetadata(output, printSignatures, signatureVersion)
@@ -414,12 +468,12 @@ class Library(val libraryNameOrPath: String, val requestedRepository: String?) {
val currentLanguageVersion = LanguageVersion.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)
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)
private enum class KnownOption(val option: String) {
@@ -428,7 +482,7 @@ private enum class KnownOption(val option: String) {
SIGNATURE_VERSION("-signature-version");
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)
when (command.verb) {
"dump-abi" -> library.dumpAbi(System.out, signatureVersion)
"dump-ir" -> library.dumpIr(System.out, printSignatures, signatureVersion)
"dump-ir-signatures" -> library.dumpIrSignatures(System.out, signatureVersion)
"dump-metadata" -> library.dumpMetadata(System.out, printSignatures, signatureVersion)