[K/N lib] add Kotlin/Native deserializer and library reader
This commit is contained in:
committed by
Mikhail Glukhikh
parent
fade311b92
commit
51bb408c56
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.file
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.net.URI
|
||||
import java.nio.file.*
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
|
||||
data class File constructor(internal val javaPath: Path) {
|
||||
constructor(parent: Path, child: String) : this(parent.resolve(child))
|
||||
constructor(parent: File, child: String) : this(parent.javaPath.resolve(child))
|
||||
constructor(path: String) : this(Paths.get(path))
|
||||
constructor(parent: String, child: String) : this(Paths.get(parent, child))
|
||||
|
||||
val path: String
|
||||
get() = javaPath.toString()
|
||||
val absolutePath: String
|
||||
get() = javaPath.toAbsolutePath().toString()
|
||||
val absoluteFile: File
|
||||
get() = File(absolutePath)
|
||||
val name: String
|
||||
get() = javaPath.fileName.toString()
|
||||
val extension: String
|
||||
get() = name.substringAfterLast('.', "")
|
||||
val parent: String
|
||||
get() = javaPath.parent.toString()
|
||||
val parentFile: File
|
||||
get() = File(javaPath.parent)
|
||||
|
||||
val exists
|
||||
get() = Files.exists(javaPath)
|
||||
val isDirectory
|
||||
get() = Files.isDirectory(javaPath)
|
||||
val isFile
|
||||
get() = Files.isRegularFile(javaPath)
|
||||
val isAbsolute
|
||||
get() = javaPath.isAbsolute()
|
||||
val listFiles: List<File>
|
||||
get() = Files.newDirectoryStream(javaPath).use { stream -> stream.map { File(it) } }
|
||||
val listFilesOrEmpty: List<File>
|
||||
get() = if (exists) listFiles else emptyList()
|
||||
|
||||
fun child(name: String) = File(this, name)
|
||||
|
||||
fun copyTo(destination: File) {
|
||||
Files.copy(javaPath, destination.javaPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
|
||||
fun recursiveCopyTo(destination: File) {
|
||||
val sourcePath = javaPath
|
||||
val destPath = destination.javaPath
|
||||
sourcePath.recursiveCopyTo(destPath)
|
||||
}
|
||||
|
||||
fun mkdirs() = Files.createDirectories(javaPath)
|
||||
fun delete() = Files.deleteIfExists(javaPath)
|
||||
fun deleteRecursively() = postorder { Files.delete(it) }
|
||||
fun deleteOnExitRecursively() = preorder { File(it).deleteOnExit() }
|
||||
|
||||
fun preorder(task: (Path) -> Unit) {
|
||||
if (!this.exists) return
|
||||
|
||||
Files.walkFileTree(javaPath, object : SimpleFileVisitor<Path>() {
|
||||
override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult {
|
||||
task(file!!)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
|
||||
override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult {
|
||||
task(dir!!)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun postorder(task: (Path) -> Unit) {
|
||||
if (!this.exists) return
|
||||
|
||||
Files.walkFileTree(javaPath, object : SimpleFileVisitor<Path>() {
|
||||
override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult {
|
||||
task(file!!)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
|
||||
override fun postVisitDirectory(dir: Path?, exc: java.io.IOException?): FileVisitResult {
|
||||
task(dir!!)
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun deleteOnExit(): File {
|
||||
// Works only on the default file system,
|
||||
// but that's okay for now.
|
||||
javaPath.toFile().deleteOnExit()
|
||||
return this // Allow streaming.
|
||||
}
|
||||
|
||||
fun readBytes() = Files.readAllBytes(javaPath)
|
||||
fun writeBytes(bytes: ByteArray) = Files.write(javaPath, bytes)
|
||||
fun appendBytes(bytes: ByteArray) = Files.write(javaPath, bytes, StandardOpenOption.APPEND)
|
||||
|
||||
fun writeLines(lines: Iterable<String>) {
|
||||
Files.write(javaPath, lines)
|
||||
}
|
||||
|
||||
fun writeText(text: String): Unit = writeLines(listOf(text))
|
||||
|
||||
fun forEachLine(action: (String) -> Unit) {
|
||||
Files.lines(javaPath).use { lines ->
|
||||
lines.forEach { action(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun createAsSymlink(target: String) {
|
||||
val targetPath = Paths.get(target)
|
||||
if (Files.isSymbolicLink(this.javaPath) && Files.readSymbolicLink(javaPath) == targetPath) {
|
||||
return
|
||||
}
|
||||
Files.createSymbolicLink(this.javaPath, targetPath)
|
||||
}
|
||||
|
||||
override fun toString() = path
|
||||
|
||||
// TODO: Consider removeing these after konanazing java.util.Properties.
|
||||
fun bufferedReader() = Files.newBufferedReader(javaPath)
|
||||
|
||||
fun outputStream() = Files.newOutputStream(javaPath)
|
||||
fun printWriter() = javaPath.toFile().printWriter()
|
||||
|
||||
companion object {
|
||||
val userDir
|
||||
get() = File(System.getProperty("user.dir"))
|
||||
|
||||
val userHome
|
||||
get() = File(System.getProperty("user.home"))
|
||||
|
||||
val javaHome
|
||||
get() = File(System.getProperty("java.home"))
|
||||
val pathSeparator = java.io.File.pathSeparator
|
||||
}
|
||||
|
||||
fun readStrings() = mutableListOf<String>().also { list -> forEachLine { list.add(it) } }
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
val otherFile = other as? File ?: return false
|
||||
return otherFile.javaPath.toAbsolutePath() == javaPath.toAbsolutePath()
|
||||
}
|
||||
|
||||
override fun hashCode() = javaPath.toAbsolutePath().hashCode()
|
||||
}
|
||||
|
||||
fun String.File(): File = File(this)
|
||||
fun Path.File(): File = File(this)
|
||||
|
||||
fun createTempFile(name: String, suffix: String? = null) = Files.createTempFile(name, suffix).File()
|
||||
fun createTempDir(name: String): File = Files.createTempDirectory(name).File()
|
||||
|
||||
private val File.zipUri: URI
|
||||
get() = URI.create("jar:${this.toPath().toUri()}")
|
||||
|
||||
fun File.zipFileSystem(mutable: Boolean = false): FileSystem {
|
||||
val zipUri = this.zipUri
|
||||
val attributes = hashMapOf("create" to mutable.toString())
|
||||
return try {
|
||||
FileSystems.newFileSystem(zipUri, attributes, null)
|
||||
} catch (e: FileSystemAlreadyExistsException) {
|
||||
FileSystems.getFileSystem(zipUri)
|
||||
}
|
||||
}
|
||||
|
||||
fun File.mutableZipFileSystem() = this.zipFileSystem(mutable = true)
|
||||
|
||||
fun File.zipPath(path: String): Path = this.zipFileSystem().getPath(path)
|
||||
|
||||
val File.asZipRoot: File
|
||||
get() = File(this.zipPath("/"))
|
||||
|
||||
val File.asWritableZipRoot: File
|
||||
get() = File(this.mutableZipFileSystem().getPath("/"))
|
||||
|
||||
private fun File.toPath() = Paths.get(this.path)
|
||||
|
||||
fun File.zipDirAs(unixFile: File) {
|
||||
val zipRoot = unixFile.asWritableZipRoot
|
||||
this.recursiveCopyTo(zipRoot)
|
||||
zipRoot.javaPath.fileSystem.close()
|
||||
}
|
||||
|
||||
fun Path.recursiveCopyTo(destPath: Path) {
|
||||
val sourcePath = this
|
||||
Files.walk(sourcePath).forEach next@{ oldPath ->
|
||||
|
||||
val relative = sourcePath.relativize(oldPath)
|
||||
val destFs = destPath.getFileSystem()
|
||||
// We are copying files between file systems,
|
||||
// so pass the relative path through the String.
|
||||
val newPath = destFs.getPath(destPath.toString(), relative.toString())
|
||||
|
||||
// File systems don't allow replacing an existing root.
|
||||
if (newPath == newPath.getRoot()) return@next
|
||||
if (Files.isDirectory(newPath)) {
|
||||
Files.createDirectories(newPath)
|
||||
} else {
|
||||
Files.copy(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bufferedReader(errorStream: InputStream?) = BufferedReader(InputStreamReader(errorStream))
|
||||
|
||||
// stdlib `use` function adapted for AutoCloseable.
|
||||
inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
|
||||
var closed = false
|
||||
try {
|
||||
return block(this)
|
||||
} catch (e: Exception) {
|
||||
closed = true
|
||||
try {
|
||||
this?.close()
|
||||
} catch (closeException: Exception) {
|
||||
}
|
||||
throw e
|
||||
} finally {
|
||||
if (!closed) {
|
||||
this?.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Path.unzipTo(directory: Path) {
|
||||
val zipUri = URI.create("jar:" + this.toUri())
|
||||
FileSystems.newFileSystem(zipUri, emptyMap<String, Any?>(), null).use { zipfs ->
|
||||
val zipPath = zipfs.getPath("/")
|
||||
zipPath.recursiveCopyTo(directory)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.library
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
|
||||
const val KLIB_FILE_EXTENSION = "klib"
|
||||
const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION"
|
||||
|
||||
const val KONAN_STDLIB_NAME = "stdlib"
|
||||
|
||||
interface SearchPathResolver {
|
||||
val searchRoots: List<File>
|
||||
fun resolve(givenPath: String): File
|
||||
fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File>
|
||||
}
|
||||
|
||||
interface SearchPathResolverWithTarget: SearchPathResolver {
|
||||
val target: KonanTarget
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.properties
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.*
|
||||
|
||||
typealias Properties = java.util.Properties
|
||||
|
||||
fun File.loadProperties(): Properties {
|
||||
val properties = java.util.Properties()
|
||||
this.bufferedReader().use { reader ->
|
||||
properties.load(reader)
|
||||
}
|
||||
return properties
|
||||
}
|
||||
|
||||
fun loadProperties(path: String): Properties = File(path).loadProperties()
|
||||
|
||||
fun File.saveProperties(properties: Properties) {
|
||||
this.outputStream().use {
|
||||
properties.store(it, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun Properties.saveToFile(file: File) = file.saveProperties(this)
|
||||
|
||||
fun Properties.propertyString(key: String, suffix: String? = null): String? = getProperty(key.suffix(suffix)) ?: this.getProperty(key)
|
||||
|
||||
/**
|
||||
* TODO: this method working with suffixes should be replaced with
|
||||
* functionality borrowed from def file parser and unified for interop tool
|
||||
* and kotlin compiler.
|
||||
*/
|
||||
fun Properties.propertyList(key: String, suffix: String? = null): List<String> {
|
||||
val value = this.getProperty(key.suffix(suffix)) ?: this.getProperty(key)
|
||||
if (value?.isBlank() == true) return emptyList()
|
||||
|
||||
return value?.split(Regex("\\s+")) ?: emptyList()
|
||||
}
|
||||
|
||||
fun Properties.hasProperty(key: String, suffix: String? = null): Boolean = this.getProperty(key.suffix(suffix)) != null
|
||||
|
||||
fun String.suffix(suf: String?): String = if (suf == null) this else "${this}.$suf"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.util
|
||||
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
fun printMillisec(message: String, body: () -> Unit) {
|
||||
val msec = measureTimeMillis {
|
||||
body()
|
||||
}
|
||||
println("$message: $msec msec")
|
||||
}
|
||||
|
||||
fun profile(message: String, body: () -> Unit) = profileIf(
|
||||
System.getProperty("konan.profile")?.equals("true") ?: false,
|
||||
message, body
|
||||
)
|
||||
|
||||
fun profileIf(condition: Boolean, message: String, body: () -> Unit) =
|
||||
if (condition) printMillisec(message, body) else body()
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.konan.util
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import java.util.*
|
||||
|
||||
fun defaultTargetSubstitutions(target: KonanTarget) =
|
||||
mapOf(
|
||||
"target" to target.visibleName,
|
||||
"arch" to target.architecture.visibleName,
|
||||
"family" to target.family.visibleName
|
||||
)
|
||||
|
||||
// Performs substitution similar to:
|
||||
// foo = ${foo} ${foo.${arch}} ${foo.${os}}
|
||||
fun substitute(properties: Properties, substitutions: Map<String, String>) {
|
||||
for (key in properties.stringPropertyNames()) {
|
||||
for (substitution in substitutions.values) {
|
||||
val suffix = ".$substitution"
|
||||
if (key.endsWith(suffix)) {
|
||||
val baseKey = key.removeSuffix(suffix)
|
||||
val oldValue = properties.getProperty(baseKey, "")
|
||||
val appendedValue = properties.getProperty(key, "")
|
||||
val newValue = if (oldValue != "") "$oldValue $appendedValue" else appendedValue
|
||||
properties.setProperty(baseKey, newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user