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 ecd900483c93..aad653e7f986 100644 --- a/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java +++ b/platform/platform-api/src/com/intellij/execution/configurations/GeneralCommandLine.java @@ -15,6 +15,7 @@ */ package com.intellij.execution.configurations; +import com.intellij.execution.CommandLineUtil; import com.intellij.execution.ExecutionException; import com.intellij.execution.process.ProcessNotCreatedException; import com.intellij.ide.IdeBundle; @@ -22,7 +23,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.UserDataHolder; -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.ContainerUtil; @@ -55,7 +55,6 @@ public class GeneralCommandLine implements UserDataHolder { Key.create("GeneralCommandLine.do.not.escape.quotes"); private static final Logger LOG = Logger.getInstance("#com.intellij.execution.configurations.GeneralCommandLine"); - private static final char QUOTE = '\uEFEF'; private String myExePath = null; private File myWorkDirectory = null; @@ -70,12 +69,12 @@ public class GeneralCommandLine implements UserDataHolder { public GeneralCommandLine() { } - public GeneralCommandLine(final String... command) { + public GeneralCommandLine(@NotNull String... command) { this(Arrays.asList(command)); } - public GeneralCommandLine(final List command) { - final int size = command.size(); + public GeneralCommandLine(@NotNull List command) { + int size = command.size(); if (size > 0) { setExePath(command.get(0)); if (size > 1) { @@ -211,13 +210,14 @@ public class GeneralCommandLine implements UserDataHolder { /** * Prepares command (quotes and escapes all arguments) and returns it as a newline-separated list - * (suitable e.g. for passing in a environment variable). + * (suitable e.g. for passing in an environment variable). * * @return command as a newline-separated list. */ @NotNull public String getPreparedCommandLine() { - return StringUtil.join(prepareCommands(), "\n"); + String exePath = myExePath != null ? myExePath : ""; + return StringUtil.join(CommandLineUtil.toCommandLine(exePath, myProgramParams.getList()), "\n"); } public Process createProcess() throws ExecutionException { @@ -225,14 +225,15 @@ public class GeneralCommandLine implements UserDataHolder { LOG.debug("Executing [" + getCommandLineString() + "]"); } - String[] commands; + List commands; try { checkWorkingDirectory(); - commands = prepareCommands(); - if (StringUtil.isEmptyOrSpaces(commands[0])) { + if (StringUtil.isEmptyOrSpaces(myExePath)) { throw new ExecutionException(IdeBundle.message("run.configuration.error.executable.not.specified")); } + + commands = CommandLineUtil.toCommandLine(myExePath, myProgramParams.getList()); } catch (ExecutionException e) { LOG.warn(e); @@ -241,8 +242,7 @@ public class GeneralCommandLine implements UserDataHolder { try { ProcessBuilder builder = new ProcessBuilder(commands); - Map environment = builder.environment(); - setupEnvironment(environment); + setupEnvironment(builder.environment()); builder.directory(myWorkDirectory); builder.redirectErrorStream(myRedirectErrorStream); return builder.start(); @@ -266,34 +266,6 @@ public class GeneralCommandLine implements UserDataHolder { } } - private String[] prepareCommands() { - final List parameters = myProgramParams.getList(); - final String[] result = new String[parameters.size() + 1]; - result[0] = myExePath != null ? prepareCommand(FileUtil.toSystemDependentName(myExePath)) : null; - for (int i = 0; i < parameters.size(); i++) { - result[i + 1] = prepareCommand(parameters.get(i)); - } - return result; - } - - // please keep in sync with com.intellij.rt.execution.junit.ProcessBuilder.prepareCommand() && org.jetbrains.jps.incremental.ExternalProcessUtil.prepareCommand() - public static String prepareCommand(String parameter) { - if (SystemInfo.isWindows) { - if (parameter.contains("\"")) { - parameter = StringUtil.replace(parameter, "\"", "\\\""); - } - else if (parameter.length() == 0) { - parameter = "\"\""; - } - } - - if (parameter.length() >= 2 && parameter.charAt(0) == QUOTE && parameter.charAt(parameter.length() - 1) == QUOTE) { - parameter = '"' + parameter.substring(1, parameter.length() - 1) + '"'; - } - - return parameter; - } - private void setupEnvironment(final Map environment) { if (!myPassParentEnvironment) { environment.clear(); @@ -327,7 +299,7 @@ public class GeneralCommandLine implements UserDataHolder { */ @NotNull public static String inescapableQuote(@NotNull String parameter) { - return QUOTE + parameter + QUOTE; + return CommandLineUtil.specialQuote(parameter); } @Override diff --git a/platform/util/src/com/intellij/execution/CommandLineUtil.java b/platform/util/src/com/intellij/execution/CommandLineUtil.java new file mode 100644 index 000000000000..ce32d75aeb67 --- /dev/null +++ b/platform/util/src/com/intellij/execution/CommandLineUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2013 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.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class CommandLineUtil { + private static final char SPECIAL_QUOTE = '\uEFEF'; + private static final String WIN_SHELL_SPECIALS = "&<>()@^|"; + + public static @NotNull String specialQuote(@NotNull String parameter) { + return quote(parameter, SPECIAL_QUOTE); + } + + public static @NotNull List toCommandLine(@NotNull List command) { + assert command.size() > 0; + return toCommandLine(command.get(0), command.subList(1, command.size())); + } + + // please keep an implementation in sync with [junit-rt] ProcessBuilder.createProcess() + public static @NotNull List toCommandLine(@NotNull String command, @NotNull List parameters) { + List commandLine = ContainerUtil.newArrayListWithExpectedSize(parameters.size() + 1); + + commandLine.add(FileUtil.toSystemDependentName(command)); + + boolean winShell = SystemInfo.isWindows && + ("cmd".equalsIgnoreCase(command) || "cmd.exe".equalsIgnoreCase(command)) && + parameters.size() > 1 && "/c".equalsIgnoreCase(parameters.get(0)); + + for (String parameter : parameters) { + if (SystemInfo.isWindows) { + if (parameter.contains("\"")) { + parameter = StringUtil.replace(parameter, "\"", "\\\""); + } + else if (parameter.length() == 0) { + parameter = "\"\""; + } + } + + if (winShell && StringUtil.containsAnyChar(parameter, WIN_SHELL_SPECIALS)) { + parameter = quote(parameter, SPECIAL_QUOTE); + } + + if (isQuoted(parameter, SPECIAL_QUOTE)) { + parameter = quote(parameter.substring(1, parameter.length() - 1), '"'); + } + + commandLine.add(parameter); + } + + return commandLine; + } + + private static String quote(String s, char ch) { + return !isQuoted(s, ch) ? ch + s + ch : s; + } + + private static boolean isQuoted(String s, char ch) { + return s.length() >= 2 && s.charAt(0) == ch && s.charAt(s.length() - 1) == ch; + } +} diff --git a/plugins/javaFX/common-javaFX-plugin/src/org/jetbrains/plugins/javaFX/packaging/AbstractJavaFxPackager.java b/plugins/javaFX/common-javaFX-plugin/src/org/jetbrains/plugins/javaFX/packaging/AbstractJavaFxPackager.java index d4c1d9c75545..7d5f7c25007b 100644 --- a/plugins/javaFX/common-javaFX-plugin/src/org/jetbrains/plugins/javaFX/packaging/AbstractJavaFxPackager.java +++ b/plugins/javaFX/common-javaFX-plugin/src/org/jetbrains/plugins/javaFX/packaging/AbstractJavaFxPackager.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.javaFX.packaging; +import com.intellij.execution.CommandLineUtil; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; @@ -23,7 +24,10 @@ import com.intellij.util.Base64Converter; import com.intellij.util.PathUtilRt; import com.intellij.util.io.ZipUtil; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -97,7 +101,7 @@ public abstract class AbstractJavaFxPackager { buf.append(""); final String artifactFileName = getArtifactRootName(); final String artifactName = FileUtil.getNameWithoutExtension(artifactFileName); - final List tags = + final List tags = JavaFxAntGenerator.createJarAndDeployTasks(this, artifactFileName, artifactName, tempUnzippedArtifactOutput.getPath()); for (JavaFxAntGenerator.SimpleTag tag : tags) { tag.generate(buf); @@ -228,16 +232,15 @@ public abstract class AbstractJavaFxPackager { registerJavaFxPackagerError(ex.getMessage()); } - protected abstract String prepareParam(String param); - private void addParameter(List commandLine, String param) { + private static void addParameter(List commandLine, String param) { if (!StringUtil.isEmptyOrSpaces(param)) { - commandLine.add(prepareParam(param)); + commandLine.add(param); } } private int startProcess(List commands) { try { - final Process process = new ProcessBuilder(commands).start(); + final Process process = new ProcessBuilder(CommandLineUtil.toCommandLine(commands)).start(); final String message = new String(FileUtil.loadBytes(process.getErrorStream())); if (!StringUtil.isEmptyOrSpaces(message)) { registerJavaFxPackagerError(message); @@ -262,9 +265,9 @@ public abstract class AbstractJavaFxPackager { commands.add("-Dant.home=" + antHome); commands.add("-classpath"); - commands.add(antHome + "/lib/ant.jar" + File.pathSeparator + + commands.add(antHome + "/lib/ant.jar" + File.pathSeparator + antHome + "/lib/ant-launcher.jar" + File.pathSeparator + - javaHome + "/lib/ant-javafx.jar" + File.pathSeparator + + javaHome + "/lib/ant-javafx.jar" + File.pathSeparator + javaHome + "/jre/lib/jfxrt.jar"); commands.add("org.apache.tools.ant.launch.Launcher"); commands.add("-f"); diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/packaging/JavaFxAntTaskTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/packaging/JavaFxAntTaskTest.java index ffd274d0f76f..bd0713e12123 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/packaging/JavaFxAntTaskTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/packaging/JavaFxAntTaskTest.java @@ -154,7 +154,7 @@ public class JavaFxAntTaskTest extends UsefulTestCase{ if (preloaderJar != null) { packager.setPreloaderJar(preloaderJar); } - + final List temp = JavaFxAntGenerator .createJarAndDeployTasks(packager, artifactFileName, artifactName, "temp"); final StringBuilder buf = new StringBuilder(); @@ -163,7 +163,7 @@ public class JavaFxAntTaskTest extends UsefulTestCase{ } assertEquals(expected .replaceAll("temp/deploy", "temp\\" + File.separator + "deploy") - .replaceAll("temp/" + artifactFileName, "temp\\" + File.separator + artifactFileName), + .replaceAll("temp/" + artifactFileName, "temp\\" + File.separator + artifactFileName), buf.toString()); } @@ -275,11 +275,6 @@ public class JavaFxAntTaskTest extends UsefulTestCase{ protected void registerJavaFxPackagerError(String message) { } - @Override - protected String prepareParam(String param) { - return param; - } - @Override public String getKeypass() { return null; diff --git a/plugins/javaFX/javaFX-jps-plugin/src/org/jetbrains/plugins/javaFX/JpsJavaFxArtifactBuildTaskProvider.java b/plugins/javaFX/javaFX-jps-plugin/src/org/jetbrains/plugins/javaFX/JpsJavaFxArtifactBuildTaskProvider.java index 4e509f22e7dd..7e961f1fe821 100644 --- a/plugins/javaFX/javaFX-jps-plugin/src/org/jetbrains/plugins/javaFX/JpsJavaFxArtifactBuildTaskProvider.java +++ b/plugins/javaFX/javaFX-jps-plugin/src/org/jetbrains/plugins/javaFX/JpsJavaFxArtifactBuildTaskProvider.java @@ -1,10 +1,24 @@ +/* + * Copyright 2000-2013 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 org.jetbrains.plugins.javaFX; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.artifacts.ArtifactBuildTaskProvider; import org.jetbrains.jps.incremental.BuildTask; import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.ExternalProcessUtil; import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; @@ -144,11 +158,6 @@ public class JpsJavaFxArtifactBuildTaskProvider extends ArtifactBuildTaskProvide myCompileContext.processMessage(new CompilerMessage(COMPILER_NAME, BuildMessage.Kind.ERROR, message)); } - @Override - protected String prepareParam(String param) { - return ExternalProcessUtil.prepareCommand(param); - } - @Override protected String getHtmlParamFile() { return myProperties.myState.getHtmlParamFile(); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/packaging/JavaFxArtifactProperties.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/packaging/JavaFxArtifactProperties.java index 499ae7786c76..9bfeac0a31f0 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/packaging/JavaFxArtifactProperties.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/packaging/JavaFxArtifactProperties.java @@ -15,7 +15,6 @@ */ package org.jetbrains.plugins.javaFX.packaging; -import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.CompilerMessageCategory; @@ -370,16 +369,11 @@ public class JavaFxArtifactProperties extends ArtifactProperties 1 && "/c".equalsIgnoreCase(myParameters.get(0).toString()); + + String[] commandLine = new String[myParameters.size()]; + commandLine[0] = command; + + for (int i = 1; i < myParameters.size(); i++) { + String parameter = myParameters.get(i).toString(); + + if (isWindows) { + int pos = parameter.indexOf('\"'); + if (pos >= 0) { + StringBuffer buffer = new StringBuffer(parameter); + do { + buffer.insert(pos, '\\'); + pos += 2; + } + while ((pos = parameter.indexOf('\"', pos)) >= 0); + parameter = buffer.toString(); + } + else if (parameter.length() == 0) { + parameter = "\"\""; + } + + if (winShell && containsAnyChar(parameter, WIN_SHELL_SPECIALS)) { + parameter = '"' + parameter + '"'; + } + } + + commandLine[i] = parameter; } - return Runtime.getRuntime().exec(command); + return Runtime.getRuntime().exec(commandLine); } - // please keep in sync with GeneralCommandLine.prepareCommand() - private static String prepareCommand(String parameter) { - if (isWindows) { - int pos = parameter.indexOf('\"'); - if (pos >= 0) { - final StringBuffer buffer = new StringBuffer(parameter); - do { - buffer.insert(pos, '\\'); - pos += 2; - } - while ((pos = parameter.indexOf('\"', pos)) >= 0); - parameter = buffer.toString(); - } - else if (parameter.length() == 0) { - parameter = "\"\""; + private static boolean containsAnyChar(String value, String chars) { + for (int i = 0; i < value.length(); i++) { + if (chars.indexOf(value.charAt(i)) >= 0) { + return true; } } - return parameter; + return false; } }