Refactoring after review

This commit is contained in:
Ilya Chernikov
2015-08-28 14:18:39 +02:00
parent 2d45a37884
commit 9bee97e810
7 changed files with 196 additions and 164 deletions
+4
View File
@@ -117,6 +117,8 @@
<include name="backend-common/**"/>
<include name="cli/**"/>
<include name="cli-common/**"/>
<include name="rmi-server/**"/>
<include name="rmi-interface/**"/>
<include name="util/**"/>
<include name="util.runtime/**"/>
<include name="light-classes/**"/>
@@ -200,6 +202,8 @@
<fileset dir="compiler/builtins-serializer/src"/>
<fileset dir="compiler/cli/src"/>
<fileset dir="compiler/cli/cli-common/src"/>
<include name="compiler/rmi/rmi-server/src"/>
<include name="compiler/rmi/rmi-interface/src"/>
<fileset dir="compiler/container/src"/>
<fileset dir="compiler/frontend/src"/>
<fileset dir="compiler/frontend.java/src"/>
@@ -26,12 +26,7 @@ import java.rmi.Remote
import java.rmi.registry.LocateRegistry
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.thread
import kotlin.concurrent.write
import kotlin.platform.platformStatic
import kotlin.reflect.KProperty1
fun Process.isAlive() =
try {
@@ -71,7 +66,7 @@ public class KotlinCompilerClient {
}
private fun startDaemon(compilerId: CompilerId, daemonLaunchingOptions: DaemonLaunchingOptions, daemonOptions: DaemonOptions, errStream: PrintStream) {
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, errStream: PrintStream) {
val javaExecutable = File(System.getProperty("java.home"), "bin").let {
val javaw = File(it, "javaw.exe")
if (javaw.exists()) javaw
@@ -80,10 +75,10 @@ public class KotlinCompilerClient {
// TODO: add some specific environment variables to the cp and may be command line, to allow some specific startup configs
val args = listOf(javaExecutable.absolutePath,
"-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
daemonLaunchingOptions.extractors.flatMap { it.extract("-") } +
daemonJVMOptions.mappers.flatMap { it.toArgs("-") } +
COMPILER_DAEMON_CLASS_FQN +
daemonOptions.extractors.flatMap { it.extract(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
compilerId.extractors.flatMap { it.extract(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) }
errStream.println("[daemon client] starting the daemon as: " + args.joinToString(" "))
val processBuilder = ProcessBuilder(args).redirectErrorStream(true)
// assuming daemon process is deaf and (mostly) silent, so do not handle streams
@@ -135,14 +130,14 @@ public class KotlinCompilerClient {
(localId.compilerDigest.isEmpty() || remoteId.compilerDigest.isEmpty() || localId.compilerDigest == remoteId.compilerDigest)
}
public fun connectToCompileService(compilerId: CompilerId, daemonLaunchingOptions: DaemonLaunchingOptions, daemonOptions: DaemonOptions, errStream: PrintStream, autostart: Boolean = true, checkId: Boolean = true): CompileService? {
public fun connectToCompileService(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, errStream: PrintStream, autostart: Boolean = true, checkId: Boolean = true): CompileService? {
val service = connectToService(compilerId, daemonOptions, errStream)
if (service != null) {
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.extractors.flatMap { it.extract("") }.joinToString(" "))
errStream.println("[daemon client] compiler identity don't match: " + compilerId.mappers.flatMap { it.toArgs("") }.joinToString(" "))
if (!autostart) return null;
errStream.println("[daemon client] shutdown the daemon")
service.shutdown()
@@ -155,13 +150,13 @@ public class KotlinCompilerClient {
else errStream.println("[daemon client] cannot connect to Compile Daemon, trying to start")
}
startDaemon(compilerId, daemonLaunchingOptions, daemonOptions, errStream)
startDaemon(compilerId, daemonJVMOptions, daemonOptions, errStream)
errStream.println("[daemon client] daemon started, trying to connect")
return connectToService(compilerId, daemonOptions, errStream)
}
public fun shutdownCompileService(daemonOptions: DaemonOptions): Unit {
KotlinCompilerClient.connectToCompileService(CompilerId(), DaemonLaunchingOptions(), daemonOptions, System.out, autostart = false, checkId = false)
KotlinCompilerClient.connectToCompileService(CompilerId(), DaemonJVMOptions(), daemonOptions, System.out, autostart = false, checkId = false)
?.shutdown()
}
@@ -196,20 +191,17 @@ public class KotlinCompilerClient {
data class ClientOptions(
public var stop: Boolean = false
) :CmdlineParams {
override val extractors: List<PropExtractor<*, *, *>>
get() = listOf( BoolPropExtractor(this, ::stop))
override val parsers: List<PropParser<*,*,*>>
get() = listOf( BoolPropParser(this, ::stop))
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf( BoolPropMapper(this, ::stop))
}
platformStatic public fun main(vararg args: String) {
jvmStatic public fun main(vararg args: String) {
val compilerId = CompilerId()
val daemonOptions = DaemonOptions()
val daemonLaunchingOptions = DaemonLaunchingOptions()
val daemonLaunchingOptions = DaemonJVMOptions()
val clientOptions = ClientOptions()
val filteredArgs = args.asIterable().filterSetProps(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 (compilerId.compilerClasspath.none()) {
@@ -21,9 +21,7 @@ import java.io.Serializable
import java.lang.management.ManagementFactory
import java.security.DigestInputStream
import java.security.MessageDigest
import kotlin.platform.platformStatic
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
public val COMPILER_JAR_NAME: String = "kotlin-compiler.jar"
@@ -34,85 +32,147 @@ public val COMPILE_DAEMON_ENABLED_PROPERTY: String ="kotlin.daemon.enabled"
public val COMPILE_DAEMON_JVM_OPTIONS_PROPERTY: String ="kotlin.daemon.jvm.options"
public val COMPILE_DAEMON_OPTIONS_PROPERTY: String ="kotlin.daemon.options"
public val COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX: String ="--daemon-"
public val COMPILE_DAEMON_TIMEOUT_INFINITE_S: Int = 0
public val COMPILE_DAEMON_MEMORY_THRESHOLD_INFINITE: Long = 0L
val COMPILER_ID_DIGEST = "MD5"
open class PropExtractor<C, V, P: KProperty1<C, V>>(val dest: C,
val prop: P,
val name: String,
val convert: ((v: V) -> String?) = { it.toString() },
val skipIf: ((v: V) -> Boolean) = { false },
val mergeWithDelimiter: String? = null)
//open class PropExtractor<C, V, P: KProperty1<C, V>>(val dest: C,
// val prop: P,
// val name: String,
// val convert: ((v: V) -> String?) = { it.toString() },
// val skipIf: ((v: V) -> Boolean) = { false },
// val mergeWithDelimiter: String? = null)
//{
// constructor(dest: C, prop: P, convert: ((v: V) -> String?) = { it.toString() }, skipIf: ((v: V) -> Boolean) = { false }) : this(dest, prop, prop.name, convert, skipIf)
// open fun extract(prefix: String = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX): List<String> =
// when {
// skipIf(prop.get(dest)) -> listOf<String>()
// mergeWithDelimiter != null -> listOf(prefix + name + mergeWithDelimiter + convert(prop.get(dest))).filterNotNull()
// else -> listOf(prefix + name, convert(prop.get(dest))).filterNotNull()
// }
//}
//
//class BoolPropExtractor<C, P: KMutableProperty1<C, Boolean>>(dest: C, prop: P, name: String? = null)
// : PropExtractor<C, Boolean, P>(dest, prop, name ?: prop.name, convert = { null }, skipIf = { !prop.get(dest) })
//
//class RestPropExtractor<C, P: KMutableProperty1<C, out MutableCollection<String>>>(dest: C, prop: P) : PropExtractor<C, MutableCollection<String>, P>(dest, prop, convert = { null }) {
// override fun extract(prefix: String): List<String> = prop.get(dest).map { prefix + it }
//}
//
//
//open class PropParser<C, V, P: KMutableProperty1<C, V>>(val dest: C,
// val prop: P, alternativeNames: List<String>,
// val parse: (s: String) -> V,
// val allowMergedArg: Boolean = false) {
// val names = listOf(prop.name) + alternativeNames
// constructor(dest: C, prop: P, parse: (s: String) -> V, allowMergedArg: Boolean = false) : this(dest, prop, listOf(), parse, allowMergedArg)
// 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 })
//
//class RestPropParser<C, P: KMutableProperty1<C, MutableCollection<String>>>(dest: C, prop: P): PropParser<C, MutableCollection<String>, P>(dest, prop, { arrayListOf() }) {
// fun add(s: String) { prop.get(dest).add(s) }
//}
// --------------------------------------------------------
open class PropMapper<C, V, P: KMutableProperty1<C, V>>(val dest: C,
val prop: P,
val names: List<String> = listOf(prop.name),
val fromString: (s: String) -> V,
val toString: ((v: V) -> String?) = { it.toString() },
val skipIf: ((v: V) -> Boolean) = { false },
val mergeDelimiter: String? = null)
{
constructor(dest: C, prop: P, convert: ((v: V) -> String?) = { it.toString() }, skipIf: ((v: V) -> Boolean) = { false }) : this(dest, prop, prop.name, convert, skipIf)
open fun extract(prefix: String = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX): List<String> =
open fun toArgs(prefix: String = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX): List<String> =
when {
skipIf(prop.get(dest)) -> listOf<String>()
mergeWithDelimiter != null -> listOf(prefix + name + mergeWithDelimiter + convert(prop.get(dest))).filterNotNull()
else -> listOf(prefix + name, convert(prop.get(dest))).filterNotNull()
mergeDelimiter != null -> listOf(prefix + names.first() + mergeDelimiter + toString(prop.get(dest))).filterNotNull()
else -> listOf(prefix + names.first(), toString(prop.get(dest))).filterNotNull()
}
fun apply(s: String) = prop.set(dest, fromString(s))
}
class BoolPropExtractor<C, P: KMutableProperty1<C, Boolean>>(dest: C, prop: P, name: String? = null)
: PropExtractor<C, Boolean, P>(dest, prop, name ?: prop.name, convert = { null }, skipIf = { !prop.get(dest) })
class StringPropMapper<C, P: KMutableProperty1<C, String>>(dest: C,
prop: P,
names: List<String> = listOf(),
fromString: ((String) -> String) = { it },
toString: ((String) -> String?) = { it.toString() },
skipIf: ((String) -> Boolean) = { it.isEmpty() },
mergeDelimiter: String? = null)
: PropMapper<C, String, P>(dest = dest, prop = prop, names = if (names.any()) names else listOf(prop.name),
fromString = fromString, toString = toString, skipIf = skipIf, mergeDelimiter = mergeDelimiter)
class RestPropExtractor<C, P: KMutableProperty1<C, out MutableCollection<String>>>(dest: C, prop: P) : PropExtractor<C, MutableCollection<String>, P>(dest, prop, convert = { null }) {
override fun extract(prefix: String): List<String> = prop.get(dest).map { prefix + it }
}
class BoolPropMapper<C, P: KMutableProperty1<C, Boolean>>(dest: C, prop: P, names: List<String> = listOf())
: PropMapper<C, Boolean, P>(dest = dest, prop = prop, names = if (names.any()) names else listOf(prop.name),
fromString = { true }, toString = { null }, skipIf = { !prop.get(dest) })
open class PropParser<C, V, P: KMutableProperty1<C, V>>(val dest: C,
val prop: P, alternativeNames: List<String>,
val parse: (s: String) -> V,
val allowMergedArg: Boolean = false) {
val names = listOf(prop.name) + alternativeNames
constructor(dest: C, prop: P, parse: (s: String) -> V, allowMergedArg: Boolean = false) : this(dest, prop, listOf(), parse, allowMergedArg)
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 })
class RestPropParser<C, P: KMutableProperty1<C, MutableCollection<String>>>(dest: C, prop: P): PropParser<C, MutableCollection<String>, P>(dest, prop, { arrayListOf() }) {
class RestPropMapper<C, P: KMutableProperty1<C, MutableCollection<String>>>(dest: C, prop: P)
: PropMapper<C, MutableCollection<String>, P>(dest = dest, prop = prop, toString = { null }, fromString = { arrayListOf() })
{
override fun toArgs(prefix: String): List<String> = prop.get(dest).map { prefix + it }
fun add(s: String) { prop.get(dest).add(s) }
}
// ------------------------------------------
fun Iterable<String>.filterSetProps(parsers: List<PropParser<*,*,*>>, prefix: String, restParser: RestPropParser<*,*>? = null) : Iterable<String> {
var currentParser: PropParser<*,*,*>? = null
fun Iterable<String>.filterExtractProps(propMappers: List<PropMapper<*,*,*>>, prefix: String, restParser: RestPropMapper<*,*>? = null) : Iterable<String> {
var currentPropMapper: PropMapper<*,*,*>? = null
var matchingOption = ""
val res = filter { param ->
if (currentParser == null) {
val parser = parsers.find { it.names.any { name ->
if (param.startsWith(prefix + name)) { matchingOption = prefix + name; true }
else false } }
if (parser != null) {
val optionLength = matchingOption.length()
when {
parser is BoolPropParser<*,*> ->
if (param.length() > optionLength) throw IllegalArgumentException("Invalid switch option '$param', expecting $matchingOption without arguments")
else parser.apply("")
param.length() > optionLength ->
if (param[optionLength] != '=') {
if (parser.allowMergedArg) parser.apply(param.substring(optionLength))
else throw IllegalArgumentException("Invalid option syntax '$param', expecting $matchingOption[= ]<arg>")
}
else parser.apply(param.substring(optionLength + 1))
else -> currentParser = parser
if (currentPropMapper == null) {
val propMapper = propMappers.find {
it !is RestPropMapper<*,*> &&
it.names.any { name ->
if (param.startsWith(prefix + name)) {
matchingOption = prefix + name
true
}
else {
false
}
}
false
}
else if (restParser != null && param.startsWith(prefix)) {
restParser.add(param.removePrefix(prefix))
false
when {
propMapper != null -> {
val optionLength = matchingOption.length()
when {
propMapper is BoolPropMapper<*,*> -> {
if (param.length() > optionLength)
throw IllegalArgumentException("Invalid switch option '$param', expecting $matchingOption without arguments")
propMapper.apply("")
}
param.length() > optionLength ->
if (param[optionLength] != '=') {
if (propMapper.mergeDelimiter == null)
throw IllegalArgumentException("Invalid option syntax '$param', expecting $matchingOption[= ]<arg>")
propMapper.apply(param.substring(optionLength))
}
else {
propMapper.apply(param.substring(optionLength + 1))
}
else ->
currentPropMapper = propMapper
}
false
}
restParser != null && param.startsWith(prefix) -> {
restParser.add(param.removePrefix(prefix))
false
}
else -> true
}
else true
}
else {
currentParser!!.apply(param)
currentParser = null
currentPropMapper!!.apply(param)
currentPropMapper = null
false
}
}
if (currentParser != null) throw IllegalArgumentException("Expecting argument for the option $matchingOption")
if (currentPropMapper != null)
throw IllegalArgumentException("Expecting argument for the option $matchingOption")
return res
}
@@ -124,59 +184,46 @@ fun Iterable<String>.filterSetProps(parsers: List<PropParser<*,*,*>>, prefix: St
public interface CmdlineParams : Serializable {
public val extractors: List<PropExtractor<*,*,*>>
public val parsers: List<PropParser<*,*,*>>
public interface OptionsGroup : Serializable {
public val mappers: List<PropMapper<*,*,*>>
}
public fun Iterable<String>.filterSetProps(vararg cs: CmdlineParams, prefix: String) : Iterable<String> =
filterSetProps(cs.flatMap { it.parsers }, prefix)
public fun Iterable<String>.filterExtractProps(vararg groups: OptionsGroup, prefix: String) : Iterable<String> =
filterExtractProps(groups.flatMap { it.mappers }, prefix)
public data class DaemonLaunchingOptions(
public data class DaemonJVMOptions(
public var maxMemory: String = "",
public var maxPermSize: String = "",
public var reservedCodeCacheSize: String = "",
public var otherJvmParams: MutableCollection<String> = arrayListOf()
) : CmdlineParams {
) : OptionsGroup {
override val extractors: List<PropExtractor<*,*,*>>
get() = listOf( PropExtractor(this, ::maxMemory, "Xmx", skipIf = { it.isEmpty() }, mergeWithDelimiter = ""),
PropExtractor(this, ::maxPermSize, "XX:MaxPermSize", skipIf = { it.isEmpty() }, mergeWithDelimiter = "="),
PropExtractor(this, ::reservedCodeCacheSize, "XX:ReservedCodeCacheSize", skipIf = { it.isEmpty() }, mergeWithDelimiter = "="),
RestPropExtractor(this, ::otherJvmParams))
override val mappers: List<PropMapper<*,*,*>>
get() = listOf( StringPropMapper(this, ::maxMemory, listOf("Xmx"), mergeDelimiter = ""),
StringPropMapper(this, ::maxPermSize, listOf("XX:MaxPermSize"), mergeDelimiter = "="),
StringPropMapper(this, ::reservedCodeCacheSize, listOf("XX:ReservedCodeCacheSize"), mergeDelimiter = "="),
restMapper)
override val parsers: List<PropParser<*,*,*>>
get() = listOf( PropParser(this, ::maxMemory, listOf("Xmx"), { it }, allowMergedArg = true),
PropParser(this, ::maxPermSize, listOf("XX:MaxPermSize"), { it }, allowMergedArg = true),
PropParser(this, ::reservedCodeCacheSize, listOf("XX:ReservedCodeCacheSize"), { it }, allowMergedArg = true))
// otherJvmParams is missing here deliberately, it is used explicitly as a restParser param to filterSetProps
val restMapper: RestPropMapper<*,*>
get() = RestPropMapper(this, ::otherJvmParams)
}
public data class DaemonOptions(
public var port: Int = COMPILE_DAEMON_DEFAULT_PORT,
public var autoshutdownMemoryThreshold: Long = 0 /* 0 means unchecked */,
public var autoshutdownIdleSeconds: Int = 0 /* 0 means unchecked */,
public var autoshutdownMemoryThreshold: Long = COMPILE_DAEMON_MEMORY_THRESHOLD_INFINITE,
public var autoshutdownIdleSeconds: Int = COMPILE_DAEMON_TIMEOUT_INFINITE_S,
public var startEcho: String = COMPILER_SERVICE_RMI_NAME
) : CmdlineParams {
) : OptionsGroup {
override val extractors: List<PropExtractor<*, *, *>>
get() = listOf( PropExtractor(this, ::port),
PropExtractor(this, ::autoshutdownMemoryThreshold, skipIf = { it == 0L }),
PropExtractor(this, ::autoshutdownIdleSeconds, skipIf = { it == 0 }),
PropExtractor(this, ::startEcho))
override val parsers: List<PropParser<*,*,*>>
get() = listOf( PropParser(this, ::port, { it.toInt()}),
PropParser(this, ::autoshutdownMemoryThreshold, { it.toLong()}),
PropParser(this, ::autoshutdownIdleSeconds, { it.toInt()}),
PropParser(this, ::startEcho, { it.trim('"') }))
override val mappers: List<PropMapper<*, *, *>>
get() = listOf( PropMapper(this, ::port, fromString = { it.toInt() }),
PropMapper(this, ::autoshutdownMemoryThreshold, fromString = { it.toLong() }, skipIf = { it == 0L }),
PropMapper(this, ::autoshutdownIdleSeconds, fromString = { it.toInt() }, skipIf = { it == 0 }),
PropMapper(this, ::startEcho, fromString = { it.trim('"') }))
}
val COMPILER_ID_DIGEST = "MD5"
fun updateSingleFileDigest(file: File, md: MessageDigest) {
DigestInputStream(file.inputStream(), md).use {
val buf = ByteArray(1024)
@@ -215,27 +262,21 @@ public data class CompilerId(
public var compilerDigest: String = "",
public var compilerVersion: String = ""
// TODO: checksum
) : CmdlineParams {
) : OptionsGroup {
override val extractors: List<PropExtractor<*, *, *>>
get() = listOf( PropExtractor(this, ::compilerClasspath, convert = { it.joinToString(File.pathSeparator) }),
PropExtractor(this, ::compilerDigest),
PropExtractor(this, ::compilerVersion, skipIf = { it.isEmpty() }))
override val parsers: List<PropParser<*,*,*>>
get() =
listOf( PropParser(this, ::compilerClasspath, { it.trim('"').split(File.pathSeparator)}),
PropParser(this, ::compilerDigest, { it.trim('"') }),
PropParser(this, ::compilerVersion, { it.trim('"') }))
override val mappers: List<PropMapper<*, *, *>>
get() = listOf( PropMapper(this, ::compilerClasspath, toString = { it.joinToString(File.pathSeparator) }, fromString = { it.trim('"').split(File.pathSeparator)}),
StringPropMapper(this, ::compilerDigest),
StringPropMapper(this, ::compilerVersion))
public fun updateDigest() {
compilerDigest = compilerClasspath.getClasspathDigest()
}
companion object {
public platformStatic fun makeCompilerId(vararg paths: File): CompilerId = makeCompilerId(paths.asIterable())
public jvmStatic fun makeCompilerId(vararg paths: File): CompilerId = makeCompilerId(paths.asIterable())
public platformStatic fun makeCompilerId(paths: Iterable<File>): CompilerId =
public jvmStatic fun makeCompilerId(paths: Iterable<File>): CompilerId =
// TODO consider reading version here
CompilerId(compilerClasspath = paths.map { it.absolutePath }, compilerDigest = paths.getFilesClasspathDigest())
}
@@ -245,27 +286,27 @@ public data class CompilerId(
public fun isDaemonEnabled(): Boolean = System.getProperty(COMPILE_DAEMON_ENABLED_PROPERTY) != null
public fun configureDaemonLaunchingOptions(opts: DaemonLaunchingOptions, inheritMemoryLimits: Boolean): DaemonLaunchingOptions {
public fun configureDaemonLaunchingOptions(opts: DaemonJVMOptions, inheritMemoryLimits: Boolean): DaemonJVMOptions {
// note: sequence matters, explicit override in COMPILE_DAEMON_JVM_OPTIONS_PROPERTY should be done after inputArguments processing
if (inheritMemoryLimits)
ManagementFactory.getRuntimeMXBean().inputArguments.filterSetProps(opts.parsers, "-")
ManagementFactory.getRuntimeMXBean().inputArguments.filterExtractProps(opts.mappers, "-")
System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY)?.let {
opts.otherJvmParams.addAll( it.trim('"', '\'').split(",").filterSetProps(opts.parsers, "-", RestPropParser(opts, DaemonLaunchingOptions::otherJvmParams)))
opts.otherJvmParams.addAll( it.trim('"', '\'').split(",").filterExtractProps(opts.mappers, "-", opts.restMapper))
}
return opts
}
public fun configureDaemonLaunchingOptions(inheritMemoryLimits: Boolean): DaemonLaunchingOptions =
configureDaemonLaunchingOptions(DaemonLaunchingOptions(), inheritMemoryLimits = inheritMemoryLimits)
public fun configureDaemonLaunchingOptions(inheritMemoryLimits: Boolean): DaemonJVMOptions =
configureDaemonLaunchingOptions(DaemonJVMOptions(), inheritMemoryLimits = inheritMemoryLimits)
jvmOverloads public fun configureDaemonOptions(opts: DaemonOptions = DaemonOptions()): DaemonOptions {
System.getProperty(COMPILE_DAEMON_OPTIONS_PROPERTY)?.let {
val unrecognized = it.trim('"', '\'').split(",").filterSetProps(opts.parsers, "")
val unrecognized = it.trim('"', '\'').split(",").filterExtractProps(opts.mappers, "")
if (unrecognized.any())
throw IllegalArgumentException(
"Unrecognized daemon options passed via property $COMPILE_DAEMON_OPTIONS_PROPERTY: " + unrecognized.joinToString(" ") +
"\nSupported options: " + opts.extractors.joinToString(", ", transform = { it.name }))
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() }))
}
return opts
}
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.rmi.service
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.rmi.COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX
import org.jetbrains.kotlin.rmi.CompilerId
import org.jetbrains.kotlin.rmi.DaemonOptions
import org.jetbrains.kotlin.rmi.filterSetProps
import org.jetbrains.kotlin.rmi.*
import org.jetbrains.kotlin.service.CompileServiceImpl
import java.io.OutputStream
import java.io.PrintStream
@@ -96,7 +93,7 @@ public class CompileDaemon {
val compilerId = CompilerId()
val daemonOptions = DaemonOptions()
val filteredArgs = args.asIterable().filterSetProps(compilerId, daemonOptions, prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX)
val filteredArgs = args.asIterable().filterExtractProps(compilerId, daemonOptions, prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX)
if (filteredArgs.any()) {
val helpLine = "usage: <daemon> <compilerId options> <daemon options>"
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.service
import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
@@ -25,15 +24,12 @@ import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompil
import org.jetbrains.kotlin.rmi.*
import org.jetbrains.kotlin.rmi.service.RemoteIncrementalCacheClient
import org.jetbrains.kotlin.rmi.service.RemoteOutputStreamClient
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.PrintStream
import java.lang.management.ManagementFactory
import java.net.URLClassLoader
import java.rmi.registry.Registry
import java.rmi.server.UnicastRemoteObject
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.jar.Manifest
@@ -86,12 +82,14 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
public class IncrementalCompilationComponentsImpl(val idToCache: Map<String, CompileService.RemoteIncrementalCache>): IncrementalCompilationComponents {
// perf: cheap object, but still the pattern may be costly if there are too many calls to cache with the same id (which seems not to be the case now)
override fun getIncrementalCache(moduleId: String): IncrementalCache = RemoteIncrementalCacheClient(idToCache[moduleId]!!)
// TODO: add appropriate proxy into interaction when lookup tracker is needed
override fun getLookupTracker(): LookupTracker = LookupTracker.DO_NOTHING
}
private fun createCompileServices(incrementalCaches: Map<String, CompileService.RemoteIncrementalCache>): Services =
Services.Builder()
.register(javaClass<IncrementalCompilationComponents>(), IncrementalCompilationComponentsImpl(incrementalCaches))
.register(IncrementalCompilationComponents::class.java, IncrementalCompilationComponentsImpl(incrementalCaches))
// TODO: add remote proxy for cancellation status tracking
// .register(javaClass<CompilationCanceledStatus>(), object: CompilationCanceledStatus {
// override fun checkCanceled(): Unit = if (context.getCancelStatus().isCanceled()) throw CompilationCanceledException()
// })
@@ -111,6 +109,7 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
return memHeap.used
}
// TODO: consider using version as a part of compiler ID or drop this function
private fun loadKotlinVersionFromResource(): String {
(javaClass.classLoader as? URLClassLoader)
?.findResource("META-INF/MANIFEST.MF")
@@ -158,6 +157,7 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
else body()
}
// sometimes used for debugging
fun<R> spy(msg: String, body: () -> R): R {
val res = body()
log.info(msg + " = " + res.toString())
@@ -178,26 +178,25 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
}
override fun remoteCompile(args: Array<out String>, errStream: RemoteOutputStream, outputFormat: CompileService.OutputFormat): Int =
ifAlive {
checkedCompile(args) {
val strm = RemoteOutputStreamClient(errStream)
val printStrm = PrintStream(strm)
when (outputFormat) {
CompileService.OutputFormat.PLAIN -> compiler.exec(printStrm, *args)
CompileService.OutputFormat.XML -> compiler.execAndOutputXml(printStrm, Services.EMPTY, *args)
}.code
}
doCompile(args, errStream) { printStream ->
when (outputFormat) {
CompileService.OutputFormat.PLAIN -> compiler.exec(printStream, *args)
CompileService.OutputFormat.XML -> compiler.execAndOutputXml(printStream, Services.EMPTY, *args)
}.code
}
override fun remoteIncrementalCompile(args: Array<out String>, caches: Map<String, CompileService.RemoteIncrementalCache>, errStream: RemoteOutputStream, outputFormat: CompileService.OutputFormat): Int =
override fun remoteIncrementalCompile(args: Array<out String>, caches: Map<String, CompileService.RemoteIncrementalCache>, outputStream: RemoteOutputStream, outputFormat: CompileService.OutputFormat): Int =
doCompile(args, outputStream) { printStream ->
when (outputFormat) {
CompileService.OutputFormat.PLAIN -> throw NotImplementedError("Only XML output is supported in remote incremental compilation")
CompileService.OutputFormat.XML -> compiler.execAndOutputXml(printStream, createCompileServices(caches), *args)
}.code
}
fun doCompile(args: Array<out String>, errStream: RemoteOutputStream, body: (PrintStream) -> Int): Int =
ifAlive {
checkedCompile(args) {
val strm = RemoteOutputStreamClient(errStream)
val printStrm = PrintStream(strm)
when (outputFormat) {
CompileService.OutputFormat.PLAIN -> throw NotImplementedError("Only XML output is supported in remote incremental compilation")
CompileService.OutputFormat.XML -> compiler.execAndOutputXml(printStrm, createCompileServices(caches), *args)
}.code
body( PrintStream( RemoteOutputStreamClient(errStream)))
}
}
}
@@ -20,7 +20,7 @@ import junit.framework.TestCase
import org.jetbrains.kotlin.cli.CliBaseTest
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.rmi.CompilerId
import org.jetbrains.kotlin.rmi.DaemonLaunchingOptions
import org.jetbrains.kotlin.rmi.DaemonJVMOptions
import org.jetbrains.kotlin.rmi.DaemonOptions
import org.jetbrains.kotlin.rmi.kotlinr.KotlinCompilerClient
import org.jetbrains.kotlin.test.JetTestUtils
@@ -34,7 +34,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
data class CompilerResults(val resultCode: Int, val out: String)
val daemonOptions = DaemonOptions(port = KOTLIN_DAEMON_TEST_PORT)
val daemonLaunchingOptions = DaemonLaunchingOptions()
val daemonLaunchingOptions = DaemonJVMOptions()
val compilerId by lazy { CompilerId.makeCompilerId( File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler.jar"),
File("dependencies/bootstrap-compiler/Kotlin/kotlinc/lib/kotlin-runtime.jar"),
File("dependencies/bootstrap-compiler/Kotlin/kotlinc/lib/kotlin-reflect.jar")) }
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.compilerRunner;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
@@ -136,9 +135,9 @@ public class KotlinCompilerRunner {
// the lifetime of JPS process is small anyway, we can neglect the probability of changed compiler
CompilerId compilerId = CompilerId.makeCompilerId(new File(libPath, "kotlin-compiler.jar"));
DaemonOptions daemonOptions = RmiPackage.configureDaemonOptions();
DaemonLaunchingOptions daemonLaunchingOptions = RmiPackage.configureDaemonLaunchingOptions(true);
DaemonJVMOptions daemonJVMOptions = RmiPackage.configureDaemonLaunchingOptions(true);
// TODO: find proper stream to report daemon connection progress
CompileService daemon = KotlinCompilerClient.Companion.connectToCompileService(compilerId, daemonLaunchingOptions, daemonOptions, System.out, true, true);
CompileService daemon = KotlinCompilerClient.Companion.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, System.out, true, true);
if (daemon != null) {
Integer res = KotlinCompilerClient.Companion.incrementalCompile(daemon, argsArray, incrementalCaches, out);
return res.toString();