From 5ab55fbb5769c26b7f792a2e86c8de5a42cdf41e Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Wed, 15 Apr 2020 15:24:12 +0200 Subject: [PATCH] Support -J and "-Dname=value" in windows runner script Otherwise, -J-ea has no effect on windows, which breaks assert tests. Wrap parameters with delimeters in quotes. #KT-30211 Fixed --- compiler/cli/bin/kotlinc.bat | 24 +++- compiler/testData/launcher/property.kt | 3 + .../kotlin/cli/LauncherScriptTest.kt | 110 +++++++++++------- 3 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 compiler/testData/launcher/property.kt diff --git a/compiler/cli/bin/kotlinc.bat b/compiler/cli/bin/kotlinc.bat index 2d51465b04d36..8f6ea431f690a 100644 --- a/compiler/cli/bin/kotlinc.bat +++ b/compiler/cli/bin/kotlinc.bat @@ -27,9 +27,27 @@ if "%_JAVACMD%"=="" set _JAVACMD=java rem We use the value of the JAVA_OPTS environment variable if defined if "%JAVA_OPTS%"=="" set JAVA_OPTS=-Xmx256M -Xms32M -if not "%_KOTLIN_RUNNER%"=="" ( +rem Iterate through arguments and split them into java and kotlin ones +:loop +set _arg=%~1 +if "%_arg%" == "" goto loopend + +if "%_arg:~0,2%"=="-J" ( + set JAVA_OPTS=%JAVA_OPTS% "%_arg:~2%" +) else ( + if "%_arg:~0,2%"=="-D" ( + set JAVA_OPTS=%JAVA_OPTS% "%_arg%" + ) else ( + set KOTLIN_OPTS=%KOTLIN_OPTS% "%_arg%" + ) +) +shift +goto loop +:loopend + +if "%_KOTLIN_RUNNER%"=="1" ( "%_JAVACMD%" %JAVA_OPTS% "-Dkotlin.home=%_KOTLIN_HOME%" -cp "%_KOTLIN_HOME%\lib\kotlin-runner.jar" ^ - org.jetbrains.kotlin.runner.Main %* + org.jetbrains.kotlin.runner.Main %KOTLIN_OPTS% ) else ( SET _ADDITIONAL_CLASSPATH= @@ -39,7 +57,7 @@ if not "%_KOTLIN_RUNNER%"=="" ( "%_JAVACMD%" %JAVA_OPTS% -noverify -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^ org.jetbrains.kotlin.preloading.Preloader -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar%_ADDITIONAL_CLASSPATH%" ^ - %_KOTLIN_COMPILER% %* + %_KOTLIN_COMPILER% %KOTLIN_OPTS% ) exit /b %ERRORLEVEL% diff --git a/compiler/testData/launcher/property.kt b/compiler/testData/launcher/property.kt new file mode 100644 index 0000000000000..da3959263238b --- /dev/null +++ b/compiler/testData/launcher/property.kt @@ -0,0 +1,3 @@ +fun main() { + println(System.getProperty("result")) +} diff --git a/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt b/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt index aeef654ecf8fe..a139d8e763efe 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/cli/LauncherScriptTest.kt @@ -16,41 +16,48 @@ package org.jetbrains.kotlin.cli -import com.intellij.execution.configurations.GeneralCommandLine -import com.intellij.execution.util.ExecUtil import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.TestCaseWithTmpdir import org.jetbrains.kotlin.utils.PathUtil -import java.io.File +import org.jetbrains.kotlin.utils.addToStdlib.cast +import java.io.* +import java.util.concurrent.TimeUnit class LauncherScriptTest : TestCaseWithTmpdir() { private fun runProcess( - executableName: String, - vararg args: String, - expectedStdout: String = "", - expectedStderr: String = "", - expectedExitCode: Int = 0, - workDirectory: File? = null + executableName: String, + vararg args: String, + expectedStdout: String = "", + expectedStderr: String = "", + expectedExitCode: Int = 0, + workDirectory: File? = null ) { val executableFileName = if (SystemInfo.isWindows) "$executableName.bat" else executableName val launcherFile = File(PathUtil.kotlinPathsForDistDirectory.homePath, "bin/$executableFileName") assertTrue("Launcher script not found, run dist task: ${launcherFile.absolutePath}", launcherFile.exists()) - val cmd = GeneralCommandLine(launcherFile.absolutePath, *args) - workDirectory?.let(cmd::withWorkDirectory) - val processOutput = ExecUtil.execAndGetOutput(cmd) - val stdout = StringUtil.convertLineSeparators(processOutput.stdout) - val stderr = StringUtil.convertLineSeparators(processOutput.stderr).replace("Picked up [_A-Z]+:.*\n".toRegex(), "") - val exitCode = processOutput.exitCode - + // For some reason, IntelliJ's ExecUtil screws quotes up on windows. + // So, use ProcessBuilder instead. + val pb = ProcessBuilder( + launcherFile.absolutePath, + // In cmd, `=` is delimeter, so we need to surround parameter with quotes. + *quoteIfNeeded(args) + ) + pb.directory(workDirectory) + pb.environment().remove("_KOTLIN_RUNNER") // FIXME: HACK + val process = pb.start() + val stdout = StringUtil.convertLineSeparators(process.inputStream.bufferedReader().use { it.readText() }) + val stderr = StringUtil.convertLineSeparators(process.errorStream.bufferedReader().use { it.readText() }) + .replace("Picked up [_A-Z]+:.*\n".toRegex(), "") + process.waitFor(10, TimeUnit.SECONDS) + val exitCode = process.exitValue() try { assertEquals(expectedStdout, stdout) assertEquals(expectedStderr, stderr) assertEquals(expectedExitCode, exitCode) - } - catch (e: Throwable) { + } catch (e: Throwable) { System.err.println("exit code $exitCode") System.err.println("=== STDOUT ===") System.err.println(stdout) @@ -60,22 +67,28 @@ class LauncherScriptTest : TestCaseWithTmpdir() { } } + private fun quoteIfNeeded(args: Array): Array = + if (SystemInfo.isWindows) args.map { + if (it.contains('=') || it.contains(" ") || it.contains(";") || it.contains(",")) "\"$it\"" else it + }.toTypedArray() + else args.cast() + private val testDataDirectory: String get() = KotlinTestUtils.getTestDataPathBase() + "/launcher" fun testKotlincSimple() { runProcess( - "kotlinc", - "$testDataDirectory/helloWorld.kt", - "-d", tmpdir.path + "kotlinc", + "$testDataDirectory/helloWorld.kt", + "-d", tmpdir.path ) } fun testKotlincJvmSimple() { runProcess( - "kotlinc-jvm", - "$testDataDirectory/helloWorld.kt", - "-d", tmpdir.path + "kotlinc-jvm", + "$testDataDirectory/helloWorld.kt", + "-d", tmpdir.path ) } @@ -108,45 +121,45 @@ class LauncherScriptTest : TestCaseWithTmpdir() { fun testKotlincJsSimple() { runProcess( - "kotlinc-js", - "$testDataDirectory/emptyMain.kt", - "-output", File(tmpdir, "out.js").path + "kotlinc-js", + "$testDataDirectory/emptyMain.kt", + "-output", File(tmpdir, "out.js").path ) } fun testKotlinNoReflect() { runProcess( - "kotlinc", - "$testDataDirectory/reflectionUsage.kt", - "-d", tmpdir.path + "kotlinc", + "$testDataDirectory/reflectionUsage.kt", + "-d", tmpdir.path ) runProcess( - "kotlin", - "-cp", tmpdir.path, - "-no-reflect", - "ReflectionUsageKt", - expectedStdout = "no reflection" + "kotlin", + "-cp", tmpdir.path, + "-no-reflect", + "ReflectionUsageKt", + expectedStdout = "no reflection" ) } fun testDoNotAppendCurrentDirToNonEmptyClasspath() { runProcess( - "kotlinc", - "$testDataDirectory/helloWorld.kt", - "-d", tmpdir.path + "kotlinc", + "$testDataDirectory/helloWorld.kt", + "-d", tmpdir.path ) runProcess("kotlin", "test.HelloWorldKt", expectedStdout = "Hello!\n", workDirectory = tmpdir) val emptyDir = KotlinTestUtils.tmpDirForTest(this) runProcess( - "kotlin", - "-cp", emptyDir.path, - "test.HelloWorldKt", - expectedStderr = "error: could not find or load main class test.HelloWorldKt\n", - expectedExitCode = 1, - workDirectory = tmpdir + "kotlin", + "-cp", emptyDir.path, + "test.HelloWorldKt", + expectedStderr = "error: could not find or load main class test.HelloWorldKt\n", + expectedExitCode = 1, + workDirectory = tmpdir ) } @@ -177,4 +190,13 @@ class LauncherScriptTest : TestCaseWithTmpdir() { runProcess("kotlin", "LegacyAssertEnabledKt", "-J-ea:kotlin._Assertions", workDirectory = tmpdir) } + + fun testProperty() { + runProcess("kotlinc", "$testDataDirectory/property.kt", "-d", tmpdir.path) + + runProcess( + "kotlin", "PropertyKt", "-Dresult=OK", + workDirectory = tmpdir, expectedStdout = "OK\n" + ) + } }