Provided library versioning in klib. (#2038)

* Provided library versioning in klib.
* A test for library mismatch messages.
This commit is contained in:
alexander-gorshenev
2018-09-13 02:10:34 +03:00
committed by Dmitriy Dolovov
parent d406b1b16d
commit 27ce3b194d
37 changed files with 454 additions and 264 deletions
+1
View File
@@ -40,6 +40,7 @@ task generateCompilerVersion(type: VersionGenerator){}
sourceSets {
main.kotlin {
srcDir 'src/main/kotlin'
srcDir 'src/library/kotlin'
srcDir generateCompilerVersion.versionSourceDirectory
}
}
@@ -93,6 +93,17 @@ open class VersionGenerator: DefaultTask() {
|
| override fun toString() = versionString
|}
|fun String.parseKonanVersion(): KonanVersion {
| val (major, minor, maintenance, meta, build) =
| Regex(""${'"'}([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-(\p{Alpha}\p{Alnum}*))?(?:-([0-9]+))?""${'"'})
| .matchEntire(this)!!.destructured
| return KonanVersionImpl(
| MetaVersion.valueOf(meta.toUpperCase()),
| major.toInt(),
| minor.toInt(),
| maintenance.toIntOrNull() ?: 0,
| build.toIntOrNull() ?: -1)
|}
""".trimMargin()
}
versionFile.printWriter().use {
@@ -0,0 +1,70 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.properties.Properties
import org.jetbrains.kotlin.konan.properties.propertyList
const val KLIB_PROPERTY_ABI_VERSION = "abi_version"
const val KLIB_PROPERTY_COMPILER_VERSION = "compiler_version"
const val KLIB_PROPERTY_DEPENDENCY_VERSION = "dependency_version"
const val KLIB_PROPERTY_LIBRARY_VERSION = "library_version"
const val KLIB_PROPERTY_UNIQUE_NAME = "unique_name"
const val KLIB_PROPERTY_LINKED_OPTS = "linkerOpts"
const val KLIB_PROPERTY_DEPENDS = "depends"
const val KLIB_PROPERTY_INTEROP = "interop"
const val KLIB_PROPERTY_PACKAGE = "package"
const val KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS = "exportForwardDeclarations"
const val KLIB_PROPERTY_INCLUDED_HEADERS = "includedHeaders"
/**
* An abstraction for getting access to the information stored inside of Kotlin/Native library.
*/
interface KonanLibrary {
val libraryName: String
val libraryFile: File
// Whether this library is default (provided by Kotlin/Native distribution)?
val isDefault: Boolean
// Properties:
val manifestProperties: Properties
val linkerOpts: List<String>
// Paths:
val bitcodePaths: List<String>
val includedPaths: List<String>
val targetList: List<String>
val versions: KonanLibraryVersioning
val dataFlowGraph: ByteArray?
val moduleHeaderData: ByteArray
fun packageMetadata(fqName: String): ByteArray
}
val KonanLibrary.uniqueName
get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)!!
val KonanLibrary.unresolvedDependencies: List<UnresolvedLibrary>
get() = manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS)
.map {
UnresolvedLibrary(it, manifestProperties.getProperty("dependency_version_$it"))
}
val KonanLibrary.isInterop
get() = manifestProperties.getProperty(KLIB_PROPERTY_INTEROP) == "true"
val KonanLibrary.packageFqName
get() = manifestProperties.getProperty(KLIB_PROPERTY_PACKAGE)
val KonanLibrary.exportForwardDeclarations
get() = manifestProperties.getProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS)
.split(' ').asSequence()
.map { it.trim() }
.filter { it.isNotEmpty() }
.map { it }
.toList()
val KonanLibrary.includedHeaders
get() = manifestProperties.getProperty(KLIB_PROPERTY_INCLUDED_HEADERS).split(' ')
@@ -0,0 +1,44 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.KonanTarget
/**
* This scheme describes the Kotlin/Native Library (KLIB) layout.
*/
interface KonanLibraryLayout {
val libraryName: String
val libDir: File
val target: KonanTarget?
// This is a default implementation. Can't make it an assignment.
get() = null
val manifestFile
get() = File(libDir, "manifest")
val resourcesDir
get() = File(libDir, "resources")
val targetsDir
get() = File(libDir, "targets")
val targetDir
get() = File(targetsDir, target!!.visibleName)
val kotlinDir
get() = File(targetDir, "kotlin")
val nativeDir
get() = File(targetDir, "native")
val includedDir
get() = File(targetDir, "included")
val linkdataDir
get() = File(libDir, "linkdata")
val moduleHeaderFile
get() = File(linkdataDir, "module")
val dataFlowGraphFile
get() = File(linkdataDir, "module_data_flow_graph")
fun packageFile(packageName: String)
= File(linkdataDir, if (packageName == "") "root_package.knm" else "package_$packageName.knm")
}
@@ -0,0 +1,46 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.file.file
import org.jetbrains.kotlin.konan.file.withMutableZipFileSystem
import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl
import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl
import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks
import org.jetbrains.kotlin.konan.target.KonanTarget
val KLIB_FILE_EXTENSION = "klib"
val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
fun File.unpackZippedKonanLibraryTo(newDir: File) {
// First, run validity checks for the given KLIB file.
zippedKonanLibraryChecks(this)
if (newDir.exists) {
if (newDir.isDirectory)
newDir.deleteRecursively()
else
newDir.delete()
}
this.withMutableZipFileSystem {
it.file("/").recursiveCopyTo(newDir)
}
check(newDir.exists) { "Could not unpack $this as $newDir." }
}
val List<String>.toUnresolvedLibraries
get() = this.map {
val version = it.substringAfterLast('@', "")
.let { if (it.isEmpty()) null else it}
val name = it.substringBeforeLast('@')
UnresolvedLibrary(name, version)
}
fun createKonanLibrary(
libraryFile: File,
target: KonanTarget? = null,
isDefault: Boolean = false,
metadataReader: MetadataReader = DefaultMetadataReaderImpl
): KonanLibrary = KonanLibraryImpl(libraryFile, target, isDefault, metadataReader)
@@ -0,0 +1,28 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.parseKonanVersion
import org.jetbrains.kotlin.konan.KonanAbiVersion
import org.jetbrains.kotlin.konan.properties.Properties
import org.jetbrains.kotlin.konan.parseKonanAbiVersion
data class KonanLibraryVersioning(
val libraryVersion: String?,
val compilerVersion: KonanVersion,
val abiVersion: KonanAbiVersion
)
fun Properties.writeKonanLibraryVersioning(versions: KonanLibraryVersioning) {
this.setProperty(KLIB_PROPERTY_ABI_VERSION, versions.abiVersion.toString())
versions.libraryVersion ?. let { this.setProperty(KLIB_PROPERTY_LIBRARY_VERSION, it) }
this.setProperty(KLIB_PROPERTY_COMPILER_VERSION, "${versions.compilerVersion}")
}
fun Properties.readKonanLibraryVersioning(): KonanLibraryVersioning {
val abiVersion = this.getProperty(KLIB_PROPERTY_ABI_VERSION)!!.parseKonanAbiVersion()
val libraryVersion = this.getProperty(KLIB_PROPERTY_LIBRARY_VERSION)
val compilerVersion = this.getProperty(KLIB_PROPERTY_COMPILER_VERSION)!!.parseKonanVersion()
return KonanLibraryVersioning(abiVersion = abiVersion, libraryVersion = libraryVersion, compilerVersion = compilerVersion)
}
@@ -0,0 +1,6 @@
package org.jetbrains.kotlin.konan.library
interface MetadataReader {
fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray
fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray
}
@@ -0,0 +1,200 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.KonanAbiVersion
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.*
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the constants below:
const val KONAN_STDLIB_NAME = "stdlib"
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface!
interface SearchPathResolver : WithLogger {
val searchRoots: List<File>
fun resolutionSequence(givenPath: String): Sequence<File>
fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean = false): KonanLibraryImpl
fun resolve(givenPath: String): KonanLibraryImpl
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<KonanLibraryImpl>
}
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface!
interface SearchPathResolverWithTarget: SearchPathResolver {
val target: KonanTarget
val knownAbiVersions: List<KonanAbiVersion>?
val knownCompilerVersions: List<KonanVersion>?
}
fun defaultResolver(
repositories: List<String>,
target: KonanTarget
): SearchPathResolverWithTarget = defaultResolver(repositories, target, Distribution())
fun defaultResolver(
repositories: List<String>,
target: KonanTarget,
distribution: Distribution,
logger: Logger = ::dummyLogger,
skipCurrentDir: Boolean = false
): SearchPathResolverWithTarget = KonanLibraryProperResolver(
repositories,
target,
listOf(KonanAbiVersion.CURRENT),
listOf(KonanVersion.CURRENT),
distribution.klib,
distribution.localKonanDir.absolutePath,
skipCurrentDir,
logger)
fun resolverByName(
repositories: List<String>,
distributionKlib: String? = null,
localKonanDir: String? = null,
skipCurrentDir: Boolean = false
): SearchPathResolver = KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir)
internal open class KonanLibrarySearchPathResolver(
repositories: List<String>,
val distributionKlib: String?,
val localKonanDir: String?,
val skipCurrentDir: Boolean,
override val logger:Logger = ::dummyLogger
) : SearchPathResolver {
val localHead: File?
get() = localKonanDir?.File()?.klib
val distHead: File?
get() = distributionKlib?.File()?.child("common")
open val distPlatformHead: File? = null
val currentDirHead: File?
get() = if (!skipCurrentDir) File.userDir else null
private val repoRoots: List<File> by lazy { repositories.map { File(it) } }
// This is the place where we specify the order of library search.
override val searchRoots: List<File> by lazy {
(listOf(currentDirHead) + repoRoots + listOf(localHead, distHead, distPlatformHead)).filterNotNull()
}
private fun found(candidate: File): File? {
fun check(file: File): Boolean =
file.exists && (file.isFile || File(file, "manifest").exists)
val noSuffix = File(candidate.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT))
val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT))
return when {
check(withSuffix) -> withSuffix
check(noSuffix) -> noSuffix
else -> null
}
}
override fun resolutionSequence(givenPath: String): Sequence<File> {
val given = File(givenPath)
val sequence = if (given.isAbsolute) {
sequenceOf(found(given))
} else {
searchRoots.asSequence().map {
found(File(it, givenPath))
}
}
return sequence.filterNotNull()
}
override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl {
val givenPath = unresolved.path
return resolutionSequence(givenPath).firstOrNull() ?. let {
createKonanLibrary(it, null, isDefaultLink) as KonanLibraryImpl
} ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.")
}
override fun resolve(givenPath: String) = resolve(UnresolvedLibrary(givenPath, null), false)
private val File.klib
get() = File(this, "klib")
// The libraries from the default root are linked automatically.
val defaultRoots: List<File>
get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists }
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<KonanLibraryImpl> {
val result = mutableListOf<KonanLibraryImpl>()
if (!noStdLib) {
result.add(resolve(UnresolvedLibrary(KONAN_STDLIB_NAME, null), true))
}
if (!noDefaultLibs) {
val defaultLibs = defaultRoots.flatMap { it.listFiles }
.filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
.map { UnresolvedLibrary(it.absolutePath, null) }
.map {resolve(it, isDefaultLink = true) }
result.addAll(defaultLibs)
}
return result
}
}
internal class KonanLibraryProperResolver(
repositories: List<String>,
override val target: KonanTarget,
override val knownAbiVersions: List<KonanAbiVersion>?,
override val knownCompilerVersions: List<KonanVersion>?,
distributionKlib: String?,
localKonanDir: String?,
skipCurrentDir: Boolean,
override val logger: Logger = ::dummyLogger
): KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir, logger), SearchPathResolverWithTarget {
override val distPlatformHead: File?
get() = distributionKlib?.File()?.child("platform")?.child(target.visibleName)
override fun resolve(unresolved: UnresolvedLibrary, isDefaultLink: Boolean): KonanLibraryImpl {
val givenPath = unresolved.path
val fileSequence = resolutionSequence(givenPath)
val matching = fileSequence.map { createKonanLibrary(it, target, isDefaultLink) as KonanLibraryImpl }
.map { it.takeIf { libraryMatch(it, unresolved) } }
.filterNotNull()
return matching.firstOrNull() ?: error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.")
}
}
internal fun SearchPathResolverWithTarget.libraryMatch(candidate: KonanLibraryImpl, unresolved: UnresolvedLibrary): Boolean {
val resolverTarget = this.target
val candidatePath = candidate.libraryFile.absolutePath
if (resolverTarget != null && !candidate.targetList.contains(resolverTarget.visibleName)) {
logger("skipping $candidatePath. The target doesn't match. Expected '$resolverTarget', found ${candidate.targetList}")
return false
}
if (knownCompilerVersions != null &&
!knownCompilerVersions!!.contains(candidate.versions.compilerVersion)) {
logger("skipping $candidatePath. The compiler versions don't match. Expected '${knownCompilerVersions}', found '${candidate.versions.compilerVersion}'")
return false
}
if (knownAbiVersions != null &&
!knownAbiVersions!!.contains(candidate.versions.abiVersion)) {
logger("skipping $candidatePath. The abi versions don't match. Expected '${knownAbiVersions}', found '${candidate.versions.abiVersion}'")
return false
}
if (candidate.versions.libraryVersion != unresolved.libraryVersion &&
candidate.versions.libraryVersion != null &&
unresolved.libraryVersion != null) {
logger("skipping $candidatePath. The library versions don't match. Expected '${unresolved.libraryVersion}', found '${candidate.versions.libraryVersion}'")
return false
}
return true
}
@@ -0,0 +1,10 @@
package org.jetbrains.kotlin.konan.library
data class UnresolvedLibrary(
val path: String,
val libraryVersion: String?) {
fun substitutePath(newPath: String): UnresolvedLibrary {
return UnresolvedLibrary(newPath, libraryVersion)
}
}
@@ -0,0 +1,13 @@
package org.jetbrains.kotlin.konan.library.impl
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
import org.jetbrains.kotlin.konan.library.MetadataReader
internal object DefaultMetadataReaderImpl : MetadataReader {
override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray =
libraryLayout.moduleHeaderFile.readBytes()
override fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray =
libraryLayout.packageFile(fqName).readBytes()
}
@@ -0,0 +1,56 @@
package org.jetbrains.kotlin.konan.library.impl
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.properties.Properties
import org.jetbrains.kotlin.konan.properties.loadProperties
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.defaultTargetSubstitutions
import org.jetbrains.kotlin.konan.util.substitute
class KonanLibraryImpl(
override val libraryFile: File,
internal val target: KonanTarget?,
override val isDefault: Boolean,
private val metadataReader: MetadataReader
) : KonanLibrary {
// For the zipped libraries inPlace gives files from zip file system
// whereas realFiles extracts them to /tmp.
// For unzipped libraries inPlace and realFiles are the same
// providing files in the library directory.
private val layout = createKonanLibraryLayout(libraryFile, target)
override val libraryName: String by lazy { layout.inPlace { it.libraryName } }
override val manifestProperties: Properties by lazy {
val properties = layout.inPlace { it.manifestFile.loadProperties() }
if (target != null) substitute(properties, defaultTargetSubstitutions(target))
properties
}
override val versions: KonanLibraryVersioning
get() = manifestProperties.readKonanLibraryVersioning()
override val linkerOpts: List<String>
get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName)
override val bitcodePaths: List<String>
get() = layout.realFiles { (it.kotlinDir.listFilesOrEmpty + it.nativeDir.listFilesOrEmpty).map { it.absolutePath } }
override val includedPaths: List<String>
get() = layout.realFiles { it.includedDir.listFilesOrEmpty.map { it.absolutePath } }
override val targetList by lazy { layout.inPlace { it.targetsDir.listFiles.map { it.name } } }
override val dataFlowGraph by lazy { layout.inPlace { it.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } } }
override val moduleHeaderData: ByteArray by lazy { layout.inPlace { metadataReader.loadSerializedModule(it) } }
override fun packageMetadata(fqName: String) = layout.inPlace { metadataReader.loadSerializedPackageFragment(it, fqName) }
override fun toString() = "$libraryName[default=$isDefault]"
}
@@ -0,0 +1,89 @@
package org.jetbrains.kotlin.konan.library.impl
import org.jetbrains.kotlin.konan.file.*
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION
import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent
import java.nio.file.FileSystem
interface KonanLibraryLayoutImpl: KonanLibraryLayout {
fun <T> inPlace(action: (KonanLibraryLayout) -> T): T
fun <T> realFiles (action: (KonanLibraryLayout) -> T): T
}
private class ZippedKonanLibraryLayout(val klibFile: File, override val target: KonanTarget?): KonanLibraryLayoutImpl {
init { zippedKonanLibraryChecks(klibFile) }
override val libraryName = klibFile.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)
override val libDir: File= File("/")
override fun <T> realFiles(action: (KonanLibraryLayout) -> T): T {
return action(FileExtractor(this))!!
}
override fun <T> inPlace(action: (KonanLibraryLayout) -> T): T {
return klibFile.withZipFileSystem { zipFileSystem ->
action(DirectFromZip(this, zipFileSystem))
}
}
}
internal fun zippedKonanLibraryChecks(klibFile: File) {
check(klibFile.exists) { "Could not find $klibFile." }
check(klibFile.isFile) { "Expected $klibFile to be a regular file." }
val extension = klibFile.extension
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" }
}
private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?): KonanLibraryLayoutImpl {
override val libraryName = libDir.path
override fun <T> inPlace(action: (KonanLibraryLayout) -> T): T = action(this)
override fun <T> realFiles (action: (KonanLibraryLayout) -> T): T = inPlace(action)
}
private class DirectFromZip(zippedLayout: ZippedKonanLibraryLayout, val zipFileSystem: FileSystem): KonanLibraryLayout {
override val libraryName = zippedLayout.libraryName
override val libDir = zipFileSystem.file(zippedLayout.libDir)
}
/**
* This class automatically extracts pieces of the library on first access. Use it if you need
* to pass extracted files to an external tool. Otherwise, stick to [DirectFromZip].
*/
private class FileExtractor(val zippedLibraryLayout: ZippedKonanLibraryLayout): KonanLibraryLayout by zippedLibraryLayout {
override val manifestFile: File by lazy { extract(super.manifestFile) }
override val resourcesDir: File by lazy { extractDir(super.resourcesDir) }
override val includedDir: File by lazy { extractDir(super.includedDir) }
override val kotlinDir: File by lazy { extractDir(super.kotlinDir) }
override val nativeDir: File by lazy { extractDir(super.nativeDir) }
override val linkdataDir: File by lazy { extractDir(super.linkdataDir) }
fun extract(file: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
val temporary = createTempFile(file.name)
zipFileSystem.file(file).copyTo(temporary)
temporary.deleteOnExit()
temporary
}
fun extractDir(directory: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
val temporary = createTempDir(directory.name)
zipFileSystem.file(directory).recursiveCopyTo(temporary)
temporary.deleteOnExitRecursively()
temporary
}
}
internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) =
if (klib.isFile) ZippedKonanLibraryLayout(klib, target) else UnzippedKonanLibraryLayout(klib, target)
@@ -0,0 +1,12 @@
package org.jetbrains.kotlin.konan
fun String.parseKonanAbiVersion(): KonanAbiVersion {
return KonanAbiVersion(this.toInt())
}
data class KonanAbiVersion(val version: Int) {
companion object {
val CURRENT = KonanAbiVersion(1)
}
override fun toString() = "$version"
}
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.konan.file
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the whole file!
import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
@@ -38,7 +39,7 @@ data class File constructor(internal val javaPath: Path) {
val absoluteFile: File
get() = File(absolutePath)
val name: String
get() = javaPath.fileName.toString()
get() = javaPath.fileName.toString().removeSuffixIfPresent("/") // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8153248
val extension: String
get() = name.substringAfterLast('.', "")
val parent: String
@@ -1,138 +0,0 @@
package org.jetbrains.kotlin.konan.library
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent
import org.jetbrains.kotlin.konan.util.suffixIfNot
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove the constants below:
const val KLIB_FILE_EXTENSION = "klib"
const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
const val KONAN_STDLIB_NAME = "stdlib"
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface!
interface SearchPathResolver {
val searchRoots: List<File>
fun resolve(givenPath: String): File
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File>
}
// FIXME(ddol): KLIB-REFACTORING-CLEANUP: remove this interface!
interface SearchPathResolverWithTarget: SearchPathResolver {
val target: KonanTarget
}
fun defaultResolver(
repositories: List<String>,
target: KonanTarget
): SearchPathResolverWithTarget = defaultResolver(repositories, target, Distribution())
fun defaultResolver(
repositories: List<String>,
target: KonanTarget,
distribution: Distribution,
skipCurrentDir: Boolean = false
): SearchPathResolverWithTarget = KonanLibrarySearchPathResolverWithTarget(
repositories,
target,
distribution.klib,
distribution.localKonanDir.absolutePath,
skipCurrentDir)
fun noTargetResolver(
repositories: List<String>,
distributionKlib: String? = null,
localKonanDir: String? = null,
skipCurrentDir: Boolean = false
): SearchPathResolver = KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir)
internal open class KonanLibrarySearchPathResolver(
repositories: List<String>,
val distributionKlib: String?,
val localKonanDir: String?,
val skipCurrentDir: Boolean
) : SearchPathResolver {
val localHead: File?
get() = localKonanDir?.File()?.klib
val distHead: File?
get() = distributionKlib?.File()?.child("common")
open val distPlatformHead: File? = null
val currentDirHead: File?
get() = if (!skipCurrentDir) File.userDir else null
private val repoRoots: List<File> by lazy { repositories.map { File(it) } }
// This is the place where we specify the order of library search.
override val searchRoots: List<File> by lazy {
(listOf(currentDirHead) + repoRoots + listOf(localHead, distHead, distPlatformHead)).filterNotNull()
}
private fun found(candidate: File): File? {
fun check(file: File): Boolean =
file.exists && (file.isFile || File(file, "manifest").exists)
val noSuffix = File(candidate.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT))
val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT))
return when {
check(withSuffix) -> withSuffix
check(noSuffix) -> noSuffix
else -> null
}
}
override fun resolve(givenPath: String): File {
val given = File(givenPath)
if (given.isAbsolute) {
found(given)?.apply { return this }
} else {
searchRoots.forEach {
found(File(it, givenPath))?.apply { return this }
}
}
error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.")
}
private val File.klib
get() = File(this, "klib")
// The libraries from the default root are linked automatically.
val defaultRoots: List<File>
get() = listOfNotNull(distHead, distPlatformHead).filter { it.exists }
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File> {
val result = mutableListOf<File>()
if (!noStdLib) {
result.add(resolve(KONAN_STDLIB_NAME))
}
if (!noDefaultLibs) {
val defaultLibs = defaultRoots.flatMap { it.listFiles }
.filterNot { it.name.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
.map { File(it.absolutePath) }
result.addAll(defaultLibs)
}
return result
}
}
internal class KonanLibrarySearchPathResolverWithTarget(
repositories: List<String>,
override val target: KonanTarget,
distributionKlib: String?,
localKonanDir: String?,
skipCurrentDir: Boolean
): KonanLibrarySearchPathResolver(repositories, distributionKlib, localKonanDir, skipCurrentDir), SearchPathResolverWithTarget {
override val distPlatformHead: File?
get() = distributionKlib?.File()?.child("platform")?.child(target.visibleName)
}
@@ -0,0 +1,10 @@
package org.jetbrains.kotlin.konan.util
typealias Logger = (String) -> Unit
interface WithLogger {
val logger: Logger
}
fun dummyLogger(message: String) {}