Repository: eclipse-corrosion/corrosion (language: rust)

Issue:
Detect GDB problems already in launch config
Since in some cases, like #149, it would be beneficial if the GDB version would be detected already when the GDB location is entered. If errors occur, they can then be presented to the user already in the launch config to give users early and detailed feedback. The necessary functionality in CDT will be added with SimRel 2018-12 M3 (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=541520).


Discussion / hints:
2018-12 was released, this should work now ;) Thanks!
@anb0s do you confirm this is fixed and we can close it?

Fix patch:
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/CorrosionPlugin.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/CorrosionPlugin.java
index 8710e44..370741f 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/CorrosionPlugin.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/CorrosionPlugin.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *  Lucas Bullen (Red Hat Inc.) - Initial implementation
+ *  Max Bureck (Fraunhofer FOKUS) - Registering additional image for debug launcher
  *******************************************************************************/
 package org.eclipse.corrosion;
 
@@ -97,6 +98,7 @@ protected void initializeImageRegistry(ImageRegistry reg) {
 		super.initializeImageRegistry(reg);
 		declareRegistryImage(reg, "images/cargo.png"); //$NON-NLS-1$
 		declareRegistryImage(reg, "images/cargo16.png"); //$NON-NLS-1$
+		declareRegistryImage(reg, "icons/rustEditorIcon.png"); //$NON-NLS-1$
 	}
 
 	private final static void declareRegistryImage(ImageRegistry reg, String image) {
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/Messages.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/Messages.java
index ce1a694..3ebfe0d 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/Messages.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/Messages.java
@@ -10,6 +10,7 @@
  * Contributors:
  *  Mickael Istria (Red Hat Inc.) - Initial implementation
  *  Nicola Orru - Added support for external RLS startup configuration
+ *  Max Bureck (Fraunhofer FOKUS) - Added message for GDB failure
  *******************************************************************************/
 package org.eclipse.corrosion;
 
@@ -167,4 +168,5 @@ public class Messages extends NLS {
 	public static String RustManager_toolchainDoesntIncludeRLS;
 	public static String CargoRunTab_Title;
 	public static String CargoTestTab_Title;
+	public static String RustDebugTabGroup_gdbErrorMsg;
 }
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java
index 607e8a3..549dd9f 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java
@@ -21,19 +21,26 @@
 import org.eclipse.corrosion.CorrosionPlugin;
 
 /**
- * This class contains static helper methods and fields to be used for the Debuging functionality in Eclipse Corrosion
+ * This class contains static helper methods and fields to be used for the
+ * Debuging functionality in Eclipse Corrosion
  */
 class DebugUtil {
 
+	/**
+	 * Default debugger executable to use for Rust
+	 */
+	/* package */ static final String DEFAULT_DEBUGGER = "rust-gdb"; //$NON-NLS-1$
 	private static final boolean IS_WINDOWS = Platform.getOS().equals(Platform.OS_WIN32);
 
 	/**
-	 * Returns the default workspace path to the executable produced for the given project. This location is a guess based on the default rust/cargo project layout and the operating system running
-	 * eclipse.
+	 * Returns the default workspace path to the executable produced for the given
+	 * project. This location is a guess based on the default rust/cargo project
+	 * layout and the operating system running eclipse.
 	 *
-	 * @param project
-	 *            Rust project for which the executable workspace path is computed.
-	 * @return default workspace path to the executable created for the given Rust {@code project}.
+	 * @param project Rust project for which the executable workspace path is
+	 *                computed.
+	 * @return default workspace path to the executable created for the given Rust
+	 *         {@code project}.
 	 */
 	static String getDefaultExecutablePath(IProject project) {
 		if (project == null) {
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java
index 1ec3663..2d73d7e 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *  Lucas Bullen (Red Hat Inc.) - Initial implementation
+ *  Max Bureck (Fraunhofer FOKUS) - Moved default GDB definition to re-usable constant
  *******************************************************************************/
 package org.eclipse.corrosion.debug;
 
@@ -50,12 +51,15 @@
 public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShortcut {
 	public static final String BUILD_COMMAND_ATTRIBUTE = CorrosionPlugin.PLUGIN_ID + ".BUILD_COMMAND"; //$NON-NLS-1$
 
-	@Override public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
+	@Override
+	public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor)
+			throws CoreException {
 		ILaunchConfiguration configuration = launch.getLaunchConfiguration();
 		String buildCommand = configuration.getAttribute(BUILD_COMMAND_ATTRIBUTE, ""); //$NON-NLS-1$
 		String projectName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
 		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
-		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "").trim()); //$NON-NLS-1$
+		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(
+				configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "").trim()); //$NON-NLS-1$
 		File workingDirectory = RustLaunchDelegateTools.convertToAbsolutePath(workingDirectoryString);
 		if (workingDirectoryString.isEmpty() || !workingDirectory.exists() || !workingDirectory.isDirectory()) {
 			workingDirectory = project.getLocation().toFile();
@@ -102,8 +106,10 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		super.launch(configuration, mode, launch, monitor);
 	}
 
-	@Override public void launch(ISelection selection, String mode) {
-		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.firstResourceFromSelection(selection));
+	@Override
+	public void launch(ISelection selection, String mode) {
+		ILaunchConfiguration launchConfig = getLaunchConfiguration(
+				RustLaunchDelegateTools.firstResourceFromSelection(selection));
 		try {
 			RustLaunchDelegateTools.launch(launchConfig, mode);
 		} catch (CoreException e) {
@@ -111,7 +117,8 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		}
 	}
 
-	@Override public void launch(IEditorPart editor, String mode) {
+	@Override
+	public void launch(IEditorPart editor, String mode) {
 		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.resourceFromEditor(editor));
 		try {
 			RustLaunchDelegateTools.launch(launchConfig, mode);
@@ -120,7 +127,9 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		}
 	}
 
-	@Override protected ISourceLocator getSourceLocator(ILaunchConfiguration configuration, DsfSession session) throws CoreException {
+	@Override
+	protected ISourceLocator getSourceLocator(ILaunchConfiguration configuration, DsfSession session)
+			throws CoreException {
 		SourceLookupDirector locator = new SourceLookupDirector();
 		String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null);
 		if (memento == null) {
@@ -131,27 +140,34 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		return locator;
 	}
 
-	@Override protected DsfSourceLookupDirector createDsfSourceLocator(ILaunchConfiguration configuration, DsfSession session) throws CoreException {
+	@Override
+	protected DsfSourceLookupDirector createDsfSourceLocator(ILaunchConfiguration configuration, DsfSession session)
+			throws CoreException {
 		DsfSourceLookupDirector sourceLookupDirector = new DsfSourceLookupDirector(session);
-		sourceLookupDirector.setSourceContainers(((SourceLookupDirector) getSourceLocator(configuration, session)).getSourceContainers());
+		sourceLookupDirector.setSourceContainers(
+				((SourceLookupDirector) getSourceLocator(configuration, session)).getSourceContainers());
 		return sourceLookupDirector;
 	}
 
-	@Override public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+	@Override
+	public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
 		setDefaultProcessFactory(configuration); // Reset process factory to what GdbLaunch expected
 		String projectName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
 		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
-		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "").trim()); //$NON-NLS-1$
+		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(
+				configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "").trim()); //$NON-NLS-1$
 		File workingDirectory = RustLaunchDelegateTools.convertToAbsolutePath(workingDirectoryString);
 		if (workingDirectoryString.isEmpty() || !workingDirectory.exists() || !workingDirectory.isDirectory()) {
 			workingDirectory = project.getLocation().toFile();
 		}
-		String executableString = RustLaunchDelegateTools.performVariableSubstitution(configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "").trim()); //$NON-NLS-1$
+		String executableString = RustLaunchDelegateTools.performVariableSubstitution(
+				configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "").trim()); //$NON-NLS-1$
 		File executable = RustLaunchDelegateTools.convertToAbsolutePath(executableString);
 		if (!executable.exists()) {
 			IPath executablePath = Path.fromPortableString(executableString);
 			if (project != null && executablePath.segment(0).equals(project.getName())) { // project relative path
-				executable = project.getFile(executablePath.removeFirstSegments(1)).getLocation().toFile().getAbsoluteFile();
+				executable = project.getFile(executablePath.removeFirstSegments(1)).getLocation().toFile()
+						.getAbsoluteFile();
 			}
 		}
 
@@ -159,12 +175,13 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, executable.getAbsolutePath());
 		wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, workingDirectory.getAbsolutePath());
 		wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_LOCATION, project.getLocation().toString());
-		
-		String stopInMainSymbol = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ""); //$NON-NLS-1$
+
+		String stopInMainSymbol = configuration
+				.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ""); //$NON-NLS-1$
 		if (stopInMainSymbol.equals("main")) { //$NON-NLS-1$
 			wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, projectName + "::main"); //$NON-NLS-1$
 		}
-		
+
 		ILaunch launch = super.getLaunch(wc.doSave(), mode);
 		if (!(launch instanceof RustGDBLaunchWrapper)) {
 			launch = new RustGDBLaunchWrapper(launch);
@@ -174,19 +191,21 @@ public class RustDebugDelegate extends GdbLaunchDelegate implements ILaunchShort
 		return launch;
 	}
 
-	@Override protected IPath checkBinaryDetails(ILaunchConfiguration config) throws CoreException {
+	@Override
+	protected IPath checkBinaryDetails(ILaunchConfiguration config) throws CoreException {
 		return LaunchUtils.verifyProgramPath(config, null);
 	}
 
 	private static ILaunchConfiguration getLaunchConfiguration(IResource resource) {
-		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource, "org.eclipse.corrosion.debug.RustDebugDelegate"); //$NON-NLS-1$
+		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource,
+				"org.eclipse.corrosion.debug.RustDebugDelegate"); //$NON-NLS-1$
 		if (launchConfiguration instanceof ILaunchConfigurationWorkingCopy) {
 			ILaunchConfigurationWorkingCopy wc = (ILaunchConfigurationWorkingCopy) launchConfiguration;
 			final IProject project = resource.getProject();
 			wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
 			wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, getDefaultExecutablePath(project)); // $NON-NLS-1$
 			wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false);
-			wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, "rust-gdb"); //$NON-NLS-1$
+			wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, DebugUtil.DEFAULT_DEBUGGER);
 		}
 		return launchConfiguration;
 	}
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTab.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTab.java
index c2793cc..45832bd 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTab.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTab.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *  Lucas Bullen (Red Hat Inc.) - Initial implementation
+ *  Max Bureck (Fraunhofer FOKUS) - Added icon
  *******************************************************************************/
 package org.eclipse.corrosion.debug;
 
@@ -18,6 +19,7 @@
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.corrosion.CorrosionPlugin;
 import org.eclipse.corrosion.Messages;
 import org.eclipse.corrosion.cargo.core.CargoProjectTester;
 import org.eclipse.corrosion.ui.InputComponent;
@@ -28,6 +30,7 @@
 import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Group;
@@ -148,4 +151,9 @@ public boolean canSave() {
 	public String getName() {
 		return Messages.LaunchUI_main;
 	}
+
+	@Override
+	public Image getImage() {
+		return CorrosionPlugin.getDefault().getImageRegistry().get("icons/rustEditorIcon.png"); //$NON-NLS-1$
+	}
 }
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTabGroup.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTabGroup.java
index 19132c4..ae0296a 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTabGroup.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugTabGroup.java
@@ -9,13 +9,21 @@
  *
  * Contributors:
  *  Lucas Bullen (Red Hat Inc.) - Initial implementation
+ *  Max Bureck (Fraunhofer FOKUS) - Check for valid GDB executable/script
  *******************************************************************************/
 package org.eclipse.corrosion.debug;
 
+import static org.eclipse.corrosion.debug.DebugUtil.DEFAULT_DEBUGGER;
+
 import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
 import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
 import org.eclipse.cdt.dsf.gdb.internal.ui.launching.LocalApplicationCDebuggerTab;
+import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
 import org.eclipse.cdt.launch.ui.CArgumentsTab;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.corrosion.Messages;
+import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
 import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
 import org.eclipse.debug.ui.CommonTab;
@@ -23,6 +31,7 @@
 import org.eclipse.debug.ui.ILaunchConfigurationDialog;
 import org.eclipse.debug.ui.ILaunchConfigurationTab;
 import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
+import org.eclipse.osgi.util.NLS;
 
 @SuppressWarnings("restriction")
 public class RustDebugTabGroup extends AbstractLaunchConfigurationTabGroup {
@@ -39,7 +48,49 @@ protected class RustLocalApplicationCDebuggerTab extends LocalApplicationCDebugg
 		public void setDefaults(ILaunchConfigurationWorkingCopy config) {
 			super.setDefaults(config);
 			config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false);
-			config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, "rust-gdb"); //$NON-NLS-1$
+			config.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, DEFAULT_DEBUGGER);
+		}
+
+		@Override
+		public boolean isValid(ILaunchConfiguration config) {
+			if (!super.isValid(config)) {
+				return false;
+			}
+			// Check for a valid GDB executable/script
+			String gdbCommand = getGdbCommand(config);
+			try {
+				LaunchUtils.getGDBVersion(gdbCommand, new String[] {});
+			} catch (CoreException e) {
+				final String msg = getMessageFromException(e);
+				setErrorMessage(msg);
+				return false;
+			}
+			return true;
+		}
+
+		private String getMessageFromException(CoreException e) {
+			final IStatus status = e.getStatus();
+			final String statusMessage = status.getMessage();
+			final Throwable statusException = status.getException();
+			if (statusException != null) {
+				String exceptionMessage = statusException.getLocalizedMessage();
+				return NLS.bind(Messages.RustDebugTabGroup_gdbErrorMsg, statusMessage, exceptionMessage);
+			}
+			// else
+			return statusMessage;
+		}
+
+		/**
+		 * Reads and returns the user specified gdb executable from the given
+		 * {@code config}.
+		 */
+		private String getGdbCommand(ILaunchConfiguration config) {
+			try {
+				return config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, DEFAULT_DEBUGGER);
+			} catch (CoreException e) {
+				// we were not able to find command
+				return DEFAULT_DEBUGGER;
+			}
 		}
 	}
 }
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/messages.properties b/org.eclipse.corrosion/src/org/eclipse/corrosion/messages.properties
index a922ff7..ba431bd 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/messages.properties
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/messages.properties
@@ -9,6 +9,7 @@
 #
 # Contributors:
 #  Mickael Istria (Red Hat Inc.) - Initial implementation
+#  Max Bureck (Fraunhofer FOKUS) - More consistent capitalization
 #################################################################
 DebugPreferencePage_seeGDBPage=See <A>GDB</A> Preferences Page
 ImplementationsSearchQuery_implementations=Implementations
@@ -75,7 +76,7 @@ CargoExportWizardPage_outputLocation=Crate will be created in: {0}/target/packag
 CargoRunDelegate_unableToFindProject=Unable to find project.
 CargoRunDelegate_unableToFindToml=Unable to find Cargo.toml file.
 CargoRunDelegate_unableToLaunch=Unable to Launch
-LaunchUI_main=main
+LaunchUI_main=Main
 LaunchUI_useDefault=Use default
 LaunchUI_variables=Variables
 LaunchUI_selection=Selection
@@ -156,3 +157,4 @@ RustManager_unableToSetDefaultToolchain=Unable to set `{0}` as the default toolc
 RustManager_toolchainDoesntIncludeRLS=The toolchain `{0}` does not contain the Rust Language Server, please select a different toolchain.
 CargoRunTab_Title=Main
 CargoTestTab_Title=Main
+RustDebugTabGroup_gdbErrorMsg={0} [cause: {1}]


Test patch: