blob: 583641e53edfac398d454c84a1ca4935d3afadd9 [file] [log] [blame]
/*
* 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.debugger;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.configurations.*;
import com.intellij.execution.console.LanguageConsoleBuilder;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.GenericProgramRunner;
import com.intellij.execution.ui.ExecutionConsole;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.console.PythonConsoleView;
import com.jetbrains.python.console.PythonDebugConsoleCommunication;
import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.run.CommandLinePatcher;
import com.jetbrains.python.run.PythonCommandLineState;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.net.ServerSocket;
import java.util.List;
/**
* @author yole
*/
public class PyDebugRunner extends GenericProgramRunner {
public static final String PY_DEBUG_RUNNER = "PyDebugRunner";
@SuppressWarnings("SpellCheckingInspection")
public static final String DEBUGGER_MAIN = "pydev/pydevd.py";
public static final String CLIENT_PARAM = "--client";
public static final String PORT_PARAM = "--port";
public static final String FILE_PARAM = "--file";
public static final String PYCHARM_PROJECT_ROOTS = "PYCHARM_PROJECT_ROOTS";
@SuppressWarnings("SpellCheckingInspection")
public static final String GEVENT_SUPPORT = "GEVENT_SUPPORT";
@Override
@NotNull
public String getRunnerId() {
return PY_DEBUG_RUNNER;
}
@Override
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) &&
profile instanceof AbstractPythonRunConfiguration &&
((AbstractPythonRunConfiguration)profile).canRunWithCoverage();
}
@Override
protected RunContentDescriptor doExecute(@NotNull final Project project, @NotNull RunProfileState profileState,
RunContentDescriptor contentToReuse,
@NotNull ExecutionEnvironment env) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
final PythonCommandLineState pyState = (PythonCommandLineState)profileState;
final ServerSocket serverSocket = PythonCommandLineState.createServerSocket();
final int serverLocalPort = serverSocket.getLocalPort();
RunProfile profile = env.getRunProfile();
final ExecutionResult result = pyState.execute(env.getExecutor(), createCommandLinePatchers(project, pyState, profile, serverLocalPort));
final XDebugSession session = XDebuggerManager.getInstance(project).
startSession(this, env, contentToReuse, new XDebugProcessStarter() {
@Override
@NotNull
public XDebugProcess start(@NotNull final XDebugSession session) {
PyDebugProcess pyDebugProcess =
new PyDebugProcess(session, serverSocket, result.getExecutionConsole(), result.getProcessHandler(),
pyState.isMultiprocessDebug());
createConsoleCommunicationAndSetupActions(project, result, pyDebugProcess);
return pyDebugProcess;
}
});
return session.getRunContentDescriptor();
}
public static int findIndex(List<String> paramList, String paramName) {
for (int i = 0; i < paramList.size(); i++) {
if (paramName.equals(paramList.get(i))) {
return i + 1;
}
}
return -1;
}
protected static void createConsoleCommunicationAndSetupActions(@NotNull final Project project,
@NotNull final ExecutionResult result,
@NotNull PyDebugProcess debugProcess) {
ExecutionConsole console = result.getExecutionConsole();
if (console instanceof PythonDebugLanguageConsoleView) {
ProcessHandler processHandler = result.getProcessHandler();
initDebugConsoleView(project, debugProcess, (PythonDebugLanguageConsoleView)console, processHandler);
}
}
public static PythonDebugConsoleCommunication initDebugConsoleView(Project project,
PyDebugProcess debugProcess,
PythonDebugLanguageConsoleView console,
ProcessHandler processHandler) {
PythonConsoleView pythonConsoleView = console.getPydevConsoleView();
PythonDebugConsoleCommunication debugConsoleCommunication = new PythonDebugConsoleCommunication(project, debugProcess);
pythonConsoleView.setConsoleCommunication(debugConsoleCommunication);
PydevDebugConsoleExecuteActionHandler consoleExecuteActionHandler = new PydevDebugConsoleExecuteActionHandler(pythonConsoleView,
processHandler,
debugConsoleCommunication);
pythonConsoleView.setExecutionHandler(consoleExecuteActionHandler);
debugProcess.getSession().addSessionListener(consoleExecuteActionHandler);
new LanguageConsoleBuilder(pythonConsoleView).processHandler(processHandler).initActions(consoleExecuteActionHandler, "py");
return debugConsoleCommunication;
}
@Nullable
private static CommandLinePatcher createRunConfigPatcher(RunProfileState state, RunProfile profile) {
CommandLinePatcher runConfigPatcher = null;
if (state instanceof PythonCommandLineState && profile instanceof AbstractPythonRunConfiguration) {
runConfigPatcher = (AbstractPythonRunConfiguration)profile;
}
return runConfigPatcher;
}
public static CommandLinePatcher[] createCommandLinePatchers(final Project project, final PythonCommandLineState state,
RunProfile profile,
final int serverLocalPort) {
return new CommandLinePatcher[]{createDebugServerPatcher(project, state, serverLocalPort), createRunConfigPatcher(state, profile)};
}
private static CommandLinePatcher createDebugServerPatcher(final Project project,
final PythonCommandLineState pyState,
final int serverLocalPort) {
return new CommandLinePatcher() {
@Override
public void patchCommandLine(GeneralCommandLine commandLine) {
// script name is the last parameter; all other params are for python interpreter; insert just before name
final ParametersList parametersList = commandLine.getParametersList();
@SuppressWarnings("ConstantConditions") @NotNull
ParamsGroup debugParams = parametersList.getParamsGroup(PythonCommandLineState.GROUP_DEBUGGER);
@SuppressWarnings("ConstantConditions") @NotNull
ParamsGroup exeParams = parametersList.getParamsGroup(PythonCommandLineState.GROUP_EXE_OPTIONS);
final PythonSdkFlavor flavor = pyState.getSdkFlavor();
if (flavor != null) {
assert exeParams != null;
for (String option : flavor.getExtraDebugOptions()) {
exeParams.addParameter(option);
}
}
assert debugParams != null;
fillDebugParameters(project, debugParams, serverLocalPort, pyState, commandLine);
}
};
}
private static void fillDebugParameters(@NotNull Project project,
@NotNull ParamsGroup debugParams,
int serverLocalPort,
@NotNull PythonCommandLineState pyState,
@NotNull GeneralCommandLine generalCommandLine) {
debugParams.addParameter(PythonHelpersLocator.getHelperPath(DEBUGGER_MAIN));
if (pyState.isMultiprocessDebug()) {
//noinspection SpellCheckingInspection
debugParams.addParameter("--multiproc");
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
debugParams.addParameter("--DEBUG");
}
if (PyDebuggerOptionsProvider.getInstance(project).isSaveCallSignatures()) {
debugParams.addParameter("--save-signatures");
addProjectRootsToEnv(project, generalCommandLine);
}
if (PyDebuggerOptionsProvider.getInstance(project).isSupportGeventDebugging()) {
generalCommandLine.getEnvironment().put(GEVENT_SUPPORT, "True");
}
final String[] debuggerArgs = new String[]{
CLIENT_PARAM, "127.0.0.1",
PORT_PARAM, String.valueOf(serverLocalPort),
FILE_PARAM
};
for (String s : debuggerArgs) {
debugParams.addParameter(s);
}
}
private static void addProjectRootsToEnv(@NotNull Project project, @NotNull GeneralCommandLine commandLine) {
List<String> roots = Lists.newArrayList();
for (VirtualFile contentRoot : ProjectRootManager.getInstance(project).getContentRoots()) {
roots.add(contentRoot.getPath());
}
commandLine.getEnvironment().put(PYCHARM_PROJECT_ROOTS, StringUtil.join(roots, File.pathSeparator));
}
}