mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-22 22:22:11 +02:00
Bug 303808: Support for full GDB console
This patch provides a full GDB console in the standard Console view. The full console is disabled for Windows due to PTY limitations. The new console will be triggered automatically when using GDB 7.12 (or its pre-release) and running on Linux. Known issues with this patch: - Cloning the console view breaks the console - Pinning the console view is not supported - Interrupting execution in all-stop mode fails with the new console - Pagination causes some events to be lost (all-stop only) Change-Id: Iee6ef5228ca17bd829eb743cb41a142afe6714dc
This commit is contained in:
parent
91c342b77c
commit
0caa750ef0
28 changed files with 1072 additions and 77 deletions
|
@ -347,6 +347,9 @@ Java and all Java-based trademarks are trademarks of Oracle Corporation in the U
|
|||
<plugin id="org.eclipse.swt.win32.win32.x86_64" fragment="true"/>
|
||||
<plugin id="org.eclipse.team.core"/>
|
||||
<plugin id="org.eclipse.team.ui"/>
|
||||
<plugin id="org.eclipse.tm.terminal.control"/>
|
||||
<plugin id="org.eclipse.tm.terminal.view.core"/>
|
||||
<plugin id="org.eclipse.tm.terminal.view.ui"/>
|
||||
<plugin id="org.eclipse.text"/>
|
||||
<plugin id="org.eclipse.tools.templates.core"/>
|
||||
<plugin id="org.eclipse.tools.templates.ui"/>
|
||||
|
|
|
@ -25,7 +25,10 @@ Require-Bundle: org.eclipse.ui,
|
|||
org.eclipse.core.filesystem;bundle-version="1.2.0",
|
||||
org.eclipse.cdt.launch;bundle-version="6.1.0",
|
||||
org.eclipse.debug.core,
|
||||
org.eclipse.core.resources
|
||||
org.eclipse.core.resources,
|
||||
org.eclipse.tm.terminal.control;bundle-version="4.0.0",
|
||||
org.eclipse.tm.terminal.view.core;bundle-version="4.0.0",
|
||||
org.eclipse.tm.terminal.view.ui;bundle-version="4.1.0"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Export-Package: org.eclipse.cdt.dsf.gdb.internal.ui;x-friends:="org.eclipse.cdt.docker.launcher",
|
||||
|
|
|
@ -515,9 +515,10 @@
|
|||
class="org.eclipse.cdt.dsf.gdb.internal.ui.console.ConsolePageParticipant"
|
||||
id="org.eclipse.cdt.dsf.gdb.ui.dsfGdbConsolePageParticipant">
|
||||
<enablement>
|
||||
<instanceof
|
||||
value="org.eclipse.ui.console.IOConsole">
|
||||
</instanceof>
|
||||
<or>
|
||||
<instanceof value="org.eclipse.ui.console.IOConsole"/>
|
||||
<instanceof value="org.eclipse.cdt.dsf.gdb.internal.ui.console.GdbCliConsole"/>
|
||||
</or>
|
||||
</enablement>
|
||||
</consolePageParticipant>
|
||||
</extension>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
package org.eclipse.cdt.dsf.gdb.internal.ui;
|
||||
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.console.GdbCliConsoleManager;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.console.TracingConsoleManager;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages;
|
||||
|
@ -46,6 +47,7 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
private static BundleContext fgBundleContext;
|
||||
|
||||
private static TracingConsoleManager fTracingConsoleManager;
|
||||
private static GdbCliConsoleManager fGdbConsoleManager;
|
||||
|
||||
private static IPreferenceStore fCorePreferenceStore;
|
||||
|
||||
|
@ -67,6 +69,9 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
|
||||
fTracingConsoleManager = new TracingConsoleManager();
|
||||
fTracingConsoleManager.startup();
|
||||
|
||||
fGdbConsoleManager = new GdbCliConsoleManager();
|
||||
fGdbConsoleManager.startup();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -76,6 +81,7 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
fTracingConsoleManager.shutdown();
|
||||
fGdbConsoleManager.shutdown();
|
||||
|
||||
disposeAdapterSets();
|
||||
plugin = null;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2010 Marc-Andre Laperle and others.
|
||||
* Copyright (c) 2010, 2016 Marc-Andre Laperle 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
|
||||
|
@ -17,7 +17,7 @@ import org.eclipse.osgi.util.NLS;
|
|||
*/
|
||||
public class ConsoleMessages extends NLS {
|
||||
public static String ConsoleMessages_trace_console_name;
|
||||
public static String ConsoleMessages_trace_console_terminated;
|
||||
public static String ConsoleMessages_console_terminated;
|
||||
|
||||
public static String ConsoleMessages_save_action_tooltip;
|
||||
public static String ConsoleMessages_save_confirm_overwrite_title;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
##########################################################################
|
||||
# Copyright (c) 2010 Marc-Andre Laperle and others.
|
||||
# Copyright (c) 2010, 2016 Marc-Andre Laperle 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
|
||||
|
@ -10,7 +10,7 @@
|
|||
##########################################################################
|
||||
|
||||
ConsoleMessages_trace_console_name=gdb traces
|
||||
ConsoleMessages_trace_console_terminated=<terminated>
|
||||
ConsoleMessages_console_terminated=<terminated>
|
||||
|
||||
ConsoleMessages_save_action_tooltip=Save console content
|
||||
ConsoleMessages_save_confirm_overwrite_title=Confirm overwrite
|
||||
|
|
|
@ -57,7 +57,8 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
|
|||
DebugUITools.getDebugContextManager().getContextService(fPage.getSite().getWorkbenchWindow()).addDebugContextListener(this);
|
||||
}
|
||||
|
||||
if(console instanceof TracingConsole || isConsoleGdbCli(console)) {
|
||||
if (console instanceof TracingConsole ||
|
||||
(isConsoleGdbCli(console) && console instanceof TextConsole)) {
|
||||
TextConsole textConsole = (TextConsole) console;
|
||||
|
||||
// Add the save console action
|
||||
|
@ -81,6 +82,9 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
|
|||
org.eclipse.debug.ui.console.IConsole debugConsole = (org.eclipse.debug.ui.console.IConsole)console;
|
||||
return (debugConsole.getProcess() instanceof GDBProcess);
|
||||
}
|
||||
if (console instanceof GdbCliConsole) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -152,6 +156,10 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
|
|||
return null;
|
||||
}
|
||||
|
||||
if (context instanceof GDBProcess) {
|
||||
return (GDBProcess)context;
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
// Look for the process that this context refers to, so we can select its console
|
||||
IDMContext dmc = context.getAdapter(IDMContext.class);
|
||||
|
@ -187,12 +195,18 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
|
|||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextListener#contextEvent(org.eclipse.debug.internal.ui.contexts.provisional.DebugContextEvent)
|
||||
*/
|
||||
@Override
|
||||
public void debugContextChanged(DebugContextEvent event) {
|
||||
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
|
||||
if (fView != null && fConsole instanceof GdbCliConsole) {
|
||||
IProcess currentProcess = getCurrentProcess();
|
||||
if (currentProcess instanceof GDBProcess &&
|
||||
((GdbCliConsole)fConsole).getLaunch().equals(currentProcess.getLaunch())) {
|
||||
fView.display(fConsole);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
IProcess consoleProcess = getConsoleProcess();
|
||||
if (fView != null && consoleProcess != null && consoleProcess.equals(getCurrentProcess())) {
|
||||
fView.display(fConsole);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Ericsson 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.dsf.gdb.internal.ui.console;
|
||||
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.debug.ui.DebugUITools;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.console.AbstractConsole;
|
||||
import org.eclipse.ui.console.IConsoleView;
|
||||
import org.eclipse.ui.part.IPageBookViewPage;
|
||||
|
||||
/**
|
||||
* A GDB CLI console.
|
||||
* This console actually runs a GDB process in CLI mode to achieve a
|
||||
* full-featured CLI interface. This is only supported with GDB >= 7.12
|
||||
* and if IGDBBackend.isFullGdbConsoleSupported() returns true.
|
||||
*/
|
||||
public class GdbCliConsole extends AbstractConsole {
|
||||
private final ILaunch fLaunch;
|
||||
private String fLabel;
|
||||
|
||||
public GdbCliConsole(ILaunch launch, String label) {
|
||||
super(label, null);
|
||||
fLaunch = launch;
|
||||
fLabel = label;
|
||||
|
||||
resetName();
|
||||
}
|
||||
|
||||
public ILaunch getLaunch() { return fLaunch; }
|
||||
|
||||
public void resetName() {
|
||||
String newName = computeName();
|
||||
String name = getName();
|
||||
if (!name.equals(newName)) {
|
||||
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> setName(newName));
|
||||
}
|
||||
}
|
||||
|
||||
protected String computeName() {
|
||||
String label = fLabel;
|
||||
|
||||
ILaunchConfiguration config = fLaunch.getLaunchConfiguration();
|
||||
if (config != null && !DebugUITools.isPrivate(config)) {
|
||||
String type = null;
|
||||
try {
|
||||
type = config.getType().getName();
|
||||
} catch (CoreException e) {
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(config.getName());
|
||||
if (type != null) {
|
||||
buffer.append(" ["); //$NON-NLS-1$
|
||||
buffer.append(type);
|
||||
buffer.append("] "); //$NON-NLS-1$
|
||||
}
|
||||
buffer.append(label);
|
||||
label = buffer.toString();
|
||||
}
|
||||
|
||||
if (fLaunch.isTerminated()) {
|
||||
return ConsoleMessages.ConsoleMessages_console_terminated + label;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPageBookViewPage createPage(IConsoleView view) {
|
||||
view.setFocus();
|
||||
return new GdbCliConsolePage(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2015 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ericsson - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.internal.ui.console;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent;
|
||||
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.debug.core.DebugPlugin;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.core.ILaunchesListener2;
|
||||
import org.eclipse.ui.console.ConsolePlugin;
|
||||
import org.eclipse.ui.console.IConsole;
|
||||
import org.eclipse.ui.console.IConsoleManager;
|
||||
|
||||
/**
|
||||
* A console manager for GDB sessions which adds and removes
|
||||
* gdb cli consoles.
|
||||
*
|
||||
* There is a single such console per debug session.
|
||||
* This console interacts directly with the GDB process using
|
||||
* the standard GDB CLI interface.
|
||||
* These consoles cannot be enabled/disabled by the user.
|
||||
* However, they are only supported by GDB >= 7.12;
|
||||
* to handle this limitation, the console manager will use the DSF Backend
|
||||
* service to establish if it should start a gdb cli console or not.
|
||||
*/
|
||||
public class GdbCliConsoleManager implements ILaunchesListener2 {
|
||||
|
||||
/**
|
||||
* Start the tracing console. We don't do this in a constructor, because
|
||||
* we need to use <code>this</code>.
|
||||
*/
|
||||
public void startup() {
|
||||
// Listen to launch events
|
||||
DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
|
||||
removeAllCliConsoles();
|
||||
}
|
||||
|
||||
protected void removeAllCliConsoles() {
|
||||
ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches();
|
||||
for (ILaunch launch : launches) {
|
||||
removeCliConsole(launch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchesAdded(ILaunch[] launches) {
|
||||
for (ILaunch launch : launches) {
|
||||
addCliConsole(launch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchesChanged(ILaunch[] launches) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchesRemoved(ILaunch[] launches) {
|
||||
for (ILaunch launch : launches) {
|
||||
removeCliConsole(launch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchesTerminated(ILaunch[] launches) {
|
||||
for (ILaunch launch : launches) {
|
||||
renameCliConsole(launch);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addCliConsole(ILaunch launch) {
|
||||
// Full CLI GDB consoles are only added for GdbLaunches
|
||||
if (launch instanceof GdbLaunch) {
|
||||
new GdbCliConsoleCreator((GdbLaunch)launch).init();
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeCliConsole(ILaunch launch) {
|
||||
GdbCliConsole console = getCliConsole(launch);
|
||||
if (console != null) {
|
||||
ConsolePlugin.getDefault().getConsoleManager().removeConsoles(new IConsole[]{console});
|
||||
}
|
||||
}
|
||||
|
||||
protected void renameCliConsole(ILaunch launch) {
|
||||
GdbCliConsole console = getCliConsole(launch);
|
||||
if (console != null) {
|
||||
console.resetName();
|
||||
}
|
||||
}
|
||||
|
||||
private GdbCliConsole getCliConsole(ILaunch launch) {
|
||||
ConsolePlugin plugin = ConsolePlugin.getDefault();
|
||||
if (plugin != null) {
|
||||
// This plugin can be null when running headless JUnit tests
|
||||
IConsoleManager manager = plugin.getConsoleManager();
|
||||
IConsole[] consoles = manager.getConsoles();
|
||||
for (IConsole console : consoles) {
|
||||
if (console instanceof GdbCliConsole) {
|
||||
GdbCliConsole gdbConsole = (GdbCliConsole)console;
|
||||
if (gdbConsole.getLaunch().equals(launch)) {
|
||||
return gdbConsole;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that determines if a GdbCliConsole should be created for
|
||||
* this particular Gdblaunch. It figures this out by asking the
|
||||
* Backend service.
|
||||
*/
|
||||
private class GdbCliConsoleCreator {
|
||||
private GdbLaunch fLaunch;
|
||||
private DsfSession fSession;
|
||||
|
||||
public GdbCliConsoleCreator(GdbLaunch launch) {
|
||||
fLaunch = launch;
|
||||
fSession = launch.getSession();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
try {
|
||||
fSession.getExecutor().submit(new DsfRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Look for backend service right away. It probably
|
||||
// won't be available yet but we must make sure.
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
|
||||
IGDBBackend backend = tracker.getService(IGDBBackend.class);
|
||||
tracker.dispose();
|
||||
|
||||
if (backend != null) {
|
||||
// Backend service already available, us it!
|
||||
verifyAndCreateCliConsole(backend);
|
||||
} else {
|
||||
// Backend service not available yet, let's wait for it to start.
|
||||
fSession.addServiceEventListener(new GdbBackendStartedListener(GdbCliConsoleCreator.this, fSession), null);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (RejectedExecutionException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@ConfinedToDsfExecutor("fSession.getExecutor()")
|
||||
private void verifyAndCreateCliConsole(IGDBBackend backend) {
|
||||
if (backend != null && backend.isFullGdbConsoleSupported()) {
|
||||
// Create an new Cli console .
|
||||
String gdbVersion;
|
||||
try {
|
||||
gdbVersion = fLaunch.getGDBVersion();
|
||||
} catch (CoreException e) {
|
||||
assert false : "Should not happen since the gdb version is cached"; //$NON-NLS-1$
|
||||
gdbVersion = "???"; //$NON-NLS-1$
|
||||
}
|
||||
String consoleTitle = fLaunch.getGDBPath().toOSString().trim() + " (" + gdbVersion +")"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
GdbCliConsole console = new GdbCliConsole(fLaunch, consoleTitle);
|
||||
|
||||
// Register this console
|
||||
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]{console});
|
||||
|
||||
// Very important to make sure the console view is open or else things will not work
|
||||
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
|
||||
}
|
||||
// Else, not the right type of backend service, or the service said not to start a GdbCliConsole
|
||||
}
|
||||
|
||||
@ConfinedToDsfExecutor("fSession.getExecutor()")
|
||||
private void backendStarted() {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
|
||||
IGDBBackend backend = tracker.getService(IGDBBackend.class);
|
||||
tracker.dispose();
|
||||
|
||||
verifyAndCreateCliConsole(backend);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used to listen for Backend started event which indicate
|
||||
* the DSF-GDB backend service can be used.
|
||||
* This class must be public to receive the event.
|
||||
*/
|
||||
public class GdbBackendStartedListener {
|
||||
private DsfSession fSession;
|
||||
private GdbCliConsoleCreator fCreator;
|
||||
|
||||
public GdbBackendStartedListener(GdbCliConsoleCreator creator, DsfSession session) {
|
||||
fCreator = creator;
|
||||
fSession = session;
|
||||
}
|
||||
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(BackendStateChangedEvent event) {
|
||||
if (event.getState() == IMIBackend.State.STARTED &&
|
||||
event.getSessionId().equals(fSession.getId()))
|
||||
{
|
||||
fCreator.backendStarted();
|
||||
// No longer need to receive events.
|
||||
fSession.removeServiceEventListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2016 Ericsson 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.dsf.gdb.internal.ui.console;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.FillLayout;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.tm.internal.terminal.control.ITerminalListener;
|
||||
import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
|
||||
import org.eclipse.tm.internal.terminal.control.TerminalViewControlFactory;
|
||||
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
|
||||
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
|
||||
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
|
||||
import org.eclipse.tm.terminal.view.core.interfaces.ITerminalServiceOutputStreamMonitorListener;
|
||||
import org.eclipse.tm.terminal.view.core.interfaces.constants.ITerminalsConnectorConstants;
|
||||
import org.eclipse.tm.terminal.view.ui.interfaces.ILauncherDelegate;
|
||||
import org.eclipse.tm.terminal.view.ui.launcher.LauncherDelegateManager;
|
||||
import org.eclipse.ui.part.Page;
|
||||
|
||||
public class GdbCliConsolePage extends Page {
|
||||
|
||||
private DsfSession fSession;
|
||||
private Composite fMainComposite;
|
||||
|
||||
/** The control for the terminal widget embedded in the console */
|
||||
private ITerminalViewControl fTerminalControl;
|
||||
|
||||
public GdbCliConsolePage(GdbCliConsole gdbConsole) {
|
||||
ILaunch launch = gdbConsole.getLaunch();
|
||||
if (launch instanceof GdbLaunch) {
|
||||
fSession = ((GdbLaunch)launch).getSession();
|
||||
} else {
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
fTerminalControl.disposeTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createControl(Composite parent) {
|
||||
fMainComposite = new Composite(parent, SWT.NONE);
|
||||
fMainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
fMainComposite.setLayout(new FillLayout());
|
||||
|
||||
// Create the terminal control that will be used to interact with GDB
|
||||
fTerminalControl = TerminalViewControlFactory.makeControl(
|
||||
new ITerminalListener() {
|
||||
@Override public void setState(TerminalState state) {}
|
||||
@Override public void setTerminalTitle(final String title) {}
|
||||
},
|
||||
fMainComposite,
|
||||
new ITerminalConnector[] {},
|
||||
true);
|
||||
|
||||
try {
|
||||
fTerminalControl.setEncoding(Charset.defaultCharset().name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
}
|
||||
|
||||
// Hook the terminal control to the GDB process
|
||||
attachTerminalToGdbProcess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Control getControl() {
|
||||
return fMainComposite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocus() {
|
||||
fTerminalControl.setFocus();
|
||||
}
|
||||
|
||||
protected void attachTerminalToGdbProcess() {
|
||||
if (fSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fSession.getExecutor().submit(new DsfRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
|
||||
IGDBBackend backend = tracker.getService(IGDBBackend.class);
|
||||
tracker.dispose();
|
||||
|
||||
if (backend != null) {
|
||||
if (backend.getProcess() != null) {
|
||||
attachTerminal(backend.getProcess());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (RejectedExecutionException e) {
|
||||
}
|
||||
}
|
||||
|
||||
protected void attachTerminal(Process process) {
|
||||
ILauncherDelegate delegate =
|
||||
LauncherDelegateManager.getInstance().getLauncherDelegate("org.eclipse.tm.terminal.connector.streams.launcher.streams", false); //$NON-NLS-1$
|
||||
if (delegate != null) {
|
||||
Map<String, Object> properties = createNewSettings(process);
|
||||
|
||||
ITerminalConnector connector = delegate.createTerminalConnector(properties);
|
||||
fTerminalControl.setConnector(connector);
|
||||
if (fTerminalControl instanceof ITerminalControl) {
|
||||
((ITerminalControl)fTerminalControl).setConnectOnEnterIfClosed(false);
|
||||
((ITerminalControl)fTerminalControl).setVT100LineWrapping(true);
|
||||
}
|
||||
|
||||
// Must use syncExec because the logic within must complete before the rest
|
||||
// of the class methods (specifically getProcess()) is called
|
||||
fMainComposite.getDisplay().syncExec(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (fTerminalControl != null && !fTerminalControl.isDisposed()) {
|
||||
fTerminalControl.clearTerminal();
|
||||
fTerminalControl.connectTerminal();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> createNewSettings(Process process) {
|
||||
|
||||
// Create the terminal connector
|
||||
Map<String, Object> properties = new HashMap<String, Object>();
|
||||
properties.put(ITerminalsConnectorConstants.PROP_LOCAL_ECHO, Boolean.FALSE);
|
||||
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDIN, process.getOutputStream());
|
||||
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDOUT, process.getInputStream());
|
||||
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDERR, process.getErrorStream());
|
||||
properties.put(ITerminalsConnectorConstants.PROP_STDOUT_LISTENERS,
|
||||
new ITerminalServiceOutputStreamMonitorListener[0]);
|
||||
properties.put(ITerminalsConnectorConstants.PROP_STDERR_LISTENERS,
|
||||
new ITerminalServiceOutputStreamMonitorListener[0]);
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -138,7 +138,7 @@ public class TracingConsole extends IOConsole {
|
|||
}
|
||||
|
||||
if (fLaunch.isTerminated()) {
|
||||
return ConsoleMessages.ConsoleMessages_trace_console_terminated + label;
|
||||
return ConsoleMessages.ConsoleMessages_console_terminated + label;
|
||||
}
|
||||
|
||||
return label;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2010 Marc-Andre Laperle and others.
|
||||
* Copyright (c) 2010, 2016 Marc-Andre Laperle 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
|
||||
|
@ -10,13 +10,21 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.launching;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.IGDBBackendProcessWithoutIO;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.core.model.IStreamMonitor;
|
||||
import org.eclipse.debug.core.model.IStreamsProxy;
|
||||
import org.eclipse.debug.core.model.RuntimeProcess;
|
||||
|
||||
/**
|
||||
* A process for the gdb backend to differentiate it from the inferior
|
||||
* A process for the gdb backend to differentiate it from the inferior.
|
||||
*
|
||||
* This process disables the base class handling of IO streams if we
|
||||
* are using the full GDB console, which is handled by the
|
||||
* GdbConsoleManager instead.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
|
@ -27,4 +35,56 @@ public class GDBProcess extends RuntimeProcess {
|
|||
super(launch, process, name, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStreamsProxy getStreamsProxy() {
|
||||
IStreamsProxy proxy = super.getStreamsProxy();
|
||||
// If our proxy is the one that ignores the streams,
|
||||
// this method should return null.
|
||||
// Returning null insures that there will not be a
|
||||
// text console automatically created for this process
|
||||
// see ProcessConsoleManager#launchChanged()
|
||||
return proxy instanceof NoStreamsProxy ? null : proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStreamsProxy createStreamsProxy() {
|
||||
// TRICKY. This method is called by the constructor of
|
||||
// the super class. This means we don't have time to
|
||||
// set any fields in this class by then. Therefore,
|
||||
// we can only use what was set by the base class constructor
|
||||
// to figure out how to behave.
|
||||
// We can call getSystemProcess() as it is set earlier
|
||||
// in the constructor then when this method is called.
|
||||
if (getSystemProcess() instanceof IGDBBackendProcessWithoutIO) {
|
||||
// If the GDB process used does not handle I/O, we return a proxy
|
||||
// that ignores the streams.
|
||||
return new NoStreamsProxy();
|
||||
}
|
||||
return super.createStreamsProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that provides a streams proxy that actually
|
||||
* ignores the I/O streams. We use this in the case
|
||||
* of the full GDB console where the GDB CLI is used directly,
|
||||
* without us needing to do anything with the I/O ourselves.
|
||||
*
|
||||
* This is different than NullStreamsProxy which would
|
||||
* still read but discard the IO, which is not what we want.
|
||||
*/
|
||||
private class NoStreamsProxy implements IStreamsProxy {
|
||||
@Override
|
||||
public IStreamMonitor getErrorStreamMonitor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStreamMonitor getOutputStreamMonitor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String input) throws IOException {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,6 @@ 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;
|
||||
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;
|
||||
|
@ -202,13 +201,13 @@ public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITr
|
|||
@ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()")
|
||||
public void addCLIProcess(String label) throws CoreException {
|
||||
try {
|
||||
// Add the CLI process object to the launch.
|
||||
AbstractCLIProcess cliProc = getDsfExecutor().submit(new Callable<AbstractCLIProcess>() {
|
||||
// Add the GDB process object to the launch.
|
||||
Process gdbProc = getDsfExecutor().submit(new Callable<Process>() {
|
||||
@Override
|
||||
public AbstractCLIProcess call() throws CoreException {
|
||||
public Process call() throws CoreException {
|
||||
IGDBControl gdb = fTracker.getService(IGDBControl.class);
|
||||
if (gdb != null) {
|
||||
return gdb.getCLIProcess();
|
||||
return gdb.getGDBBackendProcess();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -221,7 +220,7 @@ public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITr
|
|||
Map<String, String> attributes = new HashMap<String, String>();
|
||||
attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
|
||||
IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE);
|
||||
DebugPlugin.newProcess(this, cliProc, label, attributes);
|
||||
DebugPlugin.newProcess(this, gdbProc, 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$
|
||||
|
|
|
@ -157,7 +157,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
};
|
||||
}
|
||||
|
||||
private GdbLaunch getGDBLaunch() {
|
||||
/** @since 5.1 */
|
||||
protected GdbLaunch getGDBLaunch() {
|
||||
return (GdbLaunch) getSession().getModelAdapter(ILaunch.class);
|
||||
}
|
||||
|
||||
|
@ -171,7 +172,9 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
* array. Allow subclass to override.
|
||||
*
|
||||
* @since 4.6
|
||||
* @deprecated Replaced by getDebuggerCommandLine()
|
||||
*/
|
||||
@Deprecated
|
||||
protected String[] getGDBCommandLineArray() {
|
||||
// The goal here is to keep options to an absolute minimum.
|
||||
// All configuration should be done in the final launch sequence
|
||||
|
@ -189,6 +192,39 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
return CommandLineUtil.argumentsToArray(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GDB command and its arguments as an array.
|
||||
* Allow subclass to override.
|
||||
* @since 5.1
|
||||
*/
|
||||
// This method replaces getGDBCommandLineArray() because we need
|
||||
// to override it for GDB 7.12 even if an extender has overridden
|
||||
// getGDBCommandLineArray().
|
||||
// Here is the scenario:
|
||||
// An extender has overridden getGDBCommandLineArray() to launch
|
||||
// GDB in MI mode but with extra parameters. Once GDBBackend_7_12
|
||||
// is released, the extender may likely point their extension to
|
||||
// GDBBackend_7_12 instead of GDBBackend (which will even happen
|
||||
// automatically if the extender extends GDBBackend_HEAD).
|
||||
// In such a case, they would override the changes in
|
||||
// GDBBackend_7_12.getGDBCommandLineArray() and the debug session
|
||||
// is likely to fail since with GDBBackend_7_12, we launch GDB
|
||||
// in CLI mode.
|
||||
//
|
||||
// Instead, we use getDebuggerCommandLine() and override that method in
|
||||
// GDBBackend_7_12. That way an extender will not override it
|
||||
// without noticing (since it didn't exist before). Then we can call
|
||||
// the overridden getGDBCommandLineArray() and work with that to
|
||||
// make it work with the new way to launch GDB of GDBBackend_7_12
|
||||
//
|
||||
// Note that we didn't name this method getGDBCommandLine() because
|
||||
// this name had been used in CDT 8.8 and could still be part of
|
||||
// extenders' code.
|
||||
protected String[] getDebuggerCommandLine() {
|
||||
// Call the old method in case it was overridden
|
||||
return getGDBCommandLineArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGDBInitFile() throws CoreException {
|
||||
return getGDBLaunch().getGDBInitFile();
|
||||
|
@ -236,12 +272,35 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
return getGDBLaunch().getUpdateThreadListOnSuspend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch GDB process. Allow subclass to override.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
// Again, we create a new method that we know has not been already
|
||||
// overridden. That way, even if extenders have overridden the
|
||||
// original launchGDBProcess(String[]), we will instead use
|
||||
// the GDBBackend_7_12#launchGDBProcess() method when appropriate.
|
||||
// This is important because if we didn't, the new console would
|
||||
// not work properly.
|
||||
//
|
||||
// Of course, in that case, we won't call the extenders overridden
|
||||
// launchGDBProcess(String[]) and therefore will not get their
|
||||
// specialized code. I feel this is still a lower risk than
|
||||
// not starting the full GDB console properly.
|
||||
protected Process launchGDBProcess() throws CoreException {
|
||||
// Call the old method in case it was overridden
|
||||
return launchGDBProcess(getDebuggerCommandLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch GDB process with command and arguments. Allow subclass to
|
||||
* override.
|
||||
*
|
||||
* @since 4.6
|
||||
* @deprecated Replace by launchGDBProcess()
|
||||
*/
|
||||
@Deprecated
|
||||
protected Process launchGDBProcess(String[] commandLine) throws CoreException {
|
||||
Process proc = null;
|
||||
try {
|
||||
|
@ -254,6 +313,7 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
return proc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Process getProcess() {
|
||||
return fProcess;
|
||||
}
|
||||
|
@ -466,7 +526,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
}
|
||||
|
||||
try {
|
||||
fProcess = launchGDBProcess(getGDBCommandLineArray());
|
||||
fProcess = launchGDBProcess();
|
||||
|
||||
// Need to do this on the executor for thread-safety
|
||||
getExecutor().submit(new DsfRunnable() {
|
||||
@Override
|
||||
|
@ -488,8 +549,12 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
BufferedReader errorReader = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
// Must call getMIInputStream() because we always want to read from the MI stream,
|
||||
// which is not always the same as the input stream of fProcess. They are
|
||||
// different when we use the full GDB console
|
||||
InputStream inputStream = getMIInputStream();
|
||||
// Read initial GDB prompt
|
||||
inputReader = new BufferedReader(new InputStreamReader(getMIInputStream()));
|
||||
inputReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
while ((line = inputReader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
|
@ -501,7 +566,11 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
|
|||
|
||||
// Failed to read initial prompt, check for error
|
||||
if (!success) {
|
||||
errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream()));
|
||||
// Don't call getMIErrorStream() because it can be overridden with a
|
||||
// dummy stream in the case of the full GDB console.
|
||||
// Instead, make sure we read the error from the process itself.
|
||||
InputStream errorStream = fProcess.getErrorStream();
|
||||
errorReader = new BufferedReader(new InputStreamReader(errorStream));
|
||||
String errorInfo = errorReader.readLine();
|
||||
if (errorInfo == null) {
|
||||
errorInfo = "GDB prompt not read"; //$NON-NLS-1$
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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.dsf.gdb.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.eclipse.cdt.core.parser.util.StringUtil;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.cdt.utils.pty.PTY;
|
||||
import org.eclipse.cdt.utils.pty.PTY.Mode;
|
||||
import org.eclipse.cdt.utils.spawner.ProcessFactory;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||
import org.eclipse.osgi.util.NLS;
|
||||
|
||||
/**
|
||||
* Implementation of {@link IGDBBackend} using GDB 7.12. This version provides
|
||||
* full GDB console support. It achieves this by launching GDB in CLI mode
|
||||
* in a special console widget and then connecting to GDB via MI by telling GDB to
|
||||
* open a new MI console. The rest of the DSF-GDB support then stays the same.
|
||||
*
|
||||
* If we are unable to create a PTY, we then revert to the previous behavior of
|
||||
* the base class.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
public class GDBBackend_7_12 extends GDBBackend {
|
||||
|
||||
/** The PTY that is used to enable the full GDB console */
|
||||
private PTY fPty;
|
||||
/** Indicate that we failed to create a PTY. */
|
||||
private boolean fPtyFailure;
|
||||
|
||||
private InputStream fDummyErrorStream;
|
||||
|
||||
public GDBBackend_7_12(DsfSession session, ILaunchConfiguration lc) {
|
||||
super(session, lc);
|
||||
createPty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullGdbConsoleSupported() {
|
||||
return !Platform.getOS().equals(Platform.OS_WIN32)
|
||||
&& !fPtyFailure;
|
||||
}
|
||||
|
||||
protected void createPty() {
|
||||
if (!isFullGdbConsoleSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
fPty = new PTY();
|
||||
fPty.validateSlaveName();
|
||||
|
||||
// With the PTY the stderr is redirected to the PTY's output stream.
|
||||
// Therefore, return a dummy stream for the error stream.
|
||||
fDummyErrorStream = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
} catch (IOException e) {
|
||||
fPty = null;
|
||||
fPtyFailure = true;
|
||||
GdbPlugin.log(new Status(
|
||||
IStatus.INFO, GdbPlugin.PLUGIN_ID,
|
||||
NLS.bind(Messages.PTY_Console_not_available, e.getMessage())));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getMIOutputStream() {
|
||||
if (fPty == null) {
|
||||
return super.getMIOutputStream();
|
||||
}
|
||||
return fPty.getOutputStream();
|
||||
};
|
||||
|
||||
@Override
|
||||
public InputStream getMIInputStream() {
|
||||
if (fPty == null) {
|
||||
return super.getMIInputStream();
|
||||
}
|
||||
return fPty.getInputStream();
|
||||
};
|
||||
|
||||
@Override
|
||||
public InputStream getMIErrorStream() {
|
||||
if (fPty == null) {
|
||||
return super.getMIErrorStream();
|
||||
}
|
||||
return fDummyErrorStream;
|
||||
};
|
||||
|
||||
@Override
|
||||
protected String[] getDebuggerCommandLine() {
|
||||
// Start from the original command line method which
|
||||
// could have been overridden by extenders, and add what we need
|
||||
// to convert it to a command that will launch in CLI mode.
|
||||
// Then trigger the MI console
|
||||
@SuppressWarnings("deprecation")
|
||||
String[] originalCommandLine = getGDBCommandLineArray();
|
||||
|
||||
if (!isFullGdbConsoleSupported()) {
|
||||
return originalCommandLine;
|
||||
}
|
||||
|
||||
// Below are the parameters we need to add to an existing commandLine,
|
||||
// to trigger a launch with the full CLI. This would also work
|
||||
// as the only parameters for a full CLI launch (although "--interpreter console"
|
||||
// could be removed in that case)
|
||||
String[] extraArguments = new String[] {
|
||||
// Start with -q option to avoid extra output which may trigger pagination
|
||||
// This is important because if pagination is triggered on the version
|
||||
// printout, we won't be able to send the command to start the MI channel.
|
||||
// Note that we cannot turn off pagination early enough to prevent the
|
||||
// original version output from paginating
|
||||
"-q", //$NON-NLS-1$
|
||||
|
||||
// We don't put --nx at this time because our base class puts it already and if
|
||||
// if an extender has removed it, we shouldn't add it again.
|
||||
// Once we no longer extends the deprecated getGDBCommandLineArray() and simply
|
||||
// create the full commandLine here, we should put it
|
||||
// // Use the --nx option to avoid reading the gdbinit file here.
|
||||
// // The gdbinit file is read explicitly in the FinalLaunchSequence to make
|
||||
// // it easier to customize.
|
||||
// "--nx", //$NON-NLS-1$
|
||||
|
||||
// Force a CLI console since the originalCommandLine
|
||||
// probably specified "-i mi" or "--interpreter mi"
|
||||
// Once we no longer extend the deprecated
|
||||
// getGDBCommandLineArray() and simply create the full
|
||||
// commandLine here, we could remove this parameter
|
||||
"--interpreter", "console", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// Now trigger the new console towards our PTY.
|
||||
"-ex", "new-ui mi " + fPty.getSlaveName(), //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// Now print the version so the user gets that familiar output
|
||||
"-ex", "show version" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
};
|
||||
|
||||
int oriLength = originalCommandLine.length;
|
||||
int extraLength = extraArguments.length;
|
||||
String[] newCommandLine = new String[oriLength+extraLength];
|
||||
System.arraycopy(originalCommandLine, 0, newCommandLine, 0, oriLength);
|
||||
System.arraycopy(extraArguments, 0, newCommandLine, oriLength, extraLength);
|
||||
|
||||
return newCommandLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Process launchGDBProcess() throws CoreException {
|
||||
if (!isFullGdbConsoleSupported()) {
|
||||
return super.launchGDBProcess();
|
||||
}
|
||||
|
||||
// If we are launching the full console, we need to use a PTY in TERMINAL mode
|
||||
// for the GDB CLI to properly display in its view
|
||||
Process proc = null;
|
||||
String[] commandLine = getDebuggerCommandLine();
|
||||
try {
|
||||
IPath path = getGDBWorkingDirectory();
|
||||
proc = ProcessFactory.getFactory().exec(
|
||||
commandLine,
|
||||
getGDBLaunch().getLaunchEnvironment(),
|
||||
new File(path != null ? path.toOSString() : ""), //$NON-NLS-1$
|
||||
new PTY(Mode.TERMINAL));
|
||||
} 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));
|
||||
}
|
||||
|
||||
return proc;
|
||||
}
|
||||
}
|
|
@ -88,6 +88,8 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
public static final String GDB_7_7_VERSION = "7.7"; //$NON-NLS-1$
|
||||
/** @since 4.8 */
|
||||
public static final String GDB_7_10_VERSION = "7.10"; //$NON-NLS-1$
|
||||
/** @since 5.1 */
|
||||
public static final String GDB_7_12_VERSION = "7.12"; //$NON-NLS-1$
|
||||
|
||||
private final String fVersion;
|
||||
private final ILaunchConfiguration fConfiguration;
|
||||
|
@ -224,6 +226,11 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
}
|
||||
|
||||
protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) {
|
||||
if (compareVersionWith(GDB_7_12_VERSION) >= 0
|
||||
|| compareVersionWith("7.11.50") >= 0 // TODO remove once GDB 7.12 is released
|
||||
) {
|
||||
return new GDBBackend_7_12(session, lc);
|
||||
}
|
||||
return new GDBBackend(session, lc);
|
||||
}
|
||||
|
||||
|
|
|
@ -156,4 +156,21 @@ public interface IGDBBackend extends IMIBackend {
|
|||
* @since 3.0
|
||||
*/
|
||||
public boolean getUpdateThreadListOnSuspend() throws CoreException;
|
||||
|
||||
/**
|
||||
* @return True if the full GDB console should be used. False otherwise.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
default boolean isFullGdbConsoleSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The real GDB process that was started for the debug session
|
||||
* @since 5.1
|
||||
*/
|
||||
default Process getProcess() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class Messages extends NLS {
|
|||
public static String RegisterGroup_name_used;
|
||||
public static String RegisterGroup_invalid_number_of_registers;
|
||||
public static String GDB_Version_Mismatch;
|
||||
public static String PTY_Console_not_available;
|
||||
|
||||
static {
|
||||
// initialize resource bundle
|
||||
|
|
|
@ -22,4 +22,5 @@ ErrorNotSupported=Operation not supported with this GDB version
|
|||
RegisterGroup_name_reserved=The group name "{0}" is reserved
|
||||
RegisterGroup_name_used=The group name "{0}" is already in use
|
||||
RegisterGroup_invalid_number_of_registers=A minimum of one register is needed for a Register Group
|
||||
GDB_Version_Mismatch=Running older GDB version {0} when service {1} expects version {2} or higher
|
||||
GDB_Version_Mismatch=Running older GDB version {0} when service {1} expects version {2} or higher
|
||||
PTY_Console_not_available=PTY not available, cannot use full GDB console: {0}
|
|
@ -21,7 +21,12 @@ import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.MIBackendCLIProcess;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Note that starting with GDB 7.12, as long as a PTY is available,
|
||||
* this process is no longer used. Instead, the real GDB process,
|
||||
* along with its console will be used directly. A second PTY
|
||||
* will be used to communicate using MI.
|
||||
|
||||
* @author LWang
|
||||
* @since 2.0
|
||||
*
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson 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.dsf.gdb.service.command;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
|
||||
/**
|
||||
* Note that starting with GDB 7.12, as long as a PTY is available, this process
|
||||
* is used instead of GDBBackendProcess. This is because the GDB CLI is handled
|
||||
* directly by GDB and the current class only needs to handle the life-cycle of
|
||||
* the GDB process.
|
||||
*
|
||||
* This class is therefore a representation of the GDB process that will be
|
||||
* added to the launch. This class is not the real GDB process but simply an
|
||||
* entry for the launch to handle user actions but no IO.
|
||||
*
|
||||
* This class extends {@link GDBBackendCLIProcess} to re-use its implementation
|
||||
* of the {@link Process} abstract methods, but disables all I/O and
|
||||
* local CLI handling.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
public class GDBBackendProcessWithoutIO extends GDBBackendCLIProcess implements IGDBBackendProcessWithoutIO {
|
||||
|
||||
public GDBBackendProcessWithoutIO(ICommandControlService commandControl, IMIBackend backend) throws IOException {
|
||||
super(commandControl, backend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleIO() {
|
||||
// Streams are handled directly by the real process.
|
||||
// This class is just representation for the launch, without IO.
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -193,7 +193,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
|||
private IEventProcessor fMIEventProcessor;
|
||||
private IEventProcessor fCLICommandProcessor;
|
||||
private IEventProcessor fControlEventProcessor;
|
||||
private AbstractCLIProcess fCLIProcess;
|
||||
private Process fBackendProcess;
|
||||
|
||||
private GdbCommandTimeoutManager fCommandTimeoutManager;
|
||||
|
||||
|
@ -358,11 +358,24 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replaced by {@link #getGDBBackendProcess()}
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public AbstractCLIProcess getCLIProcess() {
|
||||
return fCLIProcess;
|
||||
public AbstractCLIProcess getCLIProcess() {
|
||||
if (fBackendProcess instanceof AbstractCLIProcess) {
|
||||
return (AbstractCLIProcess)fBackendProcess;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @since 5.1 */
|
||||
@Override
|
||||
public Process getGDBBackendProcess() {
|
||||
return fBackendProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
*/
|
||||
|
@ -546,8 +559,8 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
|||
|
||||
/** @since 5.1 */
|
||||
protected void doCommandProcessorsStep(final RequestMonitor requestMonitor) {
|
||||
try {
|
||||
fCLIProcess = new GDBBackendCLIProcess(GDBControl.this, fMIBackend);
|
||||
try {
|
||||
fBackendProcess = createBackendProcess();
|
||||
}
|
||||
catch(IOException e) {
|
||||
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$
|
||||
|
@ -567,7 +580,9 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
|||
fControlEventProcessor.dispose();
|
||||
fCLICommandProcessor.dispose();
|
||||
fMIEventProcessor.dispose();
|
||||
fCLIProcess.dispose();
|
||||
if (fBackendProcess instanceof AbstractCLIProcess) {
|
||||
((AbstractCLIProcess)fBackendProcess).dispose();
|
||||
}
|
||||
|
||||
requestMonitor.done();
|
||||
}
|
||||
|
@ -715,6 +730,18 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
|
|||
return new ControlEventProcessor();
|
||||
}
|
||||
|
||||
/** @since 5.1 */
|
||||
protected Process createBackendProcess() throws IOException {
|
||||
if (fMIBackend.isFullGdbConsoleSupported()) {
|
||||
// If the full GDB console is supported, which uses the GDB process itself,
|
||||
// we return a GDBBackendProcess that does not take care of I/O
|
||||
return new GDBBackendProcessWithoutIO(this, fMIBackend);
|
||||
}
|
||||
// If the full GDB console is not supported according to the backend service,
|
||||
// then we create a special GDBBackendProcess that handles the CLI
|
||||
return new GDBBackendCLIProcess(this, fMIBackend);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.1
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service.command;
|
||||
|
||||
/**
|
||||
* Interface used by a process representing the GDB process but for which there
|
||||
* is no IO.
|
||||
*
|
||||
* When using the full GDB console, this marker can be used for the class that
|
||||
* will represent the GDB process in the launch since the IO should not be
|
||||
* handled by the launch and the console it normally created, but is handled by
|
||||
* the full GDB console itself.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
public interface IGDBBackendProcessWithoutIO {
|
||||
}
|
|
@ -17,11 +17,37 @@ import java.util.List;
|
|||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
|
||||
|
||||
public interface IGDBControl extends IMICommandControl {
|
||||
|
||||
/**
|
||||
* Returns the process that represents GDB.
|
||||
* This is the process that should be added to the launch.
|
||||
* Note that this is usually not be the actual GDB process but
|
||||
* only one that is used to represent it.
|
||||
* To get the real GDB process use
|
||||
* {@link IGDBBackend#getProcess()}.
|
||||
*
|
||||
* @since 5.1
|
||||
*/
|
||||
default Process getGDBBackendProcess() {
|
||||
return getCLIProcess();
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated The return value of this method was too
|
||||
* restrictive. It has been replaced with
|
||||
* {@link #getGDBBackendProcess()}
|
||||
* @return The AbstractCLIProcess that handles the CLI.
|
||||
* Will return null if the CLI is not handled
|
||||
* by an AbstractCLIProcess; this will sometimes
|
||||
* happen for GDB >= 7.12 if the CLI is handled
|
||||
* by the GDB process itself.
|
||||
*/
|
||||
@Deprecated
|
||||
AbstractCLIProcess getCLIProcess();
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service.extensions;
|
||||
|
||||
import org.eclipse.cdt.dsf.gdb.service.GDBBackend;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GDBBackend_7_12;
|
||||
import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
|
@ -37,13 +37,13 @@ import org.eclipse.debug.core.ILaunchConfiguration;
|
|||
*
|
||||
* @since 4.8
|
||||
*/
|
||||
public class GDBBackend_HEAD extends GDBBackend {
|
||||
public class GDBBackend_HEAD extends GDBBackend_7_12 {
|
||||
public GDBBackend_HEAD(DsfSession session, ILaunchConfiguration lc) {
|
||||
super(session, lc);
|
||||
validateGdbVersion(session);
|
||||
}
|
||||
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_1_VERSION; }
|
||||
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_12_VERSION; }
|
||||
|
||||
protected void validateGdbVersion(DsfSession session) {
|
||||
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);
|
||||
|
|
|
@ -46,9 +46,14 @@ import org.eclipse.core.runtime.IStatus;
|
|||
import org.eclipse.core.runtime.Status;
|
||||
|
||||
/**
|
||||
* This Process implementation tracks the process the GDB process. This
|
||||
* process object is displayed in Debug view and is used to
|
||||
* accept CLI commands and to write their output to the console.
|
||||
* This Process implementation tracks the GDB process. This
|
||||
* process object is displayed in the Debug view and is used to
|
||||
* accept CLI commands and to write their output to the console.
|
||||
*
|
||||
* Starting with GDB 7.12, as long as a PTY is available,
|
||||
* this process is no longer used. Instead, the real GDB process,
|
||||
* along with its console will be used directly. A second PTY
|
||||
* will be used to communicate using MI.
|
||||
*
|
||||
* @see org.eclipse.debug.core.model.IProcess
|
||||
*/
|
||||
|
@ -64,7 +69,7 @@ public abstract class AbstractCLIProcess extends Process
|
|||
|
||||
private final DsfSession fSession;
|
||||
private final ICommandControlService fCommandControl;
|
||||
private final OutputStream fOutputStream = new CLIOutputStream();
|
||||
private OutputStream fOutputStream;
|
||||
|
||||
// Client process console stream.
|
||||
private PipedInputStream fMIInConsolePipe;
|
||||
|
@ -103,34 +108,42 @@ public abstract class AbstractCLIProcess extends Process
|
|||
fSession = commandControl.getSession();
|
||||
fCommandControl = commandControl;
|
||||
|
||||
commandControl.addEventListener(this);
|
||||
commandControl.addCommandListener(this);
|
||||
if (handleIO()) {
|
||||
fOutputStream = new CLIOutputStream();
|
||||
|
||||
PipedInputStream miInConsolePipe = null;
|
||||
PipedOutputStream miOutConsolePipe = null;
|
||||
PipedInputStream miInLogPipe = null;
|
||||
PipedOutputStream miOutLogPipe = null;
|
||||
|
||||
try {
|
||||
// Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154
|
||||
miOutConsolePipe = new PipedOutputStream();
|
||||
miInConsolePipe = new LargePipedInputStream(miOutConsolePipe);
|
||||
miOutLogPipe = new PipedOutputStream();
|
||||
miInLogPipe = new LargePipedInputStream(miOutLogPipe);
|
||||
} catch (IOException e) {
|
||||
ILog log = GdbPlugin.getDefault().getLog();
|
||||
if (log != null) {
|
||||
log.log(new Status(
|
||||
IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error when creating log pipes", e)); //$NON-NLS-1$
|
||||
}
|
||||
commandControl.addEventListener(this);
|
||||
commandControl.addCommandListener(this);
|
||||
|
||||
PipedInputStream miInConsolePipe = null;
|
||||
PipedOutputStream miOutConsolePipe = null;
|
||||
PipedInputStream miInLogPipe = null;
|
||||
PipedOutputStream miOutLogPipe = null;
|
||||
|
||||
try {
|
||||
// Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154
|
||||
miOutConsolePipe = new PipedOutputStream();
|
||||
miInConsolePipe = new LargePipedInputStream(miOutConsolePipe);
|
||||
miOutLogPipe = new PipedOutputStream();
|
||||
miInLogPipe = new LargePipedInputStream(miOutLogPipe);
|
||||
} catch (IOException e) {
|
||||
ILog log = GdbPlugin.getDefault().getLog();
|
||||
if (log != null) {
|
||||
log.log(new Status(
|
||||
IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error when creating log pipes", e)); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
fMIOutConsolePipe = miOutConsolePipe;
|
||||
fMIInConsolePipe = miInConsolePipe;
|
||||
fMIOutLogPipe = miOutLogPipe;
|
||||
fMIInLogPipe = miInLogPipe;
|
||||
}
|
||||
// Must initialize these outside of the try block because they are final.
|
||||
fMIOutConsolePipe = miOutConsolePipe;
|
||||
fMIInConsolePipe = miInConsolePipe;
|
||||
fMIOutLogPipe = miOutLogPipe;
|
||||
fMIInLogPipe = miInLogPipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.1
|
||||
*/
|
||||
protected boolean handleIO() { return true; }
|
||||
|
||||
protected DsfSession getSession() { return fSession; }
|
||||
|
||||
/**
|
||||
|
@ -165,18 +178,26 @@ public abstract class AbstractCLIProcess extends Process
|
|||
}
|
||||
|
||||
private void closeIO() {
|
||||
try {
|
||||
fMIOutConsolePipe.close();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
fMIInConsolePipe.close();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
fMIOutLogPipe.close();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
fMIInLogPipe.close();
|
||||
} catch (IOException e) {}
|
||||
if (fMIOutConsolePipe != null) {
|
||||
try {
|
||||
fMIOutConsolePipe.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
if (fMIInConsolePipe != null) {
|
||||
try {
|
||||
fMIInConsolePipe.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
if (fMIOutLogPipe != null) {
|
||||
try {
|
||||
fMIOutLogPipe.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
if (fMIInLogPipe != null) {
|
||||
try {
|
||||
fMIInLogPipe.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ import org.eclipse.core.runtime.Status;
|
|||
/**
|
||||
* CLI Process object implementation which uses the {@link IMIBackend} service
|
||||
* to monitor and control the underlying process.
|
||||
*
|
||||
* Note that starting with GDB 7.12, as long as a PTY is available,
|
||||
* this process is no longer used. Instead, the real GDB process,
|
||||
* along with its console will be used directly. A second PTY
|
||||
* will be used to communicate using MI.
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde version="3.8"?><target name="cdt" sequenceNumber="14">
|
||||
<?pde version="3.8"?><target name="cdt" sequenceNumber="15">
|
||||
<locations>
|
||||
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
|
||||
<unit id="org.apache.commons.compress" version="0.0.0"/>
|
||||
|
@ -60,6 +60,7 @@
|
|||
</location>
|
||||
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.tm.terminal.control" version="0.0.0"/>
|
||||
<unit id="org.eclipse.tm.terminal.view.ui" version="0.0.0"/>
|
||||
<repository location="http://download.eclipse.org/tm/terminal/builds/development/nightly/"/>
|
||||
</location>
|
||||
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
|
||||
|
|
Loading…
Add table
Reference in a new issue