From 6d539e5e40550b4418bad080d8f92afe87e752cc Mon Sep 17 00:00:00 2001 From: Alexander Gorshenev Date: Fri, 31 Aug 2018 14:37:18 +0300 Subject: [PATCH] Make sure zip filesystems are closed on exit. --- .../kotlin/konan/library/KonanLibraryUtils.kt | 8 ++- .../konan/library/impl/KonanLibraryImpl.kt | 28 ++++----- .../library/impl/KonanLibraryLayoutImpl.kt | 62 +++++++++++-------- .../org/jetbrains/kotlin/konan/file/File.kt | 40 ------------ .../jetbrains/kotlin/konan/file/ZipUtil.kt | 52 ++++++++++++++++ 5 files changed, 106 insertions(+), 84 deletions(-) create mode 100644 shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt index 0c29f224b73..da2c0a3b874 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt @@ -1,10 +1,12 @@ 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.file.withZipFileSystem 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 @@ -21,7 +23,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." } } diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt index 97246c6fcc2..00146b1c38c 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt @@ -1,10 +1,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 @@ -24,14 +21,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 } @@ -49,18 +45,18 @@ internal class KonanLibraryImpl( get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName) override val bitcodePaths: List - 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 - 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]" -} +} \ No newline at end of file diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt index 0b19abaffc5..cdb65b14536 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt @@ -1,22 +1,35 @@ 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 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 inPlace(action: (KonanLibraryLayout) -> T): T + fun 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 by lazy { zippedKonanLibraryRoot(klibFile) } + override val libDir: File= File("/") + + override fun realFiles(action: (KonanLibraryLayout) -> T): T { + return action(FileExtractor(this))!! + } + + override fun inPlace(action: (KonanLibraryLayout) -> T): T { + return klibFile.withZipFileSystem { zipFileSystem -> + action(DirectFromZip(this, zipFileSystem)) + } + } } 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" } } -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 inPlace(action: (KonanLibraryLayout) -> T): T = action(this) + override fun 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) } @@ -51,29 +70,20 @@ private class FileExtractor(zippedLibraryLayout: KonanLibraryLayout): KonanLibra 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.") - } - diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt index c91876514e2..b54534386fa 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/File.kt @@ -177,38 +177,6 @@ fun createTempFile(name: String, suffix: String? = null) 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 -> @@ -249,11 +217,3 @@ inline fun T.use(block: (T) -> R): R { } } } - -fun Path.unzipTo(directory: Path) { - val zipUri = URI.create("jar:" + this.toUri()) - FileSystems.newFileSystem(zipUri, emptyMap(), null).use { zipfs -> - val zipPath = zipfs.getPath("/") - zipPath.recursiveCopyTo(directory) - } -} diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt new file mode 100644 index 00000000000..2a9062724cf --- /dev/null +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/file/ZipUtil.kt @@ -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(), null).use { zipfs -> + val zipPath = zipfs.getPath("/") + zipPath.recursiveCopyTo(directory) + } +} + +fun File.withZipFileSystem(mutable: Boolean = false, action: (FileSystem) -> T): T { + val zipFileSystem = this.zipFileSystem(mutable) + return try { + action(zipFileSystem) + } finally { + zipFileSystem.close() + } +} + +fun File.withZipFileSystem(action: (FileSystem) -> T): T + = this.withZipFileSystem(false, action) + +fun File.withMutableZipFileSystem(action: (FileSystem) -> T): T + = this.withZipFileSystem(true, action) \ No newline at end of file