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.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind 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.*
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.platform.CommonPlatforms import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind 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 org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
import org.jetbrains.kotlin.konan.file.File as KFile
class CommonPlatformKindResolution : IdePlatformKindResolution { class CommonPlatformKindResolution : IdePlatformKindResolution {
override fun isLibraryFileForPlatform(virtualFile: VirtualFile): Boolean { 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 metadataInfo by lazy {
val metadataVersion = commonLibrary.safeMetadataVersion val metadataVersion = commonLibrary.safeMetadataVersion
@@ -114,6 +118,8 @@ class CommonKlibLibraryInfo(project: Project, library: Library, val libraryRoot:
override fun toString() = "Common" + super.toString() override fun toString() = "Common" + super.toString()
companion object { companion object {
private val LOG = IJLoggerAdapter.getInstance(CommonKlibLibraryInfo::class.java)
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion } internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
private fun <T> KotlinLibrary.readSafe(defaultValue: T, action: KotlinLibrary.() -> T) = try { 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 val VirtualFile.isMetadataKlib: Boolean
get() { get() {
val extension = extension val extension = extension
if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false 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 // native libraries
val irFile = findChild(KLIB_IR_FOLDER_NAME) val irFile = componentFile.findChild(KLIB_IR_FOLDER_NAME)
if (irFile != null && irFile.children.isNotEmpty()) return false if (irFile != null && irFile.children.isNotEmpty()) return false
val manifestProperties = try { val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } } manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) { } catch (_: IOException) {
return false 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 val extension = extension
if (!extension.isNullOrEmpty() && extension != KLIB_FILE_EXTENSION) return false 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) // 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 // TODO(dsavvinov): introduce more robust way to detect library platform
val irFolder = findChild(KLIB_IR_FOLDER_NAME) val irFolder = componentFile.findChild(KLIB_IR_FOLDER_NAME)
if (irFolder == null || irFolder.children.isEmpty()) return false if (irFolder == null || irFolder.children.isEmpty()) return false
val manifestProperties = try { val manifestProperties = try {
manifestFile.inputStream.use { Properties().apply { load(it) } } manifestFile.inputStream.use { Properties().apply { load(it) } }
} catch (_: IOException) { } catch (_: IOException) {
return false 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.project.lazyClosure
import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey import org.jetbrains.kotlin.idea.caches.resolve.BuiltInsCacheKey
import org.jetbrains.kotlin.idea.compiler.IDELanguageSettingsProvider 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.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.konan.util.KlibMetadataFactories import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.library.KotlinLibrary 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.isInterop
import org.jetbrains.kotlin.library.resolveSingleFileKlib import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.platform.TargetPlatform 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 isStdlib get() = libraryRoot.endsWith(KONAN_STDLIB_NAME)
val metadataInfo by lazy { val metadataInfo by lazy {
val metadataVersion = nativeLibrary.safeMetadataVersion val metadataVersion = nativeLibrary.safeMetadataVersion
when { when {
@@ -221,6 +227,8 @@ class NativeLibraryInfo(project: Project, library: Library, val libraryRoot: Str
override fun toString() = "Native" + super.toString() override fun toString() = "Native" + super.toString()
companion object { companion object {
private val LOG = IJLoggerAdapter.getInstance(NativeLibraryInfo::class.java)
val NATIVE_LIBRARY_CAPABILITY = ModuleDescriptor.Capability<KotlinLibrary>("KotlinNativeLibrary") val NATIVE_LIBRARY_CAPABILITY = ModuleDescriptor.Capability<KotlinLibrary>("KotlinNativeLibrary")
internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion } internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.descriptors.commonizer.cli package org.jetbrains.kotlin.descriptors.commonizer.cli
import org.jetbrains.kotlin.util.Logger
import kotlin.system.exitProcess import kotlin.system.exitProcess
internal fun parseArgs( internal fun parseArgs(
@@ -22,9 +23,13 @@ internal fun parseArgs(
return commandLine return commandLine
} }
internal fun printErrorAndExit(errorMessage: String): Nothing { internal object CliLoggerAdapter : Logger {
println("Error: $errorMessage") override fun log(message: String) = println(message)
println() 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, copyStdlib = true,
copyEndorsedLibs = true, copyEndorsedLibs = true,
withStats = withStats, withStats = withStats,
handleError = ::printErrorAndExit, logger = CliLoggerAdapter
log = ::println
).run() ).run()
} }
@@ -21,12 +21,14 @@ import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.SerializedMetadata 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.BaseWriterImpl
import org.jetbrains.kotlin.library.impl.KoltinLibraryWriterImpl import org.jetbrains.kotlin.library.impl.KoltinLibraryWriterImpl
import org.jetbrains.kotlin.library.resolveSingleFileKlib import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl import org.jetbrains.kotlin.serialization.konan.impl.KlibResolvedModuleDescriptorsFactoryImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.util.Logger
import java.io.File import java.io.File
import org.jetbrains.kotlin.konan.file.File as KFile import org.jetbrains.kotlin.konan.file.File as KFile
@@ -37,8 +39,7 @@ class NativeDistributionCommonizer(
private val copyStdlib: Boolean, private val copyStdlib: Boolean,
private val copyEndorsedLibs: Boolean, private val copyEndorsedLibs: Boolean,
private val withStats: Boolean, private val withStats: Boolean,
private val handleError: (String) -> Nothing, private val logger: Logger
private val log: (String) -> Unit
) { ) {
fun run() { fun run() {
checkPreconditions() checkPreconditions()
@@ -46,36 +47,35 @@ class NativeDistributionCommonizer(
with(ResettableClockMark()) { with(ResettableClockMark()) {
// 1. load modules // 1. load modules
val modulesByTargets = loadModules() val modulesByTargets = loadModules()
log("Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}") logger.log("Loaded lazy (uninitialized) libraries in ${elapsedSinceLast()}")
// 2. run commonization // 2. run commonization
val result = commonize(modulesByTargets) val result = commonize(modulesByTargets)
log("Commonization performed in ${elapsedSinceLast()}") logger.log("Commonization performed in ${elapsedSinceLast()}")
// 3. write new libraries // 3. write new libraries
saveModules(modulesByTargets, result) saveModules(modulesByTargets, result)
log("Written libraries in ${elapsedSinceLast()}") logger.log("Written libraries in ${elapsedSinceLast()}")
log("TOTAL: ${elapsedSinceStart()}") logger.log("TOTAL: ${elapsedSinceStart()}")
} }
log("Done.") logger.log("Done.\n")
log("")
} }
private fun checkPreconditions() { private fun checkPreconditions() {
if (!repository.isDirectory) if (!repository.isDirectory)
handleError("repository does not exist: $repository") logger.fatal("repository does not exist: $repository")
when (targets.size) { when (targets.size) {
0 -> handleError("no targets specified") 0 -> logger.fatal("no targets specified")
1 -> handleError("too few targets specified: $targets") 1 -> logger.fatal("too few targets specified: $targets")
} }
when { when {
!destination.exists() -> destination.mkdirs() !destination.exists() -> destination.mkdirs()
!destination.isDirectory -> handleError("output already exists: $destination") !destination.isDirectory -> logger.fatal("output already exists: $destination")
destination.walkTopDown().any { it != destination } -> handleError("output is not empty: $destination") destination.walkTopDown().any { it != destination } -> logger.fatal("output is not empty: $destination")
} }
} }
@@ -92,7 +92,7 @@ class NativeDistributionCommonizer(
?.listFiles() ?.listFiles()
?.takeIf { it.isNotEmpty() } ?.takeIf { it.isNotEmpty() }
?.map { loadLibrary(it) } ?.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 InputTarget(target.name, target) to platformLibs
}.toMap() }.toMap()
@@ -142,16 +142,20 @@ class NativeDistributionCommonizer(
private fun loadLibrary(location: File): KotlinLibrary { private fun loadLibrary(location: File): KotlinLibrary {
if (!location.isDirectory) 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) 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 val metadataVersion = library.metadataVersion
if (metadataVersion?.isCompatible() != true) if (metadataVersion?.isCompatible() != true)
handleError( logger.fatal(
""" """
library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location, library has incompatible metadata version ${metadataVersion ?: "\"unknown\""}: $location,
please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE} please make sure that all libraries passed to commonizer compatible metadata version ${KlibMetadataVersion.INSTANCE}
@@ -172,7 +176,7 @@ class NativeDistributionCommonizer(
val result = runCommonization(parameters) val result = runCommonization(parameters)
return when (result) { 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 is CommonizationPerformed -> result
} }
} }