/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.debug.core.launching;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.*;
import org.eclipse.debug.core.*;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.php.debug.core.debugger.parameters.IDebugParametersKeys;
import org.eclipse.php.internal.debug.core.IPHPDebugConstants;
import org.eclipse.php.internal.debug.core.Logger;
import org.eclipse.php.internal.debug.core.PHPDebugCoreMessages;
import org.eclipse.php.internal.debug.core.PHPDebugPlugin;
import org.eclipse.php.internal.debug.core.pathmapper.PathMapperRegistry;
import org.eclipse.php.internal.debug.core.phpIni.PHPINIUtil;
import org.eclipse.php.internal.debug.core.preferences.PHPProjectPreferences;
import org.eclipse.php.internal.debug.core.preferences.PHPexeItem;
import org.eclipse.php.internal.debug.core.preferences.PHPexes;
import org.eclipse.php.internal.debug.core.xdebug.IDELayerFactory;
import org.eclipse.php.internal.debug.core.xdebug.XDebugPreferenceMgr;
import org.eclipse.php.internal.debug.core.xdebug.communication.XDebugCommunicationDaemon;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpBreakpointFacade;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpProxyHandler;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.DBGpProxyHandlersManager;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.XDebugDebuggerSettingsUtil;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.model.DBGpTarget;
import org.eclipse.php.internal.debug.core.xdebug.dbgp.session.DBGpSessionHandler;
import org.eclipse.php.internal.debug.core.zend.debugger.ProcessCrashDetector;
import org.eclipse.swt.widgets.Display;
public class XDebugExeLaunchConfigurationDelegate extends LaunchConfigurationDelegate {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.
* eclipse.debug.core.ILaunchConfiguration, java.lang.String)
*/
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
return new XDebugLaunch(configuration, mode, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch(org.
* eclipse.debug.core.ILaunchConfiguration, java.lang.String,
* org.eclipse.debug.core.ILaunch,
* org.eclipse.core.runtime.IProgressMonitor)
*/
public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
throws CoreException {
if (monitor.isCanceled()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
PHPexeItem phpExeItem = PHPLaunchUtilities.getPHPExe(configuration);
if (phpExeItem == null) {
Logger.log(Logger.ERROR, "Launch configuration could not find PHP exe item"); //$NON-NLS-1$
monitor.setCanceled(true);
monitor.done();
return;
}
// get the launch info: php exe, php ini
final String phpExeString = configuration.getAttribute(IPHPDebugConstants.ATTR_EXECUTABLE_LOCATION,
(String) null);
final String phpIniString = configuration.getAttribute(IPHPDebugConstants.ATTR_INI_LOCATION, (String) null);
final String phpScriptString = configuration.getAttribute(IPHPDebugConstants.ATTR_FILE, (String) null);
if (phpScriptString == null || phpScriptString.trim().length() == 0) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
displayErrorMessage(PHPDebugCoreMessages.XDebug_ExeLaunchConfigurationDelegate_0);
return;
}
if (monitor.isCanceled()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
// locate the project from the php script
final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
final IPath filePath = new Path(phpScriptString);
final IResource scriptRes = workspaceRoot.findMember(filePath);
if (scriptRes == null) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
displayErrorMessage(PHPDebugCoreMessages.XDebug_ExeLaunchConfigurationDelegate_1);
return;
}
// resolve php exe location
final IPath phpExe = new Path(phpExeString);
// resolve project directory
IProject project = scriptRes.getProject();
// Set Project Name as this is required by the source lookup computer
// delegate
final String projectString = project.getFullPath().toString();
ILaunchConfigurationWorkingCopy wc = null;
if (configuration.isWorkingCopy()) {
wc = (ILaunchConfigurationWorkingCopy) configuration;
} else {
wc = configuration.getWorkingCopy();
}
wc.setAttribute(IPHPDebugConstants.PHP_Project, projectString);
wc.setAttribute(IDebugParametersKeys.TRANSFER_ENCODING, PHPProjectPreferences.getTransferEncoding(project));
wc.setAttribute(IDebugParametersKeys.OUTPUT_ENCODING, PHPProjectPreferences.getOutputEncoding(project));
wc.doSave();
if (monitor.isCanceled()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
// resolve the script location, but not relative to anything
IPath phpFile = scriptRes.getLocation();
if (monitor.isCanceled()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
// Resolve the PHP ini location
// Locate the php ini by using the attribute. If the attribute was null,
// try to locate an ini that exists next to the executable.
File phpIni = (phpIniString != null && new File(phpIniString).exists()) ? new File(phpIniString)
: PHPINIUtil.findPHPIni(phpExeString);
File tempIni = PHPINIUtil.prepareBeforeLaunch(phpIni, phpExeString, project);
launch.setAttribute(IDebugParametersKeys.PHP_INI_LOCATION, tempIni.getAbsolutePath());
// add process type to process attributes, basically the name of the exe
// that was launched
final Map<String, String> processAttributes = new HashMap<String, String>();
String programName = phpExe.lastSegment();
final String extension = phpExe.getFileExtension();
if (extension != null) {
programName = programName.substring(0, programName.length() - (extension.length() + 1));
}
programName = programName.toLowerCase();
// used by the console colorer extension to determine what class to use
// should allow the console color providers and line trackers to work
// process.setAttribute(IProcess.ATTR_PROCESS_TYPE,
// IPHPConstants.PHPProcessType);
processAttributes.put(IProcess.ATTR_PROCESS_TYPE, programName);
// used by the Console to give that console a name
processAttributes.put(IProcess.ATTR_CMDLINE, phpScriptString);
if (monitor.isCanceled()) {
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
// determine the environment variables
String[] envVarString = null;
DBGpTarget target = null;
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
// check the launch for stop at first line, if not there go to
// project specifics
boolean stopAtFirstLine = PHPProjectPreferences.getStopAtFirstLine(project);
stopAtFirstLine = configuration.getAttribute(IDebugParametersKeys.FIRST_LINE_BREAKPOINT, stopAtFirstLine);
String sessionID = DBGpSessionHandler.getInstance().generateSessionId();
String ideKey = null;
if (phpExeItem != null) {
DBGpProxyHandler proxyHandler = DBGpProxyHandlersManager.INSTANCE.getHandler(phpExeItem.getUniqueId());
if (proxyHandler != null && proxyHandler.useProxy()) {
ideKey = proxyHandler.getCurrentIdeKey();
if (proxyHandler.registerWithProxy() == false) {
displayErrorMessage(PHPDebugCoreMessages.XDebug_ExeLaunchConfigurationDelegate_2
+ proxyHandler.getErrorMsg());
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
return;
}
} else {
ideKey = DBGpSessionHandler.getInstance().getIDEKey();
}
}
target = new DBGpTarget(launch, phpFile.lastSegment(), ideKey, sessionID, stopAtFirstLine);
target.setPathMapper(PathMapperRegistry.getByLaunchConfiguration(configuration));
DBGpSessionHandler.getInstance().addSessionListener(target);
envVarString = createDebugLaunchEnvironment(configuration, sessionID, ideKey, phpExe);
int requestPort = getDebugPort(phpExeItem);
// Check that the debug daemon is functional
// DEBUGGER - Make sure that the active debugger id is indeed Zend's
// debugger
if (!PHPLaunchUtilities.isDebugDaemonActive(requestPort, XDebugCommunicationDaemon.XDEBUG_DEBUGGER_ID)) {
PHPLaunchUtilities
.showLaunchErrorMessage(NLS.bind(PHPDebugCoreMessages.ExeLaunchConfigurationDelegate_PortInUse,
requestPort, phpExeItem.getName()));
monitor.setCanceled(true);
monitor.done();
return;
}
} else {
envVarString = PHPLaunchUtilities.getEnvironment(configuration, new String[] { getLibraryPath(phpExe) });
}
IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 30);
subMonitor.beginTask(PHPDebugCoreMessages.XDebug_ExeLaunchConfigurationDelegate_3, 10);
// determine the working directory. default is the location of the
// script
IPath workingPath = phpFile.removeLastSegments(1);
File workingDir = workingPath.makeAbsolute().toFile();
boolean found = false;
for (int i = 0; i < envVarString.length && !found; i++) {
String envEntity = envVarString[i];
String[] elements = envEntity.split("="); //$NON-NLS-1$
if (elements.length > 0 && elements[0].equals("XDEBUG_WORKING_DIR")) { //$NON-NLS-1$
found = true;
workingPath = new Path(elements[1]);
File temp = workingPath.makeAbsolute().toFile();
if (temp.exists()) {
workingDir = temp;
}
}
}
// Detect PHP SAPI type and thus where we need arguments
File phpExeFile = new File(phpExeString);
String sapiType = null;
String phpV = null;
PHPexeItem[] items = PHPexes.getInstance().getAllItems();
for (PHPexeItem item : items) {
if (item.getExecutable().equals(phpExeFile)) {
sapiType = item.getSapiType();
phpV = item.getVersion();
break;
}
}
String[] args = null;
if (PHPexeItem.SAPI_CLI.equals(sapiType)) {
args = PHPLaunchUtilities.getProgramArguments(launch.getLaunchConfiguration());
}
// define the command line for launching
String[] cmdLine = null;
cmdLine = PHPLaunchUtilities.getCommandLine(configuration, phpExe.toOSString(), tempIni.toString(),
phpFile.toOSString(), args, phpV);
// Launch the process
final Process phpExeProcess = DebugPlugin.exec(cmdLine, workingDir, envVarString);
// Attach a crash detector
new Thread(new ProcessCrashDetector(launch, phpExeProcess)).start();
IProcess eclipseProcessWrapper = null;
if (phpExeProcess != null) {
subMonitor.worked(10);
String processName = mode.equals(ILaunchManager.DEBUG_MODE)
? (phpExe.toOSString() + ' ' + PHPDebugCoreMessages.PHPProcess_XDebug_suffix) : phpExe.toOSString();
eclipseProcessWrapper = DebugPlugin.newProcess(launch, phpExeProcess, processName, processAttributes);
if (eclipseProcessWrapper == null) {
// another error so we stop everything somehow
phpExeProcess.destroy();
subMonitor.done();
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
throw new CoreException(new Status(IStatus.ERROR, PHPDebugPlugin.ID, 0, null, null));
}
// if launching in debug mode, create the debug infrastructure and
// link it with the launched process
if (mode.equals(ILaunchManager.DEBUG_MODE) && target != null) {
target.setProcess(eclipseProcessWrapper);
launch.addDebugTarget(target);
subMonitor.subTask(PHPDebugCoreMessages.XDebug_ExeLaunchConfigurationDelegate_4);
target.waitForInitialSession((DBGpBreakpointFacade) IDELayerFactory.getIDELayer(),
XDebugPreferenceMgr.createSessionPreferences(), monitor);
}
} else {
// we did not launch
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
DBGpSessionHandler.getInstance().removeSessionListener(target);
}
DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
}
subMonitor.done();
}
/**
* create any environment variables that may be required
*
* @param configuration
* launch configuration
* @param sessionID
* the DBGp Session Id
* @param ideKey
* the DBGp ide key
* @return string array containing the environment
* @throws CoreException
* rethrown exception
*/
public String[] createDebugLaunchEnvironment(ILaunchConfiguration configuration, String sessionID, String ideKey,
IPath phpExe) throws CoreException {
// create XDebug required environment variables, need the
// session handler to start listening and generate a session id
String configEnv = "XDEBUG_CONFIG=remote_enable=1 idekey=" + ideKey; //$NON-NLS-1$
String extraDBGpEnv = "DBGP_IDEKEY=" + ideKey; //$NON-NLS-1$
String sessEnv = "DBGP_COOKIE=" + sessionID; //$NON-NLS-1$
Logger.debugMSG("env=" + configEnv + ", Cookie=" + sessEnv); //$NON-NLS-1$ //$NON-NLS-2$
String[] envVarString = PHPLaunchUtilities.getEnvironment(configuration,
new String[] { configEnv, extraDBGpEnv, sessEnv, getLibraryPath(phpExe) });
return envVarString;
}
private String getLibraryPath(IPath exePath) {
return PHPLaunchUtilities.getLibrarySearchPathEnv(new File(exePath.removeLastSegments(1).toOSString()));
}
/**
* @param phpExe
* @return debug port for given phpExe
* @throws CoreException
*/
protected int getDebugPort(PHPexeItem phpExe) throws CoreException {
int customRequestPort = XDebugDebuggerSettingsUtil.getDebugPort(phpExe.getUniqueId());
if (customRequestPort != -1)
return customRequestPort;
return PHPDebugPlugin.getDebugPort(XDebugCommunicationDaemon.XDEBUG_DEBUGGER_ID);
}
/**
* Displays a dialog with an error message.
*
* @param message
* The error to display.
*/
protected void displayErrorMessage(final String message) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
MessageDialog.openError(Display.getDefault().getActiveShell(),
PHPDebugCoreMessages.XDebugMessage_debugError, message);
}
});
}
}