Fixes after review, adding basic options parsing tests

This commit is contained in:
Ilya Chernikov
2015-09-10 16:21:14 +02:00
parent b1bdbaf06a
commit dc3c1beeb9
9 changed files with 179 additions and 114 deletions
@@ -32,6 +32,8 @@ public object KotlinCompilerClient {
val DAEMON_DEFAULT_STARTUP_TIMEOUT_MS = 10000L
val DAEMON_CONNECT_CYCLE_ATTEMPTS = 3
val verboseReporting = System.getProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) != null
// TODO: remove jvmStatic after all use sites will switch to kotlin
jvmStatic
@@ -45,6 +47,7 @@ public object KotlinCompilerClient {
var attempts = 0
var fileLock: FileBasedLock? = null
var shutdonwnPerformed = false
try {
while (attempts++ < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
val service = tryFindDaemon(File(daemonOptions.runFilesPath), compilerId, reportingTargets)
@@ -59,11 +62,12 @@ public object KotlinCompilerClient {
service.shutdown()
// TODO: find more reliable way
Thread.sleep(1000)
reportingTargets.report(DaemonReportCategory.DEBUG, "daemon shut down correctly, restarting search")
reportingTargets.report(DaemonReportCategory.DEBUG, "daemon shut down correctly")
shutdonwnPerformed = true
}
else {
if (!autostart) return null
reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found, starting a new one")
reportingTargets.report(DaemonReportCategory.DEBUG, if (shutdonwnPerformed) "starting a new daemon" else "no suitable daemon found, starting a new one")
}
if (fileLock == null || !fileLock.isLocked()) {
@@ -75,7 +79,7 @@ public object KotlinCompilerClient {
}
else {
startDaemon(compilerId, daemonJVMOptions, daemonOptions, reportingTargets)
reportingTargets.report(DaemonReportCategory.DEBUG, "daemon started, trying to resolve")
reportingTargets.report(DaemonReportCategory.DEBUG, "daemon started, trying to find it")
}
}
}
@@ -208,20 +212,28 @@ public object KotlinCompilerClient {
// --- Implementation ---------------------------------------
val verboseReporting = System.getProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) != null
fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String = "daemon client") {
if (category == DaemonReportCategory.DEBUG && !verboseReporting) return
out?.println("[$source] ${category.name()}: $message")
messages?.add(DaemonReportMessage(category, "[$source] $message"))
}
private fun String.extractPortFromRunFilename(digest: String): Int =
makeRunFilenameString(timestamp = "[0-9TZ:\\.\\+-]+", digest = digest, port = "(\\d+)", escapeSequence = "\\").toRegex()
.match(this)
?.groups?.get(1)
?.value?.toInt()
?: 0
private fun tryFindDaemon(registryDir: File, compilerId: CompilerId, reportingTargets: DaemonReportingTargets): CompileService? {
val classPathDigest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest()
val daemons = registryDir.walk()
.map { Pair(it, makeRunFilenameRegex(digest = classPathDigest, port = "(\\d+)").match(it.name)?.groups?.get(1)?.value?.toInt() ?: 0) }
.map { Pair(it, it.name.extractPortFromRunFilename(classPathDigest)) }
.filter { it.second != 0 }
.map {
assert(it.second > 0 && it.second < 0xffff)
reportingTargets.report(DaemonReportCategory.DEBUG, "found suitable daemon on port ${it.second}, trying to connect")
val daemon = tryConnectToDaemon(it.second, reportingTargets)
// cleaning orphaned file; note: daemon should shut itself down if it detects that the run file is deleted
@@ -240,9 +252,10 @@ public object KotlinCompilerClient {
}
}
private fun tryConnectToDaemon(port: Int, reportingTargets: DaemonReportingTargets): CompileService? {
try {
val daemon = LocateRegistry.getRegistry(loopbackAddrName, port)
val daemon = LocateRegistry.getRegistry(LoopbackNetworkInterface.loopbackInetAddressName, port)
?.lookup(COMPILER_SERVICE_RMI_NAME)
if (daemon != null)
return daemon as? CompileService ?:
@@ -330,13 +343,15 @@ public object KotlinCompilerClient {
class FileBasedLock(compilerId: CompilerId, daemonOptions: DaemonOptions) {
private val lockFile: File =
File(daemonOptions.runFilesPath,
makeRunFilenameString(ts = "lock",
makeRunFilenameString(timestamp = "lock",
digest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest(),
port = "0"))
private var locked: Boolean = acquireLockFile(lockFile)
private volatile var locked = acquireLockFile(lockFile)
private val channel = if (locked) RandomAccessFile(lockFile, "rw").channel else null
private val lock = channel?.lock()
public fun isLocked(): Boolean = locked
@@ -349,42 +364,26 @@ public object KotlinCompilerClient {
}
}
private val channel = if (locked) RandomAccessFile(lockFile, "rw").channel else null
private val lock = channel?.lock()
synchronized private fun acquireLockFile(lockFile: File): Boolean {
if (lockFile.createNewFile()) return true
try {
// attempt to delete if file is orphaned
if (lockFile.delete() && lockFile.createNewFile())
return true // if orphaned file deleted assuming that the probability of
}
catch (e: IOException) {
// Ignoring it - assuming it is another client process owning it
}
var attempts = 0L
while (lockFile.exists() && attempts++ * COMPILE_DAEMON_STARTUP_LOCK_TIMEOUT_CHECK_MS < COMPILE_DAEMON_STARTUP_LOCK_TIMEOUT_MS) {
val maxAttempts = COMPILE_DAEMON_STARTUP_LOCK_TIMEOUT_MS / COMPILE_DAEMON_STARTUP_LOCK_TIMEOUT_CHECK_MS + 1
var attempt = 0L
while (true) {
try {
if (lockFile.createNewFile()) break
// attempt to delete if file is orphaned
if (lockFile.delete() && lockFile.createNewFile()) break
}
catch (e: IOException) {} // Ignoring it - assuming it is another client process owning it
if (lockFile.exists() && ++attempt >= maxAttempts)
throw IOException("Timeout waiting the release of the lock file '${lockFile.absolutePath}")
Thread.sleep(COMPILE_DAEMON_STARTUP_LOCK_TIMEOUT_CHECK_MS)
}
if (lockFile.exists())
throw IOException("Timeout waiting the release of the lock file '${lockFile.absolutePath}")
return lockFile.createNewFile()
return true
}
}
}
fun Process.isAlive() =
try {
this.exitValue()
false
}
catch (e: IllegalThreadStateException) {
true
}
public enum class DaemonReportCategory {
DEBUG, INFO, EXCEPTION;
}
@@ -392,3 +391,13 @@ public enum class DaemonReportCategory {
public data class DaemonReportMessage(public val category: DaemonReportCategory, public val message: String)
public class DaemonReportingTargets(public val out: PrintStream? = null, public val messages: MutableCollection<DaemonReportMessage>? = null)
internal fun Process.isAlive() =
try {
this.exitValue()
false
}
catch (e: IllegalThreadStateException) {
true
}
@@ -18,15 +18,15 @@ package org.jetbrains.kotlin.rmi.kotlinr
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
import org.jetbrains.kotlin.rmi.CompileService
import org.jetbrains.kotlin.rmi.clientLoopbackSocketFactory
import org.jetbrains.kotlin.rmi.serverLoopbackSocketFactory
import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface
import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT
import java.rmi.server.UnicastRemoteObject
public class RemoteIncrementalCacheServer(val cache: IncrementalCache, port: Int = 0) : CompileService.RemoteIncrementalCache {
public class RemoteIncrementalCacheServer(val cache: IncrementalCache, port: Int = SOCKET_ANY_FREE_PORT) : CompileService.RemoteIncrementalCache {
init {
UnicastRemoteObject.exportObject(this, port, clientLoopbackSocketFactory, serverLoopbackSocketFactory)
UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
}
override fun getObsoletePackageParts(): Collection<String> = cache.getObsoletePackageParts()
@@ -16,17 +16,17 @@
package org.jetbrains.kotlin.rmi.kotlinr
import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface
import org.jetbrains.kotlin.rmi.RemoteOutputStream
import org.jetbrains.kotlin.rmi.clientLoopbackSocketFactory
import org.jetbrains.kotlin.rmi.serverLoopbackSocketFactory
import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT
import java.io.OutputStream
import java.rmi.server.UnicastRemoteObject
class RemoteOutputStreamServer(val out: OutputStream, port: Int = 0) : RemoteOutputStream {
class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FREE_PORT) : RemoteOutputStream {
init {
UnicastRemoteObject.exportObject(this, port, clientLoopbackSocketFactory, serverLoopbackSocketFactory)
UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
}
public fun disconnect() {
@@ -22,7 +22,6 @@ import java.lang.management.ManagementFactory
import java.security.DigestInputStream
import java.security.MessageDigest
import kotlin.reflect.KMutableProperty1
import kotlin.text.Regex
public val COMPILER_JAR_NAME: String = "kotlin-compiler.jar"
@@ -54,9 +53,7 @@ public val COMPILE_DAEMON_DEFAULT_RUN_DIR_PATH: String get() =
val COMPILER_ID_DIGEST = "MD5"
public fun makeRunFilenameString(ts: String, digest: String, port: String, esc: String = ""): String = "$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$esc.$ts$esc.$digest$esc.$port$esc.run"
public fun makeRunFilenameRegex(ts: String = "[0-9TZ:\\.\\+-]+", digest: String = "[0-9a-f]+", port: String = "\\d+"): Regex = makeRunFilenameString(ts, digest, port, esc = "\\").toRegex()
public fun makeRunFilenameString(timestamp: String, digest: String, port: String, escapeSequence: String = ""): String = "$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$escapeSequence.$timestamp$escapeSequence.$digest$escapeSequence.$port$escapeSequence.run"
open class PropMapper<C, V, P : KMutableProperty1<C, V>>(val dest: C,
@@ -114,6 +111,8 @@ class RestPropMapper<C, P : KMutableProperty1<C, MutableCollection<String>>>(des
}
// helper function combining find with map, useful for the cases then there is a calculation performed in find, which is nice to return along with
// found value; mappingPredicate should return the pair of boolean compare predicate result and transformation value, we want to get along with found value
inline fun <T, R : Any> Iterable<T>.findWithTransform(mappingPredicate: (T) -> Pair<Boolean, R?>): R? {
for (element in this) {
val (found, mapped) = mappingPredicate(element)
@@ -123,6 +122,9 @@ inline fun <T, R : Any> Iterable<T>.findWithTransform(mappingPredicate: (T) -> P
}
// filter-like function, takes list of propmappers, bound to properties of concrete objects, iterates over receiver, extract matching values via appropriate
// mappers into bound properties; if restParser is given, adds all non-matching elements to it, otherwise return them as an iterable
// note bound properties mutation!
fun Iterable<String>.filterExtractProps(propMappers: List<PropMapper<*, *, *>>, prefix: String, restParser: RestPropMapper<*, *>? = null): Iterable<String> {
val iter = iterator()
@@ -169,6 +171,9 @@ fun Iterable<String>.filterExtractProps(propMappers: List<PropMapper<*, *, *>>,
}
public fun String.trimQuotes() = trim('"','\'')
public interface OptionsGroup : Serializable {
public val mappers: List<PropMapper<*, *, *>>
}
@@ -203,10 +208,10 @@ public data class DaemonOptions(
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(PropMapper(this, ::runFilesPath, fromString = { it.trim('"') }),
get() = listOf(PropMapper(this, ::runFilesPath, fromString = { it.trimQuotes() }),
PropMapper(this, ::autoshutdownMemoryThreshold, fromString = { it.toLong() }, skipIf = { it == 0L }, mergeDelimiter = "="),
PropMapper(this, ::autoshutdownIdleSeconds, fromString = { it.toInt() }, skipIf = { it == 0 }, mergeDelimiter = "="),
NullablePropMapper(this, ::clientAliveFlagPath, fromString = { it }, toString = { "${it?.trim('\"', '\'')}" }, mergeDelimiter = "="))
NullablePropMapper(this, ::clientAliveFlagPath, fromString = { it }, toString = { "${it?.trimQuotes()}" }, mergeDelimiter = "="))
}
@@ -257,7 +262,7 @@ public data class CompilerId(
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(PropMapper(this, ::compilerClasspath, toString = { it.joinToString(File.pathSeparator) }, fromString = { it.trim('"').split(File.pathSeparator) }),
get() = listOf(PropMapper(this, ::compilerClasspath, toString = { it.joinToString(File.pathSeparator) }, fromString = { it.trimQuotes().split(File.pathSeparator) }),
StringPropMapper(this, ::compilerDigest),
StringPropMapper(this, ::compilerVersion))
@@ -283,10 +288,11 @@ public fun configureDaemonJVMOptions(opts: DaemonJVMOptions, inheritMemoryLimits
ManagementFactory.getRuntimeMXBean().inputArguments.filterExtractProps(opts.mappers, "-")
}
System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY)?.let {
opts.jvmParams.addAll(it.trim('"', '\'')
.split("(?<!\\\\),".toRegex())
.map { it.replace("[\\\\](.)".toRegex(), "$1") }
.filterExtractProps(opts.mappers, "-", opts.restMapper))
opts.jvmParams.addAll(
it.trimQuotes()
.split("(?<!\\\\),".toRegex()) // using independent non-capturing group with negative lookahead zero length assertion to split only on non-escaped commas
.map { it.replace("\\\\(.)".toRegex(), "$1") } // de-escaping characters escaped by backslash, straightforward, without exceptions
.filterExtractProps(opts.mappers, "-", opts.restMapper))
}
System.getProperty(COMPILE_DAEMON_REPORT_PERF_PROPERTY)?.let { opts.jvmParams.add("D" + COMPILE_DAEMON_REPORT_PERF_PROPERTY) }
@@ -294,19 +300,21 @@ public fun configureDaemonJVMOptions(opts: DaemonJVMOptions, inheritMemoryLimits
return opts
}
public fun configureDaemonJVMOptions(inheritMemoryLimits: Boolean): DaemonJVMOptions =
configureDaemonJVMOptions(DaemonJVMOptions(), inheritMemoryLimits = inheritMemoryLimits)
public fun configureDaemonOptions(opts: DaemonOptions): DaemonOptions {
System.getProperty(COMPILE_DAEMON_OPTIONS_PROPERTY)?.let {
val unrecognized = it.trim('"', '\'').split(",").filterExtractProps(opts.mappers, "")
val unrecognized = it.trimQuotes().split(",").filterExtractProps(opts.mappers, "")
if (unrecognized.any())
throw IllegalArgumentException(
"Unrecognized daemon options passed via property $COMPILE_DAEMON_OPTIONS_PROPERTY: " + unrecognized.joinToString(" ") +
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() }))
}
System.getProperty(COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY)?.let {
val trimmed = it.trim('"', '\'')
val trimmed = it.trimQuotes()
if (!trimmed.isBlank()) {
opts.clientAliveFlagPath = trimmed
}
@@ -314,4 +322,5 @@ public fun configureDaemonOptions(opts: DaemonOptions): DaemonOptions {
return opts
}
public fun configureDaemonOptions(): DaemonOptions = configureDaemonOptions(DaemonOptions())
@@ -1,48 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.rmi
import java.io.IOException
import java.io.Serializable
import java.net.InetAddress
import java.net.ServerSocket
import java.net.Socket
import java.rmi.server.RMIClientSocketFactory
import java.rmi.server.RMIServerSocketFactory
// TODO switch to InetAddress.getLoopbackAddress on java 7+
val loopbackAddrName by lazy { if (java.net.InetAddress.getLocalHost() is java.net.Inet6Address) "::1" else "127.0.0.1" }
val loopbackAddr by lazy { InetAddress.getByName(loopbackAddrName) }
val serverLoopbackSocketFactory by lazy { ServerLoopbackSocketFactory() }
val clientLoopbackSocketFactory by lazy { ClientLoopbackSocketFactory() }
data class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable {
throws(IOException::class)
override fun createServerSocket(port: Int): ServerSocket = ServerSocket(port, 5, loopbackAddr)
}
data class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable {
throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket = Socket(loopbackAddr, port)
}
@@ -0,0 +1,69 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.rmi
import java.io.IOException
import java.io.Serializable
import java.net.InetAddress
import java.net.ServerSocket
import java.net.Socket
import java.rmi.server.RMIClientSocketFactory
import java.rmi.server.RMIServerSocketFactory
public val SOCKET_ANY_FREE_PORT = 0
public object LoopbackNetworkInterface {
val IPV4_LOOPBACK_INET_ADDRESS = "127.0.0.1"
val IPV6_LOOPBACK_INET_ADDRESS = "::1"
val SERVER_SOCKET_BACKLOG_SIZE = 5 // size of the requests queue for daemon services, so far seems that we don't need any big numbers here
// but if we'll start getting "connection refused" errors, that could be the first place to try to fix it
public val serverLoopbackSocketFactory by lazy { ServerLoopbackSocketFactory() }
public val clientLoopbackSocketFactory by lazy { ClientLoopbackSocketFactory() }
// TODO switch to InetAddress.getLoopbackAddress on java 7+
public val loopbackInetAddressName by lazy {
try {
if (java.net.InetAddress.getLocalHost() is java.net.Inet6Address) IPV6_LOOPBACK_INET_ADDRESS else IPV4_LOOPBACK_INET_ADDRESS
}
catch (e: IOException) {
// getLocalHost may fail for unknown reasons in some situations, the fallback is to assume IPv4 for now
// TODO consider some other ways to detect default to IPv6 addresses in this case
IPV4_LOOPBACK_INET_ADDRESS
}
}
data class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable {
throws(IOException::class)
override fun createServerSocket(port: Int): ServerSocket = ServerSocket(port, SERVER_SOCKET_BACKLOG_SIZE, InetAddress.getByName(loopbackInetAddressName))
}
data class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable {
throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket = Socket(InetAddress.getByName(loopbackInetAddressName), port)
}
}
@@ -120,7 +120,7 @@ public object CompileDaemon {
val runFileDir = File(if (daemonOptions.runFilesPath.isBlank()) COMPILE_DAEMON_DEFAULT_RUN_DIR_PATH else daemonOptions.runFilesPath)
runFileDir.mkdirs()
val runFile = File(runFileDir,
makeRunFilenameString(ts = "%tFT%<tT.%<tLZ".format(Calendar.getInstance(TimeZone.getTimeZone("Z"))),
makeRunFilenameString(timestamp = "%tFT%<tT.%<tLZ".format(Calendar.getInstance(TimeZone.getTimeZone("Z"))),
digest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest(),
port = port.toString()))
if (!runFile.createNewFile()) {
@@ -190,7 +190,7 @@ public object CompileDaemon {
while (i++ < attempts) {
val port = random.nextInt(COMPILE_DAEMON_PORTS_RANGE_END - COMPILE_DAEMON_PORTS_RANGE_START) + COMPILE_DAEMON_PORTS_RANGE_START
try {
return Pair(LocateRegistry.createRegistry(port, clientLoopbackSocketFactory, serverLoopbackSocketFactory), port)
return Pair(LocateRegistry.createRegistry(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory), port)
}
catch (e: RemoteException) {
// assuming that the port is already taken
@@ -122,7 +122,7 @@ class CompileServiceImpl<Compiler: CLICompiler<*>>(
// ignoring if object already exported
}
val stub = UnicastRemoteObject.exportObject(this, port, clientLoopbackSocketFactory, serverLoopbackSocketFactory) as CompileService
val stub = UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) as CompileService
registry.rebind (COMPILER_SERVICE_RMI_NAME, stub);
alive = true
}
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.daemon
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.CliBaseTest
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.rmi.CompileService
import org.jetbrains.kotlin.rmi.CompilerId
import org.jetbrains.kotlin.rmi.DaemonJVMOptions
import org.jetbrains.kotlin.rmi.DaemonOptions
import org.jetbrains.kotlin.rmi.*
import org.jetbrains.kotlin.rmi.kotlinr.DaemonReportingTargets
import org.jetbrains.kotlin.rmi.kotlinr.KotlinCompilerClient
import org.jetbrains.kotlin.test.JetTestUtils
@@ -75,4 +72,33 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
runDaemonCompilerTwice("hello.compile", "-include-runtime", File(getTestBaseDir(), "hello.kt").absolutePath, "-d", jar)
run("hello.run", "-cp", jar, "Hello.HelloPackage")
}
public fun testDaemonJvmOptionsParsing() {
val backupJvmOptions = System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY)
try {
System.setProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY, "-aaa,-bbb\\,ccc,-ddd,-Xmx200m,-XX:MaxPermSize=10k,-XX:ReservedCodeCacheSize=100,-xxx\\,yyy")
val opts = configureDaemonJVMOptions(inheritMemoryLimits = false)
TestCase.assertEquals("200m", opts.maxMemory)
TestCase.assertEquals("10k", opts.maxPermSize)
TestCase.assertEquals("100", opts.reservedCodeCacheSize)
TestCase.assertEquals(arrayListOf("aaa", "bbb,ccc", "ddd", "xxx,yyy"), opts.jvmParams)
}
finally {
backupJvmOptions?.let { System.setProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY, it) }
}
}
public fun testDaemonOptionsParsing() {
val backupJvmOptions = System.getProperty(COMPILE_DAEMON_OPTIONS_PROPERTY)
try {
System.setProperty(COMPILE_DAEMON_OPTIONS_PROPERTY, "runFilesPath=abcd,clientAliveFlagPath=efgh,autoshutdownIdleSeconds=1111")
val opts = configureDaemonOptions()
TestCase.assertEquals("abcd", opts.runFilesPath)
TestCase.assertEquals("efgh", opts.clientAliveFlagPath)
TestCase.assertEquals(1111, opts.autoshutdownIdleSeconds)
}
finally {
backupJvmOptions?.let { System.setProperty(COMPILE_DAEMON_OPTIONS_PROPERTY, it) }
}
}
}