From 8178ec8cae6e956eff86d48be7425cd2d02d9734 Mon Sep 17 00:00:00 2001
From: Roman Shevchenko
Date: Fri, 26 Aug 2011 16:11:17 +0200
Subject: [PATCH] Consistent OS-independent command line params' quoting and
escaping
---
.../images/actions/EditExternallyAction.java | 4 +-
.../javadoc/JavadocConfiguration.java | 15 +-
java/java-tests/java-tests.iml | 1 +
.../configurations/JavaCommandLineTest.java | 100 ++++++
.../configurations/JavaParametersTest.java | 109 ++++++
.../AbstractInspectionToolStarter.java | 58 ++--
.../configurations/GeneralCommandLine.java | 275 ++++++++-------
.../configurations/ParametersList.java | 312 ++++++++++--------
.../execution/configurations/ParamsGroup.java | 5 +-
.../diff/impl/external/BaseExternalTool.java | 8 +-
.../diff/impl/external/DiffManagerImpl.java | 8 +-
.../intellij/execution/EnvPassingTest.java | 37 +++
.../execution/GeneralCommandLineTest.java | 142 ++++++++
.../com/intellij/execution/printArgs.c | 14 +
.../com/intellij/execution/printArgs.exe | Bin 0 -> 44544 bytes
.../openapi/execution/ParametersListTest.java | 93 ++++--
.../compiler/tools/AndroidMavenExecutor.java | 7 +-
.../jetbrains/android/util/AndroidUtils.java | 7 +-
.../src/git4idea/commands/GitHandler.java | 8 +-
.../intellij/execution/junit/TestObject.java | 79 +++--
.../rt/execution/junit/IdeaTestRunner.java | 4 +-
.../execution/junit/JUnitForkedStarter.java | 94 +++---
.../rt/execution/junit/JUnitStarter.java | 13 +-
.../rt/execution/junit/ProcessBuilder.java | 66 ++++
24 files changed, 1014 insertions(+), 445 deletions(-)
create mode 100644 java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java
create mode 100644 java/java-tests/testSrc/com/intellij/execution/configurations/JavaParametersTest.java
create mode 100644 platform/platform-impl/testSrc/com/intellij/execution/EnvPassingTest.java
create mode 100644 platform/platform-impl/testSrc/com/intellij/execution/GeneralCommandLineTest.java
create mode 100644 platform/platform-impl/testSrc/com/intellij/execution/printArgs.c
create mode 100644 platform/platform-impl/testSrc/com/intellij/execution/printArgs.exe
create mode 100644 plugins/junit_rt/src/com/intellij/rt/execution/junit/ProcessBuilder.java
diff --git a/images/src/org/intellij/images/actions/EditExternallyAction.java b/images/src/org/intellij/images/actions/EditExternallyAction.java
index 2273bb65830a..57bf0d018ad2 100644
--- a/images/src/org/intellij/images/actions/EditExternallyAction.java
+++ b/images/src/org/intellij/images/actions/EditExternallyAction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -87,7 +87,7 @@ public final class EditExternallyAction extends AnAction {
commandLine.addParameter(VfsUtil.virtualToIoFile(file).getAbsolutePath());
}
}
- commandLine.setWorkingDirectory(new File(executablePath).getParentFile());
+ commandLine.setWorkDirectory(new File(executablePath).getParentFile());
try {
commandLine.createProcess();
diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java
index 59a0d7b4632e..4913ac98998d 100644
--- a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java
+++ b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -164,7 +164,7 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl
cmdLine.getParametersList().prepend("-J-mx" + HEAP_SIZE + "m");
}
}
- cmdLine.setWorkingDirectory(null);
+ cmdLine.setWorkDirectory((File)null);
@NonNls final String javadocExecutableName = File.separator + (SystemInfo.isWindows ? "javadoc.exe" : "javadoc");
@NonNls String exePath = jdkPath.replace('/', File.separatorChar) + javadocExecutableName;
if (new File(exePath).exists()) {
@@ -250,10 +250,9 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl
}
try {
- File sourcepathTempFile = FileUtil.createTempFile("javadoc", "args.txt");
- sourcepathTempFile.deleteOnExit();
- parameters.add("@" + sourcepathTempFile.getCanonicalPath());
- final PrintWriter writer = new PrintWriter(new FileWriter(sourcepathTempFile));
+ final File sourcePathTempFile = FileUtil.createTempFile("javadoc", "args.txt", true);
+ parameters.add("@" + sourcePathTempFile.getCanonicalPath());
+ final PrintWriter writer = new PrintWriter(new FileWriter(sourcePathTempFile));
try {
final Collection packages = new HashSet();
final Collection sources = new HashSet();
@@ -290,7 +289,7 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl
}
sourcePath.append(file.getPath());
}
- writer.println(GeneralCommandLine.quote(sourcePath.toString()));
+ writer.println(sourcePath.toString());
}
finally {
writer.close();
@@ -340,7 +339,7 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl
final PsiJavaFile javaFile = (PsiJavaFile)file;
final String packageName = javaFile.getPackageName();
if (containsPackagePrefix(module, packageName) || (packageName.length() == 0 && !(javaFile instanceof JspFile))) {
- mySourceFiles.add(GeneralCommandLine.quote(FileUtil.toSystemIndependentName(fileOrDir.getPath())));
+ mySourceFiles.add(FileUtil.toSystemIndependentName(fileOrDir.getPath()));
}
else {
myPackages.add(packageName);
diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml
index a2196312e368..e2708d77ad84 100644
--- a/java/java-tests/java-tests.iml
+++ b/java/java-tests/java-tests.iml
@@ -25,6 +25,7 @@
+
diff --git a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java
new file mode 100644
index 000000000000..2708ac91181c
--- /dev/null
+++ b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.execution.configurations;
+
+import com.intellij.execution.CantRunException;
+import com.intellij.execution.ExecutionBundle;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.process.DefaultJavaProcessHandler;
+import com.intellij.ide.IdeBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.testFramework.LightIdeaTestCase;
+import junit.framework.Assert;
+
+public class JavaCommandLineTest extends LightIdeaTestCase {
+ public void testJdk() {
+ try {
+ CommandLineBuilder.createFromJavaParameters(new JavaParameters());
+ fail("CantRunException (main class is not specified) expected");
+ }
+ catch (CantRunException e) {
+ Assert.assertEquals(ExecutionBundle.message("run.configuration.error.no.jdk.specified"), e.getMessage());
+ }
+ }
+
+ public void testMainClass() {
+ try {
+ JavaParameters javaParameters = new JavaParameters();
+ javaParameters.setJdk(getProjectJDK());
+ CommandLineBuilder.createFromJavaParameters(javaParameters);
+ fail("CantRunException (main class is not specified) expected");
+ }
+ catch (CantRunException e) {
+ assertEquals(ExecutionBundle.message("main.class.is.not.specified.error.message"), e.getMessage());
+ }
+ }
+
+ public void testClasspath() throws CantRunException {
+ JavaParameters javaParameters;
+ String commandLineString;
+
+ javaParameters = new JavaParameters();
+ final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+ javaParameters.setJdk(internalJdk);
+ javaParameters.setMainClass("Main");
+ commandLineString = CommandLineBuilder.createFromJavaParameters(javaParameters).getCommandLineString();
+ assertTrue(containsClassPath(commandLineString));
+
+ javaParameters = new JavaParameters();
+ javaParameters.setJdk(internalJdk);
+ javaParameters.setMainClass("Main");
+ javaParameters.getVMParametersList().add("-cp");
+ javaParameters.getVMParametersList().add("..");
+ commandLineString = CommandLineBuilder.createFromJavaParameters(javaParameters).getCommandLineString();
+ commandLineString = removeClassPath(commandLineString, "-cp ..");
+ assertTrue(!containsClassPath(commandLineString));
+
+ javaParameters = new JavaParameters();
+ javaParameters.setJdk(internalJdk);
+ javaParameters.setMainClass("Main");
+ javaParameters.getVMParametersList().add("-classpath");
+ javaParameters.getVMParametersList().add("..");
+ commandLineString = CommandLineBuilder.createFromJavaParameters(javaParameters).getCommandLineString();
+ commandLineString = removeClassPath(commandLineString, "-classpath ..");
+ assertTrue(!containsClassPath(commandLineString));
+ }
+
+ private static boolean containsClassPath(String commandLineString) {
+ return commandLineString.contains("-cp") || commandLineString.contains("-classpath");
+ }
+
+ private static String removeClassPath(String commandLineString, String pathString) {
+ int i = commandLineString.indexOf(pathString);
+ commandLineString = commandLineString.substring(0, i) + commandLineString.substring(i + pathString.length());
+ return commandLineString;
+ }
+
+ public void testCreateProcess() {
+ try {
+ new DefaultJavaProcessHandler(new GeneralCommandLine());
+ fail("ExecutionException (executable is not specified) expected");
+ }
+ catch (ExecutionException e) {
+ assertEquals(IdeBundle.message("run.configuration.error.executable.not.specified"), e.getMessage());
+ }
+ }
+}
diff --git a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaParametersTest.java b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaParametersTest.java
new file mode 100644
index 000000000000..9d1007f1bc1f
--- /dev/null
+++ b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaParametersTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.execution.configurations;
+
+import com.intellij.execution.CantRunException;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.DependencyScope;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.roots.ModuleRootManagerTestCase;
+
+/**
+ * @author nik
+ */
+public class JavaParametersTest extends ModuleRootManagerTestCase {
+ public void testLibrary() throws Exception {
+ addLibraryDependency(myModule, createJDomLibrary());
+ assertClasspath(myModule, JavaParameters.JDK_AND_CLASSES_AND_TESTS,
+ getRtJar(), getJDomJar());
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY,
+ getJDomJar());
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS,
+ getJDomJar());
+ assertClasspath(myProject, JavaParameters.JDK_AND_CLASSES_AND_TESTS,
+ getRtJar(), getJDomJar());
+ }
+
+ public void testModuleSourcesAndOutput() throws Exception {
+ addSourceRoot(myModule, false);
+ addSourceRoot(myModule, true);
+ final VirtualFile output = setModuleOutput(myModule, false);
+ final VirtualFile testOutput = setModuleOutput(myModule, true);
+
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY,
+ output);
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS,
+ testOutput, output);
+ assertClasspath(myModule, JavaParameters.JDK_AND_CLASSES_AND_TESTS,
+ getRtJar(), testOutput, output);
+ }
+
+ public void testLibraryScope() throws Exception {
+ addLibraryDependency(myModule, createJDomLibrary(), DependencyScope.RUNTIME, false);
+ addLibraryDependency(myModule, createAsmLibrary(), DependencyScope.TEST, false);
+
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS,
+ getJDomJar(), getAsmJar());
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY,
+ getJDomJar());
+ }
+
+ public void testProvidedScope() throws Exception {
+ addLibraryDependency(myModule, createJDomLibrary(), DependencyScope.PROVIDED, false);
+
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS, getJDomJar());
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY);
+ }
+
+ public void testModuleDependency() throws Exception {
+ final Module dep = createModule("dep");
+ final VirtualFile depOutput = setModuleOutput(dep, false);
+ final VirtualFile depTestOutput = setModuleOutput(dep, true);
+ addLibraryDependency(dep, createJDomLibrary());
+ addModuleDependency(myModule, dep);
+
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY,
+ depOutput, getJDomJar());
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS,
+ depTestOutput, depOutput, getJDomJar());
+ }
+
+ public void testModuleDependencyScope() throws Exception {
+ final Module dep = createModule("dep");
+ addLibraryDependency(dep, createJDomLibrary());
+ addModuleDependency(myModule, dep, DependencyScope.TEST, true);
+
+ assertClasspath(myModule, JavaParameters.CLASSES_ONLY);
+ assertClasspath(myModule, JavaParameters.CLASSES_AND_TESTS,
+ getJDomJar());
+
+ assertClasspath(myProject, JavaParameters.CLASSES_ONLY,
+ getJDomJar());
+ }
+
+ private static void assertClasspath(final Module module, final int type, VirtualFile... roots) throws CantRunException {
+ final JavaParameters javaParameters = new JavaParameters();
+ javaParameters.configureByModule(module, type);
+ assertRoots(javaParameters.getClassPath(), roots);
+ }
+
+ private void assertClasspath(final Project project, final int type, VirtualFile... roots) throws CantRunException {
+ final JavaParameters javaParameters = new JavaParameters();
+ javaParameters.configureByProject(project, type, getTestProjectJdk());
+ assertRoots(javaParameters.getClassPath(), roots);
+ }
+}
diff --git a/platform/lang-impl/src/com/intellij/codeInspection/AbstractInspectionToolStarter.java b/platform/lang-impl/src/com/intellij/codeInspection/AbstractInspectionToolStarter.java
index 7a522b306b7e..9e3bf6e4c513 100644
--- a/platform/lang-impl/src/com/intellij/codeInspection/AbstractInspectionToolStarter.java
+++ b/platform/lang-impl/src/com/intellij/codeInspection/AbstractInspectionToolStarter.java
@@ -1,13 +1,30 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.codeInspection;
-import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.configurations.ParametersList;
import com.intellij.openapi.application.ApplicationStarter;
+import com.intellij.util.containers.CollectionFactory;
import com.sampullara.cli.Args;
import org.jetbrains.annotations.NotNull;
/**
* @author Roman.Chernyatchik
*/
+@SuppressWarnings("UseOfSystemOutOrSystemErr")
public abstract class AbstractInspectionToolStarter implements ApplicationStarter {
protected InspectionApplication myApplication;
protected InspectionToolCmdlineOptions myOptions;
@@ -19,7 +36,8 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
myOptions = createCmdlineOptions();
try {
Args.parse(myOptions, args);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
printHelpAndExit(args, myOptions);
return;
}
@@ -32,17 +50,18 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
}
// TODO[romeo] : if config given - parse config and set attrs
- //Properties p = new Properties();
- // p.put("input", "inputfile");
- // p.put("o", "outputfile");
- // p.put("someflag", "true");
- // p.put("m", "10");
- // p.put("values", "1:2:3");
- // p.put("strings", "sam;dave;jolly");
- // PropertiesArgs.parse(tc, p);
- try{
+ //Properties p = new Properties();
+ // p.put("input", "inputfile");
+ // p.put("o", "outputfile");
+ // p.put("someflag", "true");
+ // p.put("m", "10");
+ // p.put("values", "1:2:3");
+ // p.put("strings", "sam;dave;jolly");
+ // PropertiesArgs.parse(tc, p);
+ try {
myOptions.validate();
- } catch (InspectionToolCmdlineOptions.CmdlineArgsValidationException e) {
+ }
+ catch (InspectionToolCmdlineOptions.CmdlineArgsValidationException e) {
System.err.println(e.getMessage());
if (!myOptions.suppressHelp()) {
printHelpAndExit(args, myOptions);
@@ -66,23 +85,22 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
myApplication.startup();
}
- private void initApplication(@NotNull final InspectionApplication application,
- @NotNull final InspectionToolCmdlineOptions opts) {
+ private static void initApplication(@NotNull final InspectionApplication application,
+ @NotNull final InspectionToolCmdlineOptions opts) {
opts.initApplication(application);
}
- private boolean verbose(final InspectionToolCmdlineOptions opts) {
+ private static boolean verbose(final InspectionToolCmdlineOptions opts) {
return opts.getVerboseLevelProperty() > 0;
}
protected void printArgs(String[] args, StringBuilder buff) {
if (args.length < 2) {
buff.append(" no arguments");
- } else {
- for (int i = 1, argsLength = args.length; i < argsLength; i++) {
- String arg = args[i];
- buff.append(' ').append(GeneralCommandLine.quote(arg));
- }
+ }
+ else {
+ final String argString = ParametersList.join(CollectionFactory.arrayList(args, 1, args.length));
+ buff.append(argString);
}
}
diff --git a/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java b/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java
index 1672e82b9946..bdcbdbf5964e 100644
--- a/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java
+++ b/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -18,12 +18,10 @@ package com.intellij.execution.configurations;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessNotCreatedException;
import com.intellij.ide.IdeBundle;
-import com.intellij.openapi.diagnostic.Log;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
-import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,45 +29,65 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
-import java.util.Iterator;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+/**
+ * OS-independent way of executing external processes with complex parameters.
+ *
+ * Main idea of the class is to accept parameters "as-is", just as they should look to an external process, and quote/escape them
+ * as required by the underlying platform.
+ *
+ * todo: check help
+ */
public class GeneralCommandLine {
- private static final Logger LOG = Logger.getInstance("#" + GeneralCommandLine.class.getName());
- private Map myEnvParams;
- private boolean myPassParentEnvs;
private String myExePath = null;
private File myWorkDirectory = null;
- private ParametersList myProgramParams = new ParametersList();
+ private Map myEnvParams = null;
+ private boolean myPassParentEnvironment = true;
+ private final ParametersList myProgramParams = new ParametersList();
private Charset myCharset = CharsetToolkit.getDefaultSystemCharset();
-
- public void setExePath(@NonNls final String exePath) {
- myExePath = exePath.trim();
- }
+ private boolean myRedirectErrorStream = false;
public String getExePath() {
return myExePath;
}
- public void setWorkDirectory(@NonNls final String path) {
- setWorkingDirectory(path != null? new File(path) : null);
- }
-
- public void setWorkingDirectory(final File workingDirectory) {
- myWorkDirectory = workingDirectory;
+ public void setExePath(@NotNull @NonNls final String exePath) {
+ myExePath = exePath.trim();
}
public File getWorkDirectory() {
return myWorkDirectory;
}
- public void setEnvParams(final Map envParams) {
+ public void setWorkDirectory(@Nullable @NonNls final String path) {
+ setWorkDirectory(path != null ? new File(path) : null);
+ }
+
+ public void setWorkDirectory(@Nullable final File workDirectory) {
+ myWorkDirectory = workDirectory;
+ }
+
+ /**
+ * @deprecated use {@link #setWorkDirectory(java.io.File)} (to remove in IDEA 12).
+ */
+ public void setWorkingDirectory(@Nullable final File workDirectory) {
+ setWorkDirectory(workDirectory);
+ }
+
+ @Nullable
+ public Map getEnvParams() {
+ return myEnvParams;
+ }
+
+ public void setEnvParams(@Nullable final Map envParams) {
myEnvParams = envParams;
}
- public void setCharset(@NotNull Charset charset) {
- myCharset = charset;
+ public void setPassParentEnvs(final boolean passParentEnvironment) {
+ myPassParentEnvironment = passParentEnvironment;
}
public void addParameters(final String... parameters) {
@@ -78,7 +96,7 @@ public class GeneralCommandLine {
}
}
- public void addParameters(final List parameters) {
+ public void addParameters(@NotNull final List parameters) {
for (final String parameter : parameters) {
addParameter(parameter);
}
@@ -88,37 +106,105 @@ public class GeneralCommandLine {
myProgramParams.add(parameter);
}
- public String getCommandLineString() {
- final StringBuffer buffer = new StringBuffer(quoteParameter(FileUtil.toSystemDependentName(myExePath)));
- appendParams( buffer );
- return buffer.toString();
- }
-
- public String getCommandLineParams() {
- final StringBuffer buffer = new StringBuffer();
- appendParams( buffer );
- return buffer.toString();
- }
-
- private void appendParams( StringBuffer buffer ) {
- for( final String param : myProgramParams.getList() ) {
- buffer.append(" ").append(quoteParameter(param));
- }
+ public ParametersList getParametersList() {
+ return myProgramParams;
}
+ @NotNull
public Charset getCharset() {
return myCharset;
}
+ public void setCharset(@NotNull final Charset charset) {
+ myCharset = charset;
+ }
+
+ public void setRedirectErrorStream(final boolean redirectErrorStream) {
+ myRedirectErrorStream = redirectErrorStream;
+ }
+
+ /**
+ * @deprecated please use {@link #getCommandLineString()} (to remove in IDEA 12).
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public String getCommandLineParams() {
+ return getCommandLineString();
+ }
+
+ /**
+ * Returns string representation of this command line.
+ * Warning: resulting string is not OS-dependent - do not use it for executing this command line.
+ *
+ * @return single-string representation of this command line.
+ */
+ public String getCommandLineString() {
+ return getCommandLineString(null);
+ }
+
+ /**
+ * Returns string representation of this command line.
+ * Warning: resulting string is not OS-dependent - do not use it for executing this command line.
+ *
+ * @param exeName use this executable name instead of given by {@link #setExePath(String)}
+ * @return single-string representation of this command line.
+ */
+ public String getCommandLineString(@Nullable final String exeName) {
+ final List commands = new ArrayList();
+ if (exeName != null) {
+ commands.add(exeName);
+ }
+ else if (myExePath != null) {
+ commands.add(FileUtil.toSystemDependentName(myExePath));
+ }
+ else {
+ commands.add("");
+ }
+ commands.addAll(myProgramParams.getList());
+ return ParametersList.join(commands);
+ }
+
+ /**
+ * Returns a list of command and its parameters prepared in OS-dependent way to be executed by e.g. {@link Runtime#exec(String[])}.
+ *
+ * @deprecated this method is not intended for internal use (to remove in IDEA 12).
+ */
+ public String[] getCommands() {
+ return prepareCommands();
+ }
+
+ /**
+ * @deprecated use {@link #addParameter(String)} and {@link #addParameters(String...)} methods for adding parameters -
+ * any quoting needed will be done on {@link #createProcess()} (to remove in IDEA 12).
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static String quoteParameter(final String parameter) {
+ return parameter;
+ }
+
+ /**
+ * @deprecated use {@link #addParameter(String)} and {@link #addParameters(String...)} methods for adding parameters -
+ * any quoting needed will be done on {@link #createProcess()} (to remove in IDEA 12).
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static String quote(final String parameter) {
+ return parameter;
+ }
+
public Process createProcess() throws ExecutionException {
checkWorkingDirectory();
- try {
- final String[] commands = getCommands();
- if(commands[0] == null) throw new ExecutionException(IdeBundle.message("run.configuration.error.executable.not.specified"));
- return myWorkDirectory != null
- ? Runtime.getRuntime().exec(commands, getEnvParamsArray(), myWorkDirectory)
- : Runtime.getRuntime().exec(commands, getEnvParamsArray());
+ final String[] commands = prepareCommands();
+ if (StringUtil.isEmptyOrSpaces(commands[0])) {
+ throw new ExecutionException(IdeBundle.message("run.configuration.error.executable.not.specified"));
+ }
+
+ try {
+ final ProcessBuilder builder = new ProcessBuilder(commands);
+ final Map environment = builder.environment();
+ setupEnvironment(environment);
+ builder.directory(myWorkDirectory);
+ builder.redirectErrorStream(myRedirectErrorStream);
+ return builder.start();
}
catch (IOException e) {
throw new ProcessNotCreatedException(e.getMessage(), e, this);
@@ -138,98 +224,33 @@ public class GeneralCommandLine {
}
}
- @Nullable
- public Map getEnvParams() {
- return myEnvParams;
- }
-
- @Nullable
- private String[] getEnvParamsArray() {
- final Map envParams = collectEnvParams();
- if (envParams == null) return null;
- for (Iterator iterator = envParams.keySet().iterator(); iterator.hasNext(); ) {
- final String key = iterator.next();
- if (envParams.get(key) == null) {
- LOG.info("null value for env variable: " + key);
- iterator.remove();
- }
- }
- final String[] result = new String[envParams.size()];
- int i=0;
- for (final String key : envParams.keySet()) {
- result[i++] = key + "=" + envParams.get(key).trim();
- }
- return result;
- }
-
- @Nullable
- private Map collectEnvParams() {
- if (myEnvParams == null) {
- return null;
- }
- final Map envParams = new HashMap();
- if (myPassParentEnvs) {
- envParams.putAll(System.getenv());
- }
- envParams.putAll(myEnvParams);
- return envParams;
- }
-
- public String[] getCommands() {
+ private String[] prepareCommands() {
final List parameters = myProgramParams.getList();
final String[] result = new String[parameters.size() + 1];
- result[0] = myExePath;
- int index = 1;
- for (Iterator iterator = parameters.iterator(); iterator.hasNext(); index++) {
- result[index] = iterator.next();
+ result[0] = prepareCommand(myExePath != null ? FileUtil.toSystemDependentName(myExePath) : null);
+ for (int i = 0; i < parameters.size(); i++) {
+ result[i + 1] = prepareCommand(parameters.get(i));
}
return result;
}
- public ParametersList getParametersList() {
- return myProgramParams;
- }
-
- public static String quoteParameter(final String param) {
- if (!SystemInfo.isWindows) {
- return param;
- }
- return quote(param);
- }
-
- public static String quote(final String parameter) {
- if (parameter == null || !hasWhitespace(parameter)) {
- return parameter; // no need to quote
- }
- if (parameter.length() >= 2 && parameter.startsWith("\"") && parameter.endsWith("\"")) {
- return parameter; // already quoted
- }
- // need to escape trailing slash if any, otherwise it will escape the ending quote
- return "\"" + parameter + (parameter.endsWith("\\")? "\\\"" : "\"");
- }
-
- private static boolean hasWhitespace(final String string) {
- final int length = string.length();
- for (int i = 0; i < length; i++) {
- if (Character.isWhitespace(string.charAt(i))) {
- return true;
+ private static String prepareCommand(String parameter) {
+ // AFAIK, the only thing needed is escaping double quotes on Windows
+ if (SystemInfo.isWindows) {
+ if (parameter.contains("\"")) {
+ parameter = StringUtil.replace(parameter, "\"", "\\\"");
}
}
- return false;
+ return parameter;
}
- public GeneralCommandLine clone() {
- final GeneralCommandLine clone = new GeneralCommandLine();
- clone.myCharset = myCharset;
- clone.myExePath = myExePath;
- clone.myWorkDirectory = myWorkDirectory;
- clone.myProgramParams = myProgramParams.clone();
- clone.myEnvParams = myEnvParams != null ? new HashMap(myEnvParams) : null;
- return clone;
- }
-
- public void setPassParentEnvs(final boolean passParentEnvs) {
- myPassParentEnvs = passParentEnvs;
+ private void setupEnvironment(final Map environment) {
+ if (!myPassParentEnvironment) {
+ environment.clear();
+ }
+ if (myEnvParams != null) {
+ environment.putAll(myEnvParams);
+ }
}
@Override
diff --git a/platform/platform-api/src/com/intellij/execution/configurations/ParametersList.java b/platform/platform-api/src/com/intellij/execution/configurations/ParametersList.java
index 088c0af8ef7c..6735bbbc86a4 100644
--- a/platform/platform-api/src/com/intellij/execution/configurations/ParametersList.java
+++ b/platform/platform-api/src/com/intellij/execution/configurations/ParametersList.java
@@ -22,6 +22,8 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.StringBuilderSpinAllocator;
+import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -29,8 +31,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
-public class ParametersList implements Cloneable{
+public class ParametersList implements Cloneable {
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.configurations.ParametersList");
+
private List myParameters = new ArrayList();
private Map myMacroMap = null;
private List myGroups = new ArrayList();
@@ -47,9 +50,9 @@ public class ParametersList implements Cloneable{
}
@Nullable
- public String getPropertyValue(@NonNls final String name) {
+ public String getPropertyValue(@NotNull @NonNls final String name) {
+ final String prefix = "-D" + name + "=";
for (String parameter : myParameters) {
- @NonNls String prefix = "-D" + name + "=";
if (parameter.startsWith(prefix)) {
return parameter.substring(prefix.length());
}
@@ -57,29 +60,39 @@ public class ParametersList implements Cloneable{
return null;
}
+ @NotNull
public String getParametersString() {
- final StringBuilder buffer = new StringBuilder();
- final String separator = " ";
- for (final String param : myParameters) {
- buffer.append(separator);
- buffer.append(GeneralCommandLine.quote(param));
- }
- for (ParamsGroup paramsGroup : myGroups) {
- // params group parameters string already contains a separator
- buffer.append(paramsGroup.getParametersList().getParametersString());
- }
- return buffer.toString();
+ return join(getList());
}
+ @NotNull
public String[] getArray() {
return ArrayUtil.toStringArray(getList());
}
+ @NotNull
+ public List getList() {
+ if (myGroups.isEmpty()) {
+ return Collections.unmodifiableList(myParameters);
+ }
+
+ final List params = new ArrayList();
+ params.addAll(myParameters);
+ for (ParamsGroup group : myGroups) {
+ params.addAll(group.getParameters());
+ }
+ return Collections.unmodifiableList(params);
+ }
+
+ public void prepend(@NonNls final String parameter) {
+ addAt(0, parameter);
+ }
+
public void addParametersString(final String parameters) {
if (parameters != null) {
- final String[] parms = parse(parameters);
- for (String parm : parms) {
- add(parm);
+ final String[] split = parse(parameters);
+ for (String param : split) {
+ add(param);
}
}
}
@@ -97,14 +110,12 @@ public class ParametersList implements Cloneable{
return group;
}
- public ParamsGroup addParamsGroupAt(final int index,
- @NotNull final ParamsGroup group) {
+ public ParamsGroup addParamsGroupAt(final int index, @NotNull final ParamsGroup group) {
myGroups.add(index, group);
return group;
}
- public ParamsGroup addParamsGroupAt(final int index,
- @NotNull final String groupId) {
+ public ParamsGroup addParamsGroupAt(final int index, @NotNull final String groupId) {
final ParamsGroup group = new ParamsGroup(groupId);
myGroups.add(index, group);
return group;
@@ -173,33 +184,12 @@ public class ParametersList implements Cloneable{
replaceOrAdd(parameter, replacement, 0);
}
- public List getList() {
- if (myGroups.isEmpty()) {
- return Collections.unmodifiableList(myParameters);
- }
-
- final List params = new ArrayList();
-
- // params
- params.addAll(myParameters);
-
- // recursively add groups
- for (ParamsGroup group : myGroups) {
- params.addAll(group.getParameters());
- }
- return Collections.unmodifiableList(params);
- }
-
- public void prepend(@NonNls final String parameter) {
- addAt(0, parameter);
- }
-
- public void add(@NonNls final String name,@NonNls final String value) {
+ public void add(@NonNls final String name, @NonNls final String value) {
add(name);
add(value);
}
- public void addAll(final String[] parameters) {
+ public void addAll(final String... parameters) {
ContainerUtil.addAll(myParameters, parameters);
}
@@ -207,6 +197,7 @@ public class ParametersList implements Cloneable{
myParameters.addAll(parameters);
}
+ @Override
public ParametersList clone() {
try {
final ParametersList clone = (ParametersList)super.clone();
@@ -223,10 +214,66 @@ public class ParametersList implements Cloneable{
}
}
- public static String[] parse(final String string){
- return new ParametersTokenizer(string).execute();
+ /**
+ * Joins list of parameters into single string, which may be then parsed back into list by {@link #parse(String)}.
+ *
+ *
+ * Conversion rules:
+ *
+ * - double quotes are escaped by backslash (
\);
+ * - empty parameters parameters and parameters with spaces inside are surrounded with double quotes (
");
+ * - parameters are separated by single whitespace.
+ *
+ *
+ *
+ * Examples:
+ *
+ * ['a', 'b'] => 'a b'
+ * ['a="1 2"', 'b'] => '"a \"1 2\"" b'
+ *
+ *
+ * @param parameters a list of parameters to join.
+ * @return a string with parameters.
+ */
+ @NotNull
+ public static String join(@NotNull final List parameters) {
+ return ParametersTokenizer.encode(parameters);
}
+ @NotNull
+ public static String join(final String... parameters) {
+ return ParametersTokenizer.encode(Arrays.asList(parameters));
+ }
+
+ /**
+ * Converts single parameter string (as created by {@link #join(java.util.List)}) into list of parameters.
+ *
+ *
+ * Conversion rules:
+ *
+ * - starting/whitespaces are trimmed;
+ * - parameters are split by whitespaces, whitespaces itself are dropped
+ * - parameters inside double quotes (
"a b") are kept as single one;
+ * - double quotes are dropped, escaped double quotes (
\") are un-escaped.
+ *
+ *
+ *
+ * Examples:
+ *
+ * ' a b ' => ['a', 'b']
+ * 'a="1 2" b' => ['a=1 2', 'b']
+ * 'a " " b' => ['a', ' ', 'b']
+ * '"a \"1 2\"" b' => ['a="1 2"', 'b']
+ *
+ *
+ * @param string parameter string to split.
+ * @return array of parameters.
+ */
+ @NotNull
+ public static String[] parse(@NotNull final String string) {
+ final List params = ParametersTokenizer.decode(string);
+ return ArrayUtil.toStringArray(params);
+ }
public String expandMacros(String text) {
final Map macroMap = getMacroMap();
@@ -263,101 +310,88 @@ public class ParametersList implements Cloneable{
return myMacroMap;
}
- private static class ParametersTokenizer {
- private final String myParamsString;
- private final List myArray = new ArrayList();
- private final StringBuffer myBuffer = new StringBuffer(128);
- private boolean myTokenStarted = false;
- private boolean myUnquotedSlash = false;
- private boolean mySplittedQuotingStarted = false;
-
- public ParametersTokenizer(@NotNull final String parmsString) {
- myParamsString = parmsString;
- }
-
- public String[] execute() {
- boolean inQuotes = false;
-
- // \" sequence is turned to " inside ""
- boolean wasEscaped = false;
-
- for (int i = 0; i < myParamsString.length(); i++) {
- final char c = myParamsString.charAt(i);
-
- if (inQuotes) {
- LOG.assertTrue(!myUnquotedSlash);
- if (wasEscaped) {
- //if (c != '"') append('\\');
- append(c);
- wasEscaped = false;
- }
- else if (c == '"') {
- inQuotes = false;
- }
- else if (c == '\\') {
- myTokenStarted = true;
- append(c);
- wasEscaped = true;
- }
- else {
- append(c);
- }
- }
- else {
- inQuotes = processNotQuoted(c, myBuffer.length() == 0 || myBuffer.charAt(myBuffer.length() - 1) == ' ');
- }
- }
- tokenFinished();
- return ArrayUtil.toStringArray(myArray);
- }
-
- private boolean processNotQuoted(final char c, final boolean isPreviousSpace) {
- if (c == '"') {
- if (myUnquotedSlash) {
- append(c);
- myUnquotedSlash = false;
- return false;
- }
- else if (!isPreviousSpace || mySplittedQuotingStarted) {
- append(c);
- mySplittedQuotingStarted = !mySplittedQuotingStarted;
- return false;
- }
- myTokenStarted = true;
- return true;
- }
- else if (c == ' ') {
- tokenFinished();
- }
- else if (c == '\\') {
- myUnquotedSlash = true;
- append(c);
- return false;
- }
- else {
- append(c);
- }
- myUnquotedSlash = false;
- return false;
- }
-
- private void append(final char nextChar) {
- myBuffer.append(nextChar);
- myTokenStarted = true;
- }
-
- private void tokenFinished() {
- if (myTokenStarted) {
- final String token = myBuffer.length() == 0 ? "\"\"" : myBuffer.toString();
- myArray.add(token);
- }
- myBuffer.setLength(0);
- myTokenStarted = false;
- }
- }
-
@Override
public String toString() {
return myParameters.toString();
}
+
+ private static class ParametersTokenizer {
+ private ParametersTokenizer() { }
+
+ @NotNull
+ public static String encode(@NotNull final List parameters) {
+ final StringBuilder buffer = new StringBuilder();
+ for (final String parameter : parameters) {
+ if (buffer.length() > 0) {
+ buffer.append(' ');
+ }
+ buffer.append(encode(parameter));
+ }
+ return buffer.toString();
+ }
+
+ @NotNull
+ public static String encode(@NotNull String parameter) {
+ final StringBuilder builder = StringBuilderSpinAllocator.alloc();
+ try {
+ builder.append(parameter);
+ StringUtil.escapeQuotes(builder);
+ if (builder.length() == 0 || StringUtil.indexOf(builder, ' ') >= 0) {
+ StringUtil.quote(builder);
+ }
+ return builder.toString();
+ }
+ finally {
+ StringBuilderSpinAllocator.dispose(builder);
+ }
+ }
+
+ @NotNull
+ public static List decode(@NotNull String parameterString) {
+ parameterString = parameterString.trim();
+
+ final ArrayList params = CollectionFactory.arrayList();
+ final StringBuilder token = new StringBuilder(128);
+ boolean inQuotes = false;
+ boolean escapedQuote = false;
+ boolean nonEmpty = false;
+
+ for (int i = 0; i < parameterString.length(); i++) {
+ final char ch = parameterString.charAt(i);
+
+ if (ch == '\"') {
+ if (!escapedQuote) {
+ inQuotes = !inQuotes;
+ nonEmpty = true;
+ continue;
+ }
+ escapedQuote = false;
+ }
+ else if (Character.isWhitespace(ch)) {
+ if (!inQuotes) {
+ if (token.length() > 0 || nonEmpty) {
+ params.add(token.toString());
+ token.setLength(0);
+ nonEmpty = false;
+ }
+ continue;
+ }
+ }
+ else if (ch == '\\') {
+ if (i < parameterString.length() - 1 && parameterString.charAt(i + 1) == '"') {
+ escapedQuote = true;
+ continue;
+ }
+ }
+
+ token.append(ch);
+ }
+
+ if (token.length() > 0 || nonEmpty) {
+ params.add(token.toString());
+ }
+
+ return params;
+ }
+ }
}
diff --git a/platform/platform-api/src/com/intellij/execution/configurations/ParamsGroup.java b/platform/platform-api/src/com/intellij/execution/configurations/ParamsGroup.java
index b7ca25a54cb2..dd25946497d7 100644
--- a/platform/platform-api/src/com/intellij/execution/configurations/ParamsGroup.java
+++ b/platform/platform-api/src/com/intellij/execution/configurations/ParamsGroup.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -31,7 +31,7 @@ import java.util.List;
* different kinds of ruby tests, rails configuration, etc). Coverage support require to reorder args
* in cmdline, add rcov runner script, etc. Without groups it would be harder to parse abstract list of arguments
*/
-public class ParamsGroup implements Cloneable{
+public class ParamsGroup implements Cloneable {
private static final Logger LOG = Logger.getInstance(ParamsGroup.class.getName());
private String myGroupId;
@@ -73,6 +73,7 @@ public class ParamsGroup implements Cloneable{
return myGroupParams;
}
+ @Override
public ParamsGroup clone() {
try {
final ParamsGroup clone = (ParamsGroup)super.clone();
diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java
index 9a4b0b55bbb1..ad2735f1cb89 100644
--- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java
+++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/BaseExternalTool.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -59,14 +59,14 @@ abstract class BaseExternalTool implements DiffTool {
}
public void show(DiffRequest request) {
- //ArrayList commandLine = new ArrayList();
GeneralCommandLine commandLine = new GeneralCommandLine();
commandLine.setExePath(getToolPath());
try {
commandLine.addParameter(convertToPath(request, 0));
commandLine.addParameter(convertToPath(request, 1));
- Runtime.getRuntime().exec(commandLine.getCommands());
- } catch (IOException e) {
+ commandLine.createProcess();
+ }
+ catch (Exception e) {
ExecutionErrorDialog.show(new ExecutionException(e.getMessage()),
DiffBundle.message("cant.launch.diff.tool.error.message"), request.getProject());
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java
index 93709d2b8c96..d2c610ffb9e5 100644
--- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/external/DiffManagerImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -15,7 +15,6 @@
*/
package com.intellij.openapi.diff.impl.external;
-import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.DiffManager;
import com.intellij.openapi.diff.DiffPanel;
@@ -42,13 +41,15 @@ import java.util.Arrays;
public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.external.DiffManagerImpl");
+
private static final Externalizer TOOL_PATH_UPDATE = new Externalizer() {
@NonNls private static final String NEW_VALUE = "newValue";
+
public String readValue(Element dataElement) {
String path = dataElement.getAttributeValue(NEW_VALUE);
if (path != null) return path;
String prevValue = dataElement.getAttributeValue(VALUE_ATTRIBUTE);
- return prevValue != null ? GeneralCommandLine.quote(prevValue.trim()) : null;
+ return prevValue != null ? prevValue.trim() : null;
}
public void writeValue(Element dataElement, String path) {
@@ -56,6 +57,7 @@ public class DiffManagerImpl extends DiffManager implements JDOMExternalizable {
dataElement.setAttribute(NEW_VALUE, path);
}
};
+
static final StringProperty FOLDERS_TOOL = new StringProperty("foldersTool", "");
static final StringProperty FILES_TOOL = new StringProperty("filesTool", "");
static final BooleanProperty ENABLE_FOLDERS = new BooleanProperty(
diff --git a/platform/platform-impl/testSrc/com/intellij/execution/EnvPassingTest.java b/platform/platform-impl/testSrc/com/intellij/execution/EnvPassingTest.java
new file mode 100644
index 000000000000..b53e701c92c5
--- /dev/null
+++ b/platform/platform-impl/testSrc/com/intellij/execution/EnvPassingTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.execution;
+
+import java.util.Map;
+
+/**
+ * Invoked from {@link GeneralCommandLineTest} as external process.
+ */
+@SuppressWarnings("UtilityClassWithoutPrivateConstructor")
+public class EnvPassingTest {
+ public static void main(String[] args) {
+ final Map environment = System.getenv();
+ System.out.println("=====");
+ for (Map.Entry entry : environment.entrySet()) {
+ System.out.println(formatEntry(entry));
+ }
+ System.out.println("=====");
+ }
+
+ public static String formatEntry(final Map.Entry entry) {
+ return entry.getKey() + "=" + entry.getValue().hashCode();
+ }
+}
diff --git a/platform/platform-impl/testSrc/com/intellij/execution/GeneralCommandLineTest.java b/platform/platform-impl/testSrc/com/intellij/execution/GeneralCommandLineTest.java
new file mode 100644
index 000000000000..8b4e8b40275a
--- /dev/null
+++ b/platform/platform-impl/testSrc/com/intellij/execution/GeneralCommandLineTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.execution;
+
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.system.ExecUtil;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.testFramework.UsefulTestCase;
+
+import java.io.File;
+import java.net.URL;
+import java.util.*;
+
+public class GeneralCommandLineTest extends UsefulTestCase {
+ public void testPrintCommandLine() {
+ final GeneralCommandLine commandLine = new GeneralCommandLine();
+ commandLine.setExePath("e x e path");
+ commandLine.addParameter("with space");
+ commandLine.addParameter("\"quoted\"");
+ commandLine.addParameter("\"quoted with spaces\"");
+ commandLine.addParameters("param 1", "param2");
+ commandLine.addParameter("trailing slash\\");
+ assertEquals("\"e x e path\"" +
+ " \"with space\"" +
+ " \\\"quoted\\\"" +
+ " \"\\\"quoted with spaces\\\"\"" +
+ " \"param 1\"" +
+ " param2" +
+ " \"trailing slash\\\"",
+ commandLine.getCommandLineString());
+ }
+
+ public void testExecuteCommandLine() throws Exception {
+ final File tempFile;
+ if (SystemInfo.isWindows) {
+ final URL url = getClass().getClassLoader().getResource("com/intellij/execution/printArgs.exe");
+ assertNotNull(url);
+ tempFile = FileUtil.createTempFile("path with spaces 'and quotes' ", ".exe");
+ FileUtil.copy(new File(url.getFile()), tempFile);
+ }
+ else {
+ tempFile = ExecUtil.createTempExecutableScript(
+ "path with spaces \"and quotes\" ", ".sh",
+ "#!/bin/sh\n\n" +
+ "echo \"=====\"\n" +
+ "for f in \"$@\" ; do echo $f; done\n" +
+ "echo \"=====\"\n"
+ );
+ }
+
+ final String[] parameters = { "with space", "\"quoted\"", "\"quoted with spaces\"", "", "param 1", "\"", "param2", "trailing slash\\" };
+ try {
+ final GeneralCommandLine commandLine = new GeneralCommandLine();
+ commandLine.setExePath(tempFile.getCanonicalPath());
+ commandLine.addParameters(parameters);
+ commandLine.setRedirectErrorStream(true);
+ final Process process = commandLine.createProcess();
+ final String output = FileUtil.loadTextAndClose(process.getInputStream());
+ final int result = process.waitFor();
+
+ assertEquals("Command:\n" + commandLine.getCommandLineString() + "\nOutput:\n" + output,
+ 0, result);
+ assertEquals("=====\n" + StringUtil.join(parameters, "\n") + "\n=====\n",
+ StringUtil.convertLineSeparators(output));
+ }
+ finally {
+ FileUtil.delete(tempFile);
+ }
+ }
+
+ public void testEnvironmentPassing() throws Exception {
+ final URL url = getClass().getClassLoader().getResource("com/intellij/execution/EnvPassingTest.class");
+ assertNotNull(url);
+ final File testClass = new File(url.getFile());
+
+ final GeneralCommandLine commandLine = new GeneralCommandLine();
+ commandLine.setExePath("java");
+ commandLine.addParameter("-cp");
+ commandLine.addParameter(testClass.getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath());
+ commandLine.addParameter("com.intellij.execution." + testClass.getName().replace(".class", ""));
+ commandLine.setRedirectErrorStream(true);
+
+ final Map testEnv = new HashMap();
+ testEnv.put("VALUE_1", "some value");
+ testEnv.put("VALUE_2", "another\n\"value\"");
+
+ checkEnvPassing(commandLine, testEnv, true);
+ checkEnvPassing(commandLine, testEnv, false);
+ }
+
+ private static void checkEnvPassing(final GeneralCommandLine commandLine,
+ final Map testEnv,
+ final boolean passParentEnv) throws Exception {
+ commandLine.setEnvParams(testEnv);
+ commandLine.setPassParentEnvs(passParentEnv);
+
+ final Process process = commandLine.createProcess();
+ final String output = FileUtil.loadTextAndClose(process.getInputStream());
+ final int result = process.waitFor();
+ assertEquals("Command:\n" + commandLine.getCommandLineString() + "\nOutput:\n" + output,
+ 0, result);
+
+ final Set lines = new HashSet(Arrays.asList(StringUtil.convertLineSeparators(output).split("\n")));
+
+ for (Map.Entry entry : testEnv.entrySet()) {
+ final String str = EnvPassingTest.formatEntry(entry);
+ assertTrue("\"" + str + "\" should be in " + lines,
+ lines.contains(str));
+ }
+
+ final Map parentEnv = System.getenv();
+ final List missed = new ArrayList();
+ for (Map.Entry entry : parentEnv.entrySet()) {
+ final String str = EnvPassingTest.formatEntry(entry);
+ if (passParentEnv) {
+ assertTrue("\"" + str + "\" should be in " + lines,
+ lines.contains(str));
+ }
+ else if (lines.contains(str)) {
+ missed.add(str);
+ }
+ }
+ if (!passParentEnv && missed.size() > 0 && parentEnv.size()/missed.size() < 2) {
+ fail(missed + " shouldn't be in " + lines + " (ratio: " + parentEnv.size() + '/' + missed.size() + ')');
+ }
+ }
+}
diff --git a/platform/platform-impl/testSrc/com/intellij/execution/printArgs.c b/platform/platform-impl/testSrc/com/intellij/execution/printArgs.c
new file mode 100644
index 000000000000..54ac7debd176
--- /dev/null
+++ b/platform/platform-impl/testSrc/com/intellij/execution/printArgs.c
@@ -0,0 +1,14 @@
+#include
+
+/**
+ * Invoked from {@link GeneralCommandLineTest} as external process.
+ */
+int main(int argc, char *argv[]) {
+ int i;
+ printf("=====\n");
+ for (i = 1; i < argc; ++i) {
+ printf("%s\n", argv[i]);
+ }
+ printf("=====\n");
+ return 0;
+}
diff --git a/platform/platform-impl/testSrc/com/intellij/execution/printArgs.exe b/platform/platform-impl/testSrc/com/intellij/execution/printArgs.exe
new file mode 100644
index 0000000000000000000000000000000000000000..13f43e917733026f36e7dbc565aa70991aa3edd8
GIT binary patch
literal 44544
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
L3$$9GnFam@>7+qV
literal 0
HcmV?d00001
diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/execution/ParametersListTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/execution/ParametersListTest.java
index 837e6f6fc962..3c7cfd6d55ec 100644
--- a/platform/platform-impl/testSrc/com/intellij/openapi/execution/ParametersListTest.java
+++ b/platform/platform-impl/testSrc/com/intellij/openapi/execution/ParametersListTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -18,34 +18,14 @@ package com.intellij.openapi.execution;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.configurations.ParamsGroup;
import com.intellij.testFramework.UsefulTestCase;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.Assertion;
-import org.jetbrains.annotations.NonNls;
+import java.util.Arrays;
import java.util.Collections;
/**
* @author dyoma
*/
public class ParametersListTest extends UsefulTestCase {
- private final Assertion CHECK = new Assertion();
-
- public void testAddParametersString() {
- checkTokenizer("a b c", new String[]{"a", "b", "c"});
- checkTokenizer("a \"b\"", new String[]{"a", "b"});
- checkTokenizer("a \"b\\\"", new String[]{"a", "b\\\""});
- checkTokenizer("a \"\"", new String[]{"a", "\"\""}); // Bug #12169
- checkTokenizer("a \"x\"", new String[]{"a", "x"});
- checkTokenizer("a \"\" b", new String[]{"a", "\"\"", "b"});
- }
-
- private void checkTokenizer(@NonNls String parmsString, @NonNls String[] expected) {
- ParametersList params = new ParametersList();
- params.addParametersString(parmsString);
- String[] strings = ArrayUtil.toStringArray(params.getList());
- CHECK.compareAll(expected, strings);
- }
-
public void testParamsGroup_Empty() {
ParametersList params = new ParametersList();
@@ -82,7 +62,7 @@ public class ParametersListTest extends UsefulTestCase {
public void testParamsGroup_Remove() {
ParametersList params = new ParametersList();
- final ParamsGroup group1 = params.addParamsGroup("id1");
+ params.addParamsGroup("id1");
final ParamsGroup group2 = params.addParamsGroup("id2");
final ParamsGroup group3 = params.addParamsGroup("id3");
final ParamsGroup group4 = params.addParamsGroup("id4");
@@ -169,20 +149,61 @@ public class ParametersListTest extends UsefulTestCase {
assertEquals("group1_param1 group2_param1 group3_param1", params_clone.getParametersString().trim());
}
- public void testParamsWithSpaces() {
- checkTokenizer("a b=\"some text\" c", new String[]{"a", "b=\"some", "text\"", "c"});
- checkTokenizer("a b=\"some text with spaces\" c", new String[]{"a", "b=\"some", "text", "with", "spaces\"", "c"});
- checkTokenizer("a b=\"some text with spaces\"more c", new String[]{"a", "b=\"some", "text", "with", "spaces\"more", "c"});
- checkTokenizer("a b=\"some text with spaces \"more c", new String[]{"a", "b=\"some", "text", "with", "spaces", "\"more", "c"});
+ public void testAddParametersString() {
+ checkTokenizer("a b c",
+ "a", "b", "c");
+ checkTokenizer("a \"b\"",
+ "a", "b");
+ checkTokenizer("a \"b\\\"",
+ "a", "b\"");
+ checkTokenizer("a \"\"",
+ "a", ""); // Bug #12169
+ checkTokenizer("a \"x\"",
+ "a", "x");
+ checkTokenizer("a \"\\\"\" b",
+ "a", "\"", "b");
+ }
- //this test just fixes the way it works;
- // i don't see any use cases when it definitely should or shouldn't be this way
- checkTokenizer("a \"some text with spaces\"More c", new String[]{"a", "some text with spacesMore", "c"});
+ public void testParamsWithSpacesAndQuotes() {
+ checkTokenizer("a b=\"some text\" c",
+ "a", "b=some text", "c");
+ checkTokenizer("a b=\"some text with spaces\" c",
+ "a", "b=some text with spaces", "c");
+ checkTokenizer("a b=\"some text with spaces\".more c",
+ "a", "b=some text with spaces.more", "c");
+ checkTokenizer("a b=\"some text with spaces \"more c",
+ "a", "b=some text with spaces more", "c");
+ checkTokenizer("a \"some text with spaces\"More c",
+ "a", "some text with spacesMore", "c");
+ checkTokenizer("a \"some text with spaces more c",
+ "a", "some text with spaces more c");
+ checkTokenizer("a\"Some text with spaces \"more c",
+ "aSome text with spaces more", "c");
+ checkTokenizer("a\"Some text with spaces \"more",
+ "aSome text with spaces more");
+ checkTokenizer("a\"Some text with spaces \"more next\"Text moreText\"End c",
+ "aSome text with spaces more", "nextText moreTextEnd", "c");
+ checkTokenizer("\"\"C:\\phing.bat\"",
+ "C:\\phing.bat");
+ checkTokenizer("-Dprop.1=\"some text\" -Dprop.2=\\\"value\\\"",
+ "-Dprop.1=some text", "-Dprop.2=\"value\"");
+ }
- checkTokenizer("a \"some text with spaces \"more c", new String[]{"a", "some text with spaces more", "c"});
- checkTokenizer("a\"some text with spaces \"more c", new String[]{"a\"some", "text", "with", "spaces", "\"more", "c"});
- checkTokenizer("a\"some text with spaces \"more", new String[]{"a\"some", "text", "with", "spaces", "\"more"});
- checkTokenizer("a\"some text with spaces \"more next\"text moreText\"end c", new String[]{"a\"some", "text", "with", "spaces", "\"more",
- "next\"text", "moreText\"end", "c"});
+ public void testJoiningParams() throws Exception {
+ final String[] parameters = {"simpleParam", "param with spaces", "withQuote=\"", "param=\"complex quoted\""};
+ final ParametersList parametersList = new ParametersList();
+
+ parametersList.addAll(parameters);
+ final String joined = parametersList.getParametersString();
+ assertEquals("simpleParam \"param with spaces\" withQuote=\\\" \"param=\\\"complex quoted\\\"\"",
+ joined);
+
+ checkTokenizer(joined, parameters);
+ }
+
+ private static void checkTokenizer(final String paramString, final String... expected) {
+ final ParametersList params = new ParametersList();
+ params.addParametersString(paramString);
+ assertEquals(Arrays.asList(expected), params.getList());
}
}
diff --git a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidMavenExecutor.java b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidMavenExecutor.java
index a29c4f78abab..ce7e7b8a5c17 100644
--- a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidMavenExecutor.java
+++ b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidMavenExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -22,7 +22,6 @@ import com.intellij.execution.configurations.JavaParameters;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.idea.maven.execution.MavenExternalParameters;
@@ -66,10 +65,6 @@ public class AndroidMavenExecutor {
MavenRunner.getInstance(module.getProject()).getSettings());
GeneralCommandLine commandLine = CommandLineBuilder.createFromJavaParameters(javaParams);
-
- String[] commands = commandLine.getCommands();
- String command = StringUtil.join(commands, " ");
- LOG.info("Execute: " + command);
StringBuilder messageBuilder = new StringBuilder();
boolean success = AndroidUtils.executeCommand(commandLine, messageBuilder);
String message = messageBuilder.toString();
diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java
index 3060fce3a40c..04fb8bed9771 100644
--- a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java
+++ b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -49,7 +49,6 @@ import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
@@ -468,10 +467,6 @@ public class AndroidUtils {
GeneralCommandLine commandLine,
boolean printOutputToAndroidConsole,
ProcessHandler processHandler) {
- String[] commands = commandLine.getCommands();
- String command = StringUtil.join(commands, " ");
- LOG.info("Execute: " + command);
-
StringBuilder messageBuilder = new StringBuilder();
String result;
boolean success = false;
diff --git a/plugins/git4idea/src/git4idea/commands/GitHandler.java b/plugins/git4idea/src/git4idea/commands/GitHandler.java
index 0f4ca8692e42..af0492298061 100644
--- a/plugins/git4idea/src/git4idea/commands/GitHandler.java
+++ b/plugins/git4idea/src/git4idea/commands/GitHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -121,7 +121,7 @@ public abstract class GitHandler {
if (myAppSettings != null) {
myCommandLine.setExePath(myAppSettings.getPathToGit());
}
- myCommandLine.setWorkingDirectory(myWorkingDirectory);
+ myCommandLine.setWorkDirectory(myWorkingDirectory);
if (command.name().length() > 0) {
myCommandLine.addParameter(command.name());
}
@@ -428,9 +428,7 @@ public abstract class GitHandler {
* @return a command line with full path to executable replace to "git"
*/
public String printableCommandLine() {
- final GeneralCommandLine line = myCommandLine.clone();
- line.setExePath("git");
- return line.getCommandLineString();
+ return myCommandLine.getCommandLineString("git");
}
/**
diff --git a/plugins/junit/src/com/intellij/execution/junit/TestObject.java b/plugins/junit/src/com/intellij/execution/junit/TestObject.java
index c92f33977957..b0c1d939c99a 100644
--- a/plugins/junit/src/com/intellij/execution/junit/TestObject.java
+++ b/plugins/junit/src/com/intellij/execution/junit/TestObject.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -32,14 +32,10 @@ import com.intellij.execution.junit2.ui.model.RootTestInfo;
import com.intellij.execution.junit2.ui.properties.JUnitConsoleProperties;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
-import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.testframework.*;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.util.JavaParametersUtil;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationType;
-import com.intellij.notification.Notifications;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -48,6 +44,7 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JavaSdkType;
+import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
@@ -68,8 +65,13 @@ import com.intellij.util.IJSwingUtilities;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
-import java.io.*;
-import java.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
public abstract class TestObject implements JavaCommandLine {
protected static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit.TestObject");
@@ -147,10 +149,12 @@ public abstract class TestObject implements JavaCommandLine {
return null;
}
+ @Override
public String suggestActionName() {
throw new RuntimeException(String.valueOf(myConfiguration));
}
+ @Override
public boolean isConfiguredByElement(final JUnitConfiguration configuration,
PsiClass testClass,
PsiMethod testMethod,
@@ -158,31 +162,22 @@ public abstract class TestObject implements JavaCommandLine {
return false;
}
+ @Override
public void checkConfiguration() throws RuntimeConfigurationException {
throw new RuntimeConfigurationError(MESSAGE);
}
- public ExecutionResult execute() throws ExecutionException {
- throw createExecutionException();
- }
-
+ @Override
public JavaParameters getJavaParameters() throws ExecutionException {
- throw createExecutionException();
+ throw new ExecutionException(MESSAGE);
}
+ @Override
protected void initialize() throws ExecutionException {
- throw createExecutionException();
- }
-
- protected ProcessHandler startProcess() throws ExecutionException {
- throw createExecutionException();
+ throw new ExecutionException(MESSAGE);
}
};
- private static ExecutionException createExecutionException() {
- return new ExecutionException(MESSAGE);
- }
-
public void checkConfiguration() throws RuntimeConfigurationException{
if (myConfiguration.isAlternativeJrePathEnabled()){
if (myConfiguration.getAlternativeJrePath() == null ||
@@ -216,7 +211,7 @@ public abstract class TestObject implements JavaCommandLine {
}
final Object[] listeners = Extensions.getExtensions(IDEAJUnitListener.EP_NAME);
- final StringBuffer buf = new StringBuffer();
+ final StringBuilder buf = new StringBuilder();
for (final Object listener : listeners) {
boolean enabled = true;
for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
@@ -233,7 +228,7 @@ public abstract class TestObject implements JavaCommandLine {
}
if (buf.length() > 0) {
try {
- myListenersFile = FileUtil.createTempFile("junitlisteners", "");
+ myListenersFile = FileUtil.createTempFile("junit_listeners_", "");
myListenersFile.deleteOnExit();
myJavaParameters.getProgramParametersList().add("@@" + myListenersFile.getPath());
FileUtil.writeToFile(myListenersFile, buf.toString().getBytes());
@@ -360,40 +355,42 @@ public abstract class TestObject implements JavaCommandLine {
private void appendForkInfo() throws ExecutionException {
final String forkMode = myConfiguration.getForkMode();
- if (Comparing.strEqual(forkMode, "none")) return;
+ if (Comparing.strEqual(forkMode, "none")) {
+ return;
+ }
+
if (myRunnerSettings.getData() instanceof DebuggingRunnerData) {
- throw new CantRunException("Debug is disabled in fork mode.
Change fork mode to <none> to debug");
- }
- File tempFile = null;
- try {
- tempFile = FileUtil.createTempFile("command.line", "");
- myJavaParameters.getProgramParametersList().add("@@@" + tempFile.getAbsolutePath());
- }
- catch (IOException e) {
- LOG.error(e);
+ throw new CantRunException("Debug is disabled in fork mode.
Please change fork mode to <none> to debug.");
}
+
final JavaParameters javaParameters = getJavaParameters();
+ final Sdk jdk = javaParameters.getJdk();
+ if (jdk == null) {
+ throw new ExecutionException(ExecutionBundle.message("run.configuration.error.no.jdk.specified"));
+ }
+
try {
+ final File tempFile = FileUtil.createTempFile("command.line", "", true);
final PrintWriter writer = new PrintWriter(tempFile, "UTF-8");
try {
- writer.print(GeneralCommandLine
- .quoteParameter(((JavaSdkType)javaParameters.getJdk().getSdkType()).getVMExecutablePath(javaParameters.getJdk())) + " ");
- writer.print(javaParameters.getVMParametersList().getParametersString() + " ");
- writer.print("-classpath ");
- writer.print(GeneralCommandLine.quoteParameter(javaParameters.getClassPath().getPathsString()));
- writer.print("\n" + forkMode);
+ writer.println(((JavaSdkType)jdk.getSdkType()).getVMExecutablePath(jdk));
+ for (String vmParameter : javaParameters.getVMParametersList().getList()) {
+ writer.println(vmParameter);
+ }
+ writer.println("-classpath");
+ writer.println(javaParameters.getClassPath().getPathsString());
}
finally {
writer.close();
}
- tempFile.deleteOnExit();
+
+ myJavaParameters.getProgramParametersList().add("@@@" + forkMode + ',' + tempFile.getAbsolutePath());
}
catch (Exception e) {
LOG.error(e);
}
}
-
protected void addClassesListToJavaParameters(Collection extends T> elements, Function nameFunction, String packageName,
boolean createTempFile,
boolean junit4) {
diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java
index 66a181a95336..49e94d110e0d 100644
--- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java
+++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/IdeaTestRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -22,9 +22,7 @@ package com.intellij.rt.execution.junit;
import com.intellij.rt.execution.junit.segments.OutputObjectRegistry;
import com.intellij.rt.execution.junit.segments.SegmentedOutputStream;
-import org.junit.runner.Description;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java
index 80808d8a0f52..9c4d6890a241 100644
--- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java
+++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitForkedStarter.java
@@ -19,16 +19,14 @@ import com.intellij.rt.execution.junit.segments.SegmentedOutputStream;
import java.io.*;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
/**
-* User: anna
-* Date: 4/6/11
-*/
+ * @author anna
+ * @since 6.04.2011
+ */
public class JUnitForkedStarter {
- private JUnitForkedStarter() {
- }
+ private JUnitForkedStarter() { }
public static void main(String[] args) throws Exception {
final String testOutputPath = args[0];
@@ -45,7 +43,9 @@ public class JUnitForkedStarter {
if (!file.createNewFile()) return;
}
final FileOutputStream stream = new FileOutputStream(testOutputPath);
+ //noinspection UseOfSystemOutOrSystemErr
PrintStream oldOut = System.out;
+ //noinspection UseOfSystemOutOrSystemErr
PrintStream oldErr = System.err;
try {
final PrintStream out = new PrintStream(new ForkedVMWrapper(stream, false));
@@ -53,9 +53,11 @@ public class JUnitForkedStarter {
System.setOut(out);
System.setErr(err);
IdeaTestRunner testRunner = (IdeaTestRunner)JUnitStarter.getAgentClass(isJUnit4).newInstance();
+ //noinspection IOResourceOpenedButNotSafelyClosed
testRunner.setStreams(new SegmentedOutputStream(out, true), new SegmentedOutputStream(err, true), lastIdx);
System.exit(testRunner.startRunnerWithArgs(childTestDescription, listeners, false));
- } finally {
+ }
+ finally {
System.setOut(oldOut);
System.setErr(oldErr);
stream.close();
@@ -64,69 +66,85 @@ public class JUnitForkedStarter {
static int startForkedVMs(String[] args,
boolean isJUnit4,
- ArrayList listeners,
+ List listeners,
SegmentedOutputStream out,
- SegmentedOutputStream err, String path) throws Exception {
- final BufferedReader reader = new BufferedReader(new FileReader(path));
- final String commandline = reader.readLine();
- final String forkMode = reader.readLine();
- reader.close();
+ SegmentedOutputStream err,
+ String forkMode,
+ String path) throws Exception {
+ final List parameters = new ArrayList();
+ final BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
+ try {
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ parameters.add(line);
+ }
+ }
+ finally {
+ bufferedReader.close();
+ }
+
IdeaTestRunner testRunner = (IdeaTestRunner)JUnitStarter.getAgentClass(isJUnit4).newInstance();
testRunner.setStreams(out, err, 0);
final Object description = testRunner.getTestToStart(args);
TreeSender.sendTree(testRunner, description);
- long startTime = System.currentTimeMillis();
+ long time = System.currentTimeMillis();
final List children = testRunner.getChildTests(description);
final boolean forkTillMethod = forkMode.equalsIgnoreCase("method");
- int result = processChildren(isJUnit4, listeners, out, err, commandline, testRunner, children, 0, forkTillMethod);
+ int result = processChildren(isJUnit4, listeners, out, err, parameters, testRunner, children, 0, forkTillMethod);
- long endTime = System.currentTimeMillis();
- long runTime = endTime - startTime;
- new TimeSender(testRunner.getRegistry()).printHeader(runTime);
+ time = System.currentTimeMillis() - time;
+ new TimeSender(testRunner.getRegistry()).printHeader(time);
return result;
}
private static int processChildren(boolean isJUnit4,
- ArrayList listeners,
+ List listeners,
SegmentedOutputStream out,
SegmentedOutputStream err,
- String commandline, IdeaTestRunner testRunner, List children, int result, boolean forkTillMethod)
- throws IOException, InterruptedException {
+ List parameters,
+ IdeaTestRunner testRunner,
+ List children,
+ int result,
+ boolean forkTillMethod) throws IOException, InterruptedException {
for (int i = 0, argsLength = children.size(); i < argsLength; i++) {
final Object child = children.get(i);
final List childTests = testRunner.getChildTests(child);
- if (childTests.isEmpty() || !forkTillMethod) {
- result = Math.min(runChild(child, isJUnit4, listeners, out, err, commandline, testRunner, forkTillMethod), result);
- } else {
- result = Math.min(processChildren(isJUnit4, listeners, out, err, commandline, testRunner, childTests, result, forkTillMethod), result);
- }
+ final int childResult = childTests.isEmpty() || !forkTillMethod
+ ? runChild(child, isJUnit4, listeners, out, err, parameters, testRunner, forkTillMethod)
+ : processChildren(isJUnit4, listeners, out, err, parameters, testRunner, childTests, result, forkTillMethod);
+ result = Math.min(childResult, result);
}
return result;
}
private static int runChild(Object child,
boolean isJUnit4,
- ArrayList listeners,
+ List listeners,
SegmentedOutputStream out,
SegmentedOutputStream err,
- String commandline, IdeaTestRunner testRunner,
- boolean forkTillMethod)
- throws IOException, InterruptedException {
+ List parameters,
+ IdeaTestRunner testRunner,
+ boolean forkTillMethod) throws IOException, InterruptedException {
+ //noinspection SSBasedInspection
final File tempFile = File.createTempFile("fork", "test");
final String testOutputPath = tempFile.getAbsolutePath();
final int knownObject = testRunner.getRegistry().getKnownObject(child);
- String command = commandline + " " + JUnitForkedStarter.class.getName()+ " " + testOutputPath + " " + (knownObject + (forkTillMethod ? 0 : 1)) + " " +
- isJUnit4 + " " + testRunner.getStartDescription(child) ;
- for (Iterator iterator = listeners.iterator(); iterator.hasNext(); ) {
- command += " " + iterator.next();
- }
- final Process exec = Runtime.getRuntime().exec(command);
- int result = exec.waitFor();
+
+ final ProcessBuilder builder = new ProcessBuilder();
+ builder.add(parameters);
+ builder.add(JUnitForkedStarter.class.getName());
+ builder.add(testOutputPath);
+ builder.add(String.valueOf(knownObject + (forkTillMethod ? 0 : 1)));
+ builder.add(String.valueOf(isJUnit4));
+ builder.add(testRunner.getStartDescription(child));
+ builder.add(listeners);
+
+ final Process exec = builder.createProcess();
+ final int result = exec.waitFor();
ForkedVMWrapper.readWrapped(testOutputPath, out.getPrintStream(), err.getPrintStream());
- // tempFile.deleteOnExit();
return result;
}
}
diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
index c3d281dae47a..f492409c9e18 100644
--- a/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
+++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * Copyright 2000-2011 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.
@@ -34,7 +34,8 @@ public class JUnitStarter {
public static final String IDE_VERSION = "-ideVersion";
public static final String JUNIT4_PARAMETER = "-junit4";
private static final String SOCKET = "-socket";
- private static String ourCommandLine;
+ private static String ourForkMode;
+ private static String ourCommandFileName;
public static void main(String[] args) throws IOException {
SegmentedOutputStream out = new SegmentedOutputStream(System.out);
@@ -77,7 +78,9 @@ public class JUnitStarter {
}
else {
if (arg.startsWith("@@@")) {
- ourCommandLine = arg.substring(3);
+ final int pos = arg.indexOf(',');
+ ourForkMode = arg.substring(3, pos);
+ ourCommandFileName = arg.substring(pos + 1);
continue;
} else if (arg.startsWith("@@")) {
if (new File(arg.substring(2)).exists()) {
@@ -191,8 +194,8 @@ public class JUnitStarter {
try {
System.setOut(new PrintStream(out));
System.setErr(new PrintStream(err));
- if (ourCommandLine != null) {
- return JUnitForkedStarter.startForkedVMs(args, isJUnit4, listeners, out, err, ourCommandLine);
+ if (ourCommandFileName != null) {
+ return JUnitForkedStarter.startForkedVMs(args, isJUnit4, listeners, out, err, ourForkMode, ourCommandFileName);
}
IdeaTestRunner testRunner = (IdeaTestRunner)getAgentClass(isJUnit4).newInstance();
testRunner.setStreams(out, err, 0);
diff --git a/plugins/junit_rt/src/com/intellij/rt/execution/junit/ProcessBuilder.java b/plugins/junit_rt/src/com/intellij/rt/execution/junit/ProcessBuilder.java
new file mode 100644
index 000000000000..5a0e66b97bb7
--- /dev/null
+++ b/plugins/junit_rt/src/com/intellij/rt/execution/junit/ProcessBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2000-2011 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 com.intellij.rt.execution.junit;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Clone of GeneralCommandLine.
+ */
+public class ProcessBuilder {
+ public static final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
+
+ private final List myParameters = new ArrayList();
+
+ public void add(final String parameter) {
+ myParameters.add(parameter);
+ }
+
+ public void add(final List parameters) {
+ for (int i = 0; i < parameters.size(); i++) {
+ add((String)parameters.get(i));
+ }
+ }
+
+ public Process createProcess() throws IOException {
+ if (myParameters.size() < 1) {
+ throw new IllegalArgumentException("Executable name not specified");
+ }
+
+ final String[] command = new String[myParameters.size()];
+ for (int i = 0; i < myParameters.size(); i++) {
+ command[i] = prepareCommand(myParameters.get(i).toString());
+ }
+
+ return Runtime.getRuntime().exec(command);
+ }
+
+ private static String prepareCommand(String parameter) {
+ // AFAIK, the only thing needed is escaping double quotes on Windows
+ if (isWindows) {
+ final StringBuffer buffer = new StringBuffer(parameter);
+ int pos = 0;
+ while ((pos = parameter.indexOf('\"', pos)) >= 0) {
+ buffer.insert(pos, '\\');
+ pos += 2;
+ }
+ parameter = buffer.toString();
+ }
+ return parameter;
+ }
+}