[Commonizer] GC obsolete & unused commonized libraries to free disk space

This commit is contained in:
Dmitriy Dolovov
2020-03-11 22:29:03 +07:00
parent 75f1fb0237
commit f3f971005d
@@ -12,9 +12,9 @@ import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.AtomicMoveNotSupportedException import java.nio.file.*
import java.nio.file.Files import java.nio.file.attribute.*
import java.nio.file.StandardCopyOption import java.time.*
import java.util.* import java.util.*
internal fun runCommonizerInBulk( internal fun runCommonizerInBulk(
@@ -25,9 +25,11 @@ internal fun runCommonizerInBulk(
kotlinVersion: String kotlinVersion: String
): List<File> { ): List<File> {
val commandLineArguments = mutableListOf<String>() val commandLineArguments = mutableListOf<String>()
val postActions = mutableListOf<() -> Unit>()
val result = targetGroups.map { targets -> val successPostActions = mutableListOf<() -> Unit>()
val failurePostActions = mutableListOf<() -> Unit>()
val destinationDirs = targetGroups.map { targets ->
if (targets.size == 1) { if (targets.size == 1) {
// no need to commonize, just use the libraries from the distribution // no need to commonize, just use the libraries from the distribution
distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
@@ -60,18 +62,27 @@ internal fun runCommonizerInBulk(
commandLineArguments += "-targets" commandLineArguments += "-targets"
commandLineArguments += orderedTargets.joinToString(separator = ",") commandLineArguments += orderedTargets.joinToString(separator = ",")
postActions.add { renameDirectory(destinationTmpDir, destinationDir) } successPostActions.add { renameDirectory(destinationTmpDir, destinationDir) }
failurePostActions.add { renameToTempAndDelete(destinationTmpDir) }
} }
destinationDir destinationDir
} }
} }
callCommonizerCLI(project, commandLineArguments) // first of all remove directories with unused commonized libraries plus temporary directories with commonized libraries
// that accidentally were not cleaned up before
cleanUp(baseDestinationDir, excludedDirectories = destinationDirs)
postActions.forEach { it() } try {
callCommonizerCLI(project, commandLineArguments)
successPostActions.forEach { it() }
} catch (e: Exception) {
failurePostActions.forEach { it() }
throw e
}
return result return destinationDirs
} }
private fun callCommonizerCLI(project: Project, commandLineArguments: List<String>) { private fun callCommonizerCLI(project: Project, commandLineArguments: List<String>) {
@@ -88,7 +99,7 @@ private fun renameDirectory(source: File, destination: File) {
for (it in 0 until 3) { for (it in 0 until 3) {
try { try {
safeDelete(destination) renameToTempAndDelete(destination)
Files.move(sourcePath, destinationPath, StandardCopyOption.ATOMIC_MOVE) Files.move(sourcePath, destinationPath, StandardCopyOption.ATOMIC_MOVE)
return return
} catch (e: IOException) { } catch (e: IOException) {
@@ -104,21 +115,78 @@ private fun renameDirectory(source: File, destination: File) {
throw IllegalStateException("Failed to rename $source to $destination").apply { suppressedExceptions.forEach(::addSuppressed) } throw IllegalStateException("Failed to rename $source to $destination").apply { suppressedExceptions.forEach(::addSuppressed) }
} }
private fun safeDelete(directory: File) { private fun renameToTempAndDelete(directory: File) {
if (!directory.exists()) return if (!directory.exists()) return
// first, rename directory to some temp directory val dirToRemove = if (directory.name.startsWith("tmp-")) {
val tempDir = createTempFile( // already temp directory, return as is
prefix = "tmp-" + directory.name, directory
suffix = ".old", } else {
directory = directory.parentFile // first, rename the directory to some temp directory
) val tempDir = createTempFile(
tempDir.delete() prefix = "tmp-" + directory.name,
suffix = ".old",
directory = directory.parentFile
)
tempDir.delete()
Files.move(directory.toPath(), tempDir.toPath(), StandardCopyOption.ATOMIC_MOVE) Files.move(directory.toPath(), tempDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
// only then delete it recursively tempDir
tempDir.deleteRecursively() }
dirToRemove.deleteRecursively()
}
private fun cleanUp(baseDirectory: File, excludedDirectories: List<File>) {
fun File.getAttributes(): BasicFileAttributes? =
try {
Files.readAttributes(toPath(), BasicFileAttributes::class.java)
} catch (_: IOException) {
null
}
fun FileTime.isSameOrAfter(targetInstant: Instant): Boolean {
val fileInstant = toInstant()
if (fileInstant.atZone(ZoneOffset.UTC).toLocalDate().year <= 1970) {
// file time represents the epoch (or even a time point before it)
// such instant can't be used for reliable comparison
return false
}
return fileInstant >= targetInstant
}
val now = Instant.now()
val oneHourAgo = now.minus(Duration.ofHours(1))
val oneMonthAgo = now.minus(Duration.ofDays(31))
val excludedPaths = excludedDirectories.map { it.absolutePath }.toSet()
baseDirectory.listFiles()
?.forEach { file ->
if (file.absolutePath in excludedPaths) return@forEach
val attributes = file.getAttributes() ?: return@forEach
if (attributes.isDirectory) {
if (file.name.startsWith("tmp-")) {
// temp directories created more than 1 hour ago are stale and should be GCed
if (attributes.creationTime().isSameOrAfter(oneHourAgo)) return@forEach
} else {
// clean up other directories which were not accesses within the last month
if (attributes.lastAccessTime().isSameOrAfter(oneMonthAgo)) return@forEach
}
} /*else {
// clean up everything that is not a directory
}*/
try {
renameToTempAndDelete(file)
} catch (_: IOException) {
// do nothing
}
}
} }
private val String.base64 private val String.base64