Use "tooling" KLIB resolve strategy in IDE and commonizer

Issue #KT-36213
This commit is contained in:
Dmitriy Dolovov
2020-01-30 12:52:07 +07:00
parent 1196044df7
commit 4336096775
7 changed files with 109 additions and 52 deletions
@@ -25,9 +25,8 @@ 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.konan.file.File
import org.jetbrains.kotlin.idea.util.IJLoggerAdapter
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind
@@ -35,6 +34,7 @@ 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 {
@@ -95,7 +95,11 @@ class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot:
}
}
val commonLibrary = createKotlinLibrary(File(libraryRoot))
val commonLibrary = resolveSingleFileKlib(
libraryFile = KFile(libraryRoot),
logger = LOG,
strategy = ToolingSingleFileKlibResolveStrategy
)
val metadataInfo by lazy {
val metadataVersion = commonLibrary.safeMetadataVersion
@@ -114,6 +118,8 @@ class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot:
override fun toString() = "Common" + super.toString()
companion object {
private val LOG = IJLoggerAdapter.getInstance(CommonKlibLibraryInfo::class.java)
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
private fun <T> KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try {
@@ -124,23 +130,28 @@ class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot:
}
}
val VirtualFile.isMetadataKlib: Boolean
get() {
val extension = extension
if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false
val manifestFile = findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false
fun checkComponent(componentFile: VirtualFile): Boolean {
val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false
// native libraries
val irFile = findChild(KLIB_IR_FOLDER_NAME)
if (irFile != null && irFile.children.isNotEmpty()) return false
// native libraries
val irFile = componentFile.findChild(KLIB_IR_FOLDER_NAME)
if (irFile != null && irFile.children.isNotEmpty()) return false
val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) {
return false
val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) {
return false
}
return manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)
}
return manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)
}
// 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
}
@@ -0,0 +1,24 @@
/*
* 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.util
import org.jetbrains.kotlin.util.Logger as KLogger
import com.intellij.openapi.diagnostic.Logger as IJLogger
class IJLoggerAdapter private constructor(private val logger: IJLogger) : KLogger {
override fun log(message: String) = logger.info(message)
override fun warning(message: String) = logger.warn(message)
override fun error(message: String) = fatal(message)
override fun fatal(message: String): Nothing {
logger.error(message)
error(message) // normally, this line of code should not be reached
}
companion object {
fun getInstance(clazz: Class<*>): KLogger = IJLoggerAdapter(IJLogger.getInstance(clazz))
}
}
@@ -86,18 +86,24 @@ internal val VirtualFile.isKonanLibraryRoot: Boolean
val extension = extension
if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false
val manifestFile = findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false
fun checkComponent(componentFile: VirtualFile): Boolean {
val manifestFile = componentFile.findChild(KLIB_MANIFEST_FILE_NAME)?.takeIf { !it.isDirectory } ?: return false
// this is a hacky way to determine whether this is a Kotlin/Native .klib or common .klib (common .klibs don't have ir)
// TODO(dsavvinov): introduce more robust way to detect library platform
val irFolder = findChild(KLIB_IR_FOLDER_NAME)
if (irFolder == null || irFolder.children.isEmpty()) return false
// this is a hacky way to determine whether this is a Kotlin/Native .klib or common .klib (common .klibs don't have ir)
// TODO(dsavvinov): introduce more robust way to detect library platform
val irFolder = componentFile.findChild(KLIB_IR_FOLDER_NAME)
if (irFolder == null || irFolder.children.isEmpty()) return false
val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) {
return false
val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) {
return false
}
return manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)
}
return manifestProperties.containsKey(KLIB_PROPERTY_UNIQUE_NAME)
// 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
}
@@ -38,11 +38,12 @@ 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.konan.file.File
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.impl.createKotlinLibrary
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
import org.jetbrains.kotlin.library.isInterop
import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.platform.TargetPlatform
@@ -192,9 +193,14 @@ class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: Str
}
}
private val nativeLibrary = resolveSingleFileKlib(File(libraryRoot))
private val nativeLibrary = resolveSingleFileKlib(
libraryFile = KFile(libraryRoot),
logger = LOG,
strategy = ToolingSingleFileKlibResolveStrategy
)
val isStdlib get() = libraryRoot.endsWith(KONAN_STDLIB_NAME)
val metadataInfo by lazy {
val metadataVersion = nativeLibrary.safeMetadataVersion
when {
@@ -221,6 +227,8 @@ 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")
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.descriptors.commonizer.cli
import org.jetbrains.kotlin.util.Logger
import kotlin.system.exitProcess
internal fun parseArgs(
@@ -22,9 +23,13 @@ internal fun parseArgs(
return commandLine
}
internal fun printErrorAndExit(errorMessage: String): Nothing {
println("Error: $errorMessage")
println()
internal object CliLoggerAdapter : Logger {
override fun log(message: String) = println(message)
override fun warning(message: String) = println("Warning: $message")
exitProcess(1)
override fun error(message: String) = fatal(message)
override fun fatal(message: String): Nothing {
println("Error: $message\n")
exitProcess(1)
}
}
@@ -35,8 +35,7 @@ fun main(args: Array<String>) {
copyStdlib = true,
copyEndorsedLibs = true,
withStats = withStats,
handleError = ::printErrorAndExit,
log = ::println
logger = CliLoggerAdapter
).run()
}
@@ -21,12 +21,14 @@ import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.SerializedMetadata
import org.jetbrains.kotlin.library.ToolingSingleFileKlibResolveStrategy
import org.jetbrains.kotlin.library.impl.BaseWriterImpl
import org.jetbrains.kotlin.library.impl.KoltinLibraryWriterImpl
import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.util.Logger
import java.io.File
import org.jetbrains.kotlin.konan.file.File as KFile
@@ -37,8 +39,7 @@ class NativeDistributionCommonizer(
private val copyStdlib: Boolean,
private val copyEndorsedLibs: Boolean,
private val withStats: Boolean,
private val handleError: (String) -> Nothing,
private val log: (String) -> Unit
private val logger: Logger
) {
fun run() {
checkPreconditions()
@@ -46,36 +47,35 @@ class NativeDistributionCommonizer(
with(ResettableClockMark()) {
// 1. load modules
val modulesByTargets = loadModules()
log("Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}")
logger.log("Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}")
// 2. run commonization
val result = commonize(modulesByTargets)
log("Commonization performed in ${elapsedSinceLast()}")
logger.log("Commonization performed in ${elapsedSinceLast()}")
// 3. write new libraries
saveModules(modulesByTargets, result)
log("Written libraries in ${elapsedSinceLast()}")
logger.log("Written libraries in ${elapsedSinceLast()}")
log("TOTAL: ${elapsedSinceStart()}")
logger.log("TOTAL: ${elapsedSinceStart()}")
}
log("Done.")
log("")
logger.log("Done.\n")
}
private fun checkPreconditions() {
if (!repository.isDirectory)
handleError("repository does not exist: $repository")
logger.fatal("repository does not exist: $repository")
when (targets.size) {
0 -> handleError("no targets specified")
1 -> handleError("too few targets specified: $targets")
0 -> logger.fatal("no targets specified")
1 -> logger.fatal("too few targets specified: $targets")
}
when {
!destination.exists() -> destination.mkdirs()
!destination.isDirectory -> handleError("output already exists: $destination")
destination.walkTopDown().any { it != destination } -> handleError("output is not empty: $destination")
!destination.isDirectory -> logger.fatal("output already exists: $destination")
destination.walkTopDown().any { it != destination } -> logger.fatal("output is not empty: $destination")
}
}
@@ -92,7 +92,7 @@ class NativeDistributionCommonizer(
?.listFiles()
?.takeIf { it.isNotEmpty() }
?.map { loadLibrary(it) }
?: handleError("no platform libraries found for target $target in $platformLibsPath")
?: logger.fatal("no platform libraries found for target $target in $platformLibsPath")
InputTarget(target.name, target) to platformLibs
}.toMap()
@@ -142,16 +142,20 @@ class NativeDistributionCommonizer(
private fun loadLibrary(location: File): KotlinLibrary {
if (!location.isDirectory)
handleError("library not found: $location")
logger.fatal("library not found: $location")
val library = resolveSingleFileKlib(KFile(location.path))
val library = resolveSingleFileKlib(
libraryFile = KFile(location.path),
logger = logger,
strategy = ToolingSingleFileKlibResolveStrategy
)
if (library.versions.metadataVersion == null)
handleError("library does not have metadata version specified in manifest: $location")
logger.fatal("library does not have metadata version specified in manifest: $location")
val metadataVersion = library.metadataVersion
if (metadataVersion?.isCompatible() != true)
handleError(
logger.fatal(
"""
library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location,
please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE}
@@ -172,7 +176,7 @@ class NativeDistributionCommonizer(
val result = runCommonization(parameters)
return when (result) {
is NothingToCommonize -> handleError("too few targets specified: ${modulesByTargets.keys}")
is NothingToCommonize -> logger.fatal("too few targets specified: ${modulesByTargets.keys}")
is CommonizationPerformed -> result
}
}