Provided library versioning in klib. (#2038)
* Provided library versioning in klib. * A test for library mismatch messages.
This commit is contained in:
committed by
Dmitriy Dolovov
parent
d406b1b16d
commit
27ce3b194d
@@ -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)
|
||||
+28
@@ -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)
|
||||
}
|
||||
}
|
||||
+13
@@ -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]"
|
||||
}
|
||||
+89
@@ -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) {}
|
||||
|
||||
Reference in New Issue
Block a user