Make sure zip filesystems are closed on exit.
This commit is contained in:
committed by
alexander-gorshenev
parent
49eee47d7e
commit
6d539e5e40
+6
-2
@@ -1,10 +1,12 @@
|
|||||||
package org.jetbrains.kotlin.konan.library
|
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.file
|
||||||
|
import org.jetbrains.kotlin.konan.file.withMutableZipFileSystem
|
||||||
|
import org.jetbrains.kotlin.konan.file.withZipFileSystem
|
||||||
import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl
|
import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl
|
||||||
import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl
|
import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl
|
||||||
import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks
|
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.KonanLibraryResolver
|
||||||
import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl
|
import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
@@ -21,7 +23,9 @@ fun File.unpackZippedKonanLibraryTo(newDir: File) {
|
|||||||
newDir.delete()
|
newDir.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
zippedKonanLibraryRoot(this).recursiveCopyTo(newDir)
|
this.withMutableZipFileSystem {
|
||||||
|
it.file("/").recursiveCopyTo(newDir)
|
||||||
|
}
|
||||||
check(newDir.exists) { "Could not unpack $this as $newDir." }
|
check(newDir.exists) { "Could not unpack $this as $newDir." }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-15
@@ -1,10 +1,7 @@
|
|||||||
package org.jetbrains.kotlin.konan.library.impl
|
package org.jetbrains.kotlin.konan.library.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.file.File
|
import org.jetbrains.kotlin.konan.file.File
|
||||||
import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_ABI_VERSION
|
import org.jetbrains.kotlin.konan.library.*
|
||||||
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.properties.Properties
|
import org.jetbrains.kotlin.konan.properties.Properties
|
||||||
import org.jetbrains.kotlin.konan.properties.loadProperties
|
import org.jetbrains.kotlin.konan.properties.loadProperties
|
||||||
import org.jetbrains.kotlin.konan.properties.propertyList
|
import org.jetbrains.kotlin.konan.properties.propertyList
|
||||||
@@ -24,14 +21,13 @@ internal class KonanLibraryImpl(
|
|||||||
// whereas realFiles extracts them to /tmp.
|
// whereas realFiles extracts them to /tmp.
|
||||||
// For unzipped libraries inPlace and realFiles are the same
|
// For unzipped libraries inPlace and realFiles are the same
|
||||||
// providing files in the library directory.
|
// providing files in the library directory.
|
||||||
private val inPlace = createKonanLibraryLayout(libraryFile, target)
|
|
||||||
private val realFiles = inPlace.realFiles
|
|
||||||
|
|
||||||
override val libraryName
|
private val layout = createKonanLibraryLayout(libraryFile, target)
|
||||||
get() = inPlace.libraryName
|
|
||||||
|
override val libraryName: String by lazy { layout.inPlace { it.libraryName } }
|
||||||
|
|
||||||
override val manifestProperties: Properties by lazy {
|
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))
|
if (target != null) substitute(properties, defaultTargetSubstitutions(target))
|
||||||
properties
|
properties
|
||||||
}
|
}
|
||||||
@@ -49,18 +45,18 @@ internal class KonanLibraryImpl(
|
|||||||
get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName)
|
get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName)
|
||||||
|
|
||||||
override val bitcodePaths: List<String>
|
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>
|
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]"
|
override fun toString() = "$libraryName[default=$isDefault]"
|
||||||
}
|
}
|
||||||
+36
-26
@@ -1,22 +1,35 @@
|
|||||||
package org.jetbrains.kotlin.konan.library.impl
|
package org.jetbrains.kotlin.konan.library.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.file.File
|
import org.jetbrains.kotlin.konan.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.library.KLIB_FILE_EXTENSION
|
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.KLIB_FILE_EXTENSION_WITH_DOT
|
||||||
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
import org.jetbrains.kotlin.konan.library.KonanLibraryLayout
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent
|
import org.jetbrains.kotlin.konan.util.removeSuffixIfPresent
|
||||||
|
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) }
|
init { zippedKonanLibraryChecks(klibFile) }
|
||||||
|
|
||||||
override val libraryName = klibFile.path.removeSuffixIfPresent(KLIB_FILE_EXTENSION_WITH_DOT)
|
override val libraryName = klibFile.path.removeSuffixIfPresent(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) {
|
internal fun zippedKonanLibraryChecks(klibFile: File) {
|
||||||
@@ -27,17 +40,23 @@ internal fun zippedKonanLibraryChecks(klibFile: File) {
|
|||||||
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" }
|
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?): KonanLibraryLayoutImpl {
|
||||||
|
|
||||||
private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?): KonanLibraryLayout {
|
|
||||||
override val libraryName = libDir.path
|
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
|
* 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) }
|
override val manifestFile: File by lazy { extract(super.manifestFile) }
|
||||||
|
|
||||||
@@ -51,29 +70,20 @@ private class FileExtractor(zippedLibraryLayout: KonanLibraryLayout): KonanLibra
|
|||||||
|
|
||||||
override val linkdataDir: File by lazy { extractDir(super.linkdataDir) }
|
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)
|
val temporary = createTempFile(file.name)
|
||||||
file.copyTo(temporary)
|
zipFileSystem.file(file).copyTo(temporary)
|
||||||
temporary.deleteOnExit()
|
temporary.deleteOnExit()
|
||||||
return temporary
|
temporary
|
||||||
}
|
}
|
||||||
|
|
||||||
fun extractDir(directory: File): File {
|
fun extractDir(directory: File): File = zippedLibraryLayout.klibFile.withZipFileSystem { zipFileSystem ->
|
||||||
val temporary = createTempDir(directory.name)
|
val temporary = createTempDir(directory.name)
|
||||||
directory.recursiveCopyTo(temporary)
|
zipFileSystem.file(directory).recursiveCopyTo(temporary)
|
||||||
temporary.deleteOnExitRecursively()
|
temporary.deleteOnExitRecursively()
|
||||||
return temporary
|
temporary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) =
|
internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) =
|
||||||
if (klib.isFile) ZippedKonanLibraryLayout(klib, target) else UnzippedKonanLibraryLayout(klib, target)
|
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.")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -177,38 +177,6 @@ fun createTempFile(name: String, suffix: String? = null)
|
|||||||
fun createTempDir(name: String): File
|
fun createTempDir(name: String): File
|
||||||
= Files.createTempDirectory(name).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) {
|
fun Path.recursiveCopyTo(destPath: Path) {
|
||||||
val sourcePath = this
|
val sourcePath = this
|
||||||
Files.walk(sourcePath).forEach next@ { oldPath ->
|
Files.walk(sourcePath).forEach next@ { oldPath ->
|
||||||
@@ -249,11 +217,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,52 @@
|
|||||||
|
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