org.jetbrains.kotlin.library.metadata.resolver.impl.KotlinLibraryResolverImpl.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.library.resolver.impl
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.metadata.PackageAccessHandler
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolver
import org.jetbrains.kotlin.library.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.library.resolver.LibraryOrder
import org.jetbrains.kotlin.util.WithLogger
fun SearchPathResolver.libraryResolver()
= KotlinLibraryResolverImpl(this)
class KotlinLibraryResolverImpl(
override val searchPathResolver: SearchPathResolver
): KotlinLibraryResolver, WithLogger by searchPathResolver {
override fun resolveWithDependencies(
unresolvedLibraries: List,
noStdLib: Boolean,
noDefaultLibs: Boolean,
noEndorsedLibs: Boolean
) = findLibraries(unresolvedLibraries, noStdLib, noDefaultLibs, noEndorsedLibs)
.leaveDistinct()
.omitDuplicateNames()
.resolveDependencies()
/**
* Returns the list of libraries based on [libraryNames], [noStdLib], [noDefaultLibs] and [noEndorsedLibs] criteria.
*
* This method does not return any libraries that might be available via transitive dependencies
* from the original library set (root set).
*/
private fun findLibraries(
unresolvedLibraries: List,
noStdLib: Boolean,
noDefaultLibs: Boolean,
noEndorsedLibs: Boolean
): List {
val userProvidedLibraries = unresolvedLibraries.asSequence()
.map { searchPathResolver.resolve(it) }
.toList()
val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs, noEndorsedLibs)
// Make sure the user provided ones appear first, so that
// they have precedence over defaults when duplicates are eliminated.
return userProvidedLibraries + defaultLibraries
}
/**
* Leaves only distinct libraries (by absolute path), warns on duplicated paths.
*/
private fun List.leaveDistinct() =
this.groupBy { it.libraryFile.absolutePath }.let { groupedByAbsolutePath ->
warnOnLibraryDuplicates(groupedByAbsolutePath.filter { it.value.size > 1 }.keys)
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:
*
* 1. Evaluates other libraries that are available via transitive dependencies.
* 2. Wraps each [KotlinLibrary] into a [KotlinResolvedLibrary] with information about dependencies on other libraries.
* 3. Creates resulting [KotlinLibraryResolveResult] object.
*/
private fun List.resolveDependencies(): KotlinLibraryResolveResult {
val rootLibraries = this.map { KotlinResolvedLibraryImpl(it) }
// As far as the list of root libraries is known from the very beginning, the result can be
// constructed from the very beginning as well.
val result = KotlinLibraryResolverResultImpl(rootLibraries)
val cache = mutableMapOf()
cache.putAll(rootLibraries.map { it.library.libraryFile.absoluteFile to it })
var newDependencies = rootLibraries
do {
newDependencies = newDependencies.map { library: KotlinResolvedLibraryImpl ->
library.library.unresolvedDependencies.asSequence()
.filterNot { searchPathResolver.isProvidedByDefault(it) }
.map { KotlinResolvedLibraryImpl(searchPathResolver.resolve(it)) }
.map { resolved ->
val absoluteFile = resolved.library.libraryFile.absoluteFile
if (absoluteFile in cache) {
library.addDependency(cache[absoluteFile]!!)
null
} else {
cache.put(absoluteFile, resolved)
library.addDependency(resolved)
resolved
}
}.filterNotNull()
.toList()
}.flatten()
} while (newDependencies.isNotEmpty())
return result
}
}
class KotlinLibraryResolverResultImpl(
private val roots: List
): KotlinLibraryResolveResult {
private val all: List by lazy {
val result = mutableSetOf().also { it.addAll(roots) }
var newDependencies = result.toList()
do {
newDependencies = newDependencies
.map { it -> it.resolvedDependencies }.flatten()
.filter { it !in result }
result.addAll(newDependencies)
} while (newDependencies.isNotEmpty())
result.toList()
}
override fun filterRoots(predicate: (KotlinResolvedLibrary) -> Boolean) =
KotlinLibraryResolverResultImpl(roots.filter(predicate))
override fun getFullList(order: LibraryOrder?) = (order?.invoke(all) ?: all).asPlain()
override fun forEach(action: (KotlinLibrary, PackageAccessHandler) -> Unit) {
all.forEach { action(it.library, it) }
}
private fun List.asPlain() = map { it.library }
override fun toString() = "roots=$roots, all=$all"
}