Make sure zip filesystems are closed on exit

This commit is contained in:
Alexander Gorshenev
2018-09-06 14:41:04 +03:00
committed by Mikhail Glukhikh
parent 46a697db82
commit e8f238d53c
5 changed files with 108 additions and 83 deletions
@@ -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]"
}
}
@@ -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)