Adding daemon stop command to kotlinr, minor fixes
This commit is contained in:
@@ -31,8 +31,8 @@ import kotlin.concurrent.thread
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
val DAEMON_STARTUP_TIMEOUT_MILIS = 10000L
|
||||
val DAEMON_STARTUP_CHECH_INTERVAL_MILIS = 100L
|
||||
val DAEMON_STARTUP_TIMEOUT_MS = 10000L
|
||||
val DAEMON_STARTUP_CHECK_INTERVAL_MS = 100L
|
||||
|
||||
public class KotlinCompilerClient {
|
||||
|
||||
@@ -54,7 +54,7 @@ public class KotlinCompilerClient {
|
||||
errStream.println("[daemon client] daemon not found")
|
||||
}
|
||||
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
|
||||
}
|
||||
return null
|
||||
@@ -97,26 +97,30 @@ public class KotlinCompilerClient {
|
||||
errStream.println("[daemon] " + it)
|
||||
}
|
||||
}
|
||||
// trying to wait for process
|
||||
if (daemonOptions.startEcho.isNotEmpty()) {
|
||||
errStream.println("[daemon client] waiting for daemon to respond")
|
||||
var waitMillis: Long = DAEMON_STARTUP_TIMEOUT_MILIS / DAEMON_STARTUP_CHECH_INTERVAL_MILIS
|
||||
while (waitMillis-- > 0) {
|
||||
Thread.sleep(DAEMON_STARTUP_CHECH_INTERVAL_MILIS)
|
||||
if (!daemon.isAlive() || lock.read { isEchoRead } == true) break;
|
||||
try {
|
||||
// trying to wait for process
|
||||
if (daemonOptions.startEcho.isNotEmpty()) {
|
||||
errStream.println("[daemon client] waiting for daemon to respond")
|
||||
var waitMillis: Long = DAEMON_STARTUP_TIMEOUT_MS / DAEMON_STARTUP_CHECK_INTERVAL_MS
|
||||
while (waitMillis-- > 0) {
|
||||
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())
|
||||
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
|
||||
else
|
||||
// without startEcho defined waiting for max timeout
|
||||
Thread.sleep(DAEMON_STARTUP_TIMEOUT_MILIS)
|
||||
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself
|
||||
if (stdouThread.isAlive)
|
||||
Thread.sleep(DAEMON_STARTUP_TIMEOUT_MS)
|
||||
}
|
||||
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
|
||||
lock.write { stdouThread.stop() }
|
||||
lock.write { stdouThread.stop() }
|
||||
}
|
||||
}
|
||||
|
||||
public fun checkCompilerId(compiler: CompileService, localId: CompilerId, errStream: PrintStream): Boolean {
|
||||
@@ -127,22 +131,26 @@ public class KotlinCompilerClient {
|
||||
(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)
|
||||
if (service != null) {
|
||||
if (checkCompilerId(service, compilerId, errStream)) {
|
||||
if (!checkId || checkCompilerId(service, compilerId, errStream)) {
|
||||
errStream.println("[daemon client] found the suitable daemon")
|
||||
return service
|
||||
}
|
||||
errStream.println("[daemon client] compiler identity don't match: " + compilerId.asParams.joinToString(" "))
|
||||
if (!autostart) return null;
|
||||
errStream.println("[daemon client] shutdown the daemon")
|
||||
service.shutdown()
|
||||
// TODO: find more reliable way
|
||||
Thread.sleep(1000)
|
||||
errStream.println("[daemon client] daemon shut down correctly, restarting")
|
||||
}
|
||||
else
|
||||
errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start")
|
||||
else {
|
||||
if (!autostart) return null;
|
||||
else errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start")
|
||||
}
|
||||
|
||||
startDaemon(compilerId, daemonOptions, errStream)
|
||||
errStream.println("[daemon client] daemon started, trying to connect")
|
||||
return connectToService(compilerId, daemonOptions, errStream)
|
||||
@@ -165,48 +173,73 @@ public class KotlinCompilerClient {
|
||||
|
||||
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) {
|
||||
val compilerId = CompilerId()
|
||||
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()) {
|
||||
// attempt to find compiler to use
|
||||
println("compiler wasn't explicitly specified, attempt to find appropriate jar")
|
||||
System.getProperty("java.class.path")
|
||||
?.split(File.pathSeparator)
|
||||
?.map { File(it).parent }
|
||||
?.distinct()
|
||||
?.map { it?.walk()
|
||||
?.firstOrNull { it.getName().equals(COMPILER_JAR_NAME, ignoreCase = true) } }
|
||||
?.filterNotNull()
|
||||
?.firstOrNull()
|
||||
?.let { compilerId.compilerClasspath = listOf(it.absolutePath)}
|
||||
if (!clientOptions.stop) {
|
||||
if (compilerId.compilerClasspath.none()) {
|
||||
// attempt to find compiler to use
|
||||
println("compiler wasn't explicitly specified, attempt to find appropriate jar")
|
||||
System.getProperty("java.class.path")
|
||||
?.split(File.pathSeparator)
|
||||
?.map { File(it).parent }
|
||||
?.distinct()
|
||||
?.map {
|
||||
it?.walk()
|
||||
?.firstOrNull { it.getName().equals(COMPILER_JAR_NAME, ignoreCase = true) }
|
||||
}
|
||||
?.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 {
|
||||
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)")
|
||||
connectToCompileService(compilerId, daemonOptions, System.out, autostart = !clientOptions.stop, checkId = !clientOptions.stop)?.let {
|
||||
when {
|
||||
clientOptions.stop -> {
|
||||
println("Shutdown the daemon")
|
||||
it.shutdown()
|
||||
println("Daemon shut down successfully")
|
||||
}
|
||||
finally {
|
||||
outStrm.disconnect()
|
||||
else -> {
|
||||
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() } ) =
|
||||
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))
|
||||
}
|
||||
|
||||
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> {
|
||||
var currentParser: PropParser<*,*,*>? = null
|
||||
return filter { param ->
|
||||
if (currentParser == null) {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user