From 1196044df76d26fb59bd19c460960db4a8605276 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Thu, 30 Jan 2020 12:51:39 +0700 Subject: [PATCH] Introduce 2 KLIB resolve strategies: "compiler" & "tooling" Issue #KT-36213 --- .../kotlin/library/SearchPathResolver.kt | 24 +++++---- .../kotlin/library/SingleFileResolve.kt | 24 +++++++++ .../kotlin/library/ToolingResolve.kt | 54 +++++++++++++++++++ 3 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 compiler/util-klib/src/org/jetbrains/kotlin/library/SingleFileResolve.kt create mode 100644 compiler/util-klib/src/org/jetbrains/kotlin/library/ToolingResolve.kt diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt index 204193e090a..26ed43a7b96 100644 --- a/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt @@ -238,14 +238,18 @@ class SingleKlibComponentResolver( override fun libraryComponentBuilder(file: File, isDefault: Boolean) = createKotlinLibraryComponents(file, isDefault) } -fun resolveSingleFileKlib( - libraryFile: File, - logger: Logger = object : Logger { - override fun log(message: String) {} - override fun error(message: String) = kotlin.error("e: $message") - override fun warning(message: String) {} - override fun fatal(message: String) = kotlin.error("e: $message") - } -): KotlinLibrary { - return SingleKlibComponentResolver(libraryFile.absolutePath, listOf(KotlinAbiVersion.CURRENT), logger).resolve(libraryFile.absolutePath) +/** + * Resolves KLIB libraries by: + * - expanding the given library path to the real path that may or may not contain ".klib" extension + * - searching among user-supplied libraries by "unique_name" that matches the given library name + * - filtering out pre-1.4 libraries (with the old style layout) + * - filtering out library components that have different ABI version than the ABI version of the current compiler + * + * If no match found, fails with [Logger#fatal]. + * + * Typical usage scenario: compiler. + */ +object CompilerSingleFileKlibResolveStrategy : SingleFileKlibResolveStrategy { + override fun resolve(libraryFile: File, logger: Logger) = + SingleKlibComponentResolver(libraryFile.absolutePath, listOf(KotlinAbiVersion.CURRENT), logger).resolve(libraryFile.absolutePath) } diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/SingleFileResolve.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/SingleFileResolve.kt new file mode 100644 index 00000000000..fc7e3a562ec --- /dev/null +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/SingleFileResolve.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.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.util.Logger + +interface SingleFileKlibResolveStrategy { + fun resolve(libraryFile: File, logger: Logger): KotlinLibrary +} + +fun resolveSingleFileKlib( + libraryFile: File, + logger: Logger = object : Logger { + override fun log(message: String) {} + override fun error(message: String) = kotlin.error("e: $message") + override fun warning(message: String) {} + override fun fatal(message: String) = kotlin.error("e: $message") + }, + strategy: SingleFileKlibResolveStrategy = CompilerSingleFileKlibResolveStrategy +): KotlinLibrary = strategy.resolve(libraryFile, logger) diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/ToolingResolve.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/ToolingResolve.kt new file mode 100644 index 00000000000..cb97fde484f --- /dev/null +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/ToolingResolve.kt @@ -0,0 +1,54 @@ +/* + * 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.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.library.impl.createKotlinLibrary +import org.jetbrains.kotlin.util.Logger + +/** + * Resolves KLIB library by making sure that the library has 1.4+ layout with exactly one component. + * If it is not, then [resolve] does not fail and returns a fake [KotlinLibrary] instance with nonexistent component that can be + * treated by the callee but can't be read. + * + * The given library path is assumed to be absolute and pointing to the real KLIB. + * It's the responsibility of the callee to check this condition in the appropriate way. + * + * [ToolingSingleFileKlibResolveStrategy] does not perform any ABI or metadata version compatibility checks. + * It's the responsibility of the callee to check library versions in the appropriate way. + * + * Typical usage scenario: IDE. + */ +object ToolingSingleFileKlibResolveStrategy : SingleFileKlibResolveStrategy { + override fun resolve(libraryFile: File, logger: Logger): KotlinLibrary { + if (checkComponent(libraryFile)) { + // old style library + return fakeLibrary(libraryFile) + } + + val componentFiles = libraryFile.listFiles.filter(::checkComponent) + componentFiles.singleOrNull()?.let { componentFile -> + // single component library + return createKotlinLibrary(libraryFile, componentFile.name) + } + + // otherwise mimic as old style library and warn + if (componentFiles.isNotEmpty()) { + // TODO: choose the best fit among all available candidates + + logger.warning("Library $libraryFile can not be read. Multiple components found: ${componentFiles.map { + it.path.substringAfter(libraryFile.path) + }}") + } + + return fakeLibrary(libraryFile) + } + + private const val NONEXISTENT_COMPONENT_NAME = "__nonexistent_component_name__" + + private fun checkComponent(componentFile: File) = componentFile.child(KLIB_MANIFEST_FILE_NAME).isFile + private fun fakeLibrary(libraryFile: File) = createKotlinLibrary(libraryFile, NONEXISTENT_COMPONENT_NAME) +}