Make sure zip filesystems are closed on exit
This commit is contained in:
committed by
Mikhail Glukhikh
parent
46a697db82
commit
e8f238d53c
@@ -6,10 +6,11 @@
|
||||
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.library.impl.zippedKonanLibraryRoot
|
||||
import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolver
|
||||
import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
@@ -26,7 +27,9 @@ fun File.unpackZippedKonanLibraryTo(newDir: File) {
|
||||
newDir.delete()
|
||||
}
|
||||
|
||||
zippedKonanLibraryRoot(this).recursiveCopyTo(newDir)
|
||||
this.withMutableZipFileSystem {
|
||||
it.file("/").recursiveCopyTo(newDir)
|
||||
}
|
||||
check(newDir.exists) { "Could not unpack $this as $newDir." }
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
package org.jetbrains.kotlin.konan.library.impl
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_ABI_VERSION
|
||||
import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_LINKED_OPTS
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.library.MetadataReader
|
||||
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
|
||||
@@ -29,14 +26,13 @@ internal class KonanLibraryImpl(
|
||||
// whereas realFiles extracts them to /tmp.
|
||||
// For unzipped libraries inPlace and realFiles are the same
|
||||
// providing files in the library directory.
|
||||
private val inPlace = createKonanLibraryLayout(libraryFile, target)
|
||||
private val realFiles = inPlace.realFiles
|
||||
|
||||
override val libraryName
|
||||
get() = inPlace.libraryName
|
||||
private val layout = createKonanLibraryLayout(libraryFile, target)
|
||||
|
||||
override val libraryName: String by lazy { layout.inPlace { it.libraryName } }
|
||||
|
||||
override val manifestProperties: Properties by lazy {
|
||||
val properties = inPlace.manifestFile.loadProperties()
|
||||
val properties = layout.inPlace { it.manifestFile.loadProperties() }
|
||||
if (target != null) substitute(properties, defaultTargetSubstitutions(target))
|
||||
properties
|
||||
}
|
||||
@@ -54,18 +50,18 @@ internal class KonanLibraryImpl(
|
||||
get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName)
|
||||
|
||||
override val bitcodePaths: List<String>
|
||||
get() = (realFiles.kotlinDir.listFilesOrEmpty + realFiles.nativeDir.listFilesOrEmpty).map { it.absolutePath }
|
||||
get() = layout.realFiles { (it.kotlinDir.listFilesOrEmpty + it.nativeDir.listFilesOrEmpty).map { it.absolutePath } }
|
||||
|
||||
override val includedPaths: List<String>
|
||||
get() = realFiles.includedDir.listFilesOrEmpty.map { it.absolutePath }
|
||||
get() = layout.realFiles { it.includedDir.listFilesOrEmpty.map { it.absolutePath } }
|
||||
|
||||
override val targetList by lazy { inPlace.targetsDir.listFiles.map { it.name } }
|
||||
override val targetList by lazy { layout.inPlace { it.targetsDir.listFiles.map { it.name } } }
|
||||
|
||||
override val dataFlowGraph by lazy { inPlace.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } }
|
||||
override val dataFlowGraph by lazy { layout.inPlace { it.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } } }
|
||||
|
||||
override val moduleHeaderData: ByteArray by lazy { metadataReader.loadSerializedModule(inPlace) }
|
||||
override val moduleHeaderData: ByteArray by lazy { layout.inPlace { metadataReader.loadSerializedModule(it) } }
|
||||
|
||||
override fun packageMetadata(fqName: String) = metadataReader.loadSerializedPackageFragment(inPlace, fqName)
|
||||
override fun packageMetadata(fqName: String) = layout.inPlace { metadataReader.loadSerializedPackageFragment(it, fqName) }
|
||||
|
||||
override fun toString() = "$libraryName[default=$isDefault]"
|
||||
}
|
||||
}
|
||||
+36
-25
@@ -5,16 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.konan.library.impl
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.file.asZipRoot
|
||||
import org.jetbrains.kotlin.konan.file.createTempDir
|
||||
import org.jetbrains.kotlin.konan.file.createTempFile
|
||||
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 java.nio.file.FileSystem
|
||||
|
||||
private class ZippedKonanLibraryLayout(val klibFile: File, override val target: KonanTarget?) : KonanLibraryLayout {
|
||||
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)
|
||||
@@ -22,7 +25,17 @@ private class ZippedKonanLibraryLayout(val klibFile: File, override val target:
|
||||
|
||||
override val libraryName = klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT)
|
||||
|
||||
override val libDir by lazy { zippedKonanLibraryRoot(klibFile) }
|
||||
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) {
|
||||
@@ -33,17 +46,23 @@ internal fun zippedKonanLibraryChecks(klibFile: File) {
|
||||
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" }
|
||||
}
|
||||
|
||||
internal fun zippedKonanLibraryRoot(klibFile: File) = klibFile.asZipRoot
|
||||
|
||||
private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?) : KonanLibraryLayout {
|
||||
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 [ZippedKonanLibraryLayout].
|
||||
* to pass extracted files to an external tool. Otherwise, stick to [DirectFromZip].
|
||||
*/
|
||||
private class FileExtractor(zippedLibraryLayout: KonanLibraryLayout) : KonanLibraryLayout by zippedLibraryLayout {
|
||||
private class FileExtractor(val zippedLibraryLayout: ZippedKonanLibraryLayout) : KonanLibraryLayout by zippedLibraryLayout {
|
||||
|
||||
override val manifestFile: File by lazy { extract(super.manifestFile) }
|
||||
|
||||
@@ -57,29 +76,21 @@ private class FileExtractor(zippedLibraryLayout: KonanLibraryLayout) : KonanLibr
|
||||
|
||||
override val linkdataDir: File by lazy { extractDir(super.linkdataDir) }
|
||||
|
||||
fun extract(file: File): File {
|
||||
fun extract(file: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
|
||||
val temporary = createTempFile(file.name)
|
||||
file.copyTo(temporary)
|
||||
zipFileSystem.file(file).copyTo(temporary)
|
||||
temporary.deleteOnExit()
|
||||
return temporary
|
||||
temporary
|
||||
}
|
||||
|
||||
fun extractDir(directory: File): File {
|
||||
fun extractDir(directory: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
|
||||
val temporary = createTempDir(directory.name)
|
||||
directory.recursiveCopyTo(temporary)
|
||||
zipFileSystem.file(directory).recursiveCopyTo(temporary)
|
||||
temporary.deleteOnExitRecursively()
|
||||
return temporary
|
||||
temporary
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) =
|
||||
if (klib.isFile) ZippedKonanLibraryLayout(klib, target) else UnzippedKonanLibraryLayout(klib, target)
|
||||
|
||||
internal val KonanLibraryLayout.realFiles
|
||||
get() = when (this) {
|
||||
is ZippedKonanLibraryLayout -> FileExtractor(this)
|
||||
// Unpacked library just provides its own files.
|
||||
is UnzippedKonanLibraryLayout -> this
|
||||
else -> error("Provide an extractor for your container.")
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ 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
|
||||
|
||||
@@ -164,37 +163,6 @@ 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 ->
|
||||
@@ -235,11 +203,3 @@ inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,55 @@
|
||||
/*
|
||||
* 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.net.URI
|
||||
import java.nio.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 FileSystem.file(file: File) = File(this.getPath(file.path))
|
||||
|
||||
fun FileSystem.file(path: String) = File(this.getPath(path))
|
||||
|
||||
private fun File.toPath() = Paths.get(this.path)
|
||||
|
||||
fun File.zipDirAs(unixFile: File) {
|
||||
unixFile.withMutableZipFileSystem {
|
||||
this.recursiveCopyTo(it.file("/"))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> File.withZipFileSystem(mutable: Boolean = false, action: (FileSystem) -> T): T {
|
||||
val zipFileSystem = this.zipFileSystem(mutable)
|
||||
return try {
|
||||
action(zipFileSystem)
|
||||
} finally {
|
||||
zipFileSystem.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> File.withZipFileSystem(action: (FileSystem) -> T): T = this.withZipFileSystem(false, action)
|
||||
|
||||
fun <T> File.withMutableZipFileSystem(action: (FileSystem) -> T): T = this.withZipFileSystem(true, action)
|
||||
Reference in New Issue
Block a user