Consistent OS-independent command line params' quoting and escaping

This commit is contained in:
Roman Shevchenko
2011-08-26 18:21:49 +02:00
parent bea72c618a
commit 8178ec8cae
24 changed files with 1014 additions and 445 deletions
@@ -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();
@@ -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<String> packages = new HashSet<String>();
final Collection<String> sources = new HashSet<String>();
@@ -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);
+1
View File
@@ -25,6 +25,7 @@
<orderEntry type="module" module-name="testng" scope="TEST" />
<orderEntry type="module" module-name="ui-designer" scope="TEST" />
<orderEntry type="module" module-name="eclipse" scope="TEST" />
<orderEntry type="module" module-name="execution-openapi" />
</component>
</module>
@@ -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());
}
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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<String, String> myEnvParams;
private boolean myPassParentEnvs;
private String myExePath = null;
private File myWorkDirectory = null;
private ParametersList myProgramParams = new ParametersList();
private Map<String, String> 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<String, String> 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<String, String> getEnvParams() {
return myEnvParams;
}
public void setEnvParams(@Nullable final Map<String, String> 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<String> parameters) {
public void addParameters(@NotNull final List<String> 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.<br/>
* Warning: resulting string is not OS-dependent - <b>do not</b> 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.<br/>
* Warning: resulting string is not OS-dependent - <b>do not</b> 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<String> commands = new ArrayList<String>();
if (exeName != null) {
commands.add(exeName);
}
else if (myExePath != null) {
commands.add(FileUtil.toSystemDependentName(myExePath));
}
else {
commands.add("<null>");
}
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<String, String> 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<String, String> getEnvParams() {
return myEnvParams;
}
@Nullable
private String[] getEnvParamsArray() {
final Map<String, String> envParams = collectEnvParams();
if (envParams == null) return null;
for (Iterator<String> 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<String, String> collectEnvParams() {
if (myEnvParams == null) {
return null;
}
final Map<String, String> envParams = new HashMap<String, String>();
if (myPassParentEnvs) {
envParams.putAll(System.getenv());
}
envParams.putAll(myEnvParams);
return envParams;
}
public String[] getCommands() {
private String[] prepareCommands() {
final List<String> parameters = myProgramParams.getList();
final String[] result = new String[parameters.size() + 1];
result[0] = myExePath;
int index = 1;
for (Iterator<String> 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<String, String>(myEnvParams) : null;
return clone;
}
public void setPassParentEnvs(final boolean passParentEnvs) {
myPassParentEnvs = passParentEnvs;
private void setupEnvironment(final Map<String, String> environment) {
if (!myPassParentEnvironment) {
environment.clear();
}
if (myEnvParams != null) {
environment.putAll(myEnvParams);
}
}
@Override
@@ -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<String> myParameters = new ArrayList<String>();
private Map<String, String> myMacroMap = null;
private List<ParamsGroup> myGroups = new ArrayList<ParamsGroup>();
@@ -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<String> getList() {
if (myGroups.isEmpty()) {
return Collections.unmodifiableList(myParameters);
}
final List<String> params = new ArrayList<String>();
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<String> getList() {
if (myGroups.isEmpty()) {
return Collections.unmodifiableList(myParameters);
}
final List<String> params = new ArrayList<String>();
// 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();
/**
* <p>Joins list of parameters into single string, which may be then parsed back into list by {@link #parse(String)}.</p>
*
* <p>
* <strong>Conversion rules:</strong>
* <ul>
* <li>double quotes are escaped by backslash (<code>&#92;</code>);</li>
* <li>empty parameters parameters and parameters with spaces inside are surrounded with double quotes (<code>"</code>);</li>
* <li>parameters are separated by single whitespace.</li>
* </ul>
* </p>
*
* <p><strong>Examples:</strong></p>
* <p>
* <code>['a', 'b'] => 'a b'</code><br/>
* <code>['a="1 2"', 'b'] => '"a &#92;"1 2&#92;"" b'</code>
* </p>
*
* @param parameters a list of parameters to join.
* @return a string with parameters.
*/
@NotNull
public static String join(@NotNull final List<String> parameters) {
return ParametersTokenizer.encode(parameters);
}
@NotNull
public static String join(final String... parameters) {
return ParametersTokenizer.encode(Arrays.asList(parameters));
}
/**
* <p>Converts single parameter string (as created by {@link #join(java.util.List)}) into list of parameters.</p>
*
* <p>
* <strong>Conversion rules:</strong>
* <ul>
* <li>starting/whitespaces are trimmed;</li>
* <li>parameters are split by whitespaces, whitespaces itself are dropped</li>
* <li>parameters inside double quotes (<code>"a b"</code>) are kept as single one;</li>
* <li>double quotes are dropped, escaped double quotes (<code>&#92;"</code>) are un-escaped.</li>
* </ul>
* </p>
*
* <p><strong>Examples:</strong></p>
* <p>
* <code>' a b ' => ['a', 'b']</code><br/>
* <code>'a="1 2" b' => ['a=1 2', 'b']</code><br/>
* <code>'a " " b' => ['a', ' ', 'b']</code><br/>
* <code>'"a &#92;"1 2&#92;"" b' => ['a="1 2"', 'b']</code>
* </p>
*
* @param string parameter string to split.
* @return array of parameters.
*/
@NotNull
public static String[] parse(@NotNull final String string) {
final List<String> params = ParametersTokenizer.decode(string);
return ArrayUtil.toStringArray(params);
}
public String expandMacros(String text) {
final Map<String, String> macroMap = getMacroMap();
@@ -263,101 +310,88 @@ public class ParametersList implements Cloneable{
return myMacroMap;
}
private static class ParametersTokenizer {
private final String myParamsString;
private final List<String> myArray = new ArrayList<String>();
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<String> 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<String> decode(@NotNull String parameterString) {
parameterString = parameterString.trim();
final ArrayList<String> 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;
}
}
}
@@ -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();
@@ -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<String> commandLine = new ArrayList<String>();
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());
}
@@ -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<String> TOOL_PATH_UPDATE = new Externalizer<String>() {
@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(
@@ -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<String, String> environment = System.getenv();
System.out.println("=====");
for (Map.Entry<String, String> entry : environment.entrySet()) {
System.out.println(formatEntry(entry));
}
System.out.println("=====");
}
public static String formatEntry(final Map.Entry<String, String> entry) {
return entry.getKey() + "=" + entry.getValue().hashCode();
}
}
@@ -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<String, String> testEnv = new HashMap<String, String>();
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<String, String> 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<String> lines = new HashSet<String>(Arrays.asList(StringUtil.convertLineSeparators(output).split("\n")));
for (Map.Entry<String, String> entry : testEnv.entrySet()) {
final String str = EnvPassingTest.formatEntry(entry);
assertTrue("\"" + str + "\" should be in " + lines,
lines.contains(str));
}
final Map<String, String> parentEnv = System.getenv();
final List<String> missed = new ArrayList<String>();
for (Map.Entry<String, String> 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() + ')');
}
}
}
@@ -0,0 +1,14 @@
#include <stdio.h>
/**
* 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;
}
@@ -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());
}
}
@@ -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();
@@ -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;
@@ -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");
}
/**
@@ -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. <br> Change fork mode to &lt;none&gt; 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.<br/>Please change fork mode to &lt;none&gt; 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 <T> void addClassesListToJavaParameters(Collection<? extends T> elements, Function<T, String> nameFunction, String packageName,
boolean createTempFile,
boolean junit4) {
@@ -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;
@@ -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;
}
}
@@ -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);
@@ -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;
}
}