From 433609677551874ca92fa68a79fb0439bd9d25c1 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Thu, 30 Jan 2020 12:52:07 +0700 Subject: [PATCH] Use "tooling" KLIB resolve strategy in IDE and commonizer Issue #KT-36213 --- .../resolve/CommonPlatformKindResolution.kt | 39 ++++++++++------- .../kotlin/idea/util/IJLoggerAdapter.kt | 24 +++++++++++ .../ide/konan/KotlinNativePluginUtil.kt | 26 +++++++----- .../ide/konan/NativePlatformKindResolution.kt | 14 +++++-- .../descriptors/commonizer/cli/cliUtils.kt | 13 ++++-- .../cli/nativeDistributionCommonizer.kt | 3 +- .../konan/NativeDistributionCommonizer.kt | 42 ++++++++++--------- 7 files changed, 109 insertions(+), 52 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IJLoggerAdapter.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 62aa23896a9..05c5c9d80dc 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 @@ -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 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) - } \ No newline at end of file + // 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 + } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IJLoggerAdapter.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IJLoggerAdapter.kt new file mode 100644 index 00000000000..23e9262847d --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IJLoggerAdapter.kt @@ -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)) + } +} 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 bac7f7b5b86..ee6ca27b1fe 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 @@ -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 } 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 19b1287fc1c..51b419b1803 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 @@ -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("KotlinNativeLibrary") internal val KotlinLibrary.safeMetadataVersion get() = this.readSafe(null) { metadataVersion } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt index bfef9d10bc5..4dc691f47da 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/cliUtils.kt @@ -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) + } } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt index b640ce4537f..9940cc6cdab 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/cli/nativeDistributionCommonizer.kt @@ -35,8 +35,7 @@ fun main(args: Array) { copyStdlib = true, copyEndorsedLibs = true, withStats = withStats, - handleError = ::printErrorAndExit, - log = ::println + logger = CliLoggerAdapter ).run() } diff --git a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt index f6e0489d07f..7227b5025f7 100644 --- a/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt +++ b/native/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/konan/NativeDistributionCommonizer.kt @@ -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 } }