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.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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user