diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt index fb608e15e7d..b7384ef29ac 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/KlibMetadataMetadataVersion.kt @@ -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) - } \ No newline at end of file + } diff --git a/idea/idea-gradle-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeABICompatibilityChecker.kt b/idea/idea-gradle-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeABICompatibilityChecker.kt index 82a5b897fb8..2be20ffffe1 100644 --- a/idea/idea-gradle-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeABICompatibilityChecker.kt +++ b/idea/idea-gradle-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeABICompatibilityChecker.kt @@ -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 = synchronized(this) { val incompatibleLibraries = getModuleInfosFromIdeaModel(project).asSequence() .filterIsInstance() - .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, MutableList>>() 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 -> { diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativePlatformKindResolution.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativePlatformKindResolution.kt index 50756377a95..410878a9c4a 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativePlatformKindResolution.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativePlatformKindResolution.kt @@ -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() - .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("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 KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try { action() diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt index fc169337fc4..cfb818e7f4b 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/decompiler/KotlinNativeLoadingMetadataCache.kt @@ -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>() private val moduleHeaderCache = createConcurrentWeakValueMap>() - private val libraryVersioningCache = createConcurrentWeakValueMap>() + private val libraryMetadataVersionCache = createConcurrentWeakValueMap>() 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 diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli.kt index b5892e1687e..866d4ad26c6 100644 --- a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli.kt +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli.kt @@ -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() )