/* * 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.jetbrains.python.sdk; import com.intellij.execution.ExecutionException; import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.ProcessOutput; import com.intellij.execution.util.ExecUtil; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.OrderRootType; 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.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.remote.RemoteSdkAdditionalData; import com.intellij.util.SystemProperties; import com.intellij.util.containers.HashMap; import com.jetbrains.python.packaging.PyPackageUtil; import com.jetbrains.python.packaging.PyRequirement; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.Map; /** * A more flexible cousin of SdkVersionUtil. * Needs not to be instantiated and only holds static methods. * * @author dcheryasov * Date: Apr 24, 2008 * Time: 1:19:47 PM */ public class PySdkUtil { protected static final Logger LOG = Logger.getInstance("#com.jetbrains.python.sdk.SdkVersionUtil"); // Windows EOF marker, Ctrl+Z public static final int SUBSTITUTE = 26; public static final String PATH_ENV_VARIABLE = "PATH"; private PySdkUtil() { // explicitly none } /** * Executes a process and returns its stdout and stderr outputs as lists of lines. * * @param homePath process run directory * @param command command to execute and its arguments * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null. */ @NotNull public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command) { return getProcessOutput(homePath, command, -1); } /** * Executes a process and returns its stdout and stderr outputs as lists of lines. * Waits for process for possibly limited duration. * * @param homePath process run directory * @param command command to execute and its arguments * @param timeout how many milliseconds to wait until the process terminates; non-positive means inifinity. * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null. */ @NotNull public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command, final int timeout) { return getProcessOutput(homePath, command, null, timeout); } @NotNull public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command, @Nullable @NonNls Map extraEnv, final int timeout) { return getProcessOutput(homePath, command, extraEnv, timeout, null, true); } @NotNull public static ProcessOutput getProcessOutput(String homePath, @NonNls String[] command, @Nullable @NonNls Map extraEnv, final int timeout, @Nullable byte[] stdin, boolean needEOFMarker) { if (homePath == null || !new File(homePath).exists()) { return new ProcessOutput(); } final Map systemEnv = System.getenv(); final Map env = extraEnv != null ? mergeEnvVariables(systemEnv, extraEnv) : systemEnv; try { final Process process = ExecUtil.exec(Arrays.asList(command), homePath, env); final CapturingProcessHandler processHandler = new CapturingProcessHandler(process); if (stdin != null) { final OutputStream processInput = processHandler.getProcessInput(); assert processInput != null; processInput.write(stdin); if (SystemInfo.isWindows && needEOFMarker) { processInput.write(SUBSTITUTE); processInput.flush(); } else { processInput.close(); } } return processHandler.runProcess(timeout); } catch (ExecutionException e) { return getOutputForException(e); } catch (IOException e) { return getOutputForException(e); } } private static ProcessOutput getOutputForException(final Exception e) { LOG.warn(e); return new ProcessOutput() { @Override public String getStderr() { String err = super.getStderr(); if (!StringUtil.isEmpty(err)) { err += "\n" + e.getMessage(); } else { err = e.getMessage(); } return err; } }; } @NotNull public static Map mergeEnvVariables(@NotNull Map environment, @NotNull Map extraEnvironment) { final Map result = new HashMap(environment); for (Map.Entry entry : extraEnvironment.entrySet()) { if (PATH_ENV_VARIABLE.equals(entry.getKey()) && result.containsKey(PATH_ENV_VARIABLE)) { result.put(PATH_ENV_VARIABLE, result.get(PATH_ENV_VARIABLE) + File.pathSeparator + entry.getValue()); } else { result.put(entry.getKey(), entry.getValue()); } } return result; } public static boolean isRemote(@Nullable Sdk sdk) { return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteSdkAdditionalData; } public static String getUserSite() { if (SystemInfo.isWindows) { final String appdata = System.getenv("APPDATA"); return appdata + File.separator + "Python"; } else { final String userHome = SystemProperties.getUserHome(); return userHome + File.separator + ".local"; } } public static boolean isElementInSkeletons(@NotNull final PsiElement element) { final PsiFile file = element.getContainingFile(); if (file != null) { final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null) { final Sdk sdk = PythonSdkType.getSdk(element); if (sdk != null) { final VirtualFile skeletonsDir = findSkeletonsDir(sdk); if (skeletonsDir != null && VfsUtilCore.isAncestor(skeletonsDir, virtualFile, false)) { return true; } } } } return false; } public static String getRemoteSourcesLocalPath(String sdkHome) { String sep = File.separator; String basePath = PathManager.getSystemPath(); return basePath + File.separator + PythonSdkType.REMOTE_SOURCES_DIR_NAME + sep + FileUtil.toSystemIndependentName(sdkHome).hashCode() + sep; } @Nullable public static VirtualFile findSkeletonsDir(@NotNull final Sdk sdk) { return findLibraryDir(sdk, PythonSdkType.SKELETON_DIR_NAME, PythonSdkType.BUILTIN_ROOT_TYPE); } @Nullable public static VirtualFile findAnyRemoteLibrary(@NotNull final Sdk sdk) { return findLibraryDir(sdk, PythonSdkType.REMOTE_SOURCES_DIR_NAME, OrderRootType.CLASSES); } private static VirtualFile findLibraryDir(Sdk sdk, String dirName, OrderRootType rootType) { final VirtualFile[] virtualFiles = sdk.getRootProvider().getFiles(rootType); for (VirtualFile virtualFile : virtualFiles) { if (virtualFile.isValid() && virtualFile.getPath().contains(dirName)) { return virtualFile; } } return null; } @Nullable public static List getRequirementsFromTxt(Module module) { final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(module); if (requirementsTxt != null) { return PyRequirement.parse(requirementsTxt); } return null; } }