Extract common logic of detecting klibs and creating LibraryInfo by klib
This commit is contained in:
committed by
Dmitriy Dolovov
parent
6ccb8cd0a3
commit
b6d811c69c
+9
-60
@@ -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<LibraryInfo> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 <T> KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try {
|
||||
action()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
+8
-21
@@ -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<NativeLibraryInfo>()
|
||||
.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<ModuleDescriptor.Capability<*>, 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<KotlinLibrary>("KotlinNativeLibrary")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user