Fix: Use more precise KLIB compatibility check (by metadata version)
Issue #KT-34811
This commit is contained in:
+6
-12
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.library.KLIB_PROPERTY_METADATA_VERSION
|
||||
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
|
||||
class KlibMetadataVersion(major: Int, minor: Int, patch: Int) : BinaryVersion(major, minor, patch) {
|
||||
class KlibMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
|
||||
|
||||
override fun isCompatible(): Boolean = isCompatibleTo(INSTANCE)
|
||||
|
||||
@@ -18,19 +18,13 @@ class KlibMetadataVersion(major: Int, minor: Int, patch: Int) : BinaryVersion(ma
|
||||
val INSTANCE = KlibMetadataVersion(1, 0, 0)
|
||||
|
||||
@JvmField
|
||||
val INVALID_VERSION = KlibMetadataVersion(-1, -1, -1)
|
||||
val INVALID_VERSION = KlibMetadataVersion()
|
||||
}
|
||||
}
|
||||
|
||||
fun KlibMetadataVersion(vararg values: Int): KlibMetadataVersion {
|
||||
if (values.size != 3) error("Metadata version should be in major.minor.patch format: $values")
|
||||
return KlibMetadataVersion(values[0], values[1], values[2])
|
||||
}
|
||||
|
||||
val KotlinLibrary.metadataVersion: KlibMetadataVersion
|
||||
val KotlinLibrary.metadataVersion: KlibMetadataVersion?
|
||||
get() {
|
||||
val versionString = manifestProperties.getProperty(KLIB_PROPERTY_METADATA_VERSION)
|
||||
val versionIntArray = BinaryVersion.parseVersionArray(versionString)
|
||||
?: error("Could not parse metadata version: $versionString")
|
||||
val versionString = manifestProperties.getProperty(KLIB_PROPERTY_METADATA_VERSION) ?: return null
|
||||
val versionIntArray = BinaryVersion.parseVersionArray(versionString) ?: return null
|
||||
return KlibMetadataVersion(*versionIntArray)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.idea.configuration.KotlinNativeLibraryNameUtil
|
||||
import org.jetbrains.kotlin.idea.versions.UnsupportedAbiVersionNotificationPanelProvider
|
||||
import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
|
||||
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
|
||||
import org.jetbrains.kotlin.library.KotlinAbiVersion
|
||||
|
||||
/** TODO: merge [KotlinNativeABICompatibilityChecker] in the future with [UnsupportedAbiVersionNotificationPanelProvider], KT-34525 */
|
||||
class KotlinNativeABICompatibilityChecker(private val project: Project) : ProjectComponent, Disposable {
|
||||
@@ -84,7 +83,7 @@ class KotlinNativeABICompatibilityChecker(private val project: Project) : Projec
|
||||
private fun getLibrariesToNotifyAbout(): Map<String, NativeLibraryInfo> = synchronized(this) {
|
||||
val incompatibleLibraries = getModuleInfosFromIdeaModel(project).asSequence()
|
||||
.filterIsInstance<NativeLibraryInfo>()
|
||||
.filter { it.safeAbiVersion != KotlinAbiVersion.CURRENT }
|
||||
.filter { !it.metadataInfo.isCompatible }
|
||||
.associateBy { it.libraryRoot }
|
||||
|
||||
val newEntries = if (cachedIncompatibleLibraries.isNotEmpty())
|
||||
@@ -104,23 +103,23 @@ class KotlinNativeABICompatibilityChecker(private val project: Project) : Projec
|
||||
|
||||
val librariesByGroups = HashMap<Pair<LibraryGroup, Boolean>, MutableList<Pair<String, String>>>()
|
||||
librariesToNotify.forEach { (libraryRoot, libraryInfo) ->
|
||||
val isOldAbi = (libraryInfo.safeAbiVersion?.version ?: 0) < KotlinAbiVersion.CURRENT.version
|
||||
val isOldMetadata = (libraryInfo.metadataInfo as? NativeLibraryInfo.MetadataInfo.Incompatible)?.isOlder ?: true
|
||||
val (libraryName, libraryGroup) = parseIDELibraryName(libraryInfo)
|
||||
librariesByGroups.computeIfAbsent(libraryGroup to isOldAbi) { mutableListOf() } += libraryName to libraryRoot
|
||||
librariesByGroups.computeIfAbsent(libraryGroup to isOldMetadata) { mutableListOf() } += libraryName to libraryRoot
|
||||
}
|
||||
|
||||
return librariesByGroups.keys.sortedWith(
|
||||
compareBy(
|
||||
{ (libraryGroup, _) -> libraryGroup },
|
||||
{ (_, isOldAbi) -> isOldAbi }
|
||||
{ (_, isOldMetadata) -> isOldMetadata }
|
||||
)
|
||||
).map { key ->
|
||||
|
||||
val (libraryGroup, isOldAbi) = key
|
||||
val (libraryGroup, isOldMetadata) = key
|
||||
val libraries =
|
||||
librariesByGroups.getValue(key).sortedWith(compareBy(LIBRARY_NAME_COMPARATOR) { (libraryName, _) -> libraryName })
|
||||
|
||||
val compilerVersionText = if (isOldAbi) "an older" else "a newer"
|
||||
val compilerVersionText = if (isOldMetadata) "an older" else "a newer"
|
||||
|
||||
val message = when (libraryGroup) {
|
||||
is LibraryGroup.FromDistribution -> {
|
||||
|
||||
+28
-8
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.analyzer.getCapability
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.metadataVersion
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider
|
||||
@@ -27,10 +29,10 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
|
||||
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.safeAbiVersion
|
||||
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.isCompatible
|
||||
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.Companion.safeMetadataVersion
|
||||
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.MetadataInfo.Compatible
|
||||
import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo.MetadataInfo.Incompatible
|
||||
import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.lazyClosure
|
||||
@@ -57,7 +59,7 @@ fun KotlinLibrary.createPackageFragmentProvider(
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): PackageFragmentProvider? {
|
||||
|
||||
if (!safeAbiVersion.isCompatible) return null
|
||||
if (safeMetadataVersion?.isCompatible() != true) return null
|
||||
|
||||
val libraryProto = CachingIdeKonanLibraryMetadataLoader.loadModuleHeader(this)
|
||||
|
||||
@@ -187,14 +189,33 @@ private fun createKotlinNativeBuiltIns(moduleInfo: ModuleInfo, projectContext: P
|
||||
private fun ModuleInfo.findNativeStdlib(): NativeLibraryInfo? =
|
||||
dependencies().lazyClosure { it.dependencies() }
|
||||
.filterIsInstance<NativeLibraryInfo>()
|
||||
.firstOrNull { it.isStdlib && it.safeAbiVersion.isCompatible }
|
||||
.firstOrNull { it.isStdlib && it.metadataInfo.isCompatible }
|
||||
|
||||
class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: String) : LibraryInfo(project, library) {
|
||||
|
||||
sealed class MetadataInfo {
|
||||
abstract val isCompatible: Boolean
|
||||
|
||||
object Compatible : MetadataInfo() {
|
||||
override val isCompatible get() = true
|
||||
}
|
||||
|
||||
class Incompatible(val isOlder: Boolean) : MetadataInfo() {
|
||||
override val isCompatible get() = false
|
||||
}
|
||||
}
|
||||
|
||||
private val nativeLibrary = createKotlinLibrary(File(libraryRoot))
|
||||
|
||||
val isStdlib get() = libraryRoot.endsWith(KONAN_STDLIB_NAME)
|
||||
val safeAbiVersion get() = nativeLibrary.safeAbiVersion
|
||||
val metadataInfo by lazy {
|
||||
val metadataVersion = nativeLibrary.safeMetadataVersion
|
||||
when {
|
||||
metadataVersion == null -> Incompatible(true) // too old KLIB format, even doesn't have metadata version
|
||||
!metadataVersion.isCompatible() -> Incompatible(!metadataVersion.isAtLeast(KlibMetadataVersion.INSTANCE))
|
||||
else -> Compatible
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLibraryRoots() = listOf(libraryRoot)
|
||||
|
||||
@@ -215,8 +236,7 @@ class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: Str
|
||||
companion object {
|
||||
val NATIVE_LIBRARY_CAPABILITY = ModuleDescriptor.Capability<KotlinLibrary>("KotlinNativeLibrary")
|
||||
|
||||
internal val KotlinLibrary.safeAbiVersion get() = this.readSafe(null) { versions.abiVersion }
|
||||
internal val KotlinAbiVersion?.isCompatible get() = this == KotlinAbiVersion.CURRENT
|
||||
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
|
||||
|
||||
private fun <T> KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try {
|
||||
action()
|
||||
|
||||
+13
-10
@@ -9,11 +9,13 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.BaseComponent
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
|
||||
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
|
||||
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
|
||||
import org.jetbrains.kotlin.library.metadata.parsePackageFragment
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
@@ -39,7 +41,7 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
|
||||
|
||||
private val packageFragmentCache = createConcurrentWeakValueMap<CacheKey, CacheValue<ProtoBuf.PackageFragment>>()
|
||||
private val moduleHeaderCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KlibMetadataProtoBuf.Header>>()
|
||||
private val libraryVersioningCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KotlinLibraryVersioning>>()
|
||||
private val libraryMetadataVersionCache = createConcurrentWeakValueMap<CacheKey, CacheValue<KlibMetadataVersion>>()
|
||||
|
||||
fun getCachedPackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
|
||||
check(packageFragmentFile.extension == KLIB_METADATA_FILE_EXTENSION) {
|
||||
@@ -61,18 +63,18 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
|
||||
}.value
|
||||
}
|
||||
|
||||
private fun isAbiCompatible(libraryRoot: VirtualFile): Boolean {
|
||||
private fun isMetadataCompatible(libraryRoot: VirtualFile): Boolean {
|
||||
val manifestFile = libraryRoot.findChild(KLIB_MANIFEST_FILE_NAME) ?: return false
|
||||
|
||||
val versioning = libraryVersioningCache.computeIfAbsent(CacheKey(manifestFile)) {
|
||||
CacheValue(computeLibraryVersioning(manifestFile))
|
||||
}.value
|
||||
val metadataVersion = libraryMetadataVersionCache.computeIfAbsent(CacheKey(manifestFile)) {
|
||||
CacheValue(computeLibraryMetadataVersion(manifestFile))
|
||||
}.value ?: return false
|
||||
|
||||
return versioning?.abiVersion == KotlinAbiVersion.CURRENT
|
||||
return metadataVersion.isCompatible()
|
||||
}
|
||||
|
||||
private fun computePackageFragment(packageFragmentFile: VirtualFile): ProtoBuf.PackageFragment? {
|
||||
if (!isAbiCompatible(packageFragmentFile.parent.parent.parent))
|
||||
if (!isMetadataCompatible(packageFragmentFile.parent.parent.parent))
|
||||
return null
|
||||
|
||||
return try {
|
||||
@@ -83,7 +85,7 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
|
||||
}
|
||||
|
||||
private fun computeModuleHeader(moduleHeaderFile: VirtualFile): KlibMetadataProtoBuf.Header? {
|
||||
if (!isAbiCompatible(moduleHeaderFile.parent.parent))
|
||||
if (!isMetadataCompatible(moduleHeaderFile.parent.parent))
|
||||
return null
|
||||
|
||||
return try {
|
||||
@@ -93,8 +95,9 @@ class KotlinNativeLoadingMetadataCache : BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeLibraryVersioning(manifestFile: VirtualFile): KotlinLibraryVersioning? = try {
|
||||
Properties().apply { manifestFile.inputStream.use { load(it) } }.readKonanLibraryVersioning()
|
||||
private fun computeLibraryMetadataVersion(manifestFile: VirtualFile): KlibMetadataVersion? = try {
|
||||
val versioning = Properties().apply { manifestFile.inputStream.use { load(it) } }.readKonanLibraryVersioning()
|
||||
versioning.metadataVersion?.let(BinaryVersion.Companion::parseVersionArray)?.let(::KlibMetadataVersion)
|
||||
} catch (_: IOException) {
|
||||
// ignore and cache null value
|
||||
null
|
||||
|
||||
@@ -179,10 +179,10 @@ private fun loadLibrary(location: File): KotlinLibrary {
|
||||
printErrorAndExit("library does not have metadata version specified in manifest: $location")
|
||||
|
||||
val metadataVersion = library.metadataVersion
|
||||
if (!metadataVersion.isCompatible())
|
||||
if (metadataVersion?.isCompatible() != true)
|
||||
printErrorAndExit(
|
||||
"""
|
||||
library has incompatible metadata version $metadataVersion: $location,
|
||||
library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location,
|
||||
please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user