[Daemon] Apply autoformatting to KotlinCompilerClient.kt

This commit is contained in:
Alexander.Likhachev
2023-11-17 19:11:43 +01:00
committed by Space Team
parent 628f6e981d
commit 02b1f9854f
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import java.io.File import java.io.File
import java.io.OutputStream
import java.io.PrintStream import java.io.PrintStream
import java.net.SocketException import java.net.SocketException
import java.nio.file.Files import java.nio.file.Files
@@ -38,65 +37,70 @@ import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread import kotlin.concurrent.thread
class CompilationServices( class CompilationServices(
val incrementalCompilationComponents: IncrementalCompilationComponents? = null, val incrementalCompilationComponents: IncrementalCompilationComponents? = null,
val lookupTracker: LookupTracker? = null, val lookupTracker: LookupTracker? = null,
val compilationCanceledStatus: CompilationCanceledStatus? = null val compilationCanceledStatus: CompilationCanceledStatus? = null,
) )
data class CompileServiceSession(val compileService: CompileService, val sessionId: Int) data class CompileServiceSession(val compileService: CompileService, val sessionId: Int)
object KotlinCompilerClient { object KotlinCompilerClient {
val DAEMON_DEFAULT_STARTUP_TIMEOUT_MS = 10000L private const val DAEMON_DEFAULT_STARTUP_TIMEOUT_MS = 10000L
val DAEMON_CONNECT_CYCLE_ATTEMPTS = 3 private const val DAEMON_CONNECT_CYCLE_ATTEMPTS = 3
val verboseReporting = CompilerSystemProperties.COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY.value != null val verboseReporting = CompilerSystemProperties.COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY.value != null
fun getOrCreateClientFlagFile(daemonOptions: DaemonOptions): File = fun getOrCreateClientFlagFile(daemonOptions: DaemonOptions): File =
// for jps property is passed from IDEA to JPS in KotlinBuildProcessParametersProvider // for jps property is passed from IDEA to JPS in KotlinBuildProcessParametersProvider
CompilerSystemProperties.COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY.value CompilerSystemProperties.COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY.value
?.let(String::trimQuotes) ?.let(String::trimQuotes)
?.takeUnless(String::isBlank) ?.takeUnless(String::isBlank)
?.let(::File) ?.let(::File)
?.takeIf(File::exists) ?.takeIf(File::exists)
?: makeAutodeletingFlagFile(baseDir = File(daemonOptions.runFilesPathOrDefault)) ?: makeAutodeletingFlagFile(baseDir = File(daemonOptions.runFilesPathOrDefault))
fun connectToCompileService(compilerId: CompilerId, fun connectToCompileService(
daemonJVMOptions: DaemonJVMOptions, compilerId: CompilerId,
daemonOptions: DaemonOptions, daemonJVMOptions: DaemonJVMOptions,
reportingTargets: DaemonReportingTargets, daemonOptions: DaemonOptions,
autostart: Boolean = true, reportingTargets: DaemonReportingTargets,
@Suppress("UNUSED_PARAMETER") checkId: Boolean = true autostart: Boolean = true,
@Suppress("UNUSED_PARAMETER") checkId: Boolean = true,
): CompileService? { ): CompileService? {
val flagFile = getOrCreateClientFlagFile(daemonOptions) val flagFile = getOrCreateClientFlagFile(daemonOptions)
return connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, reportingTargets, autostart) return connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, reportingTargets, autostart)
} }
fun connectToCompileService(compilerId: CompilerId, fun connectToCompileService(
clientAliveFlagFile: File, compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions, clientAliveFlagFile: File,
daemonOptions: DaemonOptions, daemonJVMOptions: DaemonJVMOptions,
reportingTargets: DaemonReportingTargets, daemonOptions: DaemonOptions,
autostart: Boolean = true reportingTargets: DaemonReportingTargets,
autostart: Boolean = true,
): CompileService? = ): CompileService? =
connectAndLease(compilerId, connectAndLease(
clientAliveFlagFile, compilerId,
daemonJVMOptions, clientAliveFlagFile,
daemonOptions, daemonJVMOptions,
reportingTargets, daemonOptions,
autostart, reportingTargets,
leaseSession = false, autostart,
sessionAliveFlagFile = null)?.compileService leaseSession = false,
sessionAliveFlagFile = null
)?.compileService
fun connectAndLease(compilerId: CompilerId, fun connectAndLease(
clientAliveFlagFile: File, compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions, clientAliveFlagFile: File,
daemonOptions: DaemonOptions, daemonJVMOptions: DaemonJVMOptions,
reportingTargets: DaemonReportingTargets, daemonOptions: DaemonOptions,
autostart: Boolean, reportingTargets: DaemonReportingTargets,
leaseSession: Boolean, autostart: Boolean,
sessionAliveFlagFile: File? = null leaseSession: Boolean,
sessionAliveFlagFile: File? = null,
): CompileServiceSession? = connectLoop(reportingTargets, autostart) { isLastAttempt -> ): CompileServiceSession? = connectLoop(reportingTargets, autostart) { isLastAttempt ->
fun CompileService.leaseImpl(): CompileServiceSession? { fun CompileService.leaseImpl(): CompileServiceSession? {
@@ -112,12 +116,15 @@ object KotlinCompilerClient {
} }
ensureServerHostnameIsSetUp() ensureServerHostnameIsSetUp()
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) }) val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(
File(daemonOptions.runFilesPath),
compilerId,
daemonJVMOptions
) { cat, msg -> reportingTargets.report(cat, msg) }
if (service != null) { if (service != null) {
service.leaseImpl() service.leaseImpl()
} } else {
else {
if (!isLastAttempt && autostart) { if (!isLastAttempt && autostart) {
if (startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)) { if (startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)) {
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it") reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
@@ -128,8 +135,15 @@ object KotlinCompilerClient {
} }
fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions): Unit { fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions): Unit {
connectToCompileService(compilerId, DaemonJVMOptions(), daemonOptions, DaemonReportingTargets(out = System.out), autostart = false, checkId = false) connectToCompileService(
?.shutdown() compilerId,
DaemonJVMOptions(),
daemonOptions,
DaemonReportingTargets(out = System.out),
autostart = false,
checkId = false
)
?.shutdown()
} }
@@ -139,40 +153,47 @@ object KotlinCompilerClient {
fun leaseCompileSession(compilerService: CompileService, aliveFlagPath: String?): Int = fun leaseCompileSession(compilerService: CompileService, aliveFlagPath: String?): Int =
compilerService.leaseCompileSession(aliveFlagPath).get() compilerService.leaseCompileSession(aliveFlagPath).get()
fun releaseCompileSession(compilerService: CompileService, sessionId: Int): Unit { fun releaseCompileSession(compilerService: CompileService, sessionId: Int): Unit {
compilerService.releaseCompileSession(sessionId) compilerService.releaseCompileSession(sessionId)
} }
fun compile(compilerService: CompileService, fun compile(
sessionId: Int, compilerService: CompileService,
targetPlatform: CompileService.TargetPlatform, sessionId: Int,
args: Array<out String>, targetPlatform: CompileService.TargetPlatform,
messageCollector: MessageCollector, args: Array<out String>,
outputsCollector: ((File, List<File>) -> Unit)? = null, messageCollector: MessageCollector,
compilerMode: CompilerMode = CompilerMode.NON_INCREMENTAL_COMPILER, outputsCollector: ((File, List<File>) -> Unit)? = null,
reportSeverity: ReportSeverity = ReportSeverity.INFO, compilerMode: CompilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
port: Int = SOCKET_ANY_FREE_PORT, reportSeverity: ReportSeverity = ReportSeverity.INFO,
profiler: Profiler = DummyProfiler() port: Int = SOCKET_ANY_FREE_PORT,
profiler: Profiler = DummyProfiler(),
): Int = profiler.withMeasure(this) { ): Int = profiler.withMeasure(this) {
val services = BasicCompilerServicesWithResultsFacadeServer(messageCollector, outputsCollector, port) val services = BasicCompilerServicesWithResultsFacadeServer(messageCollector, outputsCollector, port)
compilerService.compile( compilerService.compile(
sessionId, sessionId,
args, args,
CompilationOptions( CompilationOptions(
compilerMode, compilerMode,
targetPlatform, targetPlatform,
arrayOf(ReportCategory.COMPILER_MESSAGE.code, ReportCategory.DAEMON_MESSAGE.code, ReportCategory.EXCEPTION.code, ReportCategory.OUTPUT_MESSAGE.code), arrayOf(
reportSeverity.code, ReportCategory.COMPILER_MESSAGE.code,
emptyArray()), ReportCategory.DAEMON_MESSAGE.code,
services, ReportCategory.EXCEPTION.code,
null ReportCategory.OUTPUT_MESSAGE.code
),
reportSeverity.code,
emptyArray()
),
services,
null
).get() ).get()
} }
data class ClientOptions( data class ClientOptions(
var stop: Boolean = false var stop: Boolean = false,
) : OptionsGroup { ) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>> override val mappers: List<PropMapper<*, *, *>>
get() = listOf(BoolPropMapper(this, ClientOptions::stop)) get() = listOf(BoolPropMapper(this, ClientOptions::stop))
@@ -183,8 +204,11 @@ object KotlinCompilerClient {
val unrecognized = it.trimQuotes().split(",").filterExtractProps(opts.mappers, "") val unrecognized = it.trimQuotes().split(",").filterExtractProps(opts.mappers, "")
if (unrecognized.any()) if (unrecognized.any())
throw IllegalArgumentException( throw IllegalArgumentException(
"Unrecognized client options passed via property ${CompilerSystemProperties.COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY.property}: " + unrecognized.joinToString(" ") + "Unrecognized client options passed via property ${CompilerSystemProperties.COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY.property}: " + unrecognized.joinToString(
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() })) " "
) +
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() })
)
} }
return opts return opts
} }
@@ -196,16 +220,23 @@ object KotlinCompilerClient {
fun main(vararg args: String) { fun main(vararg args: String) {
val compilerId = CompilerId() val compilerId = CompilerId()
val daemonOptions = configureDaemonOptions() val daemonOptions = configureDaemonOptions()
val daemonLaunchingOptions = configureDaemonJVMOptions(inheritMemoryLimits = true, inheritOtherJvmOptions = false, inheritAdditionalProperties = true) val daemonLaunchingOptions =
configureDaemonJVMOptions(inheritMemoryLimits = true, inheritOtherJvmOptions = false, inheritAdditionalProperties = true)
val clientOptions = configureClientOptions() val clientOptions = configureClientOptions()
val filteredArgs = args.asIterable().filterExtractProps(compilerId, daemonOptions, daemonLaunchingOptions, clientOptions, prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) val filteredArgs = args.asIterable().filterExtractProps(
compilerId,
daemonOptions,
daemonLaunchingOptions,
clientOptions,
prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX
)
if (!clientOptions.stop) { if (!clientOptions.stop) {
if (compilerId.compilerClasspath.none()) { if (compilerId.compilerClasspath.none()) {
// attempt to find compiler to use // attempt to find compiler to use
System.err.println("compiler wasn't explicitly specified, attempt to find appropriate jar") System.err.println("compiler wasn't explicitly specified, attempt to find appropriate jar")
detectCompilerClasspath() detectCompilerClasspath()
?.let { compilerId.compilerClasspath = it } ?.let { compilerId.compilerClasspath = it }
} }
if (compilerId.compilerClasspath.none()) if (compilerId.compilerClasspath.none())
throw IllegalArgumentException("Cannot find compiler jar") throw IllegalArgumentException("Cannot find compiler jar")
@@ -213,15 +244,20 @@ object KotlinCompilerClient {
println("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator)) println("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator))
} }
val daemon = connectToCompileService(compilerId, daemonLaunchingOptions, daemonOptions, DaemonReportingTargets(out = System.out), autostart = !clientOptions.stop, checkId = !clientOptions.stop) val daemon = connectToCompileService(
compilerId,
daemonLaunchingOptions,
daemonOptions,
DaemonReportingTargets(out = System.out),
autostart = !clientOptions.stop,
checkId = !clientOptions.stop
)
if (daemon == null) { if (daemon == null) {
if (clientOptions.stop) { if (clientOptions.stop) {
System.err.println("No daemon found to shut down") System.err.println("No daemon found to shut down")
} } else throw Exception("Unable to connect to daemon")
else throw Exception("Unable to connect to daemon") } else when {
}
else when {
clientOptions.stop -> { clientOptions.stop -> {
println("Shutdown the daemon") println("Shutdown the daemon")
daemon.shutdown() daemon.shutdown()
@@ -229,7 +265,13 @@ object KotlinCompilerClient {
} }
filteredArgs.none() -> { filteredArgs.none() -> {
// so far used only in tests // so far used only in tests
println("Warning: empty arguments list, only daemon check is performed: checkCompilerId() returns ${daemon.checkCompilerId(compilerId)}") println(
"Warning: empty arguments list, only daemon check is performed: checkCompilerId() returns ${
daemon.checkCompilerId(
compilerId
)
}"
)
} }
else -> { else -> {
println("Executing daemon compilation with args: " + filteredArgs.joinToString(" ")) println("Executing daemon compilation with args: " + filteredArgs.joinToString(" "))
@@ -247,7 +289,7 @@ object KotlinCompilerClient {
override fun hasErrors() = hasErrors override fun hasErrors() = hasErrors
} }
val outputsCollector = { x: File, y: List<File> -> println("$x $y") } val outputsCollector = { x: File, y: List<File> -> println("$x $y") }
val servicesFacade = BasicCompilerServicesWithResultsFacadeServer(messageCollector, outputsCollector) val servicesFacade = BasicCompilerServicesWithResultsFacadeServer(messageCollector, outputsCollector)
try { try {
val memBefore = daemon.getUsedMemory().get() / 1024 val memBefore = daemon.getUsedMemory().get() / 1024
@@ -256,7 +298,12 @@ object KotlinCompilerClient {
val compilationOptions = CompilationOptions( val compilationOptions = CompilationOptions(
CompilerMode.NON_INCREMENTAL_COMPILER, CompilerMode.NON_INCREMENTAL_COMPILER,
CompileService.TargetPlatform.JVM, CompileService.TargetPlatform.JVM,
arrayOf(ReportCategory.COMPILER_MESSAGE.code, ReportCategory.DAEMON_MESSAGE.code, ReportCategory.EXCEPTION.code, ReportCategory.OUTPUT_MESSAGE.code), arrayOf(
ReportCategory.COMPILER_MESSAGE.code,
ReportCategory.DAEMON_MESSAGE.code,
ReportCategory.EXCEPTION.code,
ReportCategory.OUTPUT_MESSAGE.code
),
ReportSeverity.INFO.code, ReportSeverity.INFO.code,
emptyArray() emptyArray()
) )
@@ -275,8 +322,7 @@ object KotlinCompilerClient {
val memAfter = daemon.getUsedMemory().get() / 1024 val memAfter = daemon.getUsedMemory().get() / 1024
println("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms") println("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)") println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
} } finally {
finally {
// forcing RMI to unregister all objects and stop // forcing RMI to unregister all objects and stop
UnicastRemoteObject.unexportObject(servicesFacade, true) UnicastRemoteObject.unexportObject(servicesFacade, true)
} }
@@ -291,7 +337,7 @@ object KotlinCompilerClient {
?.distinct() ?.distinct()
?.mapNotNull { ?.mapNotNull {
it?.walk() it?.walk()
?.firstOrNull { it.name.equals(COMPILER_JAR_NAME, ignoreCase = true) } ?.firstOrNull { it.name.equals(COMPILER_JAR_NAME, ignoreCase = true) }
} }
?.firstOrNull() ?.firstOrNull()
?.let { listOf(it.absolutePath) } ?.let { listOf(it.absolutePath) }
@@ -299,81 +345,100 @@ object KotlinCompilerClient {
// --- Implementation --------------------------------------- // --- Implementation ---------------------------------------
private inline fun <R> connectLoop( private inline fun <R> connectLoop(
reportingTargets: DaemonReportingTargets, autostart: Boolean, body: (Boolean) -> R? reportingTargets: DaemonReportingTargets, autostart: Boolean, body: (Boolean) -> R?,
): R? = synchronized(this) { ): R? = synchronized(this) {
try { try {
var attempts = 1 var attempts = 1
while (true) { while (true) {
val (res, err) = try { val (res, err) = try {
body(attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS) to null body(attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS) to null
} catch (e: SocketException) {
null to e
} catch (e: ConnectException) {
null to e
} catch (e: ConnectIOException) {
null to e
} catch (e: UnmarshalException) {
null to e
} catch (e: RuntimeException) {
null to e
} }
catch (e: SocketException) { null to e }
catch (e: ConnectException) { null to e }
catch (e: ConnectIOException) { null to e }
catch (e: UnmarshalException) { null to e }
catch (e: RuntimeException) { null to e }
if (res != null) return res if (res != null) return res
if (err != null) { if (err != null) {
reportingTargets.report(DaemonReportCategory.INFO, reportingTargets.report(
(if (attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) "no more retries on: " else "retrying($attempts) on: ") DaemonReportCategory.INFO,
+ err.toString()) (if (attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) "no more retries on: " else "retrying($attempts) on: ")
+ err.toString()
)
} }
if (attempts++ > DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) { if (attempts++ > DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) {
return null return null
} }
} }
} } catch (e: Throwable) {
catch (e: Throwable) {
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString()) reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
} }
return null return null
} }
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 = Files.createTempFile(registryDir.toPath(), "kotlin-daemon-client-tsmarker", null).toFile() val timestampMarker = Files.createTempFile(registryDir.toPath(), "kotlin-daemon-client-tsmarker", null).toFile()
val aliveWithMetadata = try { val aliveWithMetadata = try {
walkDaemons(registryDir, compilerId, timestampMarker, report = report).toList() walkDaemons(registryDir, compilerId, timestampMarker, report = report).toList()
} } finally {
finally {
timestampMarker.delete() timestampMarker.delete()
} }
val comparator = compareBy<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions }) val comparator =
compareBy<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator()) { it.jvmOptions }
.thenBy(FileAgeComparator()) { it.runFile } .thenBy(FileAgeComparator()) { it.runFile }
val optsCopy = daemonJVMOptions.copy() val optsCopy = daemonJVMOptions.copy()
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon // if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
@Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+. @Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+.
return aliveWithMetadata.maxWith(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let { return aliveWithMetadata.maxWith(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let {
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions)) Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions))
} }
// else combine all options from running daemon to get fattest option for a new daemon to run // else combine all options from running daemon to get fattest option for a new daemon to run
?: Pair(null, aliveWithMetadata.fold(optsCopy, { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) })) ?: Pair(null, aliveWithMetadata.fold(optsCopy) { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) })
} }
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets): Boolean { private fun startDaemon(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
): Boolean {
val javaExecutable = File(File(CompilerSystemProperties.JAVA_HOME.safeValue, "bin"), "java") val javaExecutable = File(File(CompilerSystemProperties.JAVA_HOME.safeValue, "bin"), "java")
val serverHostname = CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.value ?: error("${CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.property} is not set!") val serverHostname = CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.value
?: error("${CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.property} is not set!")
val platformSpecificOptions = listOf( val platformSpecificOptions = listOf(
// hide daemon window // hide daemon window
"-Djava.awt.headless=true", "-Djava.awt.headless=true",
"-D$${CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.property}=$serverHostname") "-D$${CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.property}=$serverHostname"
)
val javaVersion = CompilerSystemProperties.JAVA_VERSION.value?.toIntOrNull() val javaVersion = CompilerSystemProperties.JAVA_VERSION.value?.toIntOrNull()
val javaIllegalAccessWorkaround = val javaIllegalAccessWorkaround =
if (javaVersion != null && javaVersion >= 16) if (javaVersion != null && javaVersion >= 16)
listOf("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED") listOf("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
else emptyList() else emptyList()
val args = listOf( val args = listOf(
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) + javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)
platformSpecificOptions + ) +
daemonJVMOptions.mappers.flatMap { it.toArgs("-") } + platformSpecificOptions +
javaIllegalAccessWorkaround + daemonJVMOptions.mappers.flatMap { it.toArgs("-") } +
COMPILER_DAEMON_CLASS_FQN + javaIllegalAccessWorkaround +
daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } + COMPILER_DAEMON_CLASS_FQN +
compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) }
reportingTargets.report(DaemonReportCategory.DEBUG, "starting the daemon as: " + args.joinToString(" ")) reportingTargets.report(DaemonReportCategory.DEBUG, "starting the daemon as: " + args.joinToString(" "))
val processBuilder = ProcessBuilder(args) val processBuilder = ProcessBuilder(args)
processBuilder.redirectErrorStream(true) processBuilder.redirectErrorStream(true)
@@ -393,7 +458,10 @@ object KotlinCompilerClient {
.forEachLine { .forEachLine {
if (Thread.currentThread().isInterrupted) return@forEachLine if (Thread.currentThread().isInterrupted) return@forEachLine
if (it == COMPILE_DAEMON_IS_READY_MESSAGE) { if (it == COMPILE_DAEMON_IS_READY_MESSAGE) {
reportingTargets.report(DaemonReportCategory.DEBUG, "Received the message signalling that the daemon is ready") reportingTargets.report(
DaemonReportCategory.DEBUG,
"Received the message signalling that the daemon is ready"
)
isEchoRead.release() isEchoRead.release()
return@forEachLine return@forEachLine
} else { } else {
@@ -414,9 +482,11 @@ object KotlinCompilerClient {
val daemonStartupTimeout = CompilerSystemProperties.COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY.value?.let { val daemonStartupTimeout = CompilerSystemProperties.COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY.value?.let {
try { try {
it.toLong() it.toLong()
} } catch (e: Exception) {
catch (e: Exception) { reportingTargets.report(
reportingTargets.report(DaemonReportCategory.INFO, "unable to interpret ${CompilerSystemProperties.COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY.property} property ('$it'); using default timeout $DAEMON_DEFAULT_STARTUP_TIMEOUT_MS ms") DaemonReportCategory.INFO,
"unable to interpret ${CompilerSystemProperties.COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY.property} property ('$it'); using default timeout $DAEMON_DEFAULT_STARTUP_TIMEOUT_MS ms"
)
null null
} }
} ?: DAEMON_DEFAULT_STARTUP_TIMEOUT_MS } ?: DAEMON_DEFAULT_STARTUP_TIMEOUT_MS
@@ -424,7 +494,10 @@ object KotlinCompilerClient {
val succeeded = isEchoRead.tryAcquire(daemonStartupTimeout, TimeUnit.MILLISECONDS) val succeeded = isEchoRead.tryAcquire(daemonStartupTimeout, TimeUnit.MILLISECONDS)
return when { return when {
!isProcessAlive(daemon) -> { !isProcessAlive(daemon) -> {
reportingTargets.report(DaemonReportCategory.INFO, "Daemon terminated unexpectedly with error code: ${daemon.exitValue()}") reportingTargets.report(
DaemonReportCategory.INFO,
"Daemon terminated unexpectedly with error code: ${daemon.exitValue()}"
)
false false
} }
!succeeded -> { !succeeded -> {
@@ -433,13 +506,11 @@ object KotlinCompilerClient {
} }
else -> true else -> true
} }
} } else
else
// without startEcho defined waiting for max timeout // without startEcho defined waiting for max timeout
Thread.sleep(daemonStartupTimeout) Thread.sleep(daemonStartupTimeout)
return true return true
} } finally {
finally {
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself // assuming that all important output is already done, the rest should be routed to the log by the daemon itself
if (stdoutThread.isAlive) { if (stdoutThread.isAlive) {
// TODO: find better method to stop the thread, but seems it will require asynchronous consuming of the stream // TODO: find better method to stop the thread, but seems it will require asynchronous consuming of the stream
@@ -453,10 +524,12 @@ object KotlinCompilerClient {
data class DaemonReportMessage(val category: DaemonReportCategory, val message: String) data class DaemonReportMessage(val category: DaemonReportCategory, val message: String)
class DaemonReportingTargets(val out: PrintStream? = null, class DaemonReportingTargets(
val messages: MutableCollection<DaemonReportMessage>? = null, val out: PrintStream? = null,
val messageCollector: MessageCollector? = null, val messages: MutableCollection<DaemonReportMessage>? = null,
val compilerServices: CompilerServicesFacadeBase? = null) val messageCollector: MessageCollector? = null,
val compilerServices: CompilerServicesFacadeBase? = null,
)
internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) { internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) {
val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message } val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message }
@@ -479,10 +552,9 @@ internal fun DaemonReportingTargets.report(category: DaemonReportCategory, messa
} }
internal fun isProcessAlive(process: Process) = internal fun isProcessAlive(process: Process) =
try { try {
process.exitValue() process.exitValue()
false false
} } catch (e: IllegalThreadStateException) {
catch (e: IllegalThreadStateException) { true
true }
}