Impossible to run applications with long command lines on windows
^KT-29352 Fixed
This commit is contained in:
@@ -344,6 +344,7 @@ public class KotlinRunConfiguration extends JetRunConfiguration {
|
||||
JavaParametersUtil.configureModule(module, params, classPathType, jreHome);
|
||||
setupJavaParameters(params);
|
||||
|
||||
params.setShortenCommandLine(null, module.getProject());
|
||||
params.setMainClass(myConfiguration.getRunClass());
|
||||
setupModulePath(params, module);
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ class KotlinConsoleKeeper(val project: Project) {
|
||||
if (module != null) {
|
||||
val classPath = JavaParametersBuilder.getModuleDependencies(module)
|
||||
if (classPath.isNotEmpty()) {
|
||||
javaParameters.setUseDynamicParameters(javaParameters.isDynamicClasspath)
|
||||
javaParameters.programParametersList.add("-cp")
|
||||
javaParameters.programParametersList.add(
|
||||
classPath.joinToString(File.pathSeparator)
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.util
|
||||
|
||||
import com.intellij.execution.ShortenCommandLine
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.openapi.compiler.ex.CompilerPathsEx
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType
|
||||
import com.intellij.openapi.projectRoots.JdkUtil
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.projectRoots.SimpleJavaSdkType
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
@@ -29,9 +31,20 @@ class JavaParametersBuilder(private val project: Project) {
|
||||
if (jdk == null && setDefaultSdk) {
|
||||
this.jdk = SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome())
|
||||
}
|
||||
this.setShortenCommandLine(getDefaultShortenCommandLineMethod(jdk?.homePath), project)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is partially copied from IDEA sources but doesn't check presence of dynamic.classpath property
|
||||
* because we want to shorten command line for scratches and repl anyway
|
||||
* @see [com.intellij.execution.ShortenCommandLine.getDefaultMethod]
|
||||
*/
|
||||
private fun getDefaultShortenCommandLineMethod(rootPath: String?): ShortenCommandLine {
|
||||
if (rootPath != null && JdkUtil.isModularRuntime(rootPath)) return ShortenCommandLine.ARGS_FILE
|
||||
return if (JdkUtil.useClasspathJar()) ShortenCommandLine.MANIFEST else ShortenCommandLine.CLASSPATH_FILE
|
||||
}
|
||||
|
||||
fun withMainClassName(name: String?): JavaParametersBuilder {
|
||||
mainClassName = name
|
||||
return this
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package some.test
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
package org.jetbrains.kotlin.idea.repl
|
||||
|
||||
import com.intellij.execution.console.LanguageConsoleImpl
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.testFramework.PlatformTestCase
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.jetbrains.kotlin.idea.run.createLibraryWithLongPaths
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.junit.Test
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
@@ -82,6 +84,16 @@ class IdeReplExecutionTest : PlatformTestCase() {
|
||||
assertFalse(consoleRunner.processHandler.isProcessTerminated, "Process accidentally terminated")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongCommandLine() {
|
||||
ModuleRootModificationUtil.addDependency(module, createLibraryWithLongPaths(project))
|
||||
|
||||
consoleRunner.dispose()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(module)!!
|
||||
|
||||
testRunPossibility()
|
||||
}
|
||||
|
||||
@Test fun testOnePlusOne() = testSimpleCommand("1 + 1", "2")
|
||||
@Test fun testPrintlnText() = "Hello, console world!".let { testSimpleCommand("println(\"$it\")", it) }
|
||||
@Test fun testDivisionByZeroException() = testSimpleCommand("1 / 0", "java.lang.ArithmeticException: / by zero")
|
||||
|
||||
@@ -133,6 +133,20 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() {
|
||||
Assert.assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir))
|
||||
}
|
||||
|
||||
fun testLongCommandLine() {
|
||||
val myModule = configureModule(moduleDirPath("module"), getTestProject().baseDir).module
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, addJdk(testRootDisposable, ::mockJdk))
|
||||
|
||||
ModuleRootModificationUtil.addDependency(myModule, createLibraryWithLongPaths(project))
|
||||
|
||||
val kotlinRunConfiguration = createConfigurationFromMain("some.test.main")
|
||||
kotlinRunConfiguration.setModule(myModule)
|
||||
|
||||
val javaParameters = getJavaRunParameters(kotlinRunConfiguration)
|
||||
val commandLine = javaParameters.toCommandLine().commandLineString
|
||||
Assert.assertTrue(commandLine.length < javaParameters.classPath.pathList.joinToString().length)
|
||||
}
|
||||
|
||||
fun testClassesAndObjects() {
|
||||
doTest(ConfigLibraryUtil::configureKotlinRuntimeAndSdk)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,20 @@ class RunConfigurationTest: KotlinCodeInsightTestCase() {
|
||||
Assert.assertTrue(javaParameters.classPath.rootDirs.contains(moduleWithDependencySrcDir))
|
||||
}
|
||||
|
||||
fun testLongCommandLine() {
|
||||
val myModule = configureModule(moduleDirPath("module"), getTestProject().baseDir).module
|
||||
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, addJdk(testRootDisposable, ::mockJdk))
|
||||
|
||||
ModuleRootModificationUtil.addDependency(myModule, createLibraryWithLongPaths(project))
|
||||
|
||||
val kotlinRunConfiguration = createConfigurationFromMain("some.test.main")
|
||||
kotlinRunConfiguration.setModule(myModule)
|
||||
|
||||
val javaParameters = getJavaRunParameters(kotlinRunConfiguration)
|
||||
val commandLine = javaParameters.toCommandLine().commandLineString
|
||||
Assert.assertTrue(commandLine.length < javaParameters.classPath.pathList.joinToString().length)
|
||||
}
|
||||
|
||||
fun testClassesAndObjects() {
|
||||
doTest(ConfigLibraryUtil::configureKotlinRuntimeAndSdk)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,16 @@ import com.intellij.execution.configurations.RunProfile
|
||||
import com.intellij.execution.executors.DefaultRunExecutor
|
||||
import com.intellij.execution.runners.ExecutionEnvironment
|
||||
import com.intellij.execution.runners.ExecutionEnvironmentBuilder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.testFramework.MapDataContext
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.junit.Assert
|
||||
|
||||
fun getJavaRunParameters(configuration: RunConfiguration): JavaParameters {
|
||||
@@ -42,6 +50,31 @@ fun createConfigurationFromElement(element: PsiElement, save: Boolean = false):
|
||||
return runnerAndConfigurationSettings!!.configuration
|
||||
}
|
||||
|
||||
fun createLibraryWithLongPaths(project: Project): Library {
|
||||
val maxCommandlineLengthWindows = 24500
|
||||
val maxFilenameLengthWindows = 245
|
||||
|
||||
return runWriteAction {
|
||||
val modifiableModel = ProjectLibraryTable.getInstance(project).modifiableModel
|
||||
val library = try {
|
||||
modifiableModel.createLibrary("LibraryWithLongPaths", null)
|
||||
} finally {
|
||||
modifiableModel.commit()
|
||||
}
|
||||
with(library.modifiableModel) {
|
||||
for (i in 0..maxCommandlineLengthWindows / maxFilenameLengthWindows) {
|
||||
val tmpFile = VirtualFileManager.constructUrl(
|
||||
LocalFileSystem.getInstance().protocol,
|
||||
FileUtil.createTempFile("file$i", "a".repeat(maxFilenameLengthWindows)).path
|
||||
)
|
||||
addRoot(tmpFile, OrderRootType.CLASSES)
|
||||
}
|
||||
commit()
|
||||
}
|
||||
return@runWriteAction library
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private object MockExecutor : DefaultRunExecutor() {
|
||||
override fun getId() = DefaultRunExecutor.EXECUTOR_ID
|
||||
|
||||
@@ -88,15 +88,7 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
val sourceFile = File(testDataPath, fileName)
|
||||
val fileText = sourceFile.readText()
|
||||
|
||||
val scratchFile = ScratchRootType.getInstance().createScratchFile(
|
||||
project,
|
||||
sourceFile.name,
|
||||
KotlinLanguage.INSTANCE,
|
||||
fileText,
|
||||
ScratchFileService.Option.create_if_missing
|
||||
) ?: error("Couldn't create scratch file ${sourceFile.path}")
|
||||
|
||||
myFixture.openFileInEditor(scratchFile)
|
||||
val scratchFile = createScratchFile(sourceFile.name, fileText)
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(scratchFile, project)
|
||||
|
||||
@@ -115,16 +107,6 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
|
||||
launchScratch(scratchFile)
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
// wait until output is displayed in editor or for 1 minute
|
||||
while (ScratchCompilationSupport.isAnyInProgress() && (System.currentTimeMillis() - start) < 60000) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
val doc = PsiDocumentManager.getInstance(project).getDocument(psiFile) ?: error("Document for ${psiFile.name} is null")
|
||||
|
||||
val actualOutput = StringBuilder(psiFile.text)
|
||||
@@ -152,13 +134,36 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile, actualOutput.toString())
|
||||
}
|
||||
|
||||
private fun launchScratch(scratchFile: VirtualFile) {
|
||||
protected fun createScratchFile(name: String, text: String): VirtualFile {
|
||||
val scratchFile = ScratchRootType.getInstance().createScratchFile(
|
||||
project,
|
||||
name,
|
||||
KotlinLanguage.INSTANCE,
|
||||
text,
|
||||
ScratchFileService.Option.create_if_missing
|
||||
) ?: error("Couldn't create scratch file")
|
||||
|
||||
myFixture.openFileInEditor(scratchFile)
|
||||
return scratchFile
|
||||
}
|
||||
|
||||
protected fun launchScratch(scratchFile: VirtualFile) {
|
||||
val action = RunScratchAction()
|
||||
val e = getActionEvent(scratchFile, action)
|
||||
|
||||
action.beforeActionPerformedUpdate(e)
|
||||
Assert.assertTrue(e.presentation.isEnabled && e.presentation.isVisible)
|
||||
action.actionPerformed(e)
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
// wait until output is displayed in editor or for 1 minute
|
||||
while (ScratchCompilationSupport.isAnyInProgress() && (System.currentTimeMillis() - start) < 60000) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
}
|
||||
|
||||
private fun getActionEvent(virtualFile: VirtualFile, action: AnAction): TestActionEvent {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.scratch
|
||||
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import org.jetbrains.kotlin.idea.run.createLibraryWithLongPaths
|
||||
import org.jetbrains.kotlin.idea.scratch.output.InlayScratchFileRenderer
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
|
||||
class CustomScratchRunActionTest : AbstractScratchRunActionTest() {
|
||||
|
||||
fun testLongCommandLineWithRepl() {
|
||||
assertEquals("RESULT: res0: kotlin.Int = 1", getOutput(true))
|
||||
}
|
||||
|
||||
fun testLongCommandLine() {
|
||||
assertEquals("RESULT: 1", getOutput(false))
|
||||
}
|
||||
|
||||
private fun getOutput(isRepl: Boolean): String {
|
||||
val fileText = "1"
|
||||
val scratchFile = createScratchFile("scratch_1.kts", fileText)
|
||||
|
||||
val (editor, scratchPanel) = getEditorWithScratchPanel(myManager, scratchFile) ?: error("Couldn't find scratch panel")
|
||||
scratchPanel.scratchFile.saveOptions {
|
||||
copy(isRepl = isRepl, isInteractiveMode = false)
|
||||
}
|
||||
|
||||
scratchPanel.setModule(myFixture.module)
|
||||
|
||||
launchScratch(scratchFile)
|
||||
|
||||
return editor.editor.inlayModel.getInlineElementsInRange(0, fileText.length)
|
||||
.map { it.renderer }
|
||||
.filterIsInstance<InlayScratchFileRenderer>()
|
||||
.joinToString().trim()
|
||||
}
|
||||
|
||||
private val library: Library by lazy {
|
||||
createLibraryWithLongPaths(project)
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
ModuleRootModificationUtil.addDependency(myFixture.module, library)
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
removeLibraryWithLongPaths()
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
private fun removeLibraryWithLongPaths() {
|
||||
runWriteAction {
|
||||
val modifiableModel = ProjectLibraryTable.getInstance(project).modifiableModel
|
||||
try {
|
||||
modifiableModel.removeLibrary(library)
|
||||
} finally {
|
||||
modifiableModel.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.scratch
|
||||
|
||||
import com.intellij.ide.scratch.ScratchFileService
|
||||
import com.intellij.ide.scratch.ScratchRootType
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.scratch.ui.ScratchTopPanel
|
||||
import org.junit.Assert
|
||||
import javax.swing.JCheckBox
|
||||
@@ -17,18 +14,7 @@ import kotlin.reflect.full.declaredMemberProperties
|
||||
class ScratchOptionsSaveTest : AbstractScratchRunActionTest() {
|
||||
|
||||
fun testOptionsSaveOnClosingFile() {
|
||||
val fileText = "val a = 1"
|
||||
|
||||
val scratchFile = ScratchRootType.getInstance().createScratchFile(
|
||||
project,
|
||||
"scratch_1.kts",
|
||||
KotlinLanguage.INSTANCE,
|
||||
fileText,
|
||||
ScratchFileService.Option.create_if_missing
|
||||
) ?: error("Couldn't create scratch file")
|
||||
|
||||
myManager.openFile(scratchFile, true)
|
||||
|
||||
val scratchFile = createScratchFile("scratch_1.kts", "val a = 1")
|
||||
val (_, scratchPanelBeforeClosingFile) = getEditorWithScratchPanel(myManager, scratchFile) ?: error("Couldn't find scratch panel")
|
||||
|
||||
Assert.assertEquals(
|
||||
|
||||
Reference in New Issue
Block a user