diff --git a/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java b/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java index 88f2e2678cf..88564bb4cd1 100644 --- a/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java +++ b/build/org.eclipse.cdt.build.core/src/org/eclipse/cdt/build/core/internal/CBuildConfigurationManager.java @@ -18,6 +18,7 @@ import java.util.Map; import org.eclipse.cdt.build.core.CBuildConfiguration; import org.eclipse.cdt.build.core.IBuildConfigurationManager; +import org.eclipse.cdt.core.CProjectNature; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -42,7 +43,7 @@ public class CBuildConfigurationManager // TODO CBuildConfiguration config = null; - //configMap.put(config.getBuildConfiguration(), config); + // configMap.put(config.getBuildConfiguration(), config); return config; } @@ -67,6 +68,13 @@ public class CBuildConfigurationManager if (event.getType() == IResourceChangeEvent.PRE_CLOSE || event.getType() == IResourceChangeEvent.PRE_DELETE) { if (event.getResource().getType() == IResource.PROJECT) { IProject project = event.getResource().getProject(); + try { + if (!project.hasNature(CProjectNature.C_NATURE_ID)) + return; + } catch (CoreException e) { + Activator.log(e.getStatus()); + return; + } // Clean up the configMap try { diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index ca0ab35ed8d..d224f985e4c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.variables, org.eclipse.cdt.launch;bundle-version="6.1.0", org.eclipse.cdt.gdb;bundle-version="7.0.0", - org.eclipse.core.resources + org.eclipse.core.resources, + org.eclipse.launchbar.core;bundle-version="2.0.0";visibility:=reexport Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Export-Package: org.eclipse.cdt.dsf.gdb, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java index 6746a98c11f..c89ec00a000 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -13,12 +13,32 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.launching; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.cdtvariables.ICdtVariable; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.parser.util.StringUtil; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.CRequest; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; @@ -36,7 +56,9 @@ import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrieva import org.eclipse.cdt.dsf.debug.model.DsfLaunch; import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; +import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryBlockRetrievalManager; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; @@ -44,15 +66,28 @@ import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.CommandLineUtil; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.commands.IDebugCommandRequest; import org.eclipse.debug.core.commands.IDisconnectHandler; @@ -60,147 +95,160 @@ import org.eclipse.debug.core.commands.ITerminateHandler; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.model.ITerminate; +import org.eclipse.launchbar.core.target.ILaunchTarget; +import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; /** * The only object in the model that implements the traditional interfaces. */ @ThreadSafe -public class GdbLaunch extends DsfLaunch - implements ITerminate, IDisconnect, ITracedLaunch -{ - private DefaultDsfExecutor fExecutor; - private DsfSession fSession; - private DsfServicesTracker fTracker; - private boolean fInitialized = false; - private boolean fShutDown = false; - private IMemoryBlockRetrievalManager fMemRetrievalManager; - private IDsfDebugServicesFactory fServiceFactory; - - public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { - super(launchConfiguration, mode, locator); +public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITracedLaunch, ITargetedLaunch { + private DefaultDsfExecutor fExecutor; + private DsfSession fSession; + private DsfServicesTracker fTracker; + private boolean fInitialized = false; + private boolean fShutDown = false; + private IMemoryBlockRetrievalManager fMemRetrievalManager; + private IDsfDebugServicesFactory fServiceFactory; + private ILaunchTarget fLaunchTarget; - // Create the dispatch queue to be used by debugger control and services - // that belong to this launch - final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); - dsfExecutor.prestartCoreThread(); - fExecutor = dsfExecutor; - fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); - } + public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); - public DsfExecutor getDsfExecutor() { return fExecutor; } - public IDsfDebugServicesFactory getServiceFactory() { return fServiceFactory; } - - public void initialize() - { - /* - * Registering the launch as an adapter. This ensures that this launch - * will be associated with all DMContexts from this session. - * We do this here because we want to have access to the launch even - * if we run headless, but when we run headless, GdbAdapterFactory is - * not initialized. - */ - fSession.registerModelAdapter(ILaunch.class, this); + // Create the dispatch queue to be used by debugger control and services + // that belong to this launch + final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + dsfExecutor.prestartCoreThread(); + fExecutor = dsfExecutor; + fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + } - Runnable initRunnable = new DsfRunnable() { - @Override - public void run() { - fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); - fSession.addServiceEventListener(GdbLaunch.this, null); - - fInitialized = true; - fireChanged(); - } - }; - - // Invoke the execution code and block waiting for the result. - try { - fExecutor.submit(initRunnable).get(); - } catch (InterruptedException e) { - new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ - } catch (ExecutionException e) { - new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ - } - } + public DsfExecutor getDsfExecutor() { + return fExecutor; + } - public void initializeControl() - throws CoreException - { - // Create a memory retrieval manager and register it with the session - // To maintain a mapping of memory contexts to the corresponding memory retrieval in this session - try { - fExecutor.submit( new Callable() { - @Override - public Object call() throws CoreException { - fMemRetrievalManager = new GdbMemoryBlockRetrievalManager(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, getLaunchConfiguration(), fSession); - fSession.registerModelAdapter(IMemoryBlockRetrievalManager.class, fMemRetrievalManager); - fSession.addServiceEventListener(fMemRetrievalManager, null); - return null; - } - }).get(); - } catch (InterruptedException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ - } catch (ExecutionException e) { - throw (CoreException)e.getCause(); - } catch (RejectedExecutionException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ - } - } + public IDsfDebugServicesFactory getServiceFactory() { + return fServiceFactory; + } - public DsfSession getSession() { return fSession; } - - @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") - public void addCLIProcess(String label) throws CoreException { - try { - // Add the CLI process object to the launch. - AbstractCLIProcess cliProc = - getDsfExecutor().submit( new Callable() { - @Override - public AbstractCLIProcess call() throws CoreException { - IGDBControl gdb = fTracker.getService(IGDBControl.class); - if (gdb != null) { - return gdb.getCLIProcess(); - } - return null; - } - }).get(); + public void initialize() { + /* + * Registering the launch as an adapter. This ensures that this launch + * will be associated with all DMContexts from this session. We do this + * here because we want to have access to the launch even if we run + * headless, but when we run headless, GdbAdapterFactory is not + * initialized. + */ + fSession.registerModelAdapter(ILaunch.class, this); - // Need to go through DebugPlugin.newProcess so that we can use + Runnable initRunnable = new DsfRunnable() { + @Override + public void run() { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); + fSession.addServiceEventListener(GdbLaunch.this, null); + + fInitialized = true; + fireChanged(); + } + }; + + // Invoke the execution code and block waiting for the result. + try { + fExecutor.submit(initRunnable).get(); + } catch (InterruptedException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Error initializing launch", e); //$NON-NLS-1$ + } catch (ExecutionException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Error initializing launch", e); //$NON-NLS-1$ + } + } + + public void initializeControl() throws CoreException { + // Create a memory retrieval manager and register it with the session + // To maintain a mapping of memory contexts to the corresponding memory + // retrieval in this session + try { + fExecutor.submit(new Callable() { + @Override + public Object call() throws CoreException { + fMemRetrievalManager = new GdbMemoryBlockRetrievalManager(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, + getLaunchConfiguration(), fSession); + fSession.registerModelAdapter(IMemoryBlockRetrievalManager.class, fMemRetrievalManager); + fSession.addServiceEventListener(fMemRetrievalManager, null); + return null; + } + }).get(); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException) e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public DsfSession getSession() { + return fSession; + } + + @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") + public void addCLIProcess(String label) throws CoreException { + try { + // Add the CLI process object to the launch. + AbstractCLIProcess cliProc = getDsfExecutor().submit(new Callable() { + @Override + public AbstractCLIProcess call() throws CoreException { + IGDBControl gdb = fTracker.getService(IGDBControl.class); + if (gdb != null) { + return gdb.getCLIProcess(); + } + return null; + } + }).get(); + + // Need to go through DebugPlugin.newProcess so that we can use // the overrideable process factory to allow others to override. // First set attribute to specify we want to create the gdb process. // Bug 210366 Map attributes = new HashMap(); - attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR, - IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE); - DebugPlugin.newProcess(this, cliProc, label, attributes); - } catch (InterruptedException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ - } catch (ExecutionException e) { - throw (CoreException)e.getCause(); - } catch (RejectedExecutionException e) { - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ - } - } - - public void setServiceFactory(IDsfDebugServicesFactory factory) { - fServiceFactory = factory; - } - - /////////////////////////////////////////////////////////////////////////// - // IServiceEventListener - @DsfServiceEventHandler public void eventDispatched(ICommandControlShutdownDMEvent event) { - shutdownSession(new ImmediateRequestMonitor()); - } + attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR, + IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE); + DebugPlugin.newProcess(this, cliProc, label, attributes); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException) e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, + "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public void setServiceFactory(IDsfDebugServicesFactory factory) { + fServiceFactory = factory; + } + + /////////////////////////////////////////////////////////////////////////// + // IServiceEventListener + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent event) { + shutdownSession(new ImmediateRequestMonitor()); + } + + /////////////////////////////////////////////////////////////////////////// + // ITerminate + + static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest { + private Object[] elements; + + public LaunchCommandRequest(Object[] objects) { + elements = objects; + } - /////////////////////////////////////////////////////////////////////////// - // ITerminate - - static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest { - private Object[] elements; - - public LaunchCommandRequest(Object[] objects) { - elements = objects; - } - @Override public Object[] getElements() { return elements; @@ -222,165 +270,637 @@ public class GdbLaunch extends DsfLaunch } } } - } - - @Override - public boolean canTerminate() { - return fInitialized && super.canTerminate(); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.core.Launch#terminate() - */ - @Override - public void terminate() throws DebugException { - // Execute asynchronously to avoid potential deadlocks - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=434645 + } - ITerminateHandler handler = getAdapter(ITerminateHandler.class); - if (handler == null) { - super.terminate(); - return; - } + @Override + public boolean canTerminate() { + return fInitialized && super.canTerminate(); + } - LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); - handler.execute(req); - } - - // ITerminate - /////////////////////////////////////////////////////////////////////////// + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.Launch#terminate() + */ + @Override + public void terminate() throws DebugException { + // Execute asynchronously to avoid potential deadlocks + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=434645 - /////////////////////////////////////////////////////////////////////////// - // IDisconnect - @Override - public boolean canDisconnect() { - return canTerminate(); - } + ITerminateHandler handler = getAdapter(ITerminateHandler.class); + if (handler == null) { + super.terminate(); + return; + } - @Override - public boolean isDisconnected() { - return isTerminated(); - } + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this }); + handler.execute(req); + } - @Override - public void disconnect() throws DebugException { - IDisconnectHandler handler = getAdapter(IDisconnectHandler.class); - if (handler == null) { - super.disconnect(); - return; - } + // ITerminate + /////////////////////////////////////////////////////////////////////////// - LaunchCommandRequest req = new LaunchCommandRequest(new Object[] {this}); - handler.execute(req); - } + /////////////////////////////////////////////////////////////////////////// + // IDisconnect + @Override + public boolean canDisconnect() { + return canTerminate(); + } - // IDisconnect - /////////////////////////////////////////////////////////////////////////// - - /** - * Terminates the gdb session, shuts down the services, the session and - * the executor associated with this launch. - *

- * Note: The argument request monitor to this method should NOT use the - * executor that belongs to this launch. By the time the shutdown is - * complete, this executor will not be dispatching anymore and the - * request monitor will never be invoked. Instead callers should use - * the {@link ImmediateExecutor}. - *

- * @param rm The request monitor invoked when the shutdown is complete. - */ - @ConfinedToDsfExecutor("getSession().getExecutor()") - public void shutdownSession(final RequestMonitor rm) { - if (fShutDown) { - rm.done(); - return; - } - fShutDown = true; - - final Sequence shutdownSeq = new ShutdownSequence( - getDsfExecutor(), fSession.getId(), - new RequestMonitor(fSession.getExecutor(), rm) { - @Override - public void handleCompleted() { - if (fMemRetrievalManager != null) { - fSession.removeServiceEventListener(fMemRetrievalManager); - fMemRetrievalManager.dispose(); - } + @Override + public boolean isDisconnected() { + return isTerminated(); + } - fSession.removeServiceEventListener(GdbLaunch.this); - if (!isSuccess()) { - GdbPlugin.getDefault().getLog().log(new MultiStatus( - GdbPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$ - } - // Last order of business, shutdown the dispatch queue. - fTracker.dispose(); - fTracker = null; - DsfSession.endSession(fSession); + @Override + public void disconnect() throws DebugException { + IDisconnectHandler handler = getAdapter(IDisconnectHandler.class); + if (handler == null) { + super.disconnect(); + return; + } - // 'fireTerminate()' removes this launch from the list of 'DebugEvent' - // listeners. The launch may not be terminated at this point: the inferior - // and gdb processes are monitored in separate threads. This will prevent - // updating of some of the Debug view actions. - // 'DebugEvent.TERMINATE' will be fired when each of the corresponding processes - // exits and handled by 'handleDebugEvents()' method. - if (isTerminated()) { - fireTerminate(); - } - - rm.setStatus(getStatus()); - rm.done(); - } - }); - - final Step[] steps = new Step[] { - new Step() { - @Override - public void execute(RequestMonitor rm) { - IGDBControl control = fTracker.getService(IGDBControl.class); - if (control == null) { - rm.done(); - return; - } - control.terminate(rm); - } - }, - - new Step() { - @Override - public void execute(RequestMonitor rm) { - fExecutor.execute(shutdownSeq); - } - } - }; + LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this }); + handler.execute(req); + } - fExecutor.execute(new Sequence(fExecutor) { + // IDisconnect + /////////////////////////////////////////////////////////////////////////// + + /** + * Terminates the gdb session, shuts down the services, the session and the + * executor associated with this launch. + *

+ * Note: The argument request monitor to this method should NOT use the + * executor that belongs to this launch. By the time the shutdown is + * complete, this executor will not be dispatching anymore and the request + * monitor will never be invoked. Instead callers should use the + * {@link ImmediateExecutor}. + *

+ * + * @param rm + * The request monitor invoked when the shutdown is complete. + */ + @ConfinedToDsfExecutor("getSession().getExecutor()") + public void shutdownSession(final RequestMonitor rm) { + if (fShutDown) { + rm.done(); + return; + } + fShutDown = true; + + final Sequence shutdownSeq = new ShutdownSequence(getDsfExecutor(), fSession.getId(), + new RequestMonitor(fSession.getExecutor(), rm) { + @Override + public void handleCompleted() { + if (fMemRetrievalManager != null) { + fSession.removeServiceEventListener(fMemRetrievalManager); + fMemRetrievalManager.dispose(); + } + + fSession.removeServiceEventListener(GdbLaunch.this); + if (!isSuccess()) { + GdbPlugin.getDefault().getLog().log(new MultiStatus(GdbPlugin.PLUGIN_ID, -1, + new IStatus[] { getStatus() }, "Session shutdown failed", null)); //$NON-NLS-1$ + } + // Last order of business, shutdown the dispatch queue. + fTracker.dispose(); + fTracker = null; + DsfSession.endSession(fSession); + + // 'fireTerminate()' removes this launch from the list + // of 'DebugEvent' + // listeners. The launch may not be terminated at this + // point: the inferior + // and gdb processes are monitored in separate threads. + // This will prevent + // updating of some of the Debug view actions. + // 'DebugEvent.TERMINATE' will be fired when each of the + // corresponding processes + // exits and handled by 'handleDebugEvents()' method. + if (isTerminated()) { + fireTerminate(); + } + + rm.setStatus(getStatus()); + rm.done(); + } + }); + + final Step[] steps = new Step[] { new Step() { + @Override + public void execute(RequestMonitor rm) { + IGDBControl control = fTracker.getService(IGDBControl.class); + if (control == null) { + rm.done(); + return; + } + control.terminate(rm); + } + }, + + new Step() { + @Override + public void execute(RequestMonitor rm) { + fExecutor.execute(shutdownSeq); + } + } }; + + fExecutor.execute(new Sequence(fExecutor) { + + @Override + public Step[] getSteps() { + return steps; + } + }); + } - @Override - public Step[] getSteps() { - return steps; - } - }); - } - @Override public T getAdapter(Class adapter) { - if (!adapter.equals(ITerminateHandler.class)) { - // Must force adapters to be loaded. - // Except in the case of terminate. Terminate can be used - // when running headless (no UI) and therefore we should not - // force the loading of UI plugins in this case. - // This can happen when running JUnit tests for example. - Platform.getAdapterManager().loadAdapter(this, adapter.getName()); - } - return super.getAdapter(adapter); - } - - @Override + if (!adapter.equals(ITerminateHandler.class)) { + // Must force adapters to be loaded. + // Except in the case of terminate. Terminate can be used + // when running headless (no UI) and therefore we should not + // force the loading of UI plugins in this case. + // This can happen when running JUnit tests for example. + Platform.getAdapterManager().loadAdapter(this, adapter.getName()); + } + return super.getAdapter(adapter); + } + + @Override public void launchRemoved(ILaunch launch) { if (this.equals(launch)) { - fExecutor.shutdown(); - fExecutor = null; - } - super.launchRemoved(launch); - } + fExecutor.shutdown(); + fExecutor = null; + } + super.launchRemoved(launch); + } + + /** + * Returns the path to gdb. + * + * @since 5.0 + */ + public IPath getGDBPath() { + String defaultGdbCommand = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, + IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_COMMAND, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT, null); + + IPath retVal = new Path(defaultGdbCommand); + try { + String gdb = getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME); + if (gdb == null) { + gdb = getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + defaultGdbCommand); + } + gdb = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(gdb, false); + retVal = new Path(gdb); + } catch (CoreException e) { + } + return retVal; + } + + /** + * Set the path to gdb + * + * @param path + * the path to gdb + * @since 5.0 + */ + public void setGDBPath(String path) { + setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, path); + } + + /** + * This method actually launches 'gdb --version' to determine the version of + * the GDB that is being used. This method should ideally be called only + * once per session and the resulting version string stored for future uses. + * + * A timeout is scheduled which will kill the process if it takes too long. + * + * @since 5.0 + */ + public String getGDBVersion() throws CoreException { + String cmd = getGDBPath().toOSString() + " --version"; //$NON-NLS-1$ + + // Parse cmd to properly handle spaces and such things (bug 458499) + String[] args = CommandLineUtil.argumentsToArray(cmd); + + Process process = null; + Job timeoutJob = null; + try { + process = ProcessFactory.getFactory().exec(args, getLaunchEnvironment()); + + // Start a timeout job to make sure we don't get stuck waiting for + // an answer from a gdb that is hanging + // Bug 376203 + final Process finalProc = process; + timeoutJob = new Job("GDB version timeout job") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor arg) { + // Took too long. Kill the gdb process and + // let things clean up. + finalProc.destroy(); + return Status.OK_STATUS; + } + }; + timeoutJob.schedule(10000); + + String streamOutput = readStream(process.getInputStream()); + + String gdbVersion = LaunchUtils.getGDBVersionFromText(streamOutput); + if (gdbVersion == null || gdbVersion.isEmpty()) { + Exception detailedException = null; + if (!streamOutput.isEmpty()) { + // We got some output but couldn't parse it. Make that + // output visible to the user in the error dialog. + detailedException = new Exception("Unexpected output format: \n\n" + streamOutput); //$NON-NLS-1$ + } else { + // We got no output. Check if we got something on the error + // stream. + streamOutput = readStream(process.getErrorStream()); + if (!streamOutput.isEmpty()) { + detailedException = new Exception(streamOutput); + } + } + + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Could not determine GDB version using command: " + StringUtil.join(args, " "), //$NON-NLS-1$ //$NON-NLS-2$ + detailedException)); + } + return gdbVersion; + } catch (IOException e) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Error with command: " + StringUtil.join(args, " "), e));//$NON-NLS-1$ //$NON-NLS-2$ + } finally { + // If we get here we are obviously not stuck reading the stream so + // we can cancel the timeout job. + // Note that it may already have executed, but that is not a + // problem. + if (timeoutJob != null) { + timeoutJob.cancel(); + } + + if (process != null) { + process.destroy(); + } + } + } + + /** + * Read from the specified stream and return what was read. + * + * @param stream + * The input stream to be used to read the data. This method will + * close the stream. + * @return The data read from the stream + * @throws IOException + * If an IOException happens when reading the stream + */ + private static String readStream(InputStream stream) throws IOException { + StringBuilder cmdOutput = new StringBuilder(200); + try { + Reader r = new InputStreamReader(stream); + BufferedReader reader = new BufferedReader(r); + + String line; + while ((line = reader.readLine()) != null) { + cmdOutput.append(line); + cmdOutput.append('\n'); + } + return cmdOutput.toString(); + } finally { + // Cleanup to avoid leaking pipes + // Bug 345164 + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + /** + * Gets the CDT environment from the CDT project's configuration referenced + * by the launch + * + * @since 5.0 + */ + public String[] getLaunchEnvironment() throws CoreException { + // Get the project + String projectName = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, + (String) null); + IProject project = null; + if (projectName == null) { + IResource[] resources = getLaunchConfiguration().getMappedResources(); + if (resources != null && resources.length > 0 && resources[0] instanceof IProject) { + project = (IProject) resources[0]; + } + } else { + projectName = projectName.trim(); + if (projectName.length() == 0) { + return null; + } + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + } + + if (project == null || !project.isAccessible()) { + // No project + return null; + } + + HashMap envMap = new HashMap(); + ICProjectDescription projDesc = CoreModel.getDefault().getProjectDescription(project, false); + if (projDesc != null) { + String buildConfigID = getLaunchConfiguration() + .getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, ""); //$NON-NLS-1$ + ICConfigurationDescription cfg = null; + if (buildConfigID.length() != 0) { + cfg = projDesc.getConfigurationById(buildConfigID); + } + + // if configuration is null fall-back to active + if (cfg == null) { + cfg = projDesc.getActiveConfiguration(); + } + + // Environment variables and inherited vars + IEnvironmentVariable[] vars = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cfg, true); + for (IEnvironmentVariable var : vars) { + envMap.put(var.getName(), var.getValue()); + } + + // Add variables from build info + ICdtVariable[] buildVars = CCorePlugin.getDefault().getCdtVariableManager().getVariables(cfg); + for (ICdtVariable var : buildVars) { + try { + // The project_classpath variable contributed by JDT is + // useless + // for running C/C++ + // binaries, but it can be lethal if it has a very large + // value + // that exceeds shell + // limit. See + // http://bugs.eclipse.org/bugs/show_bug.cgi?id=408522 + if (!"project_classpath".equals(var.getName())) {//$NON-NLS-1$ + envMap.put(var.getName(), var.getStringValue()); + } + } catch (CdtVariableException e) { + // Some Eclipse dynamic variables can't be resolved + // dynamically... we don't care. + } + } + } + + // Turn it into an envp format + List strings = new ArrayList(envMap.size()); + for (Entry entry : envMap.entrySet()) { + StringBuffer buffer = new StringBuffer(entry.getKey()); + buffer.append('=').append(entry.getValue()); + strings.add(buffer.toString()); + } + + return strings.toArray(new String[strings.size()]); + } + + /** + * Get the location of the gdbinit file. + * + * @return gdbinit file location + * @throws CoreException + * @since 5.0 + */ + public String getGDBInitFile() throws CoreException { + String defaultGdbInit = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, + IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT, null); + + return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, defaultGdbInit); + } + + /** + * Get the working directory. + * + * @return the working directory + * @throws CoreException + * @since 5.0 + */ + public IPath getGDBWorkingDirectory() throws CoreException { + // First try to use the user-specified working directory for the + // debugged program. + // This is fine only with local debug. + // For remote debug, the working dir of the debugged program will be + // on remote device + // and hence not applicable. In such case we may just use debugged + // program path on host + // as the working dir for GDB. + // However, we cannot find a standard/common way to distinguish + // remote debug from local + // debug. For instance, a local debug may also use gdbserver+gdb. So + // it's up to each + // debugger implementation to make the distinction. + // + IPath path = null; + String location = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, + (String) null); + + if (location != null) { + String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager() + .performStringSubstitution(location); + if (!expandedLocation.isEmpty()) { + path = new Path(expandedLocation); + } + } + + if (path != null) { + // Some validity check. Should have been done by UI code. + if (path.isAbsolute()) { + File dir = new File(path.toPortableString()); + if (!dir.isDirectory()) + path = null; + } else { + IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if (res instanceof IContainer && res.exists()) { + path = res.getLocation(); + } else + // Relative but not found in workspace. + path = null; + } + } + + if (path == null) { + // default working dir is the project if this config has a + // project + ICProject cp = LaunchUtils.getCProject(getLaunchConfiguration()); + if (cp != null) { + IProject p = cp.getProject(); + path = p.getLocation(); + } else { + // no meaningful value found. Just return null. + } + } + + return path; + } + + /** + * Get the program arguments + * + * @return program arguments + * @throws CoreException + * @since 5.0 + */ + public String getProgramArguments() throws CoreException { + String programArguments = getLaunchConfiguration() + .getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null); + + if (programArguments != null) { + programArguments = VariablesPlugin.getDefault().getStringVariableManager() + .performStringSubstitution(programArguments); + } + + return programArguments; + } + + /** + * Return the program path + * + * @return the program path + * @since 5.0 + */ + public String getProgramPath() throws CoreException { + String programPath = getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME); + if (programPath == null) { + programPath = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + (String) null); + } + return programPath; + } + + /** + * Sets the program path + * + * @param programPath + * the program path + * @throws CoreException + * @since 5.0 + */ + public void setProgramPath(String programPath) throws CoreException { + setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, programPath); + } + + /** + * Return shared library paths + * + * @return shared library paths + * @throws CoreException + * @since 5.0 + */ + public List getSharedLibraryPaths() throws CoreException { + return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + new ArrayList(0)); + } + + /** + * return the environment + * + * @return the environment + * @throws CoreException + * @since 5.0 + */ + public Properties getEnvironmentVariables() throws CoreException { + Properties envVariables = new Properties(); + + // if the attribute ATTR_APPEND_ENVIRONMENT_VARIABLES is set, + // the LaunchManager will return both the new variables and the + // existing ones. + // That would force us to delete all the variables in GDB, and then + // re-create then all + // that is not very efficient. So, let's fool the LaunchManager into + // returning just the + // list of new variables. + + boolean append = getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + + String[] properties; + if (append) { + ILaunchConfigurationWorkingCopy wc = getLaunchConfiguration().copy(""); //$NON-NLS-1$ + // Don't save this change, it is just temporary, and in just a + // copy of our launchConfig. + wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); + properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(wc); + } else { + // We're getting rid of the environment anyway, so this call + // will only yield the new variables. + properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(getLaunchConfiguration()); + } + + if (properties == null) { + properties = new String[0]; + } + + for (String property : properties) { + int idx = property.indexOf('='); + if (idx != -1) { + String key = property.substring(0, idx); + String value = property.substring(idx + 1); + envVariables.setProperty(key, value); + } else { + envVariables.setProperty(property, ""); //$NON-NLS-1$ + } + } + return envVariables; + } + + /** + * Get whether to clear the environment before applying the variables + * + * @return clear + * @throws CoreException + * @since 5.0 + */ + public boolean getClearEnvironment() throws CoreException { + return !getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + } + + /** + * Get whether to update thread list on suspend + * + * @return whether + * @throws CoreException + * @since 5.0 + */ + public boolean getUpdateThreadListOnSuspend() throws CoreException { + return getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + } + + /** + * Set the launch target + * + * @param launchTarget + * the launch target + * @since 5.0 + */ + public void setLaunchTarget(ILaunchTarget launchTarget) { + this.fLaunchTarget = launchTarget; + } + + /** + * Return the launch target + * + * @since 5.0 + */ + @Override + public ILaunchTarget getLaunchTarget() { + return fLaunchTarget; + } + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java index b488dfdb6d4..499dc598217 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java @@ -296,11 +296,12 @@ public class GdbLaunchDelegate extends AbstractCLaunchDelegate2 } /** - * Returns the GDB version. - * Subclass can override for special need. - * + * Returns the GDB version. Subclass can override for special need. + * * @since 2.0 + * @deprecated Replaced by GdbLaunch.getGDBVersion() which can also be overridden */ + @Deprecated protected String getGDBVersion(ILaunchConfiguration config) throws CoreException { return LaunchUtils.getGDBVersion(config); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java index 9a388c3c75e..910989549f5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java @@ -273,7 +273,10 @@ public class LaunchUtils { * only once per session and the resulting version string stored for future uses. * * A timeout is scheduled which will kill the process if it takes too long. + * + * @deprecated Replaced with GdbLaunch.getLaunchEnvironment() */ + @Deprecated public static String getGDBVersion(final ILaunchConfiguration configuration) throws CoreException { String cmd = getGDBPath(configuration).toOSString() + " --version"; //$NON-NLS-1$ @@ -476,7 +479,9 @@ public class LaunchUtils { * Gets the CDT environment from the CDT project's configuration referenced by the * launch * @since 3.0 + * @deprecated Replaced with GdbLaunch.getLaunchEnvironment() */ + @Deprecated public static String[] getLaunchEnvironment(ILaunchConfiguration config) throws CoreException { // Get the project String projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java index a6af482a498..39ecc3e4d96 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -16,29 +16,24 @@ package org.eclipse.cdt.dsf.gdb.service; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.parser.util.StringUtil; -import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.Sequence; -import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; -import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep; import org.eclipse.cdt.dsf.mi.service.IMIBackend; @@ -50,148 +45,130 @@ import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.utils.CommandLineUtil; import org.eclipse.cdt.utils.spawner.ProcessFactory; import org.eclipse.cdt.utils.spawner.Spawner; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugException; -import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; -import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; -import org.eclipse.debug.core.ILaunchManager; import org.osgi.framework.BundleContext; /** - * Implementation of {@link IGDBBackend} for the common case where GDB is launched - * in local file system on host PC where Eclipse runs. This also manages some GDB parameters - * from a given launch configuration.
- *
+ * Implementation of {@link IGDBBackend} for the common case where GDB is + * launched in local file system on host PC where Eclipse runs. This also + * manages some GDB parameters from a given launch configuration.
+ *
* You can subclass for you special needs. * * @since 1.1 */ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBackend2 { - + private final ILaunchConfiguration fLaunchConfiguration; - + /* * Parameters for launching GDB. */ - private IPath fProgramPath; - private IPath fGDBWorkingDirectory; - private String fGDBInitFile; - private List fSharedLibPaths; - private String fProgramArguments; - - private Properties fEnvVariables; private SessionType fSessionType; - private Boolean fAttach; - private State fBackendState = State.NOT_INITIALIZED; + private Boolean fAttach; + private State fBackendState = State.NOT_INITIALIZED; + + /* + * Unique ID of this service instance. + */ + private final String fBackendId; + private static int fgInstanceCounter = 0; + + /* + * Service state parameters. + */ + private MonitorJob fMonitorJob; + private Process fProcess; + private int fGDBExitValue; + private int fGDBLaunchTimeout = 30; /** - * Unique ID of this service instance. - */ - private final String fBackendId; - private static int fgInstanceCounter = 0; - - /* - * Service state parameters. - */ - private MonitorJob fMonitorJob; - private Process fProcess; - private int fGDBExitValue; - private int fGDBLaunchTimeout = 30; - - /** - * A Job that will set a failed status - * in the proper request monitor, if the interrupt - * did not succeed after a certain time. - */ - private MonitorInterruptJob fInterruptFailedJob; + * A Job that will set a failed status in the proper request monitor, if the + * interrupt did not succeed after a certain time. + */ + private MonitorInterruptJob fInterruptFailedJob; - public GDBBackend(DsfSession session, ILaunchConfiguration lc) { super(session); - fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ - fLaunchConfiguration = lc; - - try { - // Don't call verifyCProject, because the JUnit tests are not setting a project - ICProject cproject = LaunchUtils.getCProject(lc); - fProgramPath = LaunchUtils.verifyProgramPath(lc, cproject); - } catch (CoreException e) { - fProgramPath = new Path(""); //$NON-NLS-1$ - } + this.fLaunchConfiguration = lc; + fBackendId = "gdb[" + Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new ImmediateRequestMonitor(requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + getExecutor().execute(getStartupSequence(requestMonitor)); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - super.initialize(new ImmediateRequestMonitor(requestMonitor) { - @Override - protected void handleSuccess() { - doInitialize(requestMonitor); - } - }); - } - private void doInitialize(final RequestMonitor requestMonitor) { - getExecutor().execute(getStartupSequence(requestMonitor)); - } - /** @since 5.0 */ protected Sequence getStartupSequence(final RequestMonitor requestMonitor) { - final Sequence.Step[] initializeSteps = new Sequence.Step[] { - new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), - new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), - new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), - }; + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), + new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), + new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), }; - return new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return initializeSteps; } - }; - } + return new Sequence(getExecutor(), requestMonitor) { + @Override + public Step[] getSteps() { + return initializeSteps; + } + }; + } - @Override - public void shutdown(final RequestMonitor requestMonitor) { - getExecutor().execute(getShutdownSequence(new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleCompleted() { + @Override + public void shutdown(final RequestMonitor requestMonitor) { + getExecutor().execute(getShutdownSequence(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { GDBBackend.super.shutdown(requestMonitor); - } - })); - } - - /** @since 5.0 */ - protected Sequence getShutdownSequence(RequestMonitor requestMonitor) { - final Sequence.Step[] shutdownSteps = new Sequence.Step[] { - new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), - }; - - return new Sequence(getExecutor(), requestMonitor) { - @Override public Step[] getSteps() { return shutdownSteps; } - }; - } + } + })); + } + + /** @since 5.0 */ + protected Sequence getShutdownSequence(RequestMonitor requestMonitor) { + final Sequence.Step[] shutdownSteps = new Sequence.Step[] { + new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), }; + + return new Sequence(getExecutor(), requestMonitor) { + @Override + public Step[] getSteps() { + return shutdownSteps; + } + }; + } + + private GdbLaunch getGDBLaunch() { + return (GdbLaunch) getSession().getModelAdapter(ILaunch.class); + } - /** @since 4.0 */ - protected IPath getGDBPath() { - return LaunchUtils.getGDBPath(fLaunchConfiguration); - } + protected IPath getGDBPath() { + return getGDBLaunch().getGDBPath(); + } /** - * Options for GDB process. - * Allow subclass to override. + * Options for GDB process. Allow subclass to override. + * * @deprecated Use {@link #getGDBCommandLineArray()} instead */ @Deprecated @@ -201,755 +178,656 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa } /** - * Options for GDB process. - * Returns the GDB command and its arguments as an array. - * Allow subclass to override. + * Options for GDB process. Returns the GDB command and its arguments as an + * array. Allow subclass to override. + * * @since 4.6 */ protected String[] getGDBCommandLineArray() { // The goal here is to keep options to an absolute minimum. // All configuration should be done in the final launch sequence // to allow for more flexibility. - - String cmd = getGDBPath().toOSString() + - " --interpreter" + //$NON-NLS-1$ - // We currently work with MI version 2. Don't use just 'mi' because it - // points to the latest MI version, while we want mi2 specifically. - " mi2" + //$NON-NLS-1$ - // Don't read the gdbinit file here. It is read explicitly in - // the LaunchSequence to make it easier to customize. - " --nx"; //$NON-NLS-1$ - - // Parse to properly handle spaces and such things (bug 458499) + + String cmd = getGDBPath().toOSString() + " --interpreter" + //$NON-NLS-1$ + // We currently work with MI version 2. Don't use just 'mi' because it + // points to the latest MI version, while we want mi2 specifically. + " mi2" + //$NON-NLS-1$ + // Don't read the gdbinit file here. It is read explicitly in + // the LaunchSequence to make it easier to customize. + " --nx"; //$NON-NLS-1$ + + // Parse to properly handle spaces and such things (bug 458499) return CommandLineUtil.argumentsToArray(cmd); } @Override public String getGDBInitFile() throws CoreException { - if (fGDBInitFile == null) { - String defaultGdbInit = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID, - IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_INIT, - IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT, null); - - fGDBInitFile = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, defaultGdbInit); - } - - return fGDBInitFile; + return getGDBLaunch().getGDBInitFile(); } @Override public IPath getGDBWorkingDirectory() throws CoreException { - if (fGDBWorkingDirectory == null) { - - // First try to use the user-specified working directory for the debugged program. - // This is fine only with local debug. - // For remote debug, the working dir of the debugged program will be on remote device - // and hence not applicable. In such case we may just use debugged program path on host - // as the working dir for GDB. - // However, we cannot find a standard/common way to distinguish remote debug from local - // debug. For instance, a local debug may also use gdbserver+gdb. So it's up to each - // debugger implementation to make the distinction. - // - IPath path = null; - String location = fLaunchConfiguration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String)null); - - if (location != null) { - String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(location); - if (!expandedLocation.isEmpty()) { - path = new Path(expandedLocation); - } - } - - if (path != null) { - // Some validity check. Should have been done by UI code. - if (path.isAbsolute()) { - File dir = new File(path.toPortableString()); - if (! dir.isDirectory()) - path = null; - } else { - IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path); - if (res instanceof IContainer && res.exists()) { - path = res.getLocation(); - } - else - // Relative but not found in workspace. - path = null; - } - } - - if (path == null) { - // default working dir is the project if this config has a project - ICProject cp = LaunchUtils.getCProject(fLaunchConfiguration); - if (cp != null) { - IProject p = cp.getProject(); - path = p.getLocation(); - } - else { - // no meaningful value found. Just return null. - } - } - - fGDBWorkingDirectory = path; - } - - return fGDBWorkingDirectory; + return getGDBLaunch().getGDBWorkingDirectory(); } @Override public String getProgramArguments() throws CoreException { - if (fProgramArguments == null) { - fProgramArguments = fLaunchConfiguration.getAttribute( - ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, - (String)null); - - if (fProgramArguments != null) { - fProgramArguments = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(fProgramArguments); - } - } - - return fProgramArguments; + return getGDBLaunch().getProgramArguments(); } @Override public IPath getProgramPath() { - return fProgramPath; + try { + return new Path(getGDBLaunch().getProgramPath()); + } catch (CoreException e) { + return new Path(""); //$NON-NLS-1$ + } } @Override public List getSharedLibraryPaths() throws CoreException { - if (fSharedLibPaths == null) { - fSharedLibPaths = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, - new ArrayList(0)); - } - - return fSharedLibPaths; + return getGDBLaunch().getSharedLibraryPaths(); } /** @since 3.0 */ @Override public Properties getEnvironmentVariables() throws CoreException { - if (fEnvVariables == null) { - fEnvVariables = new Properties(); - - // if the attribute ATTR_APPEND_ENVIRONMENT_VARIABLES is set, - // the LaunchManager will return both the new variables and the existing ones. - // That would force us to delete all the variables in GDB, and then re-create then all - // that is not very efficient. So, let's fool the LaunchManager into returning just the - // list of new variables. - - boolean append = fLaunchConfiguration.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); - - String[] properties; - if (append) { - ILaunchConfigurationWorkingCopy wc = fLaunchConfiguration.copy(""); //$NON-NLS-1$ - // Don't save this change, it is just temporary, and in just a copy of our launchConfig. - wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false); - properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(wc); - } else { - // We're getting rid of the environment anyway, so this call will only yield the new variables. - properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(fLaunchConfiguration); - } - - if (properties == null) { - properties = new String[0]; - } - - for (String property : properties) { - int idx = property.indexOf('='); - if (idx != -1) { - String key = property.substring(0, idx); - String value = property.substring(idx + 1); - fEnvVariables.setProperty(key, value); - } else { - fEnvVariables.setProperty(property, ""); //$NON-NLS-1$ - } - } - } - - return fEnvVariables; + return getGDBLaunch().getEnvironmentVariables(); } - + /** @since 3.0 */ @Override public boolean getClearEnvironment() throws CoreException { - return !fLaunchConfiguration.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); + return getGDBLaunch().getClearEnvironment(); } - + /** @since 3.0 */ @Override public boolean getUpdateThreadListOnSuspend() throws CoreException { - return fLaunchConfiguration - .getAttribute( - IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, - IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + return getGDBLaunch().getUpdateThreadListOnSuspend(); } private Process launchGDBProcess() throws CoreException { // Keep calling deprecated getGDBCommandLine() in case it was overridden String command = getGDBCommandLine(); - // Keep calling deprecated launchGDBProcess(String) in case it was overridden + // Keep calling deprecated launchGDBProcess(String) in case it was + // overridden return launchGDBProcess(command); } /** - * Launch GDB process. - * Allow subclass to override. + * Launch GDB process. Allow subclass to override. + * * @deprecated Use {@link #launchGDBProcess(String[])} instead */ @Deprecated protected Process launchGDBProcess(String commandLine) throws CoreException { // Backwards-compatibility check - // If the commandLine parameter is not the same as the command line array we provide - // it implies that the commandLine was modified by an extender and should be used as - // is. If it is the same, we can use the command line array instead using the more robust + // If the commandLine parameter is not the same as the command line + // array we provide + // it implies that the commandLine was modified by an extender and + // should be used as + // is. If it is the same, we can use the command line array instead + // using the more robust // non-deprecated call to launchGDBProcess. String unmodifiedCmdLine = StringUtil.join(getGDBCommandLineArray(), " ").trim(); //$NON-NLS-1$ if (unmodifiedCmdLine.equals(commandLine.trim()) == false) { Process proc = null; try { - proc = ProcessFactory.getFactory().exec(commandLine, LaunchUtils.getLaunchEnvironment(fLaunchConfiguration)); + proc = ProcessFactory.getFactory().exec(commandLine, getGDBLaunch().getLaunchEnvironment()); } catch (IOException e) { - String message = "Error while launching command " + commandLine; //$NON-NLS-1$ + String message = "Error while launching command " + commandLine; //$NON-NLS-1$ throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); } return proc; - } + } // End of Backwards-compatibility check return launchGDBProcess(getGDBCommandLineArray()); } - + /** - * Launch GDB process with command and arguments. - * Allow subclass to override. + * Launch GDB process with command and arguments. Allow subclass to + * override. + * * @since 4.6 */ protected Process launchGDBProcess(String[] commandLine) throws CoreException { - Process proc = null; + Process proc = null; try { - proc = ProcessFactory.getFactory().exec(commandLine, LaunchUtils.getLaunchEnvironment(fLaunchConfiguration)); + proc = ProcessFactory.getFactory().exec(commandLine, getGDBLaunch().getLaunchEnvironment()); } catch (IOException e) { - String message = "Error while launching command: " + StringUtil.join(commandLine, " "); //$NON-NLS-1$ //$NON-NLS-2$ - throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); + String message = "Error while launching command: " + StringUtil.join(commandLine, " "); //$NON-NLS-1$ //$NON-NLS-2$ + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); } - + return proc; } - public Process getProcess() { - return fProcess; - } - + public Process getProcess() { + return fProcess; + } + @Override - public OutputStream getMIOutputStream() { - return fProcess.getOutputStream(); - }; - + public OutputStream getMIOutputStream() { + return fProcess.getOutputStream(); + }; + @Override - public InputStream getMIInputStream() { - return fProcess.getInputStream(); - }; + public InputStream getMIInputStream() { + return fProcess.getInputStream(); + }; /** @since 4.1 */ @Override - public InputStream getMIErrorStream() { - return fProcess.getErrorStream(); - }; + public InputStream getMIErrorStream() { + return fProcess.getErrorStream(); + }; @Override - public String getId() { - return fBackendId; - } + public String getId() { + return fBackendId; + } @Override - public void interrupt() { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; - + public void interrupt() { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; + // Cygwin gdb 6.8 is capricious when it comes to interrupting the // target. The same logic here will work with MinGW, though. And on // linux it's irrelevant since interruptCTRLC()==interrupt(). So, // one odd size fits all. // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 - if (getSessionType() == SessionType.REMOTE) { - gdbSpawner.interrupt(); - } - else { - gdbSpawner.interruptCTRLC(); - } - } - } + if (getSessionType() == SessionType.REMOTE) { + gdbSpawner.interrupt(); + } else { + gdbSpawner.interruptCTRLC(); + } + } + } - /** + /** * @since 3.0 */ @Override - public void interruptAndWait(int timeout, RequestMonitor rm) { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; + public void interruptAndWait(int timeout, RequestMonitor rm) { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; // Cygwin gdb 6.8 is capricious when it comes to interrupting the // target. The same logic here will work with MinGW, though. And on // linux it's irrelevant since interruptCTRLC()==interrupt(). So, // one odd size fits all. - // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 - if (getSessionType() == SessionType.REMOTE) { - gdbSpawner.interrupt(); - } - else { - gdbSpawner.interruptCTRLC(); - } - fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$ - rm.done(); - } - } + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=304096#c54 + if (getSessionType() == SessionType.REMOTE) { + gdbSpawner.interrupt(); + } else { + gdbSpawner.interruptCTRLC(); + } + fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, + "Cannot interrupt.", null)); //$NON-NLS-1$ + rm.done(); + } + } - /** + /** * @since 3.0 */ @Override - public void interruptInferiorAndWait(long pid, int timeout, RequestMonitor rm) { - if (fProcess instanceof Spawner) { - Spawner gdbSpawner = (Spawner) fProcess; - gdbSpawner.raise((int)pid, gdbSpawner.INT); - fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); - } else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Cannot interrupt.", null)); //$NON-NLS-1$ - rm.done(); - } - } + public void interruptInferiorAndWait(long pid, int timeout, RequestMonitor rm) { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; + gdbSpawner.raise((int) pid, gdbSpawner.INT); + fInterruptFailedJob = new MonitorInterruptJob(timeout, rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, + "Cannot interrupt.", null)); //$NON-NLS-1$ + rm.done(); + } + } @Override - public void destroy() { - // Don't close the streams ourselves as it may be too early. - // Wait for the actual user of the streams to close it. - // Bug 339379 - - // destroy() should be supported even if it's not spawner. - if (getState() == State.STARTED) { - fProcess.destroy(); - } - } + public void destroy() { + // Don't close the streams ourselves as it may be too early. + // Wait for the actual user of the streams to close it. + // Bug 339379 + + // destroy() should be supported even if it's not spawner. + if (getState() == State.STARTED) { + fProcess.destroy(); + } + } @Override - public State getState() { - return fBackendState; - } - + public State getState() { + return fBackendState; + } + @Override - public int getExitCode() { - return fGDBExitValue; - } - + public int getExitCode() { + return fGDBExitValue; + } + @Override - public SessionType getSessionType() { - if (fSessionType == null) { - fSessionType = LaunchUtils.getSessionType(fLaunchConfiguration); - } - return fSessionType; - } + public SessionType getSessionType() { + if (fSessionType == null) { + fSessionType = LaunchUtils.getSessionType(fLaunchConfiguration); + } + return fSessionType; + } @Override public boolean getIsAttachSession() { - if (fAttach == null) { - fAttach = LaunchUtils.getIsAttach(fLaunchConfiguration); - } - return fAttach; + if (fAttach == null) { + fAttach = LaunchUtils.getIsAttach(fLaunchConfiguration); + } + return fAttach; } @Override protected BundleContext getBundleContext() { - return GdbPlugin.getBundleContext(); + return GdbPlugin.getBundleContext(); } protected class GDBProcessStep extends InitializationShutdownStep { - GDBProcessStep(Direction direction) { super(direction); } - - @Override - public void initialize(final RequestMonitor requestMonitor) { - doGDBProcessStep(requestMonitor); - } - - @Override - protected void shutdown(final RequestMonitor requestMonitor) { - undoGDBProcessStep(requestMonitor); - } - } + GDBProcessStep(Direction direction) { + super(direction); + } - protected class MonitorJobStep extends InitializationShutdownStep { - MonitorJobStep(Direction direction) { super(direction); } + @Override + public void initialize(final RequestMonitor requestMonitor) { + doGDBProcessStep(requestMonitor); + } - @Override - public void initialize(final RequestMonitor requestMonitor) { - doMonitorJobStep(requestMonitor); - } + @Override + protected void shutdown(final RequestMonitor requestMonitor) { + undoGDBProcessStep(requestMonitor); + } + } - @Override - protected void shutdown(RequestMonitor requestMonitor) { - undoMonitorJobStep(requestMonitor); - } - } - - protected class RegisterStep extends InitializationShutdownStep { - RegisterStep(Direction direction) { super(direction); } - @Override - public void initialize(RequestMonitor requestMonitor) { - doRegisterStep(requestMonitor); - } + protected class MonitorJobStep extends InitializationShutdownStep { + MonitorJobStep(Direction direction) { + super(direction); + } - @Override - protected void shutdown(RequestMonitor requestMonitor) { - undoRegisterStep(requestMonitor); - } - } + @Override + public void initialize(final RequestMonitor requestMonitor) { + doMonitorJobStep(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + undoMonitorJobStep(requestMonitor); + } + } + + protected class RegisterStep extends InitializationShutdownStep { + RegisterStep(Direction direction) { + super(direction); + } + + @Override + public void initialize(RequestMonitor requestMonitor) { + doRegisterStep(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + undoRegisterStep(requestMonitor); + } + } /** @since 5.0 */ protected void doGDBProcessStep(final RequestMonitor requestMonitor) { - class GDBLaunchMonitor { - boolean fLaunched = false; - boolean fTimedOut = false; - } - final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor(); + class GDBLaunchMonitor { + boolean fLaunched = false; + boolean fTimedOut = false; + } + final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor(); - final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleCompleted() { - if (!fGDBLaunchMonitor.fTimedOut) { - fGDBLaunchMonitor.fLaunched = true; - if (!isSuccess()) { - requestMonitor.setStatus(getStatus()); - } - requestMonitor.done(); - } - } - }; - - final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$ - { - setSystem(true); - } + final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + if (!fGDBLaunchMonitor.fTimedOut) { + fGDBLaunchMonitor.fLaunched = true; + if (!isSuccess()) { + requestMonitor.setStatus(getStatus()); + } + requestMonitor.done(); + } + } + }; - @Override - protected IStatus run(IProgressMonitor monitor) { - if (gdbLaunchRequestMonitor.isCanceled()) { - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.CANCEL, GdbPlugin.PLUGIN_ID, -1, "Canceled starting GDB", null)); //$NON-NLS-1$ - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - - try { - fProcess = launchGDBProcess(); - // Need to do this on the executor for thread-safety - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { fBackendState = State.STARTED; } - }); - // Don't send the backendStarted event yet. We wait until we have registered this service - // so that other services can have access to it. - } catch(CoreException e) { - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, e.getMessage(), e)); - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - - BufferedReader inputReader = null; - BufferedReader errorReader = null; - boolean success = false; - try { - // Read initial GDB prompt - inputReader = new BufferedReader(new InputStreamReader(getMIInputStream())); - String line; - while ((line = inputReader.readLine()) != null) { - line = line.trim(); - if (line.endsWith("(gdb)")) { //$NON-NLS-1$ - success = true; - break; - } - } - - // Failed to read initial prompt, check for error - if (!success) { - errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream())); - String errorInfo = errorReader.readLine(); - if (errorInfo == null) { - errorInfo = "GDB prompt not read"; //$NON-NLS-1$ - } - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null)); - } - } catch (IOException e) { - success = false; - gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$ - } + final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$ + { + setSystem(true); + } - // In the case of failure, close the MI streams so - // they are not leaked. - if (!success) - { - if (inputReader != null) { - try { + @Override + protected IStatus run(IProgressMonitor monitor) { + if (gdbLaunchRequestMonitor.isCanceled()) { + gdbLaunchRequestMonitor.setStatus( + new Status(IStatus.CANCEL, GdbPlugin.PLUGIN_ID, -1, "Canceled starting GDB", null)); //$NON-NLS-1$ + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + try { + fProcess = launchGDBProcess(); + // Need to do this on the executor for thread-safety + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + fBackendState = State.STARTED; + } + }); + // Don't send the backendStarted event yet. We wait + // until we have registered this service + // so that other services can have access to it. + } catch (CoreException e) { + gdbLaunchRequestMonitor + .setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, e.getMessage(), e)); + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + BufferedReader inputReader = null; + BufferedReader errorReader = null; + boolean success = false; + try { + // Read initial GDB prompt + inputReader = new BufferedReader(new InputStreamReader(getMIInputStream())); + String line; + while ((line = inputReader.readLine()) != null) { + line = line.trim(); + if (line.endsWith("(gdb)")) { //$NON-NLS-1$ + success = true; + break; + } + } + + // Failed to read initial prompt, check for error + if (!success) { + errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream())); + String errorInfo = errorReader.readLine(); + if (errorInfo == null) { + errorInfo = "GDB prompt not read"; //$NON-NLS-1$ + } + gdbLaunchRequestMonitor + .setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, errorInfo, null)); + } + } catch (IOException e) { + success = false; + gdbLaunchRequestMonitor.setStatus( + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB output", e)); //$NON-NLS-1$ + } + + // In the case of failure, close the MI streams so + // they are not leaked. + if (!success) { + if (inputReader != null) { + try { inputReader.close(); } catch (IOException e) { } - } - if (errorReader != null) { - try { + } + if (errorReader != null) { + try { errorReader.close(); } catch (IOException e) { } - } - } - - gdbLaunchRequestMonitor.done(); - return Status.OK_STATUS; - } - }; - startGdbJob.schedule(); - - getExecutor().schedule(new Runnable() { - @Override - public void run() { - // Only process the event if we have not finished yet (hit the breakpoint). - if (!fGDBLaunchMonitor.fLaunched) { - fGDBLaunchMonitor.fTimedOut = true; - Thread jobThread = startGdbJob.getThread(); - if (jobThread != null) { - jobThread.interrupt(); - } - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - fGDBLaunchTimeout, TimeUnit.SECONDS); + } + } + + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + }; + startGdbJob.schedule(); + + getExecutor().schedule(new Runnable() { + @Override + public void run() { + // Only process the event if we have not finished yet (hit + // the breakpoint). + if (!fGDBLaunchMonitor.fLaunched) { + fGDBLaunchMonitor.fTimedOut = true; + Thread jobThread = startGdbJob.getThread(); + if (jobThread != null) { + jobThread.interrupt(); + } + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$ + requestMonitor.done(); + } + } + }, fGDBLaunchTimeout, TimeUnit.SECONDS); } /** @since 5.0 */ protected void undoGDBProcessStep(final RequestMonitor requestMonitor) { - if (getState() != State.STARTED) { - // gdb not started yet or already killed, don't bother starting - // a job to kill it - requestMonitor.done(); - return; - } + if (getState() != State.STARTED) { + // gdb not started yet or already killed, don't bother starting + // a job to kill it + requestMonitor.done(); + return; + } - new Job("Terminating GDB process.") { //$NON-NLS-1$ - { - setSystem(true); - } - - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - // Need to do this on the executor for thread-safety - // And we should wait for it to complete since we then - // check if the killing of GDB worked. - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - destroy(); - - if (fMonitorJob.fMonitorExited) { - // Now that we have destroyed the process, - // and that the monitoring thread was killed, - // we need to set our state and send the event - fBackendState = State.TERMINATED; - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), - getProperties()); - } - } - }).get(); + new Job("Terminating GDB process.") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + // Need to do this on the executor for thread-safety + // And we should wait for it to complete since we then + // check if the killing of GDB worked. + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + destroy(); + + if (fMonitorJob.fMonitorExited) { + // Now that we have destroyed the process, and + // that the monitoring thread was killed, we + // need to set our state and send the event + fBackendState = State.TERMINATED; + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), + getProperties()); + } + } + }).get(); } catch (InterruptedException e1) { } catch (ExecutionException e1) { } - - int attempts = 0; - while (attempts < 10) { - try { - // Don't know if we really need the exit value... but what the heck. - fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited - - requestMonitor.done(); - return Status.OK_STATUS; - } catch (IllegalThreadStateException ie) { - } - try { - Thread.sleep(500); - } catch (InterruptedException e) { - } - attempts++; - } - requestMonitor.setStatus(new Status( - IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "GDB terminate failed", null)); //$NON-NLS-1$ - requestMonitor.done(); - return Status.OK_STATUS; - } - }.schedule(); + + int attempts = 0; + while (attempts < 10) { + try { + // Don't know if we really need the exit value... + // but what the heck. + // throws exception if process not exited + fGDBExitValue = fProcess.exitValue(); + + requestMonitor.done(); + return Status.OK_STATUS; + } catch (IllegalThreadStateException ie) { + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + attempts++; + } + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + IDsfStatusConstants.REQUEST_FAILED, "GDB terminate failed", null)); //$NON-NLS-1$ + requestMonitor.done(); + return Status.OK_STATUS; + } + }.schedule(); } - - /** @since 5.0 */ - protected void doMonitorJobStep(final RequestMonitor requestMonitor) { - fMonitorJob = new MonitorJob( - fProcess, - new DsfRunnable() { - @Override - public void run() { - requestMonitor.done(); - } - }); - fMonitorJob.schedule(); - } - - /** @since 5.0 */ - protected void undoMonitorJobStep(RequestMonitor requestMonitor) { - if (fMonitorJob != null) { - fMonitorJob.kill(); - } - requestMonitor.done(); - } /** @since 5.0 */ - protected void doRegisterStep(RequestMonitor requestMonitor) { - register(new String[]{ IMIBackend.class.getName(), - IMIBackend2.class.getName(), - IGDBBackend.class.getName() }, - new Hashtable()); - - getSession().addServiceEventListener(GDBBackend.this, null); - - /* - * This event is not consumed by any one at present, instead it's - * the GDBControlInitializedDMEvent that's used to indicate that GDB - * back end is ready for MI commands. But we still fire the event as - * it does no harm and may be needed sometime.... 09/29/2008 - * - * We send the event in the register step because that is when - * other services have access to it. - */ - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.STARTED), - getProperties()); - - requestMonitor.done(); - } + protected void doMonitorJobStep(final RequestMonitor requestMonitor) { + fMonitorJob = new MonitorJob(fProcess, new DsfRunnable() { + @Override + public void run() { + requestMonitor.done(); + } + }); + fMonitorJob.schedule(); + } /** @since 5.0 */ - protected void undoRegisterStep(RequestMonitor requestMonitor) { - unregister(); - getSession().removeServiceEventListener(GDBBackend.this); - requestMonitor.done(); - } - - /** - * Monitors a system process, waiting for it to terminate, and - * then notifies the associated runtime process. - */ - private class MonitorJob extends Job { - boolean fMonitorExited = false; - DsfRunnable fMonitorStarted; - Process fMonProcess; + protected void undoMonitorJobStep(RequestMonitor requestMonitor) { + if (fMonitorJob != null) { + fMonitorJob.kill(); + } + requestMonitor.done(); + } - @Override - protected IStatus run(IProgressMonitor monitor) { - synchronized(fMonProcess) { - getExecutor().submit(fMonitorStarted); - try { - fMonProcess.waitFor(); - fGDBExitValue = fMonProcess.exitValue(); + /** @since 5.0 */ + protected void doRegisterStep(RequestMonitor requestMonitor) { + register(new String[] { IMIBackend.class.getName(), IMIBackend2.class.getName(), IGDBBackend.class.getName() }, + new Hashtable()); - // Need to do this on the executor for thread-safety - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - destroy(); - fBackendState = State.TERMINATED; - getSession().dispatchEvent( - new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), - getProperties()); - } - }); - } catch (InterruptedException ie) { - // clear interrupted state - Thread.interrupted(); - } + getSession().addServiceEventListener(GDBBackend.this, null); - fMonitorExited = true; - } - return Status.OK_STATUS; - } + /* + * This event is not consumed by any one at present, instead it's the + * GDBControlInitializedDMEvent that's used to indicate that GDB back + * end is ready for MI commands. But we still fire the event as it does + * no harm and may be needed sometime.... 09/29/2008 + * + * We send the event in the register step because that is when other + * services have access to it. + */ + getSession().dispatchEvent(new BackendStateChangedEvent(getSession().getId(), getId(), State.STARTED), + getProperties()); - MonitorJob(Process process, DsfRunnable monitorStarted) { - super("GDB process monitor job."); //$NON-NLS-1$ - fMonProcess = process; - fMonitorStarted = monitorStarted; - setSystem(true); - } + requestMonitor.done(); + } - void kill() { - synchronized(fMonProcess) { - if (!fMonitorExited) { - getThread().interrupt(); - } - } - } - } + /** @since 5.0 */ + protected void undoRegisterStep(RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(GDBBackend.this); + requestMonitor.done(); + } - /** - * Stores the request monitor that must be dealt with for - * the result of the interrupt operation. If the interrupt - * successfully suspends the backend, the request monitor can - * be retrieved and completed successfully, and then this job - * should be canceled. If this job is not canceled before - * the time is up, it will imply the interrupt did not - * successfully suspend the backend, and the current job will - * indicate this in the request monitor. - * - * The specified timeout is used to indicate how many milliseconds - * this job should wait for. INTERRUPT_TIMEOUT_DEFAULT indicates - * to use the default of 5 seconds. The default is also use if the - * timeout value is 0 or negative. - * - * @since 3.0 - */ - protected class MonitorInterruptJob extends Job { - // Bug 310274. Until we have a preference to configure timeouts, - // we need a large enough default timeout to accommodate slow - // remote sessions. - private final static int TIMEOUT_DEFAULT_VALUE = 5000; - private final RequestMonitor fRequestMonitor; + /** + * Monitors a system process, waiting for it to terminate, and then notifies + * the associated runtime process. + */ + private class MonitorJob extends Job { + boolean fMonitorExited = false; + DsfRunnable fMonitorStarted; + Process fMonProcess; - public MonitorInterruptJob(int timeout, RequestMonitor rm) { - super("Interrupt monitor job."); //$NON-NLS-1$ - setSystem(true); - fRequestMonitor = rm; - - if (timeout == INTERRUPT_TIMEOUT_DEFAULT || timeout <= 0) { - timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds - } - - schedule(timeout); - } + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized (fMonProcess) { + getExecutor().submit(fMonitorStarted); + try { + fMonProcess.waitFor(); + fGDBExitValue = fMonProcess.exitValue(); - @Override - protected IStatus run(IProgressMonitor monitor) { - getExecutor().submit( - new DsfRunnable() { - @Override - public void run() { - fInterruptFailedJob = null; - fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$ - fRequestMonitor.done(); - } - }); - return Status.OK_STATUS; - } + // Need to do this on the executor for thread-safety + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + destroy(); + fBackendState = State.TERMINATED; + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), State.TERMINATED), + getProperties()); + } + }); + } catch (InterruptedException ie) { + // clear interrupted state + Thread.interrupted(); + } - public RequestMonitor getRequestMonitor() { return fRequestMonitor; } - } + fMonitorExited = true; + } + return Status.OK_STATUS; + } + + MonitorJob(Process process, DsfRunnable monitorStarted) { + super("GDB process monitor job."); //$NON-NLS-1$ + fMonProcess = process; + fMonitorStarted = monitorStarted; + setSystem(true); + } + + void kill() { + synchronized (fMonProcess) { + if (!fMonitorExited) { + getThread().interrupt(); + } + } + } + } + + /** + * Stores the request monitor that must be dealt with for the result of the + * interrupt operation. If the interrupt successfully suspends the backend, + * the request monitor can be retrieved and completed successfully, and then + * this job should be canceled. If this job is not canceled before the time + * is up, it will imply the interrupt did not successfully suspend the + * backend, and the current job will indicate this in the request monitor. + * + * The specified timeout is used to indicate how many milliseconds this job + * should wait for. INTERRUPT_TIMEOUT_DEFAULT indicates to use the default + * of 5 seconds. The default is also use if the timeout value is 0 or + * negative. + * + * @since 3.0 + */ + protected class MonitorInterruptJob extends Job { + // Bug 310274. Until we have a preference to configure timeouts, + // we need a large enough default timeout to accommodate slow + // remote sessions. + private final static int TIMEOUT_DEFAULT_VALUE = 5000; + private final RequestMonitor fRequestMonitor; + + public MonitorInterruptJob(int timeout, RequestMonitor rm) { + super("Interrupt monitor job."); //$NON-NLS-1$ + setSystem(true); + fRequestMonitor = rm; + + if (timeout == INTERRUPT_TIMEOUT_DEFAULT || timeout <= 0) { + timeout = TIMEOUT_DEFAULT_VALUE; // default of 5 seconds + } + + schedule(timeout); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + getExecutor().submit(new DsfRunnable() { + @Override + public void run() { + fInterruptFailedJob = null; + fRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + IDsfStatusConstants.REQUEST_FAILED, "Interrupt failed.", null)); //$NON-NLS-1$ + fRequestMonitor.done(); + } + }); + return Status.OK_STATUS; + } + + public RequestMonitor getRequestMonitor() { + return fRequestMonitor; + } + + } /** * We use this handler to determine if the SIGINT we sent to GDB has been @@ -967,13 +845,13 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa * @since 3.0 * */ - @DsfServiceEventHandler - public void eventDispatched(final MIStoppedEvent e) { - if (fInterruptFailedJob != null) { - if (fInterruptFailedJob.cancel()) { - fInterruptFailedJob.getRequestMonitor().done(); - } - fInterruptFailedJob = null; - } - } + @DsfServiceEventHandler + public void eventDispatched(final MIStoppedEvent e) { + if (fInterruptFailedJob != null) { + if (fInterruptFailedJob.cancel()) { + fInterruptFailedJob.getRequestMonitor().done(); + } + fInterruptFailedJob = null; + } + } } diff --git a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF index ac5d70d56a1..b84b9e14d67 100644 --- a/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF +++ b/qt/org.eclipse.cdt.qt.core/META-INF/MANIFEST.MF @@ -18,7 +18,9 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.cdt.debug.core, org.freemarker;bundle-version="2.3.22", org.eclipse.cdt.build.core;bundle-version="1.0.0", - org.eclipse.cdt.build.gcc.core;bundle-version="1.0.0" + org.eclipse.cdt.build.gcc.core;bundle-version="1.0.0", + org.eclipse.cdt.dsf.gdb;bundle-version="5.0.0", + org.eclipse.cdt.dsf;bundle-version="2.6.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Bundle-Localization: plugin diff --git a/qt/org.eclipse.cdt.qt.core/plugin.xml b/qt/org.eclipse.cdt.qt.core/plugin.xml index f868e7ce665..0e6de1a7d7d 100644 --- a/qt/org.eclipse.cdt.qt.core/plugin.xml +++ b/qt/org.eclipse.cdt.qt.core/plugin.xml @@ -165,16 +165,6 @@ class="org.eclipse.cdt.internal.qt.core.build.QtScannerInfoProvider"> - - - - + + + + + + + + + + diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java index 22c4fe143a6..6e9a589f483 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/build/QtBuildConfigurationFactory.java @@ -124,7 +124,7 @@ public class QtBuildConfigurationFactory implements IAdapterFactory { for (IBuildConfiguration config : project.getBuildConfigs()) { configNames.add(config.getName()); } - String baseName = qtInstall.getSpec() + ":" + launchMode; //$NON-NLS-1$ + String baseName = qtInstall.getSpec() + "." + launchMode; //$NON-NLS-1$ String newName = baseName; int n = 0; while (configNames.contains(newName)) { diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java new file mode 100644 index 00000000000..e360dcd0b35 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2016 QNX Software Systems 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.qt.core.launch; + +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.ServicesLaunchSequence; +import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.internal.qt.core.Activator; +import org.eclipse.cdt.qt.core.QtBuildConfiguration; +import org.eclipse.cdt.qt.core.QtLaunchConfigurationDelegate; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.launchbar.core.target.ILaunchTarget; +import org.eclipse.launchbar.core.target.launch.ITargetedLaunch; + +public class QtLocalDebugLaunchConfigDelegate extends QtLaunchConfigurationDelegate { + + @Override + public ITargetedLaunch getLaunch(ILaunchConfiguration configuration, String mode, ILaunchTarget target) + throws CoreException { + GdbLaunch launch = new GdbLaunch(configuration, mode, null); + launch.setLaunchTarget(target); + launch.initialize(); + + DsfSourceLookupDirector locator = new DsfSourceLookupDirector(launch.getSession()); + String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null); + if (memento == null) { + locator.initializeDefaults(configuration); + } else { + locator.initializeFromMemento(memento, configuration); + } + + launch.setSourceLocator(locator); + return launch; + } + + @Override + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) + throws CoreException { + GdbLaunch gdbLaunch = (GdbLaunch) launch; + ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); + QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); + + // TODO get it from the toolchain + gdbLaunch.setGDBPath("/usr/local/bin/gdb"); + String gdbVersion = gdbLaunch.getGDBVersion(); + + Path exeFile = qtBuildConfig.getProgramPath(); + gdbLaunch.setProgramPath(exeFile.toString()); + + gdbLaunch.setServiceFactory(new GdbDebugServicesFactory(gdbVersion)); + + Sequence servicesLaunchSequence = new ServicesLaunchSequence(gdbLaunch.getSession(), gdbLaunch, monitor); + gdbLaunch.getSession().getExecutor().execute(servicesLaunchSequence); + try { + servicesLaunchSequence.get(); + } catch (InterruptedException | ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, Activator.ID, "Failure launching with gdb", e)); + } + + gdbLaunch.initializeControl(); + + gdbLaunch.addCLIProcess(gdbLaunch.getGDBPath().toOSString() + " (" + gdbVersion + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + + Query ready = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + DsfServicesTracker tracker = new DsfServicesTracker( + Activator.getDefault().getBundle().getBundleContext(), gdbLaunch.getSession().getId()); + IGDBControl control = tracker.getService(IGDBControl.class); + tracker.dispose(); + control.completeInitialization( + new RequestMonitorWithProgress(ImmediateExecutor.getInstance(), monitor) { + @Override + protected void handleCompleted() { + if (isCanceled()) { + rm.cancel(); + } else { + rm.setStatus(getStatus()); + } + rm.done(); + } + }); + } + }; + + // Start it up + gdbLaunch.getSession().getExecutor().execute(ready); + try { + ready.get(); + } catch (ExecutionException | InterruptedException e) { + throw new DebugException(new Status(IStatus.ERROR, Activator.ID, "Failure to start debug session", e)); + } + } + +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java index 238b40cac47..37049e0b434 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalRunLaunchConfigDelegate.java @@ -7,7 +7,6 @@ *******************************************************************************/ package org.eclipse.cdt.internal.qt.core.launch; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Map; @@ -18,9 +17,7 @@ import org.eclipse.cdt.qt.core.QtLaunchConfigurationDelegate; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; @@ -34,70 +31,25 @@ public class QtLocalRunLaunchConfigDelegate extends QtLaunchConfigurationDelegat @Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { - new Job("Running Qt App") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); - QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); + ILaunchTarget target = ((ITargetedLaunch) launch).getLaunchTarget(); + QtBuildConfiguration qtBuildConfig = getQtBuildConfiguration(configuration, mode, target, monitor); - // get the executable - Path buildFolder = qtBuildConfig.getBuildDirectory(); - Path exeFile; - switch (Platform.getOS()) { - case Platform.OS_MACOSX: - // TODO this is mac local specific and really should be - // in the config - // TODO also need to pull the app name out of the pro - // file name - Path appFolder = buildFolder.resolve("main.app"); - Path contentsFolder = appFolder.resolve("Contents"); - Path macosFolder = contentsFolder.resolve("MacOS"); - exeFile = macosFolder.resolve("main"); - break; - case Platform.OS_WIN32: - Path releaseFolder = buildFolder.resolve("release"); - exeFile = releaseFolder.resolve("main.exe"); - break; - default: - return new Status(IStatus.ERROR, Activator.ID, "platform not supported: " + Platform.getOS()); - } + // get the executable + Path exeFile = qtBuildConfig.getProgramPath(); - ProcessBuilder builder = new ProcessBuilder(exeFile.toString()) - .directory(qtBuildConfig.getProject().getLocation().toFile()); + ProcessBuilder builder = new ProcessBuilder(exeFile.toString()) + .directory(qtBuildConfig.getProject().getLocation().toFile()); - // need to add the Qt libraries to the env - Map env = builder.environment(); - Path libPath = qtBuildConfig.getQtInstall().getLibPath(); - switch (Platform.getOS()) { - case Platform.OS_MACOSX: - String libPathEnv = env.get("DYLD_LIBRARY_PATH"); - if (libPathEnv == null) { - libPathEnv = libPath.toString(); - } else { - libPathEnv = libPath.toString() + File.pathSeparator + libPathEnv; - } - env.put("DYLD_LIBRARY_PATH", libPathEnv); - break; - case Platform.OS_WIN32: - String path = env.get("PATH"); - // TODO really need a bin path - // and resolve doesn't work properly on Windows - path = "C:/Qt/5.5/mingw492_32/bin;" + path; - env.put("PATH", path); - break; - } + // set up the environment + Map env = builder.environment(); + qtBuildConfig.setProgramEnvironment(env); - Process process = builder.start(); - DebugPlugin.newProcess(launch, process, "main"); - } catch (IOException e) { - return new Status(IStatus.ERROR, Activator.ID, "running", e); - } catch (CoreException e) { - return e.getStatus(); - } - return Status.OK_STATUS; - } - }.schedule(); + try { + Process process = builder.start(); + DebugPlugin.newProcess(launch, process, "main"); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.ID, "Failed to start", e)); + } } } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java index 0cc6762955a..fff397ac7c9 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtBuildConfiguration.java @@ -29,6 +29,10 @@ import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.internal.qt.core.Activator; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; @@ -107,6 +111,48 @@ public class QtBuildConfiguration extends CBuildConfiguration { } } + public Path getProgramPath() throws CoreException { + switch (Platform.getOS()) { + case Platform.OS_MACOSX: + // TODO this is mac local specific and really should be + // in the config + // TODO also need to pull the app name out of the pro + // file name + Path appFolder = getBuildDirectory().resolve("main.app"); + Path contentsFolder = appFolder.resolve("Contents"); + Path macosFolder = contentsFolder.resolve("MacOS"); + return macosFolder.resolve("main"); + case Platform.OS_WIN32: + Path releaseFolder = getBuildDirectory().resolve("release"); + return releaseFolder.resolve("main.exe"); + default: + throw new CoreException( + new Status(IStatus.ERROR, Activator.ID, "platform not supported: " + Platform.getOS())); + } + } + + public void setProgramEnvironment(Map env) { + Path libPath = getQtInstall().getLibPath(); + switch (Platform.getOS()) { + case Platform.OS_MACOSX: + String libPathEnv = env.get("DYLD_LIBRARY_PATH"); + if (libPathEnv == null) { + libPathEnv = libPath.toString(); + } else { + libPathEnv = libPath.toString() + File.pathSeparator + libPathEnv; + } + env.put("DYLD_LIBRARY_PATH", libPathEnv); + break; + case Platform.OS_WIN32: + String path = env.get("PATH"); + // TODO really need a bin path + // and resolve doesn't work properly on Windows + path = "C:/Qt/5.5/mingw492_32/bin;" + path; + env.put("PATH", path); + break; + } + } + public String getProperty(String key) { if (properties == null) { List cmd = new ArrayList<>(); diff --git a/qt/org.eclipse.cdt.qt.ui/plugin.xml b/qt/org.eclipse.cdt.qt.ui/plugin.xml index 327cb18b55b..476e89cc8db 100644 --- a/qt/org.eclipse.cdt.qt.ui/plugin.xml +++ b/qt/org.eclipse.cdt.qt.ui/plugin.xml @@ -86,6 +86,13 @@ class="org.eclipse.cdt.internal.qt.ui.launch.QtLocalLaunchConfigurationTabGroup" id="org.eclipse.cdt.qt.ui.launchConfigurationTabGroup" type="org.eclipse.cdt.qt.core.launchConfigurationType"> + + + +