2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python.sdk;
18 import com.intellij.execution.ExecutionException;
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import com.intellij.execution.process.CapturingProcessHandler;
21 import com.intellij.execution.process.ProcessOutput;
22 import com.intellij.openapi.application.PathManager;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.module.Module;
25 import com.intellij.openapi.projectRoots.Sdk;
26 import com.intellij.openapi.roots.OrderRootType;
27 import com.intellij.openapi.util.SystemInfo;
28 import com.intellij.openapi.util.io.FileUtil;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.openapi.vfs.VfsUtilCore;
31 import com.intellij.openapi.vfs.VirtualFile;
32 import com.intellij.psi.PsiElement;
33 import com.intellij.psi.PsiFile;
34 import com.intellij.remote.RemoteSdkAdditionalData;
35 import com.intellij.util.SystemProperties;
36 import com.intellij.util.containers.HashMap;
37 import com.jetbrains.python.packaging.PyPackageUtil;
38 import com.jetbrains.python.packaging.PyRequirement;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
44 import java.io.IOException;
45 import java.io.OutputStream;
46 import java.util.List;
50 * A more flexible cousin of SdkVersionUtil.
51 * Needs not to be instantiated and only holds static methods.
57 public class PySdkUtil {
58 protected static final Logger LOG = Logger.getInstance("#com.jetbrains.python.sdk.SdkVersionUtil");
60 // Windows EOF marker, Ctrl+Z
61 public static final int SUBSTITUTE = 26;
62 public static final String PATH_ENV_VARIABLE = "PATH";
69 * Executes a process and returns its stdout and stderr outputs as lists of lines.
71 * @param homePath process run directory
72 * @param command command to execute and its arguments
73 * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null.
76 public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command) {
77 return getProcessOutput(homePath, command, -1);
81 * Executes a process and returns its stdout and stderr outputs as lists of lines.
82 * Waits for process for possibly limited duration.
84 * @param homePath process run directory
85 * @param command command to execute and its arguments
86 * @param timeout how many milliseconds to wait until the process terminates; non-positive means inifinity.
87 * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null.
90 public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command, final int timeout) {
91 return getProcessOutput(homePath, command, null, timeout);
95 public static ProcessOutput getProcessOutput(String homePath,
96 @NonNls String[] command,
97 @Nullable @NonNls Map<String, String> extraEnv,
99 return getProcessOutput(homePath, command, extraEnv, timeout, null, true);
103 public static ProcessOutput getProcessOutput(String homePath,
104 @NonNls String[] command,
105 @Nullable @NonNls Map<String, String> extraEnv,
107 @Nullable byte[] stdin,
108 boolean needEOFMarker) {
109 if (homePath == null || !new File(homePath).exists()) {
110 return new ProcessOutput();
112 final Map<String, String> systemEnv = System.getenv();
113 final Map<String, String> env = extraEnv != null ? mergeEnvVariables(systemEnv, extraEnv) : systemEnv;
115 final Process process = new GeneralCommandLine(command).withWorkDirectory(homePath).withEnvironment(env).createProcess();
116 final CapturingProcessHandler processHandler = new CapturingProcessHandler(process);
118 final OutputStream processInput = processHandler.getProcessInput();
119 assert processInput != null;
120 processInput.write(stdin);
121 if (SystemInfo.isWindows && needEOFMarker) {
122 processInput.write(SUBSTITUTE);
123 processInput.flush();
126 processInput.close();
129 return processHandler.runProcess(timeout);
131 catch (ExecutionException e) {
132 return getOutputForException(e);
134 catch (IOException e) {
135 return getOutputForException(e);
139 private static ProcessOutput getOutputForException(final Exception e) {
141 return new ProcessOutput() {
144 public String getStderr() {
145 String err = super.getStderr();
146 if (!StringUtil.isEmpty(err)) {
147 err += "\n" + e.getMessage();
150 err = e.getMessage();
158 public static Map<String, String> mergeEnvVariables(@NotNull Map<String, String> environment,
159 @NotNull Map<String, String> extraEnvironment) {
160 final Map<String, String> result = new HashMap<String, String>(environment);
161 for (Map.Entry<String, String> entry : extraEnvironment.entrySet()) {
162 if (PATH_ENV_VARIABLE.equals(entry.getKey()) && result.containsKey(PATH_ENV_VARIABLE)) {
163 result.put(PATH_ENV_VARIABLE, result.get(PATH_ENV_VARIABLE) + File.pathSeparator + entry.getValue());
166 result.put(entry.getKey(), entry.getValue());
172 public static boolean isRemote(@Nullable Sdk sdk) {
173 return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteSdkAdditionalData;
176 public static String getUserSite() {
177 if (SystemInfo.isWindows) {
178 final String appdata = System.getenv("APPDATA");
179 return appdata + File.separator + "Python";
182 final String userHome = SystemProperties.getUserHome();
183 return userHome + File.separator + ".local";
187 public static boolean isElementInSkeletons(@NotNull final PsiElement element) {
188 final PsiFile file = element.getContainingFile();
190 final VirtualFile virtualFile = file.getVirtualFile();
191 if (virtualFile != null) {
192 final Sdk sdk = PythonSdkType.getSdk(element);
194 final VirtualFile skeletonsDir = findSkeletonsDir(sdk);
195 if (skeletonsDir != null && VfsUtilCore.isAncestor(skeletonsDir, virtualFile, false)) {
204 public static String getRemoteSourcesLocalPath(String sdkHome) {
205 String sep = File.separator;
207 String basePath = PathManager.getSystemPath();
210 PythonSdkType.REMOTE_SOURCES_DIR_NAME +
212 FileUtil.toSystemIndependentName(sdkHome).hashCode() +
217 public static VirtualFile findSkeletonsDir(@NotNull final Sdk sdk) {
218 return findLibraryDir(sdk, PythonSdkType.SKELETON_DIR_NAME, PythonSdkType.BUILTIN_ROOT_TYPE);
222 public static VirtualFile findAnyRemoteLibrary(@NotNull final Sdk sdk) {
223 return findLibraryDir(sdk, PythonSdkType.REMOTE_SOURCES_DIR_NAME, OrderRootType.CLASSES);
226 private static VirtualFile findLibraryDir(Sdk sdk, String dirName, OrderRootType rootType) {
227 final VirtualFile[] virtualFiles = sdk.getRootProvider().getFiles(rootType);
228 for (VirtualFile virtualFile : virtualFiles) {
229 if (virtualFile.isValid() && virtualFile.getPath().contains(dirName)) {
237 public static List<PyRequirement> getRequirementsFromTxt(Module module) {
238 final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(module);
239 if (requirementsTxt != null) {
240 return PyRequirement.parse(requirementsTxt);