Report compiler messages from daemon in Gradle thread
#KT-28836 fixed RMI messages are reported from RMI threads. When Gradle logger is used from non-Gradle thread, messages are not grouped in command-line and they are not shown in build scans. To fix this, we store all messages from Kotlin daemon in a buffer, then report them from a Gradle thread.
This commit is contained in:
+6
-3
@@ -5,17 +5,20 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.compilerRunner
|
package org.jetbrains.kotlin.compilerRunner
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|
||||||
import org.jetbrains.kotlin.daemon.client.reportFromDaemon
|
import org.jetbrains.kotlin.daemon.client.reportFromDaemon
|
||||||
import org.jetbrains.kotlin.daemon.common.*
|
import org.jetbrains.kotlin.daemon.common.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||||
|
import org.jetbrains.kotlin.gradle.logging.GradleBufferingMessageCollector
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.rmi.Remote
|
import java.rmi.Remote
|
||||||
import java.rmi.server.UnicastRemoteObject
|
import java.rmi.server.UnicastRemoteObject
|
||||||
|
|
||||||
internal open class GradleCompilerServicesFacadeImpl(
|
internal open class GradleCompilerServicesFacadeImpl(
|
||||||
private val log: KotlinLogger,
|
private val log: KotlinLogger,
|
||||||
private val compilerMessageCollector: MessageCollector,
|
// RMI messages are reported from RMI threads.
|
||||||
|
// Messages reported from non-Gradle threads are not grouped and not shown in build scans.
|
||||||
|
// To fix this, we store all messages in a buffer, then report them from a Gradle thread
|
||||||
|
private val compilerMessageCollector: GradleBufferingMessageCollector,
|
||||||
port: Int = SOCKET_ANY_FREE_PORT
|
port: Int = SOCKET_ANY_FREE_PORT
|
||||||
) : UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory),
|
) : UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory),
|
||||||
CompilerServicesFacadeBase,
|
CompilerServicesFacadeBase,
|
||||||
@@ -44,7 +47,7 @@ internal open class GradleCompilerServicesFacadeImpl(
|
|||||||
|
|
||||||
internal class GradleIncrementalCompilerServicesFacadeImpl(
|
internal class GradleIncrementalCompilerServicesFacadeImpl(
|
||||||
log: KotlinLogger,
|
log: KotlinLogger,
|
||||||
messageCollector: MessageCollector,
|
messageCollector: GradleBufferingMessageCollector,
|
||||||
port: Int = SOCKET_ANY_FREE_PORT
|
port: Int = SOCKET_ANY_FREE_PORT
|
||||||
) : GradleCompilerServicesFacadeImpl(log, messageCollector, port),
|
) : GradleCompilerServicesFacadeImpl(log, messageCollector, port),
|
||||||
IncrementalCompilerServicesFacade
|
IncrementalCompilerServicesFacade
|
||||||
+30
-20
@@ -6,7 +6,6 @@
|
|||||||
package org.jetbrains.kotlin.compilerRunner
|
package org.jetbrains.kotlin.compilerRunner
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.invocation.Gradle
|
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
@@ -14,7 +13,9 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||||
import org.jetbrains.kotlin.config.Services
|
import org.jetbrains.kotlin.config.Services
|
||||||
import org.jetbrains.kotlin.daemon.common.*
|
import org.jetbrains.kotlin.daemon.common.*
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.TaskLoggers
|
||||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||||
|
import org.jetbrains.kotlin.gradle.logging.GradleBufferingMessageCollector
|
||||||
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
|
import org.jetbrains.kotlin.gradle.tasks.GradleMessageCollector
|
||||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalStateDirectories
|
import org.jetbrains.kotlin.gradle.tasks.clearLocalStateDirectories
|
||||||
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
|
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
|
||||||
@@ -26,10 +27,8 @@ import java.io.*
|
|||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.rmi.RemoteException
|
import java.rmi.RemoteException
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import java.util.concurrent.ExecutorService
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
internal class ProjectFilesForCompilation(
|
internal class ProjectFilesForCompilation(
|
||||||
val projectRootFile: File,
|
val projectRootFile: File,
|
||||||
@@ -93,7 +92,6 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
System.err.println("Could not get logger for '$taskPath'. Falling back to sl4j logger")
|
System.err.println("Could not get logger for '$taskPath'. Falling back to sl4j logger")
|
||||||
SL4JKotlinLogger(LoggerFactory.getLogger("GradleKotlinCompilerWork"))
|
SL4JKotlinLogger(LoggerFactory.getLogger("GradleKotlinCompilerWork"))
|
||||||
}
|
}
|
||||||
private val messageCollector = GradleMessageCollector(log)
|
|
||||||
|
|
||||||
private val isIncremental: Boolean
|
private val isIncremental: Boolean
|
||||||
get() = incrementalCompilationEnvironment != null
|
get() = incrementalCompilationEnvironment != null
|
||||||
@@ -103,8 +101,9 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
clearLocalStateDirectories(log, localStateDirectories, "IC is disabled")
|
clearLocalStateDirectories(log, localStateDirectories, "IC is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val messageCollector = GradleMessageCollector(log)
|
||||||
val exitCode = try {
|
val exitCode = try {
|
||||||
compileWithDaemonOrFallbackImpl()
|
compileWithDaemonOrFallbackImpl(messageCollector)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
clearLocalStateDirectories(log, localStateDirectories, "exception when running compiler")
|
clearLocalStateDirectories(log, localStateDirectories, "exception when running compiler")
|
||||||
throw e
|
throw e
|
||||||
@@ -127,7 +126,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
throwGradleExceptionIfError(exitCode)
|
throwGradleExceptionIfError(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileWithDaemonOrFallbackImpl(): ExitCode {
|
private fun compileWithDaemonOrFallbackImpl(messageCollector: MessageCollector): ExitCode {
|
||||||
with(log) {
|
with(log) {
|
||||||
kotlinDebug { "Kotlin compiler class: ${compilerClassName}" }
|
kotlinDebug { "Kotlin compiler class: ${compilerClassName}" }
|
||||||
kotlinDebug { "Kotlin compiler classpath: ${compilerFullClasspath.joinToString { it.canonicalPath }}" }
|
kotlinDebug { "Kotlin compiler classpath: ${compilerFullClasspath.joinToString { it.canonicalPath }}" }
|
||||||
@@ -136,7 +135,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
|
|
||||||
val executionStrategy = kotlinCompilerExecutionStrategy()
|
val executionStrategy = kotlinCompilerExecutionStrategy()
|
||||||
if (executionStrategy == DAEMON_EXECUTION_STRATEGY) {
|
if (executionStrategy == DAEMON_EXECUTION_STRATEGY) {
|
||||||
val daemonExitCode = compileWithDaemon()
|
val daemonExitCode = compileWithDaemon(messageCollector)
|
||||||
|
|
||||||
if (daemonExitCode != null) {
|
if (daemonExitCode != null) {
|
||||||
return daemonExitCode
|
return daemonExitCode
|
||||||
@@ -147,21 +146,25 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
|
|
||||||
val isGradleDaemonUsed = System.getProperty("org.gradle.daemon")?.let(String::toBoolean)
|
val isGradleDaemonUsed = System.getProperty("org.gradle.daemon")?.let(String::toBoolean)
|
||||||
return if (executionStrategy == IN_PROCESS_EXECUTION_STRATEGY || isGradleDaemonUsed == false) {
|
return if (executionStrategy == IN_PROCESS_EXECUTION_STRATEGY || isGradleDaemonUsed == false) {
|
||||||
compileInProcess()
|
compileInProcess(messageCollector)
|
||||||
} else {
|
} else {
|
||||||
compileOutOfProcess()
|
compileOutOfProcess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileWithDaemon(): ExitCode? {
|
private fun compileWithDaemon(messageCollector: MessageCollector): ExitCode? {
|
||||||
|
val isDebugEnabled = log.isDebugEnabled || System.getProperty("kotlin.daemon.debug.log")?.toBoolean() ?: false
|
||||||
|
val daemonMessageCollector =
|
||||||
|
if (isDebugEnabled) messageCollector else MessageCollector.NONE
|
||||||
|
|
||||||
val connection =
|
val connection =
|
||||||
try {
|
try {
|
||||||
GradleCompilerRunner.getDaemonConnectionImpl(
|
GradleCompilerRunner.getDaemonConnectionImpl(
|
||||||
clientIsAliveFlagFile,
|
clientIsAliveFlagFile,
|
||||||
sessionFlagFile,
|
sessionFlagFile,
|
||||||
compilerFullClasspath,
|
compilerFullClasspath,
|
||||||
messageCollector,
|
daemonMessageCollector,
|
||||||
log.isDebugEnabled
|
isDebugEnabled
|
||||||
)
|
)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.error("Caught an exception trying to connect to Kotlin Daemon:")
|
log.error("Caught an exception trying to connect to Kotlin Daemon:")
|
||||||
@@ -184,14 +187,17 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
KotlinCompilerClass.METADATA -> CompileService.TargetPlatform.METADATA
|
KotlinCompilerClass.METADATA -> CompileService.TargetPlatform.METADATA
|
||||||
else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName")
|
else -> throw IllegalArgumentException("Unknown compiler type $compilerClassName")
|
||||||
}
|
}
|
||||||
|
val bufferingMessageCollector = GradleBufferingMessageCollector()
|
||||||
val exitCode = try {
|
val exitCode = try {
|
||||||
val res = if (isIncremental) {
|
val res = if (isIncremental) {
|
||||||
incrementalCompilationWithDaemon(daemon, sessionId, targetPlatform)
|
incrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, bufferingMessageCollector)
|
||||||
} else {
|
} else {
|
||||||
nonIncrementalCompilationWithDaemon(daemon, sessionId, targetPlatform)
|
nonIncrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, bufferingMessageCollector)
|
||||||
}
|
}
|
||||||
|
bufferingMessageCollector.flush(messageCollector)
|
||||||
exitCodeFromProcessExitCode(log, res.get())
|
exitCodeFromProcessExitCode(log, res.get())
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
bufferingMessageCollector.flush(messageCollector)
|
||||||
log.error("Compilation with Kotlin compile daemon was not successful")
|
log.error("Compilation with Kotlin compile daemon was not successful")
|
||||||
log.error(e.stackTraceAsString())
|
log.error(e.stackTraceAsString())
|
||||||
null
|
null
|
||||||
@@ -211,7 +217,8 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
private fun nonIncrementalCompilationWithDaemon(
|
private fun nonIncrementalCompilationWithDaemon(
|
||||||
daemon: CompileService,
|
daemon: CompileService,
|
||||||
sessionId: Int,
|
sessionId: Int,
|
||||||
targetPlatform: CompileService.TargetPlatform
|
targetPlatform: CompileService.TargetPlatform,
|
||||||
|
bufferingMessageCollector: GradleBufferingMessageCollector
|
||||||
): CompileService.CallResult<Int> {
|
): CompileService.CallResult<Int> {
|
||||||
val compilationOptions = CompilationOptions(
|
val compilationOptions = CompilationOptions(
|
||||||
compilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
|
compilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
|
||||||
@@ -220,14 +227,15 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
reportSeverity = reportSeverity(isVerbose),
|
reportSeverity = reportSeverity(isVerbose),
|
||||||
requestedCompilationResults = emptyArray()
|
requestedCompilationResults = emptyArray()
|
||||||
)
|
)
|
||||||
val servicesFacade = GradleCompilerServicesFacadeImpl(log, messageCollector)
|
val servicesFacade = GradleCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||||
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
|
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incrementalCompilationWithDaemon(
|
private fun incrementalCompilationWithDaemon(
|
||||||
daemon: CompileService,
|
daemon: CompileService,
|
||||||
sessionId: Int,
|
sessionId: Int,
|
||||||
targetPlatform: CompileService.TargetPlatform
|
targetPlatform: CompileService.TargetPlatform,
|
||||||
|
bufferingMessageCollector: GradleBufferingMessageCollector
|
||||||
): CompileService.CallResult<Int> {
|
): CompileService.CallResult<Int> {
|
||||||
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
|
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
|
||||||
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
|
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
|
||||||
@@ -249,7 +257,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
||||||
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, messageCollector)
|
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||||
val compilationResults = GradleCompilationResults(log, projectRootFile)
|
val compilationResults = GradleCompilationResults(log, projectRootFile)
|
||||||
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
||||||
}
|
}
|
||||||
@@ -257,21 +265,23 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
private fun compileOutOfProcess(): ExitCode =
|
private fun compileOutOfProcess(): ExitCode =
|
||||||
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log, loggingMessageCollector)
|
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log, loggingMessageCollector)
|
||||||
|
|
||||||
private fun compileInProcess(): ExitCode {
|
private fun compileInProcess(messageCollector: MessageCollector): ExitCode {
|
||||||
// in-process compiler should always be run in a different thread
|
// in-process compiler should always be run in a different thread
|
||||||
// to avoid leaking thread locals from compiler (see KT-28037)
|
// to avoid leaking thread locals from compiler (see KT-28037)
|
||||||
val threadPool = Executors.newSingleThreadExecutor()
|
val threadPool = Executors.newSingleThreadExecutor()
|
||||||
|
val bufferingMessageCollector = GradleBufferingMessageCollector()
|
||||||
try {
|
try {
|
||||||
val future = threadPool.submit(Callable {
|
val future = threadPool.submit(Callable {
|
||||||
compileInProcessImpl()
|
compileInProcessImpl(bufferingMessageCollector)
|
||||||
})
|
})
|
||||||
return future.get()
|
return future.get()
|
||||||
} finally {
|
} finally {
|
||||||
|
bufferingMessageCollector.flush(messageCollector)
|
||||||
threadPool.shutdown()
|
threadPool.shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileInProcessImpl(): ExitCode {
|
private fun compileInProcessImpl(messageCollector: MessageCollector): ExitCode {
|
||||||
val stream = ByteArrayOutputStream()
|
val stream = ByteArrayOutputStream()
|
||||||
val out = PrintStream(stream)
|
val out = PrintStream(stream)
|
||||||
// todo: cache classloader?
|
// todo: cache classloader?
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 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.logging
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
internal class GradleBufferingMessageCollector : MessageCollector {
|
||||||
|
private class MessageData(
|
||||||
|
val severity: CompilerMessageSeverity,
|
||||||
|
val message: String,
|
||||||
|
val location: CompilerMessageLocation?
|
||||||
|
)
|
||||||
|
|
||||||
|
private val messages = ArrayList<MessageData>()
|
||||||
|
|
||||||
|
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
||||||
|
synchronized(messages) {
|
||||||
|
messages.add(MessageData(severity, message, location))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasErrors() =
|
||||||
|
synchronized(messages) {
|
||||||
|
messages.any { it.severity.isError }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
synchronized(messages) {
|
||||||
|
messages.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flush(delegate: MessageCollector) {
|
||||||
|
messages.forEach {
|
||||||
|
delegate.report(it.severity, it.message, it.location)
|
||||||
|
}
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user