Optionally make java executable path contribute to the daemon id.

This change will allow to start Kotlin daemon instances using different
from the current user JDK version and use it to compile Kotlin files.
Old behaviour, when java executable path is not set, is still working
 and still,by default, using current user JDK version.

For example, user for some reason wants to use JDK 1.8 as active one,
 but compile current project using JDK 16.

Main goal is to support Gradle toolchains.

^KT-45611 In Progress
This commit is contained in:
Yahor Berdnikau
2021-04-22 17:38:58 +02:00
committed by TeamCityServer
parent 1d47f4ad2b
commit ac86ad252f
3 changed files with 166 additions and 6 deletions
@@ -367,8 +367,14 @@ object KotlinCompilerClient {
}
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets): Boolean {
val javaExecutable = File(File(CompilerSystemProperties.JAVA_HOME.safeValue, "bin"), "java")
private fun startDaemon(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets
): Boolean {
val daemonJavaExecutable = compilerId.javaExecutable
?: File(File(CompilerSystemProperties.JAVA_HOME.safeValue, "bin"), "java")
val serverHostname = CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.value ?: error("${CompilerSystemProperties.JAVA_RMI_SERVER_HOSTNAME.property} is not set!")
val platformSpecificOptions = listOf(
// hide daemon window
@@ -380,7 +386,7 @@ object KotlinCompilerClient {
listOf("--illegal-access=permit")
else emptyList()
val args = listOf(
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
daemonJavaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
platformSpecificOptions +
daemonJVMOptions.mappers.flatMap { it.toArgs("-") } +
javaIllegalAccessWorkaround +
@@ -231,15 +231,36 @@ fun ByteArray.toHexString(): String = joinToString("", transform = { "%02x".form
data class CompilerId(
var compilerClasspath: List<String> = listOf(),
var compilerVersion: String = ""
var compilerVersion: String = "",
var javaExecutable: File? = null
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(PropMapper(this, CompilerId::compilerClasspath, toString = { it.joinToString(File.pathSeparator) }, fromString = { it.trimQuotes().split(File.pathSeparator) }),
StringPropMapper(this, CompilerId::compilerVersion))
get() = listOf(
PropMapper(
dest = this,
prop = CompilerId::compilerClasspath,
toString = { it.joinToString(File.pathSeparator) },
fromString = { it.trimQuotes().split(File.pathSeparator) }
),
StringPropMapper(
dest = this,
prop = CompilerId::compilerVersion
),
PropMapper(
dest = this,
prop = CompilerId::javaExecutable,
toString = { it?.absolutePath },
fromString = { File(it.trimQuotes()) },
skipIf = { it == null }
)
)
fun digest(): String = compilerClasspath
.map { File(it).absolutePath }
.run {
javaExecutable?.let { plus(it.absolutePath) } ?: this
}
.distinctStringsDigest()
.toHexString()
@@ -250,6 +271,15 @@ data class CompilerId(
@JvmStatic
fun makeCompilerId(paths: Iterable<File>): CompilerId =
CompilerId(compilerClasspath = paths.map { it.absolutePath })
@JvmStatic
fun makeCompilerId(
paths: Iterable<File>,
javaExecutable: File
): CompilerId = CompilerId(
compilerClasspath = paths.map { it.absolutePath },
javaExecutable = javaExecutable
)
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.daemon
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import junit.framework.TestCase
@@ -277,6 +278,129 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
}
}
private fun getJdk8Location() = System.getenv("JDK_18") ?: System.getenv("JAVA_HOME")
fun testNewDaemonIsNotStartedForSameJavaExecutable() {
withFlagFile(getTestName(true), "-client1.alive") { flagFile1 ->
withFlagFile(getTestName(true), "-client2.alive") { flagFile2 ->
val daemonOptions = makeTestDaemonOptions(getTestName(true))
val compilerIdJdk8 = CompilerId.makeCompilerId(
compilerClassPath +
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"),
File(getJdk8Location()).resolve("bin/java")
)
withLogFile("kotlin-daemon-test-1") { logFile ->
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
assertTrue(logFile.length() == 0L)
val daemon1 = KotlinCompilerClient.connectToCompileService(
compilerIdJdk8,
flagFile1,
daemonJVMOptions,
daemonOptions,
DaemonReportingTargets(out = System.err),
autostart = true
)
assertNotNull("failed to connect daemon", daemon1)
logFile.assertLogContainsSequence("INFO: starting daemon")
val daemon2 = KotlinCompilerClient.connectToCompileService(
compilerIdJdk8,
flagFile2,
daemonJVMOptions,
daemonOptions,
DaemonReportingTargets(out = System.err),
autostart = true
)
assertNotNull("failed to connect daemon", daemon2)
val logContent = logFile.readText().lines()
assert(
logContent.filter { it.contains("INFO: starting daemon") }.size == 1
) {
"Second daemon instance was started!"
}
assert(
logContent.filter {
it.contains("INFO: Registered a client alive file: ${flagFile2.absolutePath}")
}.size == 1
) {
"Second client was not connected to the same instance!"
}
KotlinCompilerClient.shutdownCompileService(compilerIdJdk8, daemonOptions)
Thread.sleep(100)
logFile.assertLogContainsSequence("Shutdown started")
}
}
}
}
// Ignored on Windows OS due to https://bugs.openjdk.java.net/browse/JDK-8189953 bug in JDK 9
// Should be unignored once JDK10+ will be available by default on CI agents
fun testNewDaemonIsStartedOnJavaExecutableChange() {
if (SystemInfo.isWindows) return
withFlagFile(getTestName(true), "-client1.alive") { flagFile1 ->
withFlagFile(getTestName(true), "-client2.alive") { flagFile2 ->
val daemonOptions = makeTestDaemonOptions(getTestName(true))
val compilerIdJdk8 = CompilerId.makeCompilerId(
compilerClassPath +
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"),
File(getJdk8Location()).resolve("bin/java")
)
val compilerIdJdk9 = CompilerId.makeCompilerId(
compilerClassPath +
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"),
File(System.getenv("JDK_9")).resolve("bin/java")
)
withLogFile("kotlin-daemon-test-1") { logFile1 ->
withLogFile("kotlin-daemon-test-2") { logFile2 ->
val daemonJdk8JVMOptions = makeTestDaemonJvmOptions(logFile1)
assertTrue(logFile1.length() == 0L)
val daemonJdk9JVMOptions = makeTestDaemonJvmOptions(logFile2)
assertTrue(logFile2.length() == 0L)
val daemonJdk7 = KotlinCompilerClient.connectToCompileService(
compilerIdJdk8,
flagFile1,
daemonJdk8JVMOptions,
daemonOptions,
DaemonReportingTargets(out = System.err),
autostart = true
)
assertNotNull("failed to connect daemon", daemonJdk7)
logFile1.assertLogContainsSequence("INFO: starting daemon")
val daemonJdk9 = KotlinCompilerClient.connectToCompileService(
compilerIdJdk9,
flagFile2,
daemonJdk9JVMOptions,
daemonOptions,
DaemonReportingTargets(out = System.err),
autostart = true
)
assertNotNull("failed to connect daemon", daemonJdk9)
logFile2.assertLogContainsSequence("INFO: starting daemon")
KotlinCompilerClient.shutdownCompileService(compilerIdJdk8, daemonOptions)
KotlinCompilerClient.shutdownCompileService(compilerIdJdk9, daemonOptions)
Thread.sleep(100)
logFile1.assertLogContainsSequence("Shutdown started")
logFile2.assertLogContainsSequence("Shutdown started")
}
}
}
}
}
fun testDaemonRunError() {
withFlagFile(getTestName(true), ".alive") { flagFile ->
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, verbose = true, runFilesPath = File(testTempDir, getTestName(true)).absolutePath)