From b6d811c69cf912780e20bac9122e5c7c957ff7c9 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Tue, 3 Mar 2020 18:29:57 +0300 Subject: [PATCH] Extract common logic of detecting klibs and creating LibraryInfo by klib --- .../resolve/CommonPlatformKindResolution.kt | 69 +++---------------- .../idea/klib/AbstractKlibLibraryInfo.kt | 31 +++++++++ .../org/jetbrains/kotlin/idea/klib/utils.kt | 60 +++++++++++++++- .../ide/konan/KotlinNativePluginUtil.kt | 39 ----------- .../ide/konan/NativePlatformKindResolution.kt | 29 +++----- 5 files changed, 107 insertions(+), 121 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/AbstractKlibLibraryInfo.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt index bfc6ce87742..411662647c1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/caches/resolve/CommonPlatformKindResolution.kt @@ -23,22 +23,18 @@ import org.jetbrains.kotlin.idea.caches.project.LibraryInfo import org.jetbrains.kotlin.idea.caches.project.SdkInfo import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.framework.CommonLibraryKind -import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo -import org.jetbrains.kotlin.idea.util.IJLoggerAdapter -import org.jetbrains.kotlin.library.* -import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo +import org.jetbrains.kotlin.idea.klib.isKlibLibraryRootForPlatform import org.jetbrains.kotlin.platform.CommonPlatforms import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind import org.jetbrains.kotlin.resolve.TargetEnvironment import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment -import java.io.IOException -import java.util.* -import org.jetbrains.kotlin.konan.file.File as KFile class CommonPlatformKindResolution : IdePlatformKindResolution { override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean { - return virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION || virtualFile.isMetadataKlib + return virtualFile.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION || + virtualFile.isKlibLibraryRootForPlatform(CommonPlatforms.defaultCommonPlatform) } override val libraryKind: PersistentLibraryKind<*>? @@ -53,7 +49,9 @@ class CommonPlatformKindResolution : IdePlatformKindResolution { } override fun createLibraryInfo(project: Project, library: Library): List { - val klibFiles = library.getFiles(OrderRootType.CLASSES).filter { it.isMetadataKlib } + val klibFiles = library.getFiles(OrderRootType.CLASSES).filter { + it.isKlibLibraryRootForPlatform(CommonPlatforms.defaultCommonPlatform) + } return if (klibFiles.isNotEmpty()) { klibFiles.mapNotNull { @@ -80,56 +78,7 @@ class CommonPlatformKindResolution : IdePlatformKindResolution { } } -// TODO(dsavvinov): unify with NativeLibraryInfo -class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot: String) : LibraryInfo(project, library) { - - val commonLibrary = resolveSingleFileKlib( - libraryFile = KFile(libraryRoot), - logger = LOG, - strategy = ToolingSingleFileKlibResolveStrategy - ) - - val compatibilityInfo by lazy { commonLibrary.getCompatibilityInfo() } - - override fun getLibraryRoots() = listOf(libraryRoot) - +class CommonKlibLibraryInfo(project: Project, library: Library, libraryRoot: String) : AbstractKlibLibraryInfo(project, library, libraryRoot) { override val platform: TargetPlatform get() = CommonPlatforms.defaultCommonPlatform - - override fun toString() = "CommonKlib" + super.toString() - - companion object { - private val LOG = IJLoggerAdapter.getInstance(CommonKlibLibraryInfo::class.java) - } -} - -val VirtualFile.isMetadataKlib: Boolean - get() { - val extension = extension - if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false - - fun checkComponent(componentFile: VirtualFile): Boolean { - val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false - - val manifestProperties = try { - manifestFile.inputStream.use { Properties().apply { load(it) } } - } catch (_: IOException) { - return false - } - - if (!manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)) return false - - // No builtins_platform property => either a new common klib (we don't write builtins_platform for common) or old Native klib - return manifestProperties.getProperty(KLIB_PROPERTY_BUILTINS_PLATFORM) == null && !componentFile.isLegacyNativeKlibComponent - } - - // run check for library root too - // this is necessary to recognize old style KLIBs that do not have components, and report them to user appropriately - return checkComponent(this) || children?.any(::checkComponent) == true - } - -private val VirtualFile.isLegacyNativeKlibComponent: Boolean - get() { - val irFolder = findChild(KLIB_IR_FOLDER_NAME) - return irFolder != null && irFolder.children.isNotEmpty() - } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/AbstractKlibLibraryInfo.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/AbstractKlibLibraryInfo.kt new file mode 100644 index 00000000000..13acf74384c --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/AbstractKlibLibraryInfo.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2020 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.idea.klib + +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.libraries.Library +import org.jetbrains.kotlin.idea.caches.project.LibraryInfo +import org.jetbrains.kotlin.idea.util.IJLoggerAdapter +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy +import org.jetbrains.kotlin.library.resolveSingleFileKlib + +abstract class AbstractKlibLibraryInfo(project: Project, library: Library, protected val libraryRoot: String) : LibraryInfo(project, library) { + + val resolvedKotlinLibrary = resolveSingleFileKlib( + libraryFile = File(libraryRoot), + logger = LOG, + strategy = ToolingSingleFileKlibResolveStrategy + ) + + val compatibilityInfo by lazy { resolvedKotlinLibrary.getCompatibilityInfo() } + + override fun getLibraryRoots() = listOf(libraryRoot) + + companion object { + private val LOG = IJLoggerAdapter.getInstance(AbstractKlibLibraryInfo::class.java) + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/utils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/utils.kt index d9d2359b49d..66762f68826 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/utils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/klib/utils.kt @@ -5,8 +5,66 @@ package org.jetbrains.kotlin.idea.klib -import org.jetbrains.kotlin.library.KotlinLibrary +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon +import org.jetbrains.kotlin.platform.js.isJs +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlin.platform.konan.isNative import java.io.IOException +import java.util.* + +fun VirtualFile.isKlibLibraryRootForPlatform(targetPlatform: TargetPlatform): Boolean { + // The virtual file for a library packed in a ZIP file will have path like "/some/path/to/the/file.klib!/", + // and therefore will be recognized by VFS as a directory (isDirectory == true). + // So, first, let's check the extension. + val extension = extension + if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false + + // run check for library root too + // this is necessary to recognize old style KLIBs that do not have components, and report tem to user appropriately + val requestedBuiltInsPlatform = targetPlatform.toBuiltInsPlatform() + return checkKlibComponent(this, requestedBuiltInsPlatform) || + children?.any { checkKlibComponent(it, requestedBuiltInsPlatform) } == true +} + +private fun checkKlibComponent(componentFile: VirtualFile, requestedBuiltInsPlatform: BuiltInsPlatform): Boolean { + val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false + + val manifestProperties = try { + manifestFile.inputStream.use { Properties().apply { load(it) } } + } catch (_: IOException) { + return false + } + + if (!manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)) return false + + // No builtins_platform property => either a new common klib (we don't write builtins_platform for common) or old Native klib + val builtInsPlatformProperty = manifestProperties.getProperty(KLIB_PROPERTY_BUILTINS_PLATFORM) + // TODO(dsavvinov): drop additional legacy check after 1.4 + ?: return requestedBuiltInsPlatform == BuiltInsPlatform.NATIVE && componentFile.isLegacyNativeKlibComponent + + val builtInsPlatform = BuiltInsPlatform.parseFromString(builtInsPlatformProperty) ?: return false + + return builtInsPlatform == requestedBuiltInsPlatform +} + +private fun TargetPlatform.toBuiltInsPlatform() = when { + isCommon() -> BuiltInsPlatform.COMMON + isNative() -> BuiltInsPlatform.NATIVE + isJvm() -> BuiltInsPlatform.JVM + isJs() -> BuiltInsPlatform.JS + else -> throw IllegalArgumentException("Unknown platform $this") +} + +private val VirtualFile.isLegacyNativeKlibComponent: Boolean + get() { + val irFolder = findChild(KLIB_IR_FOLDER_NAME) + return irFolder != null && irFolder.children.isNotEmpty() + } + fun KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try { action() diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt index 927e4df7e1a..0ed5bfe3739 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativePluginUtil.kt @@ -78,42 +78,3 @@ internal object CachingIdeKonanLibraryMetadataLoader : PackageAccessHandler { private val KotlinLibrary.isZipped get() = (this as KotlinLibraryImpl).base.access.layout.isZipped } - -internal val VirtualFile.isKonanLibraryRoot: Boolean - get() { - // The virtual file for a library packed in a ZIP file will have path like "/some/path/to/the/file.klib!/", - // and therefore will be recognized by VFS as a directory (isDirectory == true). - // So, first, let's check the extension. - val extension = extension - if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false - - fun checkComponent(componentFile: VirtualFile): Boolean { - val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false - - val manifestProperties = try { - manifestFile.inputStream.use { Properties().apply { load(it) } } - } catch (_: IOException) { - return false - } - - if (!manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)) return false - - // No builtins_platform property => either a new common klib (we don't write builtins_platform for common) or old Native klib - val builtInsPlatformProperty = manifestProperties.getProperty(KLIB_PROPERTY_BUILTINS_PLATFORM) - ?: return componentFile.isLegacyNativeKlibComponent // TODO(dsavvinov): drop additional legacy check after 1.4 - - val builtInsPlatform = BuiltInsPlatform.parseFromString(builtInsPlatformProperty) ?: return false - - return builtInsPlatform == BuiltInsPlatform.NATIVE - } - - // run check for library root too - // this is necessary to recognize old style KLIBs that do not have components, and report tem to user appropriately - return checkComponent(this) || children?.any(::checkComponent) == true - } - -private val VirtualFile.isLegacyNativeKlibComponent: Boolean - get() { - val irFolder = findChild(KLIB_IR_FOLDER_NAME) - return irFolder != null && irFolder.children.isNotEmpty() - } 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 69ce9b6624a..d192b229351 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 @@ -33,14 +33,10 @@ import org.jetbrains.kotlin.idea.caches.project.SdkInfo import org.jetbrains.kotlin.idea.caches.project.lazyClosure import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider -import org.jetbrains.kotlin.idea.util.IJLoggerAdapter -import org.jetbrains.kotlin.konan.file.File as KFile import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME import org.jetbrains.kotlin.konan.util.KlibMetadataFactories import org.jetbrains.kotlin.library.KotlinLibrary -import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy import org.jetbrains.kotlin.library.isInterop -import org.jetbrains.kotlin.library.resolveSingleFileKlib import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind import org.jetbrains.kotlin.platform.konan.KonanPlatforms @@ -52,6 +48,8 @@ import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescripto import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo import org.jetbrains.kotlin.idea.klib.readSafe +import org.jetbrains.kotlin.idea.klib.AbstractKlibLibraryInfo +import org.jetbrains.kotlin.idea.klib.isKlibLibraryRootForPlatform private val NativeFactories = KlibMetadataFactories(::KonanBuiltIns, NullFlexibleTypeDeserializer) @@ -101,7 +99,8 @@ class NativePlatformKindResolution : IdePlatformKindResolution { ) } - override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean = virtualFile.isKonanLibraryRoot + override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean = + virtualFile.isKlibLibraryRootForPlatform(KonanPlatforms.defaultKonanPlatform) override fun createResolverForModuleFactory( settings: PlatformAnalysisParameters, @@ -175,26 +174,16 @@ private fun ModuleInfo.findNativeStdlib(): NativeLibraryInfo? = .filterIsInstance() .firstOrNull { it.isStdlib && it.compatibilityInfo.isCompatible } -class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: String) : LibraryInfo(project, library) { - - private val nativeLibrary = resolveSingleFileKlib( - libraryFile = KFile(libraryRoot), - logger = LOG, - strategy = ToolingSingleFileKlibResolveStrategy - ) +class NativeLibraryInfo(project: Project, library: Library, libraryRoot: String) : AbstractKlibLibraryInfo(project, library, libraryRoot) { val isStdlib get() = libraryRoot.endsWith(KONAN_STDLIB_NAME) - val compatibilityInfo by lazy { nativeLibrary.getCompatibilityInfo() } - - override fun getLibraryRoots() = listOf(libraryRoot) - override val capabilities: Map, Any?> get() { val capabilities = super.capabilities.toMutableMap() - capabilities += KlibModuleOrigin.CAPABILITY to DeserializedKlibModuleOrigin(nativeLibrary) - capabilities += NATIVE_LIBRARY_CAPABILITY to nativeLibrary - capabilities += ImplicitIntegerCoercion.MODULE_CAPABILITY to nativeLibrary.readSafe(false) { isInterop } + capabilities += KlibModuleOrigin.CAPABILITY to DeserializedKlibModuleOrigin(resolvedKotlinLibrary) + capabilities += NATIVE_LIBRARY_CAPABILITY to resolvedKotlinLibrary + capabilities += ImplicitIntegerCoercion.MODULE_CAPABILITY to resolvedKotlinLibrary.readSafe(false) { isInterop } return capabilities } @@ -204,8 +193,6 @@ class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: Str override fun toString() = "Native" + super.toString() companion object { - private val LOG = IJLoggerAdapter.getInstance(NativeLibraryInfo::class.java) - val NATIVE_LIBRARY_CAPABILITY = ModuleDescriptor.Capability("KotlinNativeLibrary") } }