Clear outputs before compiling with in-process/out-of-process execution mode

Issue #KT-30492 Fixed
This commit is contained in:
Alexey Tsvetkov
2019-03-18 21:45:24 +03:00
parent b2ad82b7d2
commit 2ec6ab92b5
6 changed files with 54 additions and 18 deletions
@@ -1,5 +1,6 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.checkedReplace
import org.jetbrains.kotlin.gradle.util.getFileByName
import org.jetbrains.kotlin.gradle.util.modify
import org.junit.Test
@@ -18,9 +19,29 @@ class ExecutionStrategyJsIT : ExecutionStrategyIT() {
override fun CompiledProject.checkOutput() {
assertFileExists("web/js/out.js")
}
override fun CompiledProject.checkOutputAfterChange() {
assertFileExists("web/js/out.js")
}
}
open class ExecutionStrategyIT : BaseGradleIT() {
class ExecutionStrategyJvmIT : ExecutionStrategyIT() {
override fun CompiledProject.checkOutput() {
val classesDir = kotlinClassesDir(subproject = "app") + "foo/"
assertFileExists("${classesDir}MainKt.class")
assertFileExists("${classesDir}A.class")
assertFileExists("${classesDir}B.class")
}
override fun CompiledProject.checkOutputAfterChange() {
val classesDir = kotlinClassesDir(subproject = "app") + "foo/"
assertFileExists("${classesDir}MainKt.class")
assertFileExists("${classesDir}A.class")
assertNoSuchFile("${classesDir}B.class")
}
}
abstract class ExecutionStrategyIT : BaseGradleIT() {
@Test
fun testDaemon() {
doTestExecutionStrategy("daemon")
@@ -48,14 +69,18 @@ open class ExecutionStrategyIT : BaseGradleIT() {
assertContains(finishMessage)
checkOutput()
assertNoWarnings()
assertFileExists(kotlinClassesDir(subproject = "app") + "foo/B.class")
}
val fKt = project.projectDir.getFileByName("f.kt")
fKt.delete()
val classesKt = project.projectDir.getFileByName("classes.kt")
classesKt.modify {
it.checkedReplace("class B", "//class B")
}
project.build("build", strategyCLIArg) {
assertFailed()
assertSuccessful()
assertContains(finishMessage)
assert(output.contains("Unresolved reference: f", ignoreCase = true))
checkOutputAfterChange()
assertNoWarnings()
}
}
@@ -66,7 +91,6 @@ open class ExecutionStrategyIT : BaseGradleIT() {
)
}
protected open fun CompiledProject.checkOutput() {
assertFileExists(kotlinClassesDir(subproject = "app") + "foo/MainKt.class")
}
protected abstract fun CompiledProject.checkOutput()
protected abstract fun CompiledProject.checkOutputAfterChange()
}
@@ -1,4 +0,0 @@
package foo
@Suppress("unused_parameter")
fun f(fn: (x: Int)->Unit) { }
@@ -1,5 +1,5 @@
package foo
fun main() {
f { it * 2 }
A()
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
import org.jetbrains.kotlin.gradle.utils.stackTraceAsString
import org.jetbrains.kotlin.incremental.ChangedFiles
@@ -292,8 +293,10 @@ internal class GradleKotlinCompilerWork @Inject constructor(
return result
}
private fun compileOutOfProcess(): ExitCode =
try {
private fun compileOutOfProcess(): ExitCode {
clearLocalState(outputFiles, log, reason = "out-of-process execution strategy is non-incremental")
return try {
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log)
} finally {
reportExecutionResultIfNeeded {
@@ -303,17 +306,20 @@ internal class GradleKotlinCompilerWork @Inject constructor(
)
}
}
}
private fun compileInProcess(messageCollector: MessageCollector): ExitCode {
clearLocalState(outputFiles, log, reason = "in-process execution strategy is non-incremental")
// in-process compiler should always be run in a different thread
// to avoid leaking thread locals from compiler (see KT-28037)
val threadPool = Executors.newSingleThreadExecutor()
val bufferingMessageCollector = GradleBufferingMessageCollector()
try {
return try {
val future = threadPool.submit(Callable {
compileInProcessImpl(bufferingMessageCollector)
})
return future.get()
future.get()
} finally {
bufferingMessageCollector.flush(messageCollector)
threadPool.shutdown()
@@ -2,10 +2,12 @@ package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.GradleException
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import java.io.File
fun throwGradleExceptionIfError(exitCode: ExitCode) {
when (exitCode) {
@@ -20,13 +22,16 @@ fun throwGradleExceptionIfError(exitCode: ExitCode) {
internal fun TaskWithLocalState.clearLocalState(reason: String? = null) {
val log = GradleKotlinLogger(logger)
clearLocalState(allOutputFiles(), log, reason)
}
internal fun clearLocalState(outputFiles: Iterable<File>, log: KotlinLogger, reason: String? = null) {
log.kotlinDebug {
val suffix = reason?.let { " ($it)" }.orEmpty()
"Clearing output$suffix:"
}
for (file in allOutputFiles()) {
for (file in outputFiles) {
if (!file.exists()) continue
when {
file.isDirectory -> {