diff --git a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt index 091bdfa647a..2322a16d2a3 100644 --- a/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt +++ b/compiler/rmi/kotlinr/src/org/jetbrains/kotlin/rmi/kotlinr/KotlinCompilerClient.kt @@ -20,6 +20,7 @@ import net.rubygrapefruit.platform.ProcessLauncher import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.rmi.* +import org.jetbrains.kotlin.utils.addToStdlib.check import java.io.File import java.io.OutputStream import java.io.PrintStream @@ -57,9 +58,9 @@ public object KotlinCompilerClient { val flagFile = System.getProperty(COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY) ?.let { it.trimQuotes() } - ?.ifOrNull { !it.isBlank() } + ?.check { !it.isBlank() } ?.let { File(it) } - ?.ifOrNull { it.exists() } + ?.check { it.exists() } ?: newFlagFile() return connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, reportingTargets, autostart) } @@ -82,13 +83,11 @@ public object KotlinCompilerClient { reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon") return service } - else { - if (!autostart) return null - reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found, starting a new one") + reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found") + if (autostart) { + startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets) + reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it") } - - startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets) - reportingTargets.report(DaemonReportCategory.DEBUG, "daemon started, trying to find it") } } catch (e: Exception) { @@ -258,13 +257,13 @@ public object KotlinCompilerClient { .map { Pair(it, it.getDaemonJVMOptions()) } .filter { it.second.isGood } .sortedWith(compareBy(DaemonJVMOptionsMemoryComparator().reversed(), { it.second.get() })) - val opts = daemonJVMOptions.copy() + val optsCopy = daemonJVMOptions.copy() // if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon - return aliveWithOpts.firstOrNull()?.ifOrNull { daemonJVMOptions memorywiseFitsInto it.second.get() }?.let { - Pair(it.first, opts.updateMemoryUpperBounds(it.second.get())) + return aliveWithOpts.firstOrNull()?.check { daemonJVMOptions memorywiseFitsInto it.second.get() }?.let { + Pair(it.first, optsCopy.updateMemoryUpperBounds(it.second.get())) } // else combine all options from running daemon to get fattest option for a new daemon to run - ?: Pair(null, aliveWithOpts.fold(daemonJVMOptions, { opts, d -> opts.updateMemoryUpperBounds(d.second.get()) })) + ?: Pair(null, aliveWithOpts.fold(optsCopy, { opts, d -> opts.updateMemoryUpperBounds(d.second.get()) })) } @@ -353,6 +352,3 @@ internal fun isProcessAlive(process: Process) = catch (e: IllegalThreadStateException) { true } - -internal inline fun T.ifOrNull(pred: (T) -> Boolean): T? = if (pred(this)) this else null - diff --git a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/ClientUtils.kt b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/ClientUtils.kt index 9ea20f37d9c..626ac75e5f7 100644 --- a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/ClientUtils.kt +++ b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/ClientUtils.kt @@ -29,30 +29,33 @@ enum class DaemonReportCategory { } -fun makeRunFilenameString(timestamp: String, digest: String, port: String, escapeSequence: String = ""): String = "$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$escapeSequence.$timestamp$escapeSequence.$digest$escapeSequence.$port$escapeSequence.run" +fun makeRunFilenameString(timestamp: String, digest: String, port: String, escapeSequence: String = ""): String = + "$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$escapeSequence.$timestamp$escapeSequence.$digest$escapeSequence.$port$escapeSequence.run" -fun String.extractPortFromRunFilename(digest: String): Int = - makeRunFilenameString(timestamp = "[0-9TZ:\\.\\+-]+", digest = digest, port = "(\\d+)", escapeSequence = "\\").toRegex() - .find(this) - ?.groups?.get(1) - ?.value?.toInt() - ?: 0 +fun makePortFromRunFilenameExtractor(digest: String): (String) -> Int? { + val regex = makeRunFilenameString(timestamp = "[0-9TZ:\\.\\+-]+", digest = digest, port = "(\\d+)", escapeSequence = "\\").toRegex() + return { regex.find(it) + ?.groups?.get(1) + ?.value?.toInt() + } +} fun walkDaemons(registryDir: File, compilerId: CompilerId, - filter: (File, Int) -> Boolean = { f,p -> true }, - report: (DaemonReportCategory, String) -> Unit = { cat, msg -> ; } + filter: (File, Int) -> Boolean = { f, p -> true }, + report: (DaemonReportCategory, String) -> Unit = { cat, msg -> } ): Sequence { val classPathDigest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString() + val portExtractor = makePortFromRunFilenameExtractor(classPathDigest) return registryDir.walk() - .map { Pair(it, it.name.extractPortFromRunFilename(classPathDigest)) } - .filter { it.second != 0 && filter(it.first, it.second) } + .map { Pair(it, portExtractor(it.name)) } + .filter { it.second != null && filter(it.first, it.second!!) } .map { - assert(it.second > 0 && it.second < MAX_PORT_NUMBER) + assert(it.second!! > 0 && it.second!! < MAX_PORT_NUMBER) report(DaemonReportCategory.DEBUG, "found daemon on port ${it.second}, trying to connect") - val daemon = tryConnectToDaemon(it.second, report) + val daemon = tryConnectToDaemon(it.second!!, report) // cleaning orphaned file; note: daemon should shut itself down if it detects that the run file is deleted if (daemon == null && !it.first.delete()) { report(DaemonReportCategory.INFO, "WARNING: unable to delete seemingly orphaned file '${it.first.absolutePath}', cleanup recommended") diff --git a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt index a0874b51003..e91a1a76687 100644 --- a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt +++ b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt @@ -313,25 +313,26 @@ public fun configureDaemonOptions(opts: DaemonOptions): DaemonOptions { public fun configureDaemonOptions(): DaemonOptions = configureDaemonOptions(DaemonOptions()) -fun String.memToBytes(): Long? = - "(\\d+)([kmg]?)".toRegex() - .matchEntire(this.trim().toLowerCase()) - ?.groups?.let { match -> - match.get(1)?.value?.let { - it.toLong() * - when (match.get(2)?.value) { - "k" -> 1 shl 10 - "m" -> 1 shl 20 - "g" -> 1 shl 30 - else -> 1 - } +private val humanizedMemorySizeRegex = "(\\d+)([kmg]?)".toRegex() + +private fun String.memToBytes(): Long? = + humanizedMemorySizeRegex + .matchEntire(this.trim().toLowerCase()) + ?.groups?.let { match -> + match[1]?.value?.let { + it.toLong() * + when (match[2]?.value) { + "k" -> 1 shl 10 + "m" -> 1 shl 20 + "g" -> 1 shl 30 + else -> 1 } } + } -private val daemonJVMOptionsMemoryProps: List> by lazy { +private val daemonJVMOptionsMemoryProps = listOf(DaemonJVMOptions::maxMemory, DaemonJVMOptions::maxPermSize, DaemonJVMOptions::reservedCodeCacheSize) -} infix fun DaemonJVMOptions.memorywiseFitsInto(other: DaemonJVMOptions): Boolean = daemonJVMOptionsMemoryProps diff --git a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt index 8a572e2c261..a048e369592 100644 --- a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt +++ b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileServiceImpl.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents import org.jetbrains.kotlin.progress.CompilationCanceledStatus import org.jetbrains.kotlin.rmi.* +import org.jetbrains.kotlin.utils.addToStdlib.check import java.io.BufferedOutputStream import java.io.File import java.io.PrintStream @@ -86,9 +87,9 @@ class CompileServiceImpl( private val classpathWatcher = LazyClasspathWatcher(compilerId.compilerClasspath) - enum class Aliveness(val value: Int) { - // ordering of values is used in state comparison - Alive(2), LastSession(1), Dying(0) + enum class Aliveness { + // !!! ordering of values is used in state comparison + Dying, LastSession, Alive } // TODO: encapsulate operations on state here @@ -99,13 +100,13 @@ class CompileServiceImpl( val delayedShutdownQueued = AtomicBoolean(false) - var alive = AtomicInteger(Aliveness.Alive.value) + var alive = AtomicInteger(Aliveness.Alive.ordinal) } @Volatile private var _lastUsedSeconds = nowSeconds() public val lastUsedSeconds: Long get() = if (rwlock.isWriteLocked || rwlock.readLockCount - rwlock.readHoldCount > 0) nowSeconds() else _lastUsedSeconds - val log by lazy { Logger.getLogger("compiler") } + private val log by lazy { Logger.getLogger("compiler") } private val rwlock = ReentrantReadWriteLock() @@ -154,6 +155,7 @@ class CompileServiceImpl( synchronized(state.sessions) { if (!state.sessions.containsKey(newId)) { state.sessions.put(newId, session) + log.info("leased a new session $newId, client alive file: $aliveFlagPath") return@ifAlive newId } } @@ -161,13 +163,13 @@ class CompileServiceImpl( // assuming wrap, jumping to random number to reduce probability of further clashes newId = sessionsIdCounter.addAndGet(internalRng.nextInt()) } - throw Exception("Invalid state or algorithm error") + throw IllegalStateException("Invalid state or algorithm error") } override fun releaseCompileSession(sessionId: Int) = ifAlive_Nothing(minAliveness = Aliveness.LastSession) { synchronized(state.sessions) { state.sessions.remove(sessionId) - log.info("cleaning after session") + log.info("cleaning after session $sessionId") clearJarCache() if (state.sessions.isEmpty()) { // TODO: and some goes here @@ -190,7 +192,7 @@ class CompileServiceImpl( } override fun scheduleShutdown(graceful: Boolean): CompileService.CallResult = ifAlive(minAliveness = Aliveness.Alive) { - if (!graceful || state.alive.compareAndSet(Aliveness.Alive.value, Aliveness.LastSession.value)) { + if (!graceful || state.alive.compareAndSet(Aliveness.Alive.ordinal, Aliveness.LastSession.ordinal)) { timer.schedule(0) { ifAliveExclusive(minAliveness = Aliveness.LastSession, ignoreCompilerChanged = true) { if (!graceful || state.sessions.isEmpty()) { @@ -299,13 +301,13 @@ class CompileServiceImpl( }.forEach { releaseCompileSession(it) } // 3. check if in graceful shutdown state and all sessions are closed - if (shutdownCondition({state.alive.get() == Aliveness.LastSession.value && state.sessions.none()}, "All sessions finished, shutting down")) { + if (shutdownCondition({state.alive.get() == Aliveness.LastSession.ordinal && state.sessions.none()}, "All sessions finished, shutting down")) { shutdown() } // 4. clean dead clients, then check if any left - conditional shutdown (with small delay) synchronized(state.clientProxies) { state.clientProxies.removeAll(state.clientProxies.filter { !it.isAlive }) } - if (state.clientProxies.none() && compilationsCounter.get() > 0 && !state.delayedShutdownQueued.get()) { + if (state.clientProxies.isEmpty() && compilationsCounter.get() > 0 && !state.delayedShutdownQueued.get()) { log.info("No more clients left, delayed shutdown in ${daemonOptions.shutdownDelayMilliseconds}ms") shutdownWithDelay() } @@ -338,7 +340,7 @@ class CompileServiceImpl( if (fattestOpts memorywiseFitsInto daemonJVMOptions && !(daemonJVMOptions memorywiseFitsInto fattestOpts)) { // all others are smaller that me, take overs' clients and shut them down aliveWithOpts.forEach { - it.first.getClients().ifOrNull { it.isGood }?.let { + it.first.getClients().check { it.isGood }?.let { it.get().forEach { registerClient(it) } } it.first.scheduleShutdown(true) @@ -348,14 +350,14 @@ class CompileServiceImpl( // there is at least one bigger, handover my clients to it and shutdown scheduleShutdown(true) aliveWithOpts.first().first.let { fattest -> - getClients().ifOrNull { it.isGood }?.let { + getClients().check { it.isGood }?.let { it.get().forEach { fattest.registerClient(it) } } } } // else - do nothing, all daemons are staying // TODO: implement some behaviour here, e.g.: - // - shutdown/takeover smaller daemosn + // - shutdown/takeover smaller daemon // - run (or better persuade client to run) a bigger daemon (in fact may be even simple shutdown will do, because of client's daemon choosing logic) } } @@ -363,7 +365,7 @@ class CompileServiceImpl( private fun shutdownImpl() { log.info("Shutdown started") - state.alive.set(Aliveness.Dying.value) + state.alive.set(Aliveness.Dying.ordinal) UnicastRemoteObject.unexportObject(this, true) log.info("Shutdown complete") onShutdown() @@ -384,7 +386,7 @@ class CompileServiceImpl( } } - private fun shutdownCondition(check: () -> Boolean, message: String): Boolean { + private inline fun shutdownCondition(check: () -> Boolean, message: String): Boolean { val res = check() if (res) { log.info(message) @@ -476,7 +478,7 @@ class CompileServiceImpl( } private fun clearJarCache() { - callVoidStaticMethod("com.intellij.openapi.vfs.impl.ZipHandler", "clearFileAccessorCache") + com.intellij.openapi.vfs.impl.ZipHandler.clearFileAccessorCache() val classloader = javaClass.classLoader // TODO: replace the following code with direct call to CoreJarFileSystem. as soon as it will be available (hopefully in 15.02) try { @@ -539,7 +541,7 @@ class CompileServiceImpl( inline private fun ifAliveChecksImpl(minAliveness: Aliveness = Aliveness.Alive, ignoreCompilerChanged: Boolean = false, body: () -> CompileService.CallResult): CompileService.CallResult = when { - state.alive.get() < minAliveness.value -> CompileService.CallResult.Dying() + state.alive.get() < minAliveness.ordinal -> CompileService.CallResult.Dying() !ignoreCompilerChanged && classpathWatcher.isChanged -> { log.info("Compiler changed, scheduling shutdown") timer.schedule(0) { shutdown() } @@ -555,8 +557,4 @@ class CompileServiceImpl( } } } - } - -internal inline fun T.ifOrNull(pred: (T) -> Boolean): T? = if (pred(this)) this else null - diff --git a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt index b439b7dd728..fe07942c725 100644 --- a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt @@ -28,6 +28,7 @@ import java.io.File import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.concurrent.thread +import kotlin.test.fail val TIMEOUT_DAEMON_RUNNER_EXIT_MS = 10000L @@ -44,7 +45,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { private fun compileOnDaemon(clientAliveFile: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, vararg args: String): CompilerResults { val daemon = KotlinCompilerClient.connectToCompileService(compilerId, clientAliveFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true) - TestCase.assertNotNull("failed to connect daemon", daemon) + assertNotNull("failed to connect daemon", daemon) daemon?.registerClient(clientAliveFile.absolutePath) val strm = ByteArrayOutputStream() val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, args, strm) @@ -53,10 +54,10 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { private fun runDaemonCompilerTwice(clientAliveFile: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, vararg args: String): Unit { val res1 = compileOnDaemon(clientAliveFile, compilerId, daemonJVMOptions, daemonOptions, *args) - TestCase.assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) + assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) val res2 = compileOnDaemon(clientAliveFile, compilerId, daemonJVMOptions, daemonOptions, *args) - TestCase.assertEquals("second compilation failed:\n${res2.out}", 0, res2.resultCode) - TestCase.assertEquals("build results differ", CliBaseTest.removePerfOutput(res1.out), CliBaseTest.removePerfOutput(res2.out)) + assertEquals("second compilation failed:\n${res2.out}", 0, res2.resultCode) + assertEquals("build results differ", CliBaseTest.removePerfOutput(res1.out), CliBaseTest.removePerfOutput(res2.out)) } private fun getTestBaseDir(): String = KotlinTestUtils.getTestDataPathBase() + "/integration/smoke/" + getTestName(true) @@ -66,7 +67,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { public fun testHelloApp() { - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath, verbose = true, reportPerf = true) @@ -96,12 +97,12 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { LinePattern("Compile on daemon: (\\d+) ms", { it.groups.get(1)?.value?.toLong()?.let { compileTime2 = it }; true }), LinePattern("Shutdown complete")) { unmatchedPattern, lineNo -> - TestCase.fail("pattern not found in the input: " + unmatchedPattern.regex + + fail("pattern not found in the input: " + unmatchedPattern.regex + "\nunmatched part of the log file (" + logFile.absolutePath + ") from line " + lineNo + ":\n\n" + logFile.reader().useLines { it.drop(lineNo).joinToString("\n") }) } } - TestCase.assertTrue("Expecting that compilation 1 ($compileTime1 ms) is at least two times longer than compilation 2 ($compileTime2 ms)", + assertTrue("Expecting that compilation 1 ($compileTime1 ms) is at least two times longer than compilation 2 ($compileTime2 ms)", compileTime1 > compileTime2 * 2) logFile.delete() run("hello.run", "-cp", jar, "Hello.HelloKt") @@ -118,10 +119,10 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { 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, inheritAdditionalProperties = 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) + assertEquals("200m", opts.maxMemory) + assertEquals("10k", opts.maxPermSize) + assertEquals("100", opts.reservedCodeCacheSize) + assertEquals(arrayListOf("aaa", "bbb,ccc", "ddd", "xxx,yyy"), opts.jvmParams) } finally { restoreSystemProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY, backupJvmOptions) @@ -133,8 +134,8 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { try { System.setProperty(COMPILE_DAEMON_OPTIONS_PROPERTY, "runFilesPath=abcd,autoshutdownIdleSeconds=1111") val opts = configureDaemonOptions() - TestCase.assertEquals("abcd", opts.runFilesPath) - TestCase.assertEquals(1111, opts.autoshutdownIdleSeconds) + assertEquals("abcd", opts.runFilesPath) + assertEquals(1111, opts.autoshutdownIdleSeconds) } finally { restoreSystemProperty(COMPILE_DAEMON_OPTIONS_PROPERTY, backupOptions) @@ -142,9 +143,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { } public fun testDaemonInstancesSimple() { - val jar1 = tmpdir.absolutePath + File.separator + "hello1.jar" - val jar2 = tmpdir.absolutePath + File.separator + "hello2.jar" - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath) val compilerId2 = CompilerId.makeCompilerId(compilerClassPath + File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar")) @@ -162,16 +161,18 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile2.loggerCompatiblePath}\"", inheritMemoryLimits = false, inheritAdditionalProperties = false) - TestCase.assertTrue(logFile1.length() == 0L && logFile2.length() == 0L) + assertTrue(logFile1.length() == 0L && logFile2.length() == 0L) + val jar1 = tmpdir.absolutePath + File.separator + "hello1.jar" val res1 = compileOnDaemon(flagFile, compilerId, daemonJVMOptions1, daemonOptions, "-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar1) - TestCase.assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) + assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) logFile1.assertLogContainsSequence("Starting compilation with args: ") - TestCase.assertEquals("expecting '${logFile2.absolutePath}' to be empty", 0L, logFile2.length()) + assertEquals("expecting '${logFile2.absolutePath}' to be empty", 0L, logFile2.length()) + val jar2 = tmpdir.absolutePath + File.separator + "hello2.jar" val res2 = compileOnDaemon(flagFile, compilerId2, daemonJVMOptions2, daemonOptions, "-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar2) - TestCase.assertEquals("second compilation failed:\n${res2.out}", 0, res1.resultCode) + assertEquals("second compilation failed:\n${res2.out}", 0, res1.resultCode) logFile2.assertLogContainsSequence("Starting compilation with args: ") @@ -186,7 +187,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { } public fun testDaemonAutoshutdownOnUnused() { - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(autoshutdownUnusedSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath) KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) @@ -196,7 +197,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { inheritMemoryLimits = false, inheritAdditionalProperties = false) val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true) - TestCase.assertNotNull("failed to connect daemon", daemon) + assertNotNull("failed to connect daemon", daemon) daemon?.registerClient(flagFile.absolutePath) // wait up to 4s (more than 1s unused timeout) @@ -213,8 +214,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { } public fun testDaemonAutoshutdownOnIdle() { - val jar = tmpdir.absolutePath + File.separator + "hello1.jar" - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath) KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) @@ -224,11 +224,12 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { inheritMemoryLimits = false, inheritAdditionalProperties = false) val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true) - TestCase.assertNotNull("failed to connect daemon", daemon) + assertNotNull("failed to connect daemon", daemon) daemon?.registerClient(flagFile.absolutePath) + val jar = tmpdir.absolutePath + File.separator + "hello1.jar" val strm = ByteArrayOutputStream() val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar), strm) - TestCase.assertEquals("compilation failed:\n${strm.toString()}", 0, code) + assertEquals("compilation failed:\n${strm.toString()}", 0, code) logFile.assertLogContainsSequence("Starting compilation with args: ") @@ -245,7 +246,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { } public fun testDaemonGracefulShutdown() { - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath) KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) @@ -255,13 +256,13 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { inheritMemoryLimits = false, inheritAdditionalProperties = false) val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true) - TestCase.assertNotNull("failed to connect daemon", daemon) + assertNotNull("failed to connect daemon", daemon) daemon?.registerClient(flagFile.absolutePath) val sessionId = daemon?.leaseCompileSession(null) val scheduleShutdownRes = daemon?.scheduleShutdown(true) - TestCase.assertTrue("failed to schedule shutdown ($scheduleShutdownRes)", scheduleShutdownRes?.let { it.isGood && it.get() } ?: false ) + assertTrue("failed to schedule shutdown ($scheduleShutdownRes)", scheduleShutdownRes?.let { it.isGood && it.get() } ?: false ) Thread.sleep(100) // to allow timer task to run in the daemon @@ -269,7 +270,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { val res = daemon?.getUsedMemory() - TestCase.assertEquals("Invalid state", CompileService.CallResult.Dying(), res) + assertEquals("Invalid state", CompileService.CallResult.Dying(), res) daemon?.releaseCompileSession(sessionId!!.get()) @@ -320,8 +321,8 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { } waitThread.join(TIMEOUT_DAEMON_RUNNER_EXIT_MS) - TestCase.assertFalse("process.waitFor() hangs:\n$resOutput", waitThread.isAlive) - TestCase.assertEquals("Compilation failed:\n$resOutput", 0, resCode) + assertFalse("process.waitFor() hangs:\n$resOutput", waitThread.isAlive) + assertEquals("Compilation failed:\n$resOutput", 0, resCode) } finally { if (clientAliveFile.exists()) @@ -345,13 +346,13 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { public fun testParallelCompilationOnDaemon() { - TestCase.assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE) + assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE) - witFlagFile(getTestName(true), ".alive") { flagFile -> + withFlagFile(getTestName(true), ".alive") { flagFile -> val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath) val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false) val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true) - TestCase.assertNotNull("failed to connect daemon", daemon) + assertNotNull("failed to connect daemon", daemon) val (registry, port) = findPortAndCreateRegistry(10, 16384, 65535) val tracer = SynchronizationTracer(CountDownLatch(1), CountDownLatch(PARALLEL_THREADS_TO_COMPILE), port) @@ -381,11 +382,11 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { tracer.startSignal.countDown() val succeeded = tracer.doneSignal.await(PARALLEL_WAIT_TIMEOUT_S, TimeUnit.SECONDS) - TestCase.assertTrue("parallel compilation failed to complete in $PARALLEL_WAIT_TIMEOUT_S ms, ${tracer.doneSignal.count} unfinished threads", succeeded) + assertTrue("parallel compilation failed to complete in $PARALLEL_WAIT_TIMEOUT_S ms, ${tracer.doneSignal.count} unfinished threads", succeeded) localEndSignal.await(PARALLEL_WAIT_TIMEOUT_S, TimeUnit.SECONDS) (1..PARALLEL_THREADS_TO_COMPILE).forEach { - TestCase.assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1]) + assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1]) } } } @@ -401,7 +402,7 @@ internal fun File.ifLogNotContainsSequence(vararg patterns: String, body: (LineP internal fun File.assertLogContainsSequence(vararg patterns: String) { ifLogNotContainsSequence(*patterns) { - pattern,lineNo -> TestCase.fail("Pattern '${pattern.regex}' is not found in the log file '$absolutePath'") + pattern,lineNo -> fail("Pattern '${pattern.regex}' is not found in the log file '$absolutePath'") } } @@ -420,7 +421,7 @@ fun restoreSystemProperty(propertyName: String, backupValue: String?) { } } -internal inline fun witFlagFile(prefix: String, suffix: String? = null, body: (File) -> Unit) { +internal inline fun withFlagFile(prefix: String, suffix: String? = null, body: (File) -> Unit) { val file = createTempFile(prefix, suffix) try { body(file) @@ -430,10 +431,10 @@ internal inline fun witFlagFile(prefix: String, suffix: String? = null, body: (F } } -// java.util.logger used in the daemon silently forgets to log into a file specified in the config on Windows, +// java.util.Logger used in the daemon silently forgets to log into a file specified in the config on Windows, // if file path is given in windows form (using backslash as a separator); the reason is unknown // this function makes a path with forward slashed, that works on windows too -internal val File.loggerCompatiblePath: String +private val File.loggerCompatiblePath: String get() = if (OSKind.current == OSKind.Windows) absolutePath.replace('\\', '/') else absolutePath \ No newline at end of file diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt index b723b1c66b6..7c8fb1d0139 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/KotlinCompilerRunner.kt @@ -137,7 +137,7 @@ public object KotlinCompilerRunner { internal object getDaemonConnection { private @Volatile var connection: DaemonConnection? = null - @Synchronized operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection? { + @Synchronized operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection { if (connection == null) { val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, messageCollector) val compilerId = CompilerId.makeCompilerId(File(libPath, "kotlin-compiler.jar")) @@ -159,7 +159,7 @@ public object KotlinCompilerRunner { return flagFile } val daemon = KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(null, daemonReportMessages), true, true) - connection = DaemonConnection(daemon, daemon?.leaseCompileSession(newFlagFile().absolutePath)?.get() ?:CompileService.NO_SESSION) + connection = DaemonConnection(daemon, daemon?.leaseCompileSession(newFlagFile().absolutePath)?.get() ?: CompileService.NO_SESSION) } for (msg in daemonReportMessages) { @@ -170,7 +170,7 @@ public object KotlinCompilerRunner { reportTotalAndThreadPerf("Daemon connect", daemonOptions, messageCollector, profiler) } - return connection + return connection!! } } diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/SimpleKotlinJpsBuildTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/SimpleKotlinJpsBuildTest.kt index c0458769bd8..5f4127160a0 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/SimpleKotlinJpsBuildTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/SimpleKotlinJpsBuildTest.kt @@ -88,7 +88,7 @@ public class SimpleKotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { // copied from CompilerDaemonTest.kt // TODO: find shared place for this function -// java.util.logger used in the daemon silently forgets to log into a file specified in the config on Windows, +// java.util.Logger used in the daemon silently forgets to log into a file specified in the config on Windows, // if file path is given in windows form (using backslash as a separator); the reason is unknown // this function makes a path with forward slashed, that works on windows too internal val File.loggerCompatiblePath: String