/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Author: atotic * Created on Mar 18, 2004 */ package org.python.pydev.debug.ui.launching; import java.io.File; import java.io.IOException; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.python.copiedfromeclipsesrc.JDTNotAvailableException; import org.python.pydev.core.log.Log; import org.python.pydev.debug.core.Constants; import org.python.pydev.debug.core.PydevDebugPlugin; import org.python.pydev.debug.model.PyDebugTarget; import org.python.pydev.debug.model.PySourceLocator; import org.python.pydev.debug.model.remote.RemoteDebugger; import org.python.pydev.debug.pyunit.PyUnitServer; import org.python.pydev.debug.pyunit.PyUnitView; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.runners.SimpleRunner; /** * Launches Python process, and connects it to Eclipse's debugger. * Waits for process to complete. * * Modeled after org.eclipse.jdt.internal.launching.StandardVMDebugger. */ public class PythonRunner { /** * Launches the configuration * * The code is modeled after Ant launching example. */ public static void run(final PythonRunnerConfig config, ILaunch launch, IProgressMonitor monitor) throws CoreException, IOException { try { PyUnitServer pyUnitServer = null; if (config.isUnittest()) { pyUnitServer = config.createPyUnitServer(config, launch); PyUnitView.registerPyUnitServer(pyUnitServer); } if (config.isDebug) { runDebug(config, launch, monitor); } else { //default - just configured by command line (the others need special attention) doIt(config, monitor, config.envp, config.getCommandLine(true), config.workingDirectory, launch); } } catch (final JDTNotAvailableException e) { Log.log(e); final Display display = Display.getDefault(); display.syncExec(new Runnable() { public void run() { MessageDialog.openError(display.getActiveShell(), "Unable to run the selected configuration.", e.getMessage()); } }); } } /** * Launches the config in the debug mode. * * Loosely modeled upon Ant launcher. * @throws JDTNotAvailableException */ private static void runDebug(PythonRunnerConfig config, ILaunch launch, IProgressMonitor monitor) throws CoreException, IOException, JDTNotAvailableException { if (monitor == null) monitor = new NullProgressMonitor(); IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 5); subMonitor.beginTask("Launching python", 1); // Launch & connect to the debugger RemoteDebugger debugger = new RemoteDebugger(); debugger.startConnect(subMonitor, config); subMonitor.subTask("Constructing command_line..."); String[] cmdLine = config.getCommandLine(true); Process p = createProcess(launch, config.envp, cmdLine, config.workingDirectory); HashMap<Object, Object> processAttributes = new HashMap<Object, Object>(); processAttributes.put(IProcess.ATTR_CMDLINE, config.getCommandLineAsString()); processAttributes.put(Constants.PYDEV_DEBUG_IPROCESS_ATTR, Constants.PYDEV_DEBUG_IPROCESS_ATTR_TRUE); //Set the debug target before registering with the debug plugin (we want it before creating the console). PyDebugTarget t = new PyDebugTarget(launch, null, config.resource, debugger, config.project); IProcess process; try { process = registerWithDebugPluginForProcessType(config.getRunningName(), launch, p, processAttributes, config); t.process = process; } finally { t.finishedInit = true; } subMonitor.subTask("Waiting for connection..."); Socket socket = null; try { socket = debugger.waitForConnect(subMonitor, p, process); if (socket == null) { debugger.dispose(); return; } } catch (Exception ex) { process.terminate(); p.destroy(); String message = "Unexpected error setting up the debugger"; if (ex instanceof SocketTimeoutException) message = "Timed out after " + Float.toString(config.acceptTimeout / 1000) + " seconds while waiting for python script to connect."; throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, message, ex)); } subMonitor.subTask("Done"); // hook up debug model, and we are off & running launch.setSourceLocator(new PySourceLocator()); t.startTransmission(socket); // this starts reading/writing from sockets t.initialize(); t.addConsoleInputListener(); } private static IProcess doIt(PythonRunnerConfig config, IProgressMonitor monitor, String[] envp, String[] cmdLine, File workingDirectory, ILaunch launch) throws CoreException { if (monitor == null) monitor = new NullProgressMonitor(); IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 5); subMonitor.beginTask("Launching python", 1); // Launch & connect to the debugger subMonitor.subTask("Constructing command_line..."); String commandLineAsString = SimpleRunner.getArgumentsAsStr(cmdLine); //System.out.println("running command line: "+commandLineAsString); Map<Object, Object> processAttributes = new HashMap<Object, Object>(); processAttributes.put(IProcess.ATTR_CMDLINE, commandLineAsString); subMonitor.subTask("Exec..."); //it was dying before register, so, I made this faster to see if this fixes it Process p = createProcess(launch, envp, cmdLine, workingDirectory); IProcess process; String label = cmdLine[cmdLine.length - 1]; //in the interactive session, we'll just create the process, it won't actually be registered //in the debug plugin (the communication is all done through xml-rpc). if (config.isInteractive) { throw new RuntimeException("Interactive not supported here!"); } process = registerWithDebugPluginForProcessType(label, launch, p, processAttributes, config); // Registered the process with the debug plugin subMonitor.subTask("Done"); return process; } /** * Actually creates the process (and create the encoding config file) */ @SuppressWarnings("deprecation") private static Process createProcess(ILaunch launch, String[] envp, String[] cmdLine, File workingDirectory) throws CoreException { //Not using DebugPlugin.ATTR_CONSOLE_ENCODING to provide backward compatibility for eclipse 3.2 String encoding = launch.getAttribute(IDebugUIConstants.ATTR_CONSOLE_ENCODING); if (encoding != null && encoding.trim().length() > 0) { String[] s = new String[envp.length + 3]; System.arraycopy(envp, 0, s, 0, envp.length); //This is used so that we can get code-completion in a debug session. s[s.length - 3] = "PYDEV_COMPLETER_PYTHONPATH=" + PydevPlugin.getBundleInfo().getRelativePath(new Path("pysrc")).toString(); s[s.length - 2] = "PYDEV_CONSOLE_ENCODING=" + encoding; //In Python 3.0, we can use the PYTHONIOENCODING. s[s.length - 1] = "PYTHONIOENCODING=" + encoding; envp = s; } Process p = DebugPlugin.exec(cmdLine, workingDirectory, envp); if (p == null) { throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "Could not execute python process.", null)); } PythonRunnerCallbacks.afterCreatedProcess.call(p); return p; } /** * The debug plugin needs to be notified about our process. * It'll then display the appropriate UI. */ private static IProcess registerWithDebugPluginForProcessType(String label, ILaunch launch, Process p, Map<Object, Object> processAttributes, PythonRunnerConfig config) { processAttributes.put(IProcess.ATTR_PROCESS_TYPE, config.getProcessType()); processAttributes.put(IProcess.ATTR_PROCESS_LABEL, label); processAttributes.put(Constants.PYDEV_CONFIG_RUN, config.run); processAttributes.put(Constants.PYDEV_ADD_RELAUNCH_IPROCESS_ATTR, Constants.PYDEV_ADD_RELAUNCH_IPROCESS_ATTR_TRUE); processAttributes.put(DebugPlugin.ATTR_CAPTURE_OUTPUT, "true"); return DebugPlugin.newProcess(launch, p, label, processAttributes); } }