diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java index 8cf6ab30512a..84775ffbda6a 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java +++ b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java @@ -1,20 +1,19 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 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.util; +import com.intellij.execution.CommandLineUtil; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.CapturingProcessHandler; @@ -93,7 +92,7 @@ public class ExecUtil { @NotNull public static String getWindowsShellName() { - return SystemInfo.isWin2kOrNewer ? "cmd.exe" : "command.com"; + return CommandLineUtil.getWinShellName(); } @NotNull diff --git a/platform/util-rt/src/com/intellij/util/ReadEnv.java b/platform/util-rt/src/com/intellij/util/ReadEnv.java new file mode 100644 index 000000000000..391bd5eafb97 --- /dev/null +++ b/platform/util-rt/src/com/intellij/util/ReadEnv.java @@ -0,0 +1,33 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.util; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Map; + +public class ReadEnv { + public static void main(String[] args) throws Exception { + if (args.length != 1) throw new Exception("Exactly one argument expected"); + + Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(args[0]), "UTF-8")); + try { + for (Map.Entry each : System.getenv().entrySet()) { + // on Windows Java getenv() includes variables that start from '='. + // These variables are not available available in normal command environment. + + // https://stackoverflow.com/questions/30102750/java-system-getenv-environment-names-starting-with + if (each.getKey().startsWith("=")) continue; + + out.write(each.getKey()); + out.write("="); + out.write(each.getValue()); + out.write("\0"); + } + } + finally { + out.close(); + } + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 44f21f1ed344..7357aec19f97 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -1060,9 +1060,6 @@ cidr.indexer.thread.count.description=Number of CPU cores which will be used to how many cores should NOT be involved, e.g. "0" equals to "use every available CPU core", "-1" means "use every core but one". cidr.max.intellisense.file.length=500000 cidr.max.intellisense.file.length.description=Maximum length of C-based language file (in characters) IDE should provide code assistance for. -clion.enable.msvc=false -clion.enable.msvc.description=Experimental MSVC compiler support.\n\ - Please submit feedback and issues in https://youtrack.jetbrains.com/issue/CPP-819 ide.window.shadow.painter=false ide.window.shadow.painter.restartRequired=true diff --git a/platform/util/src/com/intellij/execution/CommandLineUtil.java b/platform/util/src/com/intellij/execution/CommandLineUtil.java index 91942b726eeb..1513665ca7c9 100644 --- a/platform/util/src/com/intellij/execution/CommandLineUtil.java +++ b/platform/util/src/com/intellij/execution/CommandLineUtil.java @@ -1,18 +1,16 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 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.diagnostic.Logger; @@ -421,6 +419,11 @@ public class CommandLineUtil { return numTrailingBackslashes / 2; } + + @NotNull + public static String getWinShellName() { + return "cmd.exe"; + } private static boolean isWinShell(String command) { return "cmd".equalsIgnoreCase(command) || "cmd.exe".equalsIgnoreCase(command); diff --git a/platform/util/src/com/intellij/util/EnvironmentUtil.java b/platform/util/src/com/intellij/util/EnvironmentUtil.java index 4872757931ae..55e9a9bd2977 100644 --- a/platform/util/src/com/intellij/util/EnvironmentUtil.java +++ b/platform/util/src/com/intellij/util/EnvironmentUtil.java @@ -1,25 +1,25 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 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.util; +import com.intellij.execution.CommandLineUtil; import com.intellij.execution.process.UnixProcessManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -34,7 +34,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import java.io.*; +import java.io.File; +import java.io.InputStream; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.Callable; @@ -161,8 +162,11 @@ public class EnvironmentUtil { public static class ShellEnvReader { - public Map readShellEnv() throws Exception { + return readShellEnv(null); + } + + protected Map readShellEnv(@Nullable Map additionalEnvironment) throws Exception { File reader = PathManager.findBinFileWithException("printenv.py"); File envFile = FileUtil.createTempFile("intellij-shell-env.", ".tmp", false); @@ -180,7 +184,34 @@ public class EnvironmentUtil { LOG.info("loading shell env: " + StringUtil.join(command, " ")); - return dumpProcessEnvToFile(command, envFile, "\0"); + return runProcessAndReadOutputAndEnvs(command, null, additionalEnvironment, envFile).second; + } + finally { + FileUtil.delete(envFile); + } + } + + @NotNull + public Map readBatEnv(@NotNull File batchFile, List args) throws Exception { + return readBatOutputAndEnv(batchFile, args).second; + } + + @NotNull + protected Pair> readBatOutputAndEnv(@NotNull File batchFile, List args) throws Exception { + File envFile = FileUtil.createTempFile("intellij-cmd-env.", ".tmp", false); + try { + List cl = new ArrayList(); + cl.add(CommandLineUtil.getWinShellName()); + cl.add("/c"); + cl.add("call"); + cl.add(batchFile.getPath()); + cl.addAll(args); + cl.add("&&"); + cl.addAll(getReadEnvCommand()); + cl.add(envFile.getPath()); + cl.addAll(Arrays.asList("||", "exit", "/B", "%ERRORLEVEL%")); + + return runProcessAndReadOutputAndEnvs(cl, batchFile.getParentFile(), null, envFile); } finally { FileUtil.delete(envFile); @@ -188,35 +219,21 @@ public class EnvironmentUtil { } @NotNull - protected Map dumpProcessEnvToFile(@NotNull List command, @NotNull File envFile, String lineSeparator) - throws Exception { - return runProcessAndReadEnvs(command, envFile, lineSeparator); + private static List getReadEnvCommand() { + return Arrays.asList(FileUtil.toSystemDependentName(System.getProperty("java.home") + "/bin/java"), + "-cp", PathManager.getJarPathForClass(ReadEnv.class), + ReadEnv.class.getCanonicalName()); } @NotNull - protected static Map runProcessAndReadEnvs(@NotNull List command, @NotNull File envFile, String lineSeparator) - throws Exception { - return runProcessAndReadEnvs(command, null, envFile, lineSeparator); - } - - @NotNull - protected static Map runProcessAndReadEnvs(@NotNull List command, - @Nullable File workingDir, - @NotNull File envFile, - String lineSeparator) throws Exception { - return runProcessAndReadEnvs(command, workingDir, null, envFile, lineSeparator); - } - - @NotNull - protected static Map runProcessAndReadEnvs(@NotNull List command, - @Nullable File workingDir, - @Nullable Map envs, - @NotNull File envFile, - String lineSeparator) throws Exception { + protected static Pair> runProcessAndReadOutputAndEnvs(@NotNull List command, + @Nullable File workingDir, + @Nullable Map scriptEnvironment, + @NotNull File envFile) throws Exception { ProcessBuilder builder = new ProcessBuilder(command).redirectErrorStream(true); - if (envs != null) { + if (scriptEnvironment != null) { // we might need default environment for the process to launch correctly - builder.environment().putAll(envs); + builder.environment().putAll(scriptEnvironment); } if (workingDir != null) builder.directory(workingDir); builder.environment().put(DISABLE_OMZ_AUTO_UPDATE, "true"); @@ -230,7 +247,7 @@ public class EnvironmentUtil { if (rv != 0 || lines.isEmpty()) { throw new Exception("rv:" + rv + " text:" + lines.length() + " out:" + StringUtil.trimEnd(gobbler.getText(), '\n')); } - return parseEnv(lines, lineSeparator); + return Pair.create(gobbler.getText(), parseEnv(lines)); } @NotNull @@ -250,18 +267,18 @@ public class EnvironmentUtil { } @Nullable - protected String getShell() throws Exception { + protected String getShell() { return System.getenv("SHELL"); } } @NotNull - private static Map parseEnv(String text, String lineSeparator) throws Exception { + private static Map parseEnv(String text) throws Exception { Set toIgnore = new HashSet(Arrays.asList("_", "PWD", "SHLVL", DISABLE_OMZ_AUTO_UPDATE, INTELLIJ_ENVIRONMENT_READER)); Map env = System.getenv(); Map newEnv = new HashMap(); - String[] lines = text.split(lineSeparator); + String[] lines = text.split("\0"); for (String line : lines) { int pos = line.indexOf('='); if (pos <= 0) { @@ -390,7 +407,7 @@ public class EnvironmentUtil { @TestOnly static Map testParser(@NotNull String lines) { try { - return parseEnv(lines, "\0"); + return parseEnv(lines); } catch (Exception e) { throw new RuntimeException(e); diff --git a/platform/util/testSrc/com/intellij/util/EnvironmentUtilTest.java b/platform/util/testSrc/com/intellij/util/EnvironmentUtilTest.java index 4d51cb94a75f..17afc9565792 100644 --- a/platform/util/testSrc/com/intellij/util/EnvironmentUtilTest.java +++ b/platform/util/testSrc/com/intellij/util/EnvironmentUtilTest.java @@ -1,23 +1,12 @@ -/* - * Copyright 2000-2016 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.util; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import org.junit.Test; +import java.io.File; +import java.util.Arrays; import java.util.Map; import static org.junit.Assert.*; @@ -63,4 +52,32 @@ public class EnvironmentUtilTest { Map env = EnvironmentUtil.testLoader(); assertTrue(env.size() >= System.getenv().size() / 2); } + + @Test(timeout = 30000) + public void loadingBatEnv() throws Exception { + assumeTrue(SystemInfo.isWindows); + + File file = FileUtil.createTempFile("test", ".bat", true); + FileUtil.writeToFile(file, "set FOO_TEST_1=123\r\nset FOO_TEST_2=%1"); + + Map result = new EnvironmentUtil.ShellEnvReader().readBatEnv(file, Arrays.asList("arg_value")); + assertEquals("123", result.get("FOO_TEST_1")); + assertEquals("arg_value", result.get("FOO_TEST_2")); + } + + @Test(timeout = 30000) + public void loadingBatEnv_ErrorHandling() throws Exception { + assumeTrue(SystemInfo.isWindows); + + File file = FileUtil.createTempFile("test", ".bat", true); + FileUtil.writeToFile(file, "echo some error\r\nexit /B 1"); + + try { + new EnvironmentUtil.ShellEnvReader().readBatEnv(file, Arrays.asList()); + fail("error should be reported"); + } + catch (Exception e) { + assertTrue(e.getMessage(), e.getMessage().contains("some error")); + } + } } \ No newline at end of file diff --git a/python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt b/python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt index c24baa6d1eec..56db5220da6e 100644 --- a/python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt +++ b/python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.sdk import com.intellij.openapi.components.PersistentStateComponent @@ -69,7 +55,7 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() { reader.activate?.let { // we add only envs that are setup by the activate script, because adding other variables from the different shell // can break the actual shell - envs.putAll(reader.readShellEnv().mapKeys { k -> k.key.toUpperCase() }.filterKeys { k -> + envs.putAll(reader.readPythonEnv().mapKeys { k -> k.key.toUpperCase() }.filterKeys { k -> k in PyVirtualEnvReader.virtualEnvVars }) } diff --git a/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt b/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt index 9a4be523d65e..3e402cb7c2cd 100644 --- a/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt +++ b/python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt @@ -1,25 +1,10 @@ -/* - * Copyright 2000-2016 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.run import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.SystemInfo -import com.intellij.openapi.util.io.FileUtil import com.intellij.util.EnvironmentUtil -import com.intellij.util.LineSeparator +import com.intellij.util.containers.ContainerUtil import com.jetbrains.python.sdk.PythonSdkType import java.io.File @@ -52,14 +37,15 @@ class PyVirtualEnvReader(val virtualEnvSdkPath: String) : EnvironmentUtil.ShellE } } - override fun readShellEnv(): MutableMap { + fun readPythonEnv(): MutableMap { try { if (SystemInfo.isUnix) { - return super.readShellEnv() + // pass shell environment for correct virtualenv environment setup (virtualenv expects to be executed from the terminal) + return super.readShellEnv(EnvironmentUtil.getEnvironmentMap()) } else { if (activate != null) { - return readVirtualEnvOnWindows(activate); + return readBatEnv(File(activate.first), ContainerUtil.createMaybeSingletonList(activate.second)) } else { LOG.error("Can't find activate script for $virtualEnvSdkPath") @@ -72,30 +58,6 @@ class PyVirtualEnvReader(val virtualEnvSdkPath: String) : EnvironmentUtil.ShellE return mutableMapOf() } - override fun dumpProcessEnvToFile(command: MutableList, envFile: File, lineSeparator: String?): MutableMap { - // pass shell environment for correct virtualenv environment setup (virtualenv expects to be executed from the terminal) - return runProcessAndReadEnvs(command, null, EnvironmentUtil.getEnvironmentMap(), envFile, lineSeparator) - } - - private fun readVirtualEnvOnWindows(activate: Pair): MutableMap { - val activateFile = FileUtil.createTempFile("pycharm-virualenv-activate.", ".bat", false) - val envFile = FileUtil.createTempFile("pycharm-virualenv-envs.", ".tmp", false) - try { - FileUtil.copy(File(activate.first), activateFile); - FileUtil.appendToFile(activateFile, "\n\nset >" + envFile.absoluteFile) - - val command = if (activate.second != null) listOf(activateFile.path, activate.second!!) - else listOf(activateFile.path) - - return runProcessAndReadEnvs(command, envFile, LineSeparator.CRLF.separatorString) - } - finally { - FileUtil.delete(activateFile) - FileUtil.delete(envFile) - } - - } - override fun getShellProcessCommand(): MutableList { val shellPath = shell diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 8d48be50e085..1404e5c88efb 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.jetbrains.python.sdk; import com.google.common.collect.ImmutableMap; @@ -949,7 +935,7 @@ public final class PythonSdkType extends SdkType { PyVirtualEnvReader reader = new PyVirtualEnvReader(sdkHome); if (reader.getActivate() != null) { try { - env.putAll(reader.readShellEnv().entrySet().stream() + env.putAll(reader.readPythonEnv().entrySet().stream() .filter((entry) -> PyVirtualEnvReader.Companion.getVirtualEnvVars().contains(entry.getKey()) ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); }