Reuse daemon launching logic for out-of-process compilation in gradle
This commit is contained in:
+20
-32
@@ -338,25 +338,6 @@ object KotlinCompilerClient {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String = "daemon client") {
|
|
||||||
out?.println("[$source] ${category.name}: $message")
|
|
||||||
messages?.add(DaemonReportMessage(category, "[$source] $message"))
|
|
||||||
messageCollector?.let {
|
|
||||||
when (category) {
|
|
||||||
DaemonReportCategory.DEBUG -> it.report(CompilerMessageSeverity.LOGGING, message)
|
|
||||||
DaemonReportCategory.INFO -> it.report(CompilerMessageSeverity.INFO, message)
|
|
||||||
DaemonReportCategory.EXCEPTION -> it.report(CompilerMessageSeverity.EXCEPTION, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compilerServices?.let {
|
|
||||||
when (category) {
|
|
||||||
DaemonReportCategory.DEBUG -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.DEBUG, message, source)
|
|
||||||
DaemonReportCategory.INFO -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.INFO, message, source)
|
|
||||||
DaemonReportCategory.EXCEPTION -> it.report(ReportCategory.EXCEPTION, ReportSeverity.ERROR, message, source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun tryFindSuitableDaemonOrNewOpts(registryDir: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, report: (DaemonReportCategory, String) -> Unit): Pair<CompileService?, DaemonJVMOptions> {
|
private fun tryFindSuitableDaemonOrNewOpts(registryDir: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, report: (DaemonReportCategory, String) -> Unit): Pair<CompileService?, DaemonJVMOptions> {
|
||||||
registryDir.mkdirs()
|
registryDir.mkdirs()
|
||||||
val timestampMarker = createTempFile("kotlin-daemon-client-tsmarker", directory = registryDir)
|
val timestampMarker = createTempFile("kotlin-daemon-client-tsmarker", directory = registryDir)
|
||||||
@@ -397,19 +378,7 @@ object KotlinCompilerClient {
|
|||||||
val processBuilder = ProcessBuilder(args)
|
val processBuilder = ProcessBuilder(args)
|
||||||
processBuilder.redirectErrorStream(true)
|
processBuilder.redirectErrorStream(true)
|
||||||
// assuming daemon process is deaf and (mostly) silent, so do not handle streams
|
// assuming daemon process is deaf and (mostly) silent, so do not handle streams
|
||||||
val daemon =
|
val daemon = launchProcessWithFallback(processBuilder, reportingTargets, "daemon client")
|
||||||
try {
|
|
||||||
launchWithNativePlatformLauncher(processBuilder)
|
|
||||||
}
|
|
||||||
catch (e: IOException) {
|
|
||||||
reportingTargets.report(DaemonReportCategory.DEBUG, "Could not start daemon with native process launcher, falling back to ProcessBuilder#start (${e.cause})")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
catch (e: NoClassDefFoundError) {
|
|
||||||
reportingTargets.report(DaemonReportCategory.DEBUG, "net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?: processBuilder.start()
|
|
||||||
|
|
||||||
val isEchoRead = Semaphore(1)
|
val isEchoRead = Semaphore(1)
|
||||||
isEchoRead.acquire()
|
isEchoRead.acquire()
|
||||||
@@ -478,6 +447,25 @@ class DaemonReportingTargets(val out: PrintStream? = null,
|
|||||||
val messageCollector: MessageCollector? = null,
|
val messageCollector: MessageCollector? = null,
|
||||||
val compilerServices: CompilerServicesFacadeBase? = null)
|
val compilerServices: CompilerServicesFacadeBase? = null)
|
||||||
|
|
||||||
|
internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) {
|
||||||
|
val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message }
|
||||||
|
out?.println("${category.name}: $sourceMessage")
|
||||||
|
messages?.add(DaemonReportMessage(category, sourceMessage))
|
||||||
|
messageCollector?.let {
|
||||||
|
when (category) {
|
||||||
|
DaemonReportCategory.DEBUG -> it.report(CompilerMessageSeverity.LOGGING, sourceMessage)
|
||||||
|
DaemonReportCategory.INFO -> it.report(CompilerMessageSeverity.INFO, sourceMessage)
|
||||||
|
DaemonReportCategory.EXCEPTION -> it.report(CompilerMessageSeverity.EXCEPTION, sourceMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compilerServices?.let {
|
||||||
|
when (category) {
|
||||||
|
DaemonReportCategory.DEBUG -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.DEBUG, message, source)
|
||||||
|
DaemonReportCategory.INFO -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.INFO, message, source)
|
||||||
|
DaemonReportCategory.EXCEPTION -> it.report(ReportCategory.EXCEPTION, ReportSeverity.ERROR, message, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun isProcessAlive(process: Process) =
|
internal fun isProcessAlive(process: Process) =
|
||||||
try {
|
try {
|
||||||
|
|||||||
+26
-12
@@ -16,17 +16,31 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.daemon.client
|
package org.jetbrains.kotlin.daemon.client
|
||||||
|
|
||||||
import net.rubygrapefruit.platform.Native
|
import org.jetbrains.kotlin.daemon.common.DaemonReportCategory
|
||||||
import net.rubygrapefruit.platform.NativeException
|
|
||||||
import net.rubygrapefruit.platform.ProcessLauncher
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
internal fun launchWithNativePlatformLauncher(processBuilder: ProcessBuilder): Process {
|
private class NativePlatformLauncherWrapper {
|
||||||
return try {
|
fun launch(processBuilder: ProcessBuilder): Process =
|
||||||
val nativeLauncher = Native.get(ProcessLauncher::class.java)
|
try {
|
||||||
nativeLauncher.start(processBuilder)
|
val nativeLauncher = net.rubygrapefruit.platform.Native.get(net.rubygrapefruit.platform.ProcessLauncher::class.java)
|
||||||
}
|
nativeLauncher.start(processBuilder)
|
||||||
catch (e: NativeException) {
|
}
|
||||||
throw IOException(e)
|
catch (e: net.rubygrapefruit.platform.NativeException) {
|
||||||
}
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun launchProcessWithFallback(processBuilder: ProcessBuilder, reportingTargets: DaemonReportingTargets, reportingSource: String = "process launcher"): Process =
|
||||||
|
try {
|
||||||
|
NativePlatformLauncherWrapper().launch(processBuilder)
|
||||||
|
}
|
||||||
|
catch (e: IOException) {
|
||||||
|
reportingTargets.report(DaemonReportCategory.DEBUG, "Could not start process with native process launcher, falling back to ProcessBuilder#start (${e.cause})")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
catch (e: NoClassDefFoundError) {
|
||||||
|
reportingTargets.report(DaemonReportCategory.DEBUG, "net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
?: processBuilder.start()
|
||||||
|
|||||||
+43
-4
@@ -24,9 +24,15 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2MetadataCompilerArguments
|
||||||
|
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 org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||||
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil
|
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.config.Services
|
import org.jetbrains.kotlin.config.Services
|
||||||
import org.jetbrains.kotlin.daemon.client.CompileServiceSession
|
import org.jetbrains.kotlin.daemon.client.CompileServiceSession
|
||||||
|
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
|
||||||
|
import org.jetbrains.kotlin.daemon.client.launchProcessWithFallback
|
||||||
import org.jetbrains.kotlin.daemon.common.*
|
import org.jetbrains.kotlin.daemon.common.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.ParentLastURLClassLoader
|
import org.jetbrains.kotlin.gradle.plugin.ParentLastURLClassLoader
|
||||||
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
|
||||||
@@ -49,6 +55,34 @@ const val COULD_NOT_CONNECT_TO_DAEMON_MESSAGE = "Could not connect to Kotlin com
|
|||||||
internal class GradleCompilerRunner(private val project: Project) : KotlinCompilerRunner<GradleCompilerEnvironment>() {
|
internal class GradleCompilerRunner(private val project: Project) : KotlinCompilerRunner<GradleCompilerEnvironment>() {
|
||||||
override val log = GradleKotlinLogger(project.logger)
|
override val log = GradleKotlinLogger(project.logger)
|
||||||
|
|
||||||
|
// used only for process launching so far, but implements unused proper contract
|
||||||
|
private val loggingMessageCollector: MessageCollector by lazy {
|
||||||
|
object : MessageCollector {
|
||||||
|
private var hasErrors = false
|
||||||
|
private val messageRenderer = MessageRenderer.PLAIN_FULL_PATHS
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
hasErrors = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasErrors(): Boolean = hasErrors
|
||||||
|
|
||||||
|
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
||||||
|
val locMessage = messageRenderer.render(severity, message, location)
|
||||||
|
when (severity) {
|
||||||
|
CompilerMessageSeverity.EXCEPTION -> log.error(locMessage)
|
||||||
|
CompilerMessageSeverity.ERROR,
|
||||||
|
CompilerMessageSeverity.STRONG_WARNING,
|
||||||
|
CompilerMessageSeverity.WARNING,
|
||||||
|
CompilerMessageSeverity.INFO -> log.info(locMessage)
|
||||||
|
CompilerMessageSeverity.LOGGING -> log.debug(locMessage)
|
||||||
|
CompilerMessageSeverity.OUTPUT -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun runJvmCompiler(
|
fun runJvmCompiler(
|
||||||
sourcesToCompile: List<File>,
|
sourcesToCompile: List<File>,
|
||||||
javaSourceRoots: Iterable<File>,
|
javaSourceRoots: Iterable<File>,
|
||||||
@@ -69,11 +103,17 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
|||||||
args.destination = null
|
args.destination = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deleteModuleFile = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return runCompiler(K2JVM_COMPILER, args, environment)
|
val res = runCompiler(K2JVM_COMPILER, args, environment)
|
||||||
|
deleteModuleFile = (res == ExitCode.OK || System.getProperty("kotlin.compiler.leave.module.file.on.error") == null)
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
moduleFile.delete()
|
if (deleteModuleFile) {
|
||||||
|
moduleFile.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,8 +289,7 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
|||||||
val javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"
|
val javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"
|
||||||
val classpathString = environment.compilerClasspath.map {it.absolutePath}.joinToString(separator = File.pathSeparator)
|
val classpathString = environment.compilerClasspath.map {it.absolutePath}.joinToString(separator = File.pathSeparator)
|
||||||
val builder = ProcessBuilder(javaBin, "-cp", classpathString, compilerClassName, *argsArray)
|
val builder = ProcessBuilder(javaBin, "-cp", classpathString, compilerClassName, *argsArray)
|
||||||
val processLauncher = Native.get(ProcessLauncher::class.java)
|
val process = launchProcessWithFallback(builder, DaemonReportingTargets(messageCollector = loggingMessageCollector))
|
||||||
val process = processLauncher.start(builder)
|
|
||||||
|
|
||||||
// important to read inputStream, otherwise the process may hang on some systems
|
// important to read inputStream, otherwise the process may hang on some systems
|
||||||
val readErrThread = thread {
|
val readErrThread = thread {
|
||||||
|
|||||||
Reference in New Issue
Block a user