JS search path resolver with attributes.

Warn on inclusion of klibs with the same name in manifest
This commit is contained in:
Alexander Gorshenev
2019-10-10 01:11:56 +03:00
committed by alexander-gorshenev
parent bc4be53569
commit 55805ddeb8
4 changed files with 166 additions and 39 deletions
@@ -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<String>,
directLibs: List<String>,
knownAbiVersions: List<KotlinAbiVersion>?,
knownCompilerVersions: List<KonanVersion>?,
distributionKlib: String?,
localKotlinDir: String?,
skipCurrentDir: Boolean,
logger: Logger
) : KotlinLibraryProperResolverWithAttributes<KotlinLibrary>(
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<String>, 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<KonanVersion>(),
distributionKlib = null,
localKotlinDir = null,
skipCurrentDir = false,
logger = logger
).libraryResolver()
val resolvedLibraries =
libraryResolver.resolveWithDependencies(
unresolvedLibraries = unresolvedLibraries,
noStdLib = true,
noDefaultLibs = true,
noEndorsedLibs = true
)
return resolvedLibraries
}
@@ -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<String>, 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<KotlinLibrary>(
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<KtFile>,
@@ -39,6 +39,7 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary>(
noEndorsedLibs: Boolean
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs, noEndorsedLibs)
.leaveDistinct()
.omitDuplicateNames()
.resolveDependencies()
/**
@@ -74,10 +75,24 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary>(
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<KotlinLibrary>.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<String>) {
duplicatedPaths.forEach { logger.warning("library included more than once: $it") }
}
private fun warnOnLibraryDuplicateNames(duplicatedPaths: Iterable<String>) {
duplicatedPaths.forEach { logger.warning("duplicate library name: $it") }
}
/**
* Given the list of root libraries does the following:
@@ -7,35 +7,47 @@ import org.jetbrains.kotlin.util.*
const val KOTLIN_STDLIB_NAME = "stdlib"
interface SearchPathResolver<out L: KotlinLibrary> : WithLogger {
interface SearchPathResolver<L: KotlinLibrary> : WithLogger {
val searchRoots: List<File>
fun resolutionSequence(givenPath: String): Sequence<File>
fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): L
fun resolve(givenPath: String): L
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean, noEndorsedLibs: Boolean): List<L>
fun libraryMatch(candidate: L, unresolved: UnresolvedLibrary): Boolean
}
interface SearchPathResolverWithAttributes<out L: KotlinLibrary>: SearchPathResolver<L> {
interface SearchPathResolverWithAttributes<L: KotlinLibrary>: SearchPathResolver<L> {
val knownAbiVersions: List<KotlinAbiVersion>?
val knownCompilerVersions: List<KonanVersion>?
}
fun resolverByName(
repositories: List<String>,
directLibs: List<String> = emptyList(),
distributionKlib: String? = null,
localKotlinDir: String? = null,
skipCurrentDir: Boolean = false,
logger: Logger = DummyLogger
): SearchPathResolver<KotlinLibrary> = KotlinLibrarySearchPathResolver(repositories, directLibs, distributionKlib, localKotlinDir, skipCurrentDir, logger)
repositories: List<String>,
directLibs: List<String> = emptyList(),
distributionKlib: String? = null,
localKotlinDir: String? = null,
skipCurrentDir: Boolean = false,
logger: Logger
): SearchPathResolver<KotlinLibrary> =
KotlinLibrarySearchPathResolver(
repositories,
directLibs,
distributionKlib,
localKotlinDir,
skipCurrentDir,
logger,
::createKotlinLibrary
)
open class KotlinLibrarySearchPathResolver<out L: KotlinLibrary>(
// This is a simple library resolver that only cares for file names.
open class KotlinLibrarySearchPathResolver<L: KotlinLibrary>(
repositories: List<String>,
directLibs: List<String>,
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<L> {
val localHead: File?
@@ -52,7 +64,7 @@ open class KotlinLibrarySearchPathResolver<out L: KotlinLibrary>(
private val repoRoots: List<File> by lazy { repositories.map { File(it) } }
private val directLibraries: List<KotlinLibrary> 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<out L: KotlinLibrary>(
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<L: KotlinLibrary>(
repositories: List<String>,
directLibs: List<String>,
override val knownAbiVersions: List<KotlinAbiVersion>?,
override val knownCompilerVersions: List<KonanVersion>?,
distributionKlib: String?,
localKotlinDir: String?,
skipCurrentDir: Boolean,
override val logger: Logger,
override val libraryBuilder: (File, Boolean) -> L
) : KotlinLibrarySearchPathResolver<L>(repositories, directLibs, distributionKlib, localKotlinDir, skipCurrentDir, logger, libraryBuilder),
SearchPathResolverWithAttributes<L>
{
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
}
}