diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsLibraryResolver.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsLibraryResolver.kt new file mode 100644 index 00000000000..4240f1f87f5 --- /dev/null +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsLibraryResolver.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2019 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.ir.backend.js + +import org.jetbrains.kotlin.konan.KonanVersion +import org.jetbrains.kotlin.library.KotlinAbiVersion +import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.library.KotlinLibraryProperResolverWithAttributes +import org.jetbrains.kotlin.library.UnresolvedLibrary +import org.jetbrains.kotlin.library.impl.createKotlinLibrary +import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult +import org.jetbrains.kotlin.library.resolver.impl.libraryResolver +import org.jetbrains.kotlin.util.Logger +import java.io.File + +class JsLibraryResolver( + repositories: List, + directLibs: List, + knownAbiVersions: List?, + knownCompilerVersions: List?, + distributionKlib: String?, + localKotlinDir: String?, + skipCurrentDir: Boolean, + logger: Logger +) : KotlinLibraryProperResolverWithAttributes( + repositories, + directLibs, + knownAbiVersions, + knownCompilerVersions, + distributionKlib, + localKotlinDir, + skipCurrentDir, + logger, + ::createKotlinLibrary +) + +// TODO: This is a temporary set of library resolver policies for js compiler. +fun jsResolveLibraries(libraries: List, logger: Logger): KotlinLibraryResolveResult { + val unresolvedLibraries = libraries.map { UnresolvedLibrary(it, null) } + val libraryAbsolutePaths = libraries.map { File(it).absolutePath } + // Configure the resolver to only work with absolute paths for now. + val libraryResolver = JsLibraryResolver( + repositories = emptyList(), + directLibs = libraryAbsolutePaths, + knownAbiVersions = listOf(KotlinAbiVersion.CURRENT), + knownCompilerVersions = emptyList(), + distributionKlib = null, + localKotlinDir = null, + skipCurrentDir = false, + logger = logger + ).libraryResolver() + val resolvedLibraries = + libraryResolver.resolveWithDependencies( + unresolvedLibraries = unresolvedLibraries, + noStdLib = true, + noDefaultLibs = true, + noEndorsedLibs = true + ) + return resolvedLibraries +} diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt index f3904c8351e..2ac07eb2d4e 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/klib.kt @@ -80,29 +80,6 @@ private val CompilerConfiguration.metadataVersion class KotlinFileSerializedData(val metadata: ByteArray, val irData: SerializedIrFile) -// TODO: This is a temporary set of library resolver policies for js compiler. -fun jsResolveLibraries(libraries: List, logger: Logger): KotlinLibraryResolveResult { - val unresolvedLibraries = libraries.map { UnresolvedLibrary(it ,null) } - val libraryAbsolutePaths = libraries.map{ File(it).absolutePath } - // Configure the resolver to only work with absolute paths for now. - val libraryResolver = KotlinLibrarySearchPathResolver( - repositories = emptyList(), - directLibs = libraryAbsolutePaths, - distributionKlib = null, - localKotlinDir = null, - skipCurrentDir = false, - logger = logger - ).libraryResolver() - val resolvedLibraries = - libraryResolver.resolveWithDependencies( - unresolvedLibraries = unresolvedLibraries, - noStdLib = true, - noDefaultLibs = true, - noEndorsedLibs = true - ) - return resolvedLibraries -} - fun generateKLib( project: Project, files: List, diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt index e7d98a1d90c..5c399c9f79a 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/resolver/impl/KotlinLibraryResolverImpl.kt @@ -39,6 +39,7 @@ class KotlinLibraryResolverImpl( noEndorsedLibs: Boolean ) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs, noEndorsedLibs) .leaveDistinct() + .omitDuplicateNames() .resolveDependencies() /** @@ -74,10 +75,24 @@ class KotlinLibraryResolverImpl( groupedByAbsolutePath.map { it.value.first() } } + /** + * Having two libraries with the same uniqName we only keep the first one. + * TODO: The old JS plugin passes libraries with the same uniqName twice, + * so make it a warning for now. + */ + private fun List.omitDuplicateNames() = + this.groupBy { it.uniqueName }.let { groupedByUniqName -> + warnOnLibraryDuplicateNames(groupedByUniqName.filter { it.value.size > 1 }.keys) + groupedByUniqName.map { it.value.first() } + } + private fun warnOnLibraryDuplicates(duplicatedPaths: Iterable) { duplicatedPaths.forEach { logger.warning("library included more than once: $it") } } + private fun warnOnLibraryDuplicateNames(duplicatedPaths: Iterable) { + duplicatedPaths.forEach { logger.warning("duplicate library name: $it") } + } /** * Given the list of root libraries does the following: 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 c3d0005e1a4..446e4874ee3 100644 --- a/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/SearchPathResolver.kt @@ -7,35 +7,47 @@ import org.jetbrains.kotlin.util.* const val KOTLIN_STDLIB_NAME = "stdlib" -interface SearchPathResolver : WithLogger { +interface SearchPathResolver : WithLogger { val searchRoots: List fun resolutionSequence(givenPath: String): Sequence fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): L fun resolve(givenPath: String): L fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean, noEndorsedLibs: Boolean): List + fun libraryMatch(candidate: L, unresolved: UnresolvedLibrary): Boolean } -interface SearchPathResolverWithAttributes: SearchPathResolver { +interface SearchPathResolverWithAttributes: SearchPathResolver { val knownAbiVersions: List? val knownCompilerVersions: List? } fun resolverByName( - repositories: List, - directLibs: List = emptyList(), - distributionKlib: String? = null, - localKotlinDir: String? = null, - skipCurrentDir: Boolean = false, - logger: Logger = DummyLogger -): SearchPathResolver = KotlinLibrarySearchPathResolver(repositories, directLibs, distributionKlib, localKotlinDir, skipCurrentDir, logger) + repositories: List, + directLibs: List = emptyList(), + distributionKlib: String? = null, + localKotlinDir: String? = null, + skipCurrentDir: Boolean = false, + logger: Logger +): SearchPathResolver = + KotlinLibrarySearchPathResolver( + repositories, + directLibs, + distributionKlib, + localKotlinDir, + skipCurrentDir, + logger, + ::createKotlinLibrary + ) -open class KotlinLibrarySearchPathResolver( +// This is a simple library resolver that only cares for file names. +open class KotlinLibrarySearchPathResolver( repositories: List, directLibs: List, val distributionKlib: String?, val localKotlinDir: String?, val skipCurrentDir: Boolean, - override val logger: Logger = DummyLogger + override val logger: Logger, + open val libraryBuilder: (File, Boolean) -> L ) : SearchPathResolver { val localHead: File? @@ -52,7 +64,7 @@ open class KotlinLibrarySearchPathResolver( private val repoRoots: List by lazy { repositories.map { File(it) } } private val directLibraries: List by lazy { - directLibs.mapNotNull { found(File(it)) }.map { createKotlinLibrary(it) } + directLibs.mapNotNull { found(File(it)) }.map { libraryBuilder(it, false) } } // This is the place where we specify the order of library search. @@ -102,15 +114,19 @@ open class KotlinLibrarySearchPathResolver( override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): L { val givenPath = unresolved.path - return resolutionSequence(givenPath).firstOrNull() ?. let { - createKotlinLibrary(it, isDefaultLink) as L - } ?: run { + val fileSequence = resolutionSequence(givenPath) + val matching = fileSequence.map { libraryBuilder(it, isDefaultLink) } + .map { it.takeIf { libraryMatch(it, unresolved) } } + .filterNotNull() + + return matching.firstOrNull() ?: run { logger.fatal("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.") } } - override fun resolve(givenPath: String) = resolve(UnresolvedLibrary(givenPath, null), false) + override fun libraryMatch(candidate: L, unresolved: UnresolvedLibrary) = true + override fun resolve(givenPath: String) = resolve(UnresolvedLibrary(givenPath, null), false) private val File.klib get() = File(this, "klib") @@ -158,3 +174,59 @@ fun KonanVersion.compatible(other: KonanVersion) = this.major == other.major && this.minor == other.minor && this.maintenance == other.maintenance + + +// This is a library resolver aware of attributes shared between platforms, +// such as abi version. +// JS and Native resolvers are inherited from this one. +open class KotlinLibraryProperResolverWithAttributes( + repositories: List, + directLibs: List, + override val knownAbiVersions: List?, + override val knownCompilerVersions: List?, + distributionKlib: String?, + localKotlinDir: String?, + skipCurrentDir: Boolean, + override val logger: Logger, + override val libraryBuilder: (File, Boolean) -> L +) : KotlinLibrarySearchPathResolver(repositories, directLibs, distributionKlib, localKotlinDir, skipCurrentDir, logger, libraryBuilder), + SearchPathResolverWithAttributes +{ + override fun libraryMatch(candidate: L, unresolved: UnresolvedLibrary): Boolean { + val candidatePath = candidate.libraryFile.absolutePath + + val candidateCompilerVersion = candidate.versions.compilerVersion + val candidateAbiVersion = candidate.versions.abiVersion + val candidateLibraryVersion = candidate.versions.libraryVersion + + val abiVersionMatch = candidateAbiVersion != null && + knownAbiVersions != null && + knownAbiVersions!!.contains(candidateAbiVersion) + + val compilerVersionMatch = candidateCompilerVersion != null && + knownCompilerVersions != null && + knownCompilerVersions!!.any { it.compatible(candidateCompilerVersion) } + + if (!abiVersionMatch && !compilerVersionMatch) { + logger.warning("skipping $candidatePath. The abi versions don't match. Expected '${knownAbiVersions}', found '${candidateAbiVersion}'") + + if (knownCompilerVersions != null) { + val expected = knownCompilerVersions?.map { it.toString(false, false) } + val found = candidateCompilerVersion?.toString(true, true) + logger.warning("The compiler versions don't match either. Expected '${expected}', found '${found}'") + } + + return false + } + + if (candidateLibraryVersion != unresolved.libraryVersion && + candidateLibraryVersion != null && + unresolved.libraryVersion != null + ) { + logger.warning("skipping $candidatePath. The library versions don't match. Expected '${unresolved.libraryVersion}', found '${candidateLibraryVersion}'") + return false + } + + return true + } +} \ No newline at end of file