Measure time spent on backing up and restoring outputs

This commit is contained in:
Alexey Tsvetkov
2019-01-20 22:16:47 +03:00
parent beabbb95db
commit 5a6df10522
3 changed files with 73 additions and 33 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.logging
import org.gradle.api.Task
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
@@ -31,4 +32,17 @@ internal inline fun KotlinLogger.kotlinDebug(fn: () -> String) {
val msg = fn()
debug("[KOTLIN] $msg")
}
}
internal inline fun <T> KotlinLogger.logTime(action: String, fn: () -> T): T {
val startNs = System.nanoTime()
val result = fn()
val endNs = System.nanoTime()
val timeNs = endNs - startNs
val timeMs = timeNs.toDouble() / 1_000_000
debug(String.format("%s took %.2f ms", action, timeMs))
return result
}
@@ -28,16 +28,13 @@ import org.jetbrains.kotlin.gradle.internal.CompilerArgumentAwareWithInput
import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.logging.kotlinWarn
import org.jetbrains.kotlin.gradle.logging.*
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
import org.jetbrains.kotlin.utils.LibraryUtils
import org.jetbrains.kotlin.utils.keysToMap
import java.io.File
import java.util.*
import javax.inject.Inject
@@ -254,6 +251,8 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
}
}
private val kotlinLogger by lazy { GradleKotlinLogger(logger) }
internal open fun compilerRunner(): GradleCompilerRunner =
GradleCompilerRunner(this)
@@ -266,18 +265,12 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
// If task throws exception, but its outputs are changed during execution,
// then Gradle forces next build to be non-incremental (see Gradle's DefaultTaskArtifactStateRepository#persistNewOutputs)
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
val prevOutputs: Map<File, ByteArray>? = if (incremental && inputs.isIncremental) {
val outputFiles = HashSet<File>()
allOutputFiles().forEach {
if (it.isDirectory) {
it.walk().filterTo(outputFiles, File::isFile)
} else if (it.isFile) {
outputFiles.add(it)
val outputsBackup: TaskOutputsBackup? =
if (incremental && inputs.isIncremental)
kotlinLogger.logTime("Backing up outputs for incremental build") {
TaskOutputsBackup(allOutputFiles())
}
}
outputFiles.keysToMap { it.readBytes() }
} else null
else null
if (!incremental) {
clearLocalState("IC is disabled")
@@ -286,26 +279,11 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
try {
executeImpl(inputs)
} catch (t: Throwable) {
allOutputFiles().forEach {
if (it.isDirectory) {
it.deleteRecursively()
} else if (it.isFile) {
it.delete()
if (outputsBackup != null) {
kotlinLogger.logTime("Restoring previous outputs on error") {
outputsBackup.restoreOutputs()
}
}
if (prevOutputs != null) {
val dirs = HashSet<File>()
for ((file, bytes) in prevOutputs) {
val dir = file.parentFile
if (dirs.add(dir)) {
dir.mkdirs()
}
file.writeBytes(bytes)
}
}
throw t
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2019 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.gradle.tasks
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.utils.keysToMap
import java.io.File
import java.util.HashSet
internal class TaskOutputsBackup(private val outputs: FileCollection) {
private val previousOutputs: Map<File, ByteArray>
init {
val outputFiles = HashSet<File>()
outputs.forEach {
if (it.isDirectory) {
it.walk().filterTo(outputFiles, File::isFile)
} else if (it.isFile) {
outputFiles.add(it)
}
}
previousOutputs = outputFiles.keysToMap { it.readBytes() }
}
fun restoreOutputs() {
outputs.forEach {
if (it.isDirectory) {
it.deleteRecursively()
} else if (it.isFile) {
it.delete()
}
}
val dirs = HashSet<File>()
for ((file, bytes) in previousOutputs) {
val dir = file.parentFile
if (dirs.add(dir)) {
dir.mkdirs()
}
file.writeBytes(bytes)
}
}
}