Adding daemon stop command to kotlinr, minor fixes

This commit is contained in:
ligee
2015-08-17 12:47:14 +02:00
committed by Ilya Chernikov
parent d8be831339
commit f08476cba9
2 changed files with 99 additions and 58 deletions
@@ -31,8 +31,8 @@ import kotlin.concurrent.thread
import kotlin.concurrent.write import kotlin.concurrent.write
import kotlin.platform.platformStatic import kotlin.platform.platformStatic
val DAEMON_STARTUP_TIMEOUT_MILIS = 10000L val DAEMON_STARTUP_TIMEOUT_MS = 10000L
val DAEMON_STARTUP_CHECH_INTERVAL_MILIS = 100L val DAEMON_STARTUP_CHECK_INTERVAL_MS = 100L
public class KotlinCompilerClient { public class KotlinCompilerClient {
@@ -54,7 +54,7 @@ public class KotlinCompilerClient {
errStream.println("[daemon client] daemon not found") errStream.println("[daemon client] daemon not found")
} }
catch (e: ConnectException) { catch (e: ConnectException) {
errStream.println("[daemon client] cannot connect to registry: " + e.getMessage()) errStream.println("[daemon client] cannot connect to registry: " + (e.getCause()?.getMessage() ?: e.getMessage() ?: "unknown exception"))
// ignoring it - processing below // ignoring it - processing below
} }
return null return null
@@ -97,26 +97,30 @@ public class KotlinCompilerClient {
errStream.println("[daemon] " + it) errStream.println("[daemon] " + it)
} }
} }
// trying to wait for process try {
if (daemonOptions.startEcho.isNotEmpty()) { // trying to wait for process
errStream.println("[daemon client] waiting for daemon to respond") if (daemonOptions.startEcho.isNotEmpty()) {
var waitMillis: Long = DAEMON_STARTUP_TIMEOUT_MILIS / DAEMON_STARTUP_CHECH_INTERVAL_MILIS errStream.println("[daemon client] waiting for daemon to respond")
while (waitMillis-- > 0) { var waitMillis: Long = DAEMON_STARTUP_TIMEOUT_MS / DAEMON_STARTUP_CHECK_INTERVAL_MS
Thread.sleep(DAEMON_STARTUP_CHECH_INTERVAL_MILIS) while (waitMillis-- > 0) {
if (!daemon.isAlive() || lock.read { isEchoRead } == true) break; Thread.sleep(DAEMON_STARTUP_CHECK_INTERVAL_MS)
if (!daemon.isAlive() || lock.read { isEchoRead } == true) break;
}
if (!daemon.isAlive())
throw Exception("Daemon terminated unexpectedly")
if (lock.read { isEchoRead } == false)
throw Exception("Unable to get response from daemon in $DAEMON_STARTUP_TIMEOUT_MS ms")
} }
if (!daemon.isAlive()) else
throw Exception("Daemon terminated unexpectedly")
if (lock.read { isEchoRead } == false)
throw Exception("Unable to get response from daemon in $DAEMON_STARTUP_TIMEOUT_MILIS ms")
}
else
// without startEcho defined waiting for max timeout // without startEcho defined waiting for max timeout
Thread.sleep(DAEMON_STARTUP_TIMEOUT_MILIS) Thread.sleep(DAEMON_STARTUP_TIMEOUT_MS)
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself }
if (stdouThread.isAlive) finally {
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself
if (stdouThread.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
lock.write { stdouThread.stop() } lock.write { stdouThread.stop() }
}
} }
public fun checkCompilerId(compiler: CompileService, localId: CompilerId, errStream: PrintStream): Boolean { public fun checkCompilerId(compiler: CompileService, localId: CompilerId, errStream: PrintStream): Boolean {
@@ -127,22 +131,26 @@ public class KotlinCompilerClient {
(localId.compilerClasspath.all { remoteId.compilerClasspath.contains(it) }) (localId.compilerClasspath.all { remoteId.compilerClasspath.contains(it) })
} }
public fun connectToCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions, errStream: PrintStream): CompileService? { public fun connectToCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions, errStream: PrintStream, autostart: Boolean = true, checkId: Boolean = true): CompileService? {
val service = connectToService(compilerId, daemonOptions, errStream) val service = connectToService(compilerId, daemonOptions, errStream)
if (service != null) { if (service != null) {
if (checkCompilerId(service, compilerId, errStream)) { if (!checkId || checkCompilerId(service, compilerId, errStream)) {
errStream.println("[daemon client] found the suitable daemon") errStream.println("[daemon client] found the suitable daemon")
return service return service
} }
errStream.println("[daemon client] compiler identity don't match: " + compilerId.asParams.joinToString(" ")) errStream.println("[daemon client] compiler identity don't match: " + compilerId.asParams.joinToString(" "))
if (!autostart) return null;
errStream.println("[daemon client] shutdown the daemon") errStream.println("[daemon client] shutdown the daemon")
service.shutdown() service.shutdown()
// TODO: find more reliable way // TODO: find more reliable way
Thread.sleep(1000) Thread.sleep(1000)
errStream.println("[daemon client] daemon shut down correctly, restarting") errStream.println("[daemon client] daemon shut down correctly, restarting")
} }
else else {
errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start") if (!autostart) return null;
else errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start")
}
startDaemon(compilerId, daemonOptions, errStream) startDaemon(compilerId, daemonOptions, errStream)
errStream.println("[daemon client] daemon started, trying to connect") errStream.println("[daemon client] daemon started, trying to connect")
return connectToService(compilerId, daemonOptions, errStream) return connectToService(compilerId, daemonOptions, errStream)
@@ -165,48 +173,73 @@ public class KotlinCompilerClient {
public fun isDaemonEnabled(): Boolean = System.getProperty(COMPILE_DAEMON_ENABLED_PROPERTY) != null public fun isDaemonEnabled(): Boolean = System.getProperty(COMPILE_DAEMON_ENABLED_PROPERTY) != null
data class ClientOptions(
public var stop: Boolean = false
) :CmdlineParams {
override val asParams: Iterable<String>
get() =
if (stop) listOf("stop") else listOf()
override val parsers: List<PropParser<*,*,*>>
get() = listOf( BoolPropParser(this, ::stop))
}
platformStatic public fun main(vararg args: String) { platformStatic public fun main(vararg args: String) {
val compilerId = CompilerId() val compilerId = CompilerId()
val daemonOptions = DaemonOptions() val daemonOptions = DaemonOptions()
val filteredArgs = args.asIterable().propParseFilter(compilerId, daemonOptions) val clientOptions = ClientOptions()
val filteredArgs = args.asIterable().propParseFilter(compilerId, daemonOptions, clientOptions)
if (compilerId.compilerClasspath.none()) { if (!clientOptions.stop) {
// attempt to find compiler to use if (compilerId.compilerClasspath.none()) {
println("compiler wasn't explicitly specified, attempt to find appropriate jar") // attempt to find compiler to use
System.getProperty("java.class.path") println("compiler wasn't explicitly specified, attempt to find appropriate jar")
?.split(File.pathSeparator) System.getProperty("java.class.path")
?.map { File(it).parent } ?.split(File.pathSeparator)
?.distinct() ?.map { File(it).parent }
?.map { it?.walk() ?.distinct()
?.firstOrNull { it.getName().equals(COMPILER_JAR_NAME, ignoreCase = true) } } ?.map {
?.filterNotNull() it?.walk()
?.firstOrNull() ?.firstOrNull { it.getName().equals(COMPILER_JAR_NAME, ignoreCase = true) }
?.let { compilerId.compilerClasspath = listOf(it.absolutePath)} }
?.filterNotNull()
?.firstOrNull()
?.let { compilerId.compilerClasspath = listOf(it.absolutePath) }
}
if (compilerId.compilerClasspath.none())
throw IllegalArgumentException("Cannot find compiler jar")
else
println("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator))
} }
if (compilerId.compilerClasspath.none())
throw IllegalArgumentException("Cannot find compiler jar")
else
println("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator))
connectToCompileService(compilerId, daemonOptions, System.out)?.let { connectToCompileService(compilerId, daemonOptions, System.out, autostart = !clientOptions.stop, checkId = !clientOptions.stop)?.let {
println("Executing daemon compilation with args: " + args.joinToString(" ")) when {
val outStrm = RemoteOutputStreamServer(System.out) clientOptions.stop -> {
try { println("Shutdown the daemon")
val memBefore = it.getUsedMemory() / 1024 it.shutdown()
val startTime = System.nanoTime() println("Daemon shut down successfully")
val res = it.remoteCompile(args, outStrm, CompileService.OutputFormat.PLAIN)
val endTime = System.nanoTime()
println("Compilation result code: $res")
val memAfter = it.getUsedMemory() / 1024
println("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
} }
finally { else -> {
outStrm.disconnect() println("Executing daemon compilation with args: " + args.joinToString(" "))
val outStrm = RemoteOutputStreamServer(System.out)
try {
val memBefore = it.getUsedMemory() / 1024
val startTime = System.nanoTime()
val res = it.remoteCompile(args, outStrm, CompileService.OutputFormat.PLAIN)
val endTime = System.nanoTime()
println("Compilation result code: $res")
val memAfter = it.getUsedMemory() / 1024
println("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
}
finally {
outStrm.disconnect()
}
} }
} }
?: throw Exception("Unable to connect to daemon") }
?: if (clientOptions.stop) println("No daemon found to shut down")
else throw Exception("Unable to connect to daemon")
} }
} }
} }
@@ -33,16 +33,24 @@ public val COMPILE_DAEMON_ENABLED_PROPERTY: String ="kotlin.daemon.enabled"
fun<C, V, P: KProperty1<C,V>> C.propToParams(p: P, conv: ((v: V) -> String) = { it.toString() } ) = fun<C, V, P: KProperty1<C,V>> C.propToParams(p: P, conv: ((v: V) -> String) = { it.toString() } ) =
listOf("--daemon-" + p.name, conv(p.get(this))) listOf("--daemon-" + p.name, conv(p.get(this)))
class PropParser<C, V, P: KMutableProperty1<C, V>>(val dest: C, val prop: P, val parse: (s: String) -> V) { open class PropParser<C, V, P: KMutableProperty1<C, V>>(val dest: C, val prop: P, val parse: (s: String) -> V) {
fun apply(s: String) = prop.set(dest, parse(s)) fun apply(s: String) = prop.set(dest, parse(s))
} }
class BoolPropParser<C, P: KMutableProperty1<C, Boolean>>(dest: C, prop: P): PropParser<C, Boolean, P>(dest, prop, { true })
fun Iterable<String>.propParseFilter(parsers: List<PropParser<*,*,*>>) : Iterable<String> { fun Iterable<String>.propParseFilter(parsers: List<PropParser<*,*,*>>) : Iterable<String> {
var currentParser: PropParser<*,*,*>? = null var currentParser: PropParser<*,*,*>? = null
return filter { param -> return filter { param ->
if (currentParser == null) { if (currentParser == null) {
currentParser = parsers.find { param.equals("--daemon-" + it.prop.name) } currentParser = parsers.find { param.equals("--daemon-" + it.prop.name) }
if (currentParser != null) false if (currentParser != null) {
if (currentParser is BoolPropParser<*,*>) {
currentParser!!.apply("")
currentParser = null
}
false
}
else true else true
} }
else { else {