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

Issue:
[Debug] Program File doesn't exist
Steps to reproduce:
0. with rust-gdb and gdb installed
1. Create a new project
2. Debug As -> Rust
EXPECTED: Debug works
GOT: ❌ Program file doesn't exist

It seems like the debug configuration is instructed with the wrong path. See screenshot showing that the path used by the debugger doesn't match the path of the project
![notfound](https://user-images.githubusercontent.com/469058/48273326-ec66cc80-e440-11e8-87ce-81d45bb30ec6.png)



Discussion / hints:
Looks like I may have introduced this with #148. I am currently traveling without my laptop. I hope I can look into it on Friday. Hopefully, I am not too tired in the evening. My parental leave turned out to be as exhausting as it is rewarding 😊.
Anyway, I think in some exceptional case the working directory is determined as the parent of the project to debug. Maybe I missed a valid case.
Ok thanks. No need to rush if you're tired, I can work on it later if you cannot make it in the meantime. Not being exhausted to enjoy your baby is more important than a patch for this, and will prove being more rewarding too ;)
I think the issue is that the workspace can use absolute path like /project/path/to/file which are understood as file-system absolute by `RustLaunchDelegateTools.convertToAbsolutePath(...)` and not turned into resource path.
This method should probably check for File existence to decide whether an absolute path is workspace or filestystem "relative"/
If this is the case, I think the error message should look different, because the method returns early on absolute paths. In your case it would be something like "/new_rust_project/target/debug/new_rust_project not found" but in your screenshot the project relative path seems to be appended to the path of your eclipse installation, which I guess is also the working directory. This can happen if the given path is relative, but was not found from the workspace root.

Independent from the root cause of this problem, it might anyway be better to refactor `RustLaunchDelegateTools.convertToAbsolutePath(...)` to always check the path from the workspace root first.
I use Eclipse variables such as `${project_loc}` and `${workspace_loc}` to circumvent similar issues.

Fix patch:
diff --git a/Jenkinsfile b/Jenkinsfile
index 798a99d..9e6f873 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -29,7 +29,8 @@ pipeline {
 			}
 			post {
 				always {
-					junit '**/surefire-reports/TEST-*.xml'
+					junit '*/target/surefire-reports/TEST-*.xml'
+					archiveArtifacts artifacts: '*/target/work/data/.metadata/.log'
 				}
 			}
 		}
diff --git a/org.eclipse.corrosion/META-INF/MANIFEST.MF b/org.eclipse.corrosion/META-INF/MANIFEST.MF
index 08e55e9..e2b73d4 100644
--- a/org.eclipse.corrosion/META-INF/MANIFEST.MF
+++ b/org.eclipse.corrosion/META-INF/MANIFEST.MF
@@ -41,6 +41,7 @@ Export-Package: org.eclipse.corrosion;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.edit;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.extensions;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.run;x-friends:="org.eclipse.corrosion.tests",
+ org.eclipse.corrosion.debug;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.snippet;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.wizards.export;x-friends:="org.eclipse.corrosion.tests",
  org.eclipse.corrosion.wizards.newproject;x-friends:="org.eclipse.corrosion.tests"
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 c98f700..607e8a3 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/DebugUtil.java
@@ -12,32 +12,54 @@
  *******************************************************************************/
 package org.eclipse.corrosion.debug;
 
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.Platform;
+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 {
 
 	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) {
 			return ""; //$NON-NLS-1$
 		}
-		return project.getName() + "/target/debug/" + project.getName() + (IS_WINDOWS ? ".exe" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		IFile cargoFile = project.getFile("Cargo.toml"); //$NON-NLS-1$
+		if (!cargoFile.exists()) {
+			return ""; //$NON-NLS-1$
+		}
+		StringBuilder builder = new StringBuilder(project.getName());
+		builder.append("/target/debug/"); //$NON-NLS-1$
+		try (InputStream file = cargoFile.getContents()) {
+			Properties properties = new Properties();
+			properties.load(file);
+			String name = properties.getProperty("name"); //$NON-NLS-1$
+			if (!name.isEmpty()) {
+				name = name.substring(name.indexOf('"') + 1, name.lastIndexOf('"'));
+			}
+			builder.append(name);
+		} catch (Exception e) {
+			CorrosionPlugin.logError(e);
+		}
+		if (IS_WINDOWS) {
+			builder.append(".exe"); //$NON-NLS-1$
+		}
+		return builder.toString();
 	}
 
 }
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 3960df4..2dd8eb7 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/debug/RustDebugDelegate.java
@@ -31,11 +31,10 @@
 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.variables.IStringVariableManager;
 import org.eclipse.core.variables.VariablesPlugin;
 import org.eclipse.corrosion.CorrosionPlugin;
-import org.eclipse.corrosion.Messages;
 import org.eclipse.corrosion.cargo.core.CargoTools;
 import org.eclipse.corrosion.launch.RustLaunchDelegateTools;
 import org.eclipse.debug.core.DebugPlugin;
@@ -51,15 +50,12 @@
 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();
@@ -103,34 +99,28 @@ public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IPr
 		if (!(launch instanceof RustGDBLaunchWrapper)) {
 			launch = new RustGDBLaunchWrapper(launch);
 		}
+		super.launch(configuration, mode, launch, monitor);
+	}
+
+	@Override public void launch(ISelection selection, String mode) {
+		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.firstResourceFromSelection(selection));
 		try {
-			super.launch(configuration, mode, launch, monitor);
+			RustLaunchDelegateTools.launch(launchConfig, mode);
 		} catch (CoreException e) {
-			IStatus status = e.getStatus();
-			if (status != null) {
-				CorrosionPlugin.logError(status);
-			}
-			CorrosionPlugin.showError(Messages.RustDebugDelegate_unableToLaunch_title,
-					Messages.RustDebugDelegate_unableToLaunch_message, e);
+			CorrosionPlugin.logError(e);
 		}
 	}
 
-	@Override
-	public void launch(ISelection selection, String mode) {
-		ILaunchConfiguration launchConfig = getLaunchConfiguration(
-				RustLaunchDelegateTools.firstResourceFromSelection(selection));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
-	}
-
-	@Override
-	public void launch(IEditorPart editor, String mode) {
+	@Override public void launch(IEditorPart editor, String mode) {
 		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.resourceFromEditor(editor));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
+		try {
+			RustLaunchDelegateTools.launch(launchConfig, mode);
+		} catch (CoreException e) {
+			CorrosionPlugin.logError(e);
+		}
 	}
 
-	@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) {
@@ -141,29 +131,29 @@ protected ISourceLocator getSourceLocator(ILaunchConfiguration configuration, Ds
 		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();
+			}
+		}
 
 		ILaunchConfigurationWorkingCopy wc = configuration.copy(configuration.getName() + "[Variables Parsed]") //$NON-NLS-1$
 				.getWorkingCopy();
@@ -179,14 +169,12 @@ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws
 		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();
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/launch/RustLaunchDelegateTools.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/launch/RustLaunchDelegateTools.java
index 509b2c1..4d41bd0 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/launch/RustLaunchDelegateTools.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/launch/RustLaunchDelegateTools.java
@@ -84,14 +84,12 @@ public static IResource resourceFromEditor(IEditorPart editor) {
 	}
 
 	/**
-	 * Converts the given relative path to a workspace resource and converts it to a
-	 * {@code File} with the absolute path on the file system. If file does not
-	 * exist in the workspace, the returned file will be based on the given relative
-	 * path.
+	 * Converts the given relative path to a workspace resource and converts it to a {@code File} with the absolute path on the file system. If file does not exist in the workspace, the returned file will
+	 * be based on the given relative path.
 	 *
-	 * @param path to a workspace resource
-	 * @return File object of the given {@code path}, with an absolute path on the
-	 *         file system
+	 * @param path
+	 *            to a workspace resource
+	 * @return File object of the given {@code path}, with an absolute path on the file system
 	 */
 	public static File convertToAbsolutePath(String path) {
 		final File file = new File(path);
@@ -111,24 +109,18 @@ public static File convertToAbsolutePath(String path) {
 	 * @param launchConfig
 	 * @param mode
 	 */
-	public static void launch(ILaunchConfiguration launchConfig, String mode) {
-		try {
-			if (launchConfig != null) {
-				launchConfig.launch(mode, new NullProgressMonitor());
-			}
-		} catch (CoreException e) {
-			CorrosionPlugin.logError(e);
+	public static void launch(ILaunchConfiguration launchConfig, String mode) throws CoreException {
+		if (launchConfig != null) {
+			launchConfig.launch(mode, new NullProgressMonitor());
 		}
 	}
 
 	/**
-	 * Finds or creates a new launch configuration that matches the given search
-	 * terms
+	 * Finds or creates a new launch configuration that matches the given search terms
 	 *
 	 * @param resource
 	 * @param launchConfigurationType
-	 * @return The matching launch configuration or a new launch configuration
-	 *         working copy or null if unable to make a new one
+	 * @return The matching launch configuration or a new launch configuration working copy or null if unable to make a new one
 	 */
 	public static ILaunchConfiguration getLaunchConfiguration(IResource resource, String launchConfigurationType) {
 		ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
@@ -164,8 +156,7 @@ public static String performVariableSubstitution(String string) throws CoreExcep
 	 */
 	public static void openError(String title, String message) {
 		Display.getDefault().asyncExec(() -> {
-			MessageDialog dialog = new MessageDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
-					title, null, message, MessageDialog.ERROR, 0, IDialogConstants.OK_LABEL);
+			MessageDialog dialog = new MessageDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), title, null, message, MessageDialog.ERROR, 0, IDialogConstants.OK_LABEL);
 			dialog.setBlockOnOpen(false);
 			dialog.open();
 		});
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/run/CargoRunDelegate.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/run/CargoRunDelegate.java
index e7eb07c..994fcbb 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/run/CargoRunDelegate.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/run/CargoRunDelegate.java
@@ -24,6 +24,7 @@
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.corrosion.CorrosionPlugin;
 import org.eclipse.corrosion.Messages;
 import org.eclipse.corrosion.cargo.core.CargoTools;
 import org.eclipse.corrosion.launch.RustLaunchDelegateTools;
@@ -39,36 +40,37 @@
 
 public class CargoRunDelegate extends LaunchConfigurationDelegate implements ILaunchShortcut {
 
-	@Override
-	public void launch(ISelection selection, String mode) {
-		ILaunchConfiguration launchConfig = getLaunchConfiguration(
-				RustLaunchDelegateTools.firstResourceFromSelection(selection));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
+	@Override public void launch(ISelection selection, String mode) {
+		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.firstResourceFromSelection(selection));
+		try {
+			RustLaunchDelegateTools.launch(launchConfig, mode);
+		} catch (CoreException e) {
+			CorrosionPlugin.logError(e);
+		}
 	}
 
-	@Override
-	public void launch(IEditorPart editor, String mode) {
+	@Override public void launch(IEditorPart editor, String mode) {
 		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.resourceFromEditor(editor));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
+		try {
+			RustLaunchDelegateTools.launch(launchConfig, mode);
+		} catch (CoreException e) {
+			CorrosionPlugin.logError(e);
+		}
 	}
 
-	@Override
-	public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
-			throws CoreException {
+	@Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
 		String projectName = configuration.getAttribute(RustLaunchDelegateTools.PROJECT_ATTRIBUTE, ""); //$NON-NLS-1$
 		IProject project = null;
 		if (!projectName.isEmpty()) {
 			project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
 		}
 		if (project == null || !project.exists()) {
-			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch,
-					Messages.CargoRunDelegate_unableToFindProject);
+			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch, Messages.CargoRunDelegate_unableToFindProject);
 			return;
 		}
 		String options = configuration.getAttribute(RustLaunchDelegateTools.OPTIONS_ATTRIBUTE, "").trim(); //$NON-NLS-1$
 		String arguments = configuration.getAttribute(RustLaunchDelegateTools.ARGUMENTS_ATTRIBUTE, "").trim(); //$NON-NLS-1$
-		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(
-				configuration.getAttribute(RustLaunchDelegateTools.WORKING_DIRECTORY_ATTRIBUTE, "").trim()); //$NON-NLS-1$
+		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(configuration.getAttribute(RustLaunchDelegateTools.WORKING_DIRECTORY_ATTRIBUTE, "").trim()); //$NON-NLS-1$
 		File workingDirectory = RustLaunchDelegateTools.convertToAbsolutePath(workingDirectoryString);
 		if (workingDirectoryString.isEmpty() || !workingDirectory.exists() || !workingDirectory.isDirectory()) {
 			workingDirectory = project.getLocation().toFile();
@@ -76,8 +78,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 
 		IFile cargoManifest = project.getFile("Cargo.toml"); //$NON-NLS-1$
 		if (!cargoManifest.exists()) {
-			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch,
-					Messages.CargoRunDelegate_unableToFindToml);
+			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch, Messages.CargoRunDelegate_unableToFindToml);
 			return;
 		}
 
@@ -85,8 +86,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 		cargoRunCommand.add(CargoTools.getCargoCommand());
 		cargoRunCommand.add("run"); //$NON-NLS-1$
 		if (!options.isEmpty()) {
-			cargoRunCommand
-					.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(options).split("\\s+"))); //$NON-NLS-1$
+			cargoRunCommand.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(options).split("\\s+"))); //$NON-NLS-1$
 		}
 
 		final String cargoPathString = cargoManifest.getLocation().toPortableString();
@@ -95,8 +95,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 
 		if (!arguments.isEmpty()) {
 			cargoRunCommand.add("--"); //$NON-NLS-1$
-			cargoRunCommand.addAll(
-					Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(arguments).split("\\s+"))); //$NON-NLS-1$
+			cargoRunCommand.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(arguments).split("\\s+"))); //$NON-NLS-1$
 		}
 
 		final List<String> finalRunCommand = cargoRunCommand;
@@ -114,8 +113,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 	}
 
 	private static ILaunchConfiguration getLaunchConfiguration(IResource resource) {
-		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource,
-				"org.eclipse.corrosion.run.CargoRunDelegate"); //$NON-NLS-1$
+		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource, "org.eclipse.corrosion.run.CargoRunDelegate"); //$NON-NLS-1$
 		if (launchConfiguration instanceof ILaunchConfigurationWorkingCopy) {
 			ILaunchConfigurationWorkingCopy wc = (ILaunchConfigurationWorkingCopy) launchConfiguration;
 			wc.setAttribute(RustLaunchDelegateTools.PROJECT_ATTRIBUTE, resource.getProject().getName());


Test patch:
diff --git a/org.eclipse.corrosion.tests/META-INF/MANIFEST.MF b/org.eclipse.corrosion.tests/META-INF/MANIFEST.MF
index 958c627..a9e7c21 100644
--- a/org.eclipse.corrosion.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.corrosion.tests/META-INF/MANIFEST.MF
@@ -24,6 +24,7 @@ Require-Bundle: org.eclipse.corrosion;bundle-version="0.1.0",
  org.eclipse.search,
  org.eclipse.ui.console,
  org.eclipse.debug.ui;bundle-version="3.13.0",
- org.eclipse.jface.text;bundle-version="3.13.0"
+ org.eclipse.jface.text;bundle-version="3.13.0",
+ org.eclipse.cdt.dsf.gdb;bundle-version="5.5.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-ActivationPolicy: lazy
diff --git a/org.eclipse.corrosion.tests/pom.xml b/org.eclipse.corrosion.tests/pom.xml
index 6320e88..ce359ec 100644
--- a/org.eclipse.corrosion.tests/pom.xml
+++ b/org.eclipse.corrosion.tests/pom.xml
@@ -15,6 +15,22 @@
 
 	<build>
 		<plugins>
+			<plugin>
+				<groupId>org.eclipse.tycho</groupId>
+				<artifactId>target-platform-configuration</artifactId>
+				<version>${tycho-version}</version>
+				<configuration>
+					<dependency-resolution>
+						<extraRequirements>
+							<requirement>
+								<type>eclipse-plugin</type>
+								<id>javax.annotation</id>
+								<versionRange>0.0.0</versionRange>
+							</requirement>
+						</extraRequirements>
+					</dependency-resolution>
+				</configuration>
+			</plugin>
 			<plugin>
 				<groupId>org.eclipse.tycho</groupId>
 				<artifactId>tycho-surefire-plugin</artifactId>
diff --git a/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/AllTests.java b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/AllTests.java
index ce7d913..afb6d48 100644
--- a/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/AllTests.java
+++ b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/AllTests.java
@@ -17,9 +17,18 @@
 import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
-@SuiteClasses({ TestIDEIntegration.class, TestSyntaxHighlighting.class, TestLSPIntegration.class,
-	TestNewCargoProjectWizard.class, TestRunConfiguration.class, TestExportCargoProjectWizard.class,
-	TestPerspective.class, TestLSPExtensions.class, TestSnippetContentAssistProcessor.class })
+@SuiteClasses({
+	TestIDEIntegration.class,
+	TestSyntaxHighlighting.class,
+	TestLSPIntegration.class,
+	TestNewCargoProjectWizard.class,
+	TestRunConfiguration.class,
+	TestDebugConfiguration.class,
+	TestExportCargoProjectWizard.class,
+	TestPerspective.class,
+	TestLSPExtensions.class,
+	TestSnippetContentAssistProcessor.class
+})
 public class AllTests {
 
 }
diff --git a/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestDebugConfiguration.java b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestDebugConfiguration.java
new file mode 100644
index 0000000..1414815
--- /dev/null
+++ b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestDebugConfiguration.java
@@ -0,0 +1,149 @@
+/*********************************************************************
+ * Copyright (c) 2018 Red Hat Inc. and others.
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *   Mickael Istria (Red Hat Inc.) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.corrosion.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.corrosion.CorrosionPlugin;
+import org.eclipse.corrosion.debug.RustDebugDelegate;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.ui.console.IConsole;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsoleConstants;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.tests.harness.util.DisplayHelper;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestDebugConfiguration extends AbstractCorrosionTest {
+
+	private static List<IStatus> errors = new ArrayList<>();
+	private static ILogListener listener = (status, plugin) -> {
+		if (status.getSeverity() == IStatus.ERROR) {
+			errors.add(status);
+		}
+	};
+
+	@BeforeClass
+	public static void setUpListener() {
+		CorrosionPlugin.getDefault().getLog().addLogListener(listener);
+	}
+
+	@AfterClass
+	public static void removeListener() {
+		CorrosionPlugin.getDefault().getLog().removeLogListener(listener);
+	}
+
+	@Before
+	public void clearErrors() {
+		errors.clear();
+	}
+
+	@After
+	@Before
+	public void stopLaunchesAndCloseConsoles() throws DebugException {
+		for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) {
+			launch.terminate();
+		}
+		IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager();
+		consoleManager.removeConsoles(consoleManager.getConsoles());
+		IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+		for (IViewReference view : activePage.getViewReferences()) {
+			if (IConsoleConstants.ID_CONSOLE_VIEW.equals(view.getId())) {
+				activePage.hideView(view);
+			}
+		}
+	}
+
+	@Test
+	public void testDebugLaunch() throws Exception {
+		IProject project = getProject(BASIC_PROJECT_NAME);
+		IFile file = project.getFile("src/main.rs");
+		assertTrue(file.exists());
+		RustDebugDelegate delegate = new RustDebugDelegate();
+		delegate.launch(new StructuredSelection(project), "debug");
+		assertEquals(Collections.emptyList(), errors);
+		Assume.assumeTrue("rust-gdb not found, skipping test continuation", Runtime.getRuntime().exec("rust-gdb --version").waitFor() == 0);
+		new DisplayHelper() {
+			@Override
+			protected boolean condition() {
+				IConsole console = getApplicationConsole("basic");
+				return (console != null && console.getDocument().get().contains("5 is positive")) || getErrorPopupMessage() != null;
+			}
+		}.waitForCondition(Display.getCurrent(), 15000);
+		assertNull(getErrorPopupMessage());
+		assertTrue(getApplicationConsole("basic").getDocument().get().contains("5 is positive"));
+	}
+
+	/**
+	 *
+	 * @return the error message or null if there is no "Unable to Launch" popup
+	 */
+	static String getErrorPopupMessage() {
+		for (Shell shell : Display.getDefault().getShells()) {
+			if (shell.getText().equals("Unable to Launch")) {
+				String res = findLabel(shell);
+				if (res == null || res.isEmpty()) {
+					res = "Could not determine error message, but there was an error popup";
+				}
+				return res;
+			}
+		}
+		return null;
+	}
+
+	static IConsole getApplicationConsole(String binaryName) {
+		for (org.eclipse.ui.console.IConsole console : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) {
+			if (console instanceof IConsole && ((IConsole)console).getProcess().getLabel().endsWith(binaryName)) {
+				return (IConsole)console;
+			}
+		}
+		return null;
+	}
+
+	static String findLabel(Composite composite) {
+		for (Control control : composite.getChildren()) {
+			if (control instanceof Label && ((Label)control).getText().length() > 0) {
+				return ((Label)control).getText();
+			} else if (control instanceof Composite) {
+				return findLabel((Composite)control);
+			}
+		}
+		return null;
+	}
+}
diff --git a/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestRunConfiguration.java b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestRunConfiguration.java
index c823c63..64b453f 100644
--- a/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestRunConfiguration.java
+++ b/org.eclipse.corrosion.tests/src/org/eclipse/corrosion/tests/TestRunConfiguration.java
@@ -13,7 +13,8 @@
 package org.eclipse.corrosion.tests;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -35,10 +36,19 @@
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.tests.harness.util.DisplayHelper;
+import org.junit.After;
 import org.junit.Test;
 
 public class TestRunConfiguration extends AbstractCorrosionTest {
 
+	@After
+	public void testErrorPopup() {
+		Shell errorPopup = getErrorPopup();
+		if (errorPopup != null) {
+			errorPopup.close();
+		}
+	}
+
 	@Test
 	public void testBasicRun() throws IOException, CoreException, InterruptedException {
 		CargoRunDelegate delegate = new CargoRunDelegate();
@@ -47,10 +57,10 @@ public void testBasicRun() throws IOException, CoreException, InterruptedExcepti
 		new DisplayHelper() {
 			@Override
 			protected boolean condition() {
-				return DebugPlugin.getDefault().getLaunchManager().getProcesses().length != 0 || errorPopupExists();
+				return DebugPlugin.getDefault().getLaunchManager().getProcesses().length != 0 || getErrorPopup() != null;
 			}
 		}.waitForCondition(Display.getCurrent(), 15000);
-		assertFalse(errorPopupExists());
+		assertNull(getErrorPopup());
 		assertTrue(DebugPlugin.getDefault().getLaunchManager().getProcesses().length != 0);
 		for (IProcess process : DebugPlugin.getDefault().getLaunchManager().getProcesses()) {
 			if (process.getLabel().equals("cargo run")) {
@@ -130,19 +140,19 @@ private static void confirmErrorPopup(ILaunchConfiguration configuration) throws
 		new DisplayHelper() {
 			@Override
 			protected boolean condition() {
-				return errorPopupExists();
+				return getErrorPopup() != null;
 			}
 		}.waitForCondition(Display.getDefault(), 15000);
 
-		assertTrue(errorPopupExists());
+		assertNotNull(getErrorPopup());
 	}
 
-	private static boolean errorPopupExists() {
+	private static Shell getErrorPopup() {
 		for (Shell shell : Display.getDefault().getShells()) {
 			if (shell.getText().equals("Unable to Launch")) {
-				return true;
+				return shell;
 			}
 		}
-		return false;
+		return null;
 	}
 }
diff --git a/org.eclipse.corrosion/src/org/eclipse/corrosion/test/CargoTestDelegate.java b/org.eclipse.corrosion/src/org/eclipse/corrosion/test/CargoTestDelegate.java
index 157ae41..4464a59 100644
--- a/org.eclipse.corrosion/src/org/eclipse/corrosion/test/CargoTestDelegate.java
+++ b/org.eclipse.corrosion/src/org/eclipse/corrosion/test/CargoTestDelegate.java
@@ -24,6 +24,7 @@
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.corrosion.CorrosionPlugin;
 import org.eclipse.corrosion.Messages;
 import org.eclipse.corrosion.cargo.core.CargoTools;
 import org.eclipse.corrosion.launch.RustLaunchDelegateTools;
@@ -40,28 +41,30 @@
 public class CargoTestDelegate extends LaunchConfigurationDelegate implements ILaunchShortcut {
 	public static final String TEST_NAME_ATTRIBUTE = "TEST_NAME"; //$NON-NLS-1$
 
-	@Override
-	public void launch(ISelection selection, String mode) {
-		ILaunchConfiguration launchConfig = getLaunchConfiguration(
-				RustLaunchDelegateTools.firstResourceFromSelection(selection));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
+	@Override public void launch(ISelection selection, String mode) {
+		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.firstResourceFromSelection(selection));
+		try {
+			RustLaunchDelegateTools.launch(launchConfig, mode);
+		} catch (CoreException e) {
+			CorrosionPlugin.logError(e);
+		}
 	}
 
-	@Override
-	public void launch(IEditorPart editor, String mode) {
+	@Override public void launch(IEditorPart editor, String mode) {
 		ILaunchConfiguration launchConfig = getLaunchConfiguration(RustLaunchDelegateTools.resourceFromEditor(editor));
-		RustLaunchDelegateTools.launch(launchConfig, mode);
+		try {
+			RustLaunchDelegateTools.launch(launchConfig, mode);
+		} catch (CoreException e) {
+			CorrosionPlugin.logError(e);
+		}
 	}
 
-	@Override
-	public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
-			throws CoreException {
+	@Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
 		String projectName = configuration.getAttribute(RustLaunchDelegateTools.PROJECT_ATTRIBUTE, ""); //$NON-NLS-1$
 		String options = configuration.getAttribute(RustLaunchDelegateTools.OPTIONS_ATTRIBUTE, "").trim(); //$NON-NLS-1$
 		String testName = configuration.getAttribute(TEST_NAME_ATTRIBUTE, ""); //$NON-NLS-1$
 		String arguments = configuration.getAttribute(RustLaunchDelegateTools.ARGUMENTS_ATTRIBUTE, "").trim(); //$NON-NLS-1$
-		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(
-				configuration.getAttribute(RustLaunchDelegateTools.WORKING_DIRECTORY_ATTRIBUTE, "").trim()); //$NON-NLS-1$
+		String workingDirectoryString = RustLaunchDelegateTools.performVariableSubstitution(configuration.getAttribute(RustLaunchDelegateTools.WORKING_DIRECTORY_ATTRIBUTE, "").trim()); //$NON-NLS-1$
 		File workingDirectory = RustLaunchDelegateTools.convertToAbsolutePath(workingDirectoryString);
 		if (workingDirectoryString.isEmpty() || !workingDirectory.exists() || !workingDirectory.isDirectory()) {
 			workingDirectory = null;
@@ -72,14 +75,12 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 			project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
 		}
 		if (project == null || !project.exists()) {
-			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch,
-					Messages.CargoRunDelegate_unableToFindProject);
+			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch, Messages.CargoRunDelegate_unableToFindProject);
 			return;
 		}
 		IFile cargoManifest = project.getFile("Cargo.toml"); //$NON-NLS-1$
 		if (!cargoManifest.exists()) {
-			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch,
-					Messages.CargoRunDelegate_unableToFindToml);
+			RustLaunchDelegateTools.openError(Messages.CargoRunDelegate_unableToLaunch, Messages.CargoRunDelegate_unableToFindToml);
 			return;
 		}
 
@@ -87,8 +88,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 		cargoTestCommand.add(CargoTools.getCargoCommand());
 		cargoTestCommand.add("test"); //$NON-NLS-1$
 		if (!options.isEmpty()) {
-			cargoTestCommand
-					.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(options).split("\\s+"))); //$NON-NLS-1$
+			cargoTestCommand.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(options).split("\\s+"))); //$NON-NLS-1$
 		}
 
 		final String cargoPathString = cargoManifest.getLocation().toPortableString();
@@ -101,8 +101,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 
 		if (!arguments.isEmpty()) {
 			cargoTestCommand.add("--"); //$NON-NLS-1$
-			cargoTestCommand.addAll(
-					Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(arguments).split("\\s+"))); //$NON-NLS-1$
+			cargoTestCommand.addAll(Arrays.asList(RustLaunchDelegateTools.performVariableSubstitution(arguments).split("\\s+"))); //$NON-NLS-1$
 		}
 
 		final List<String> finalTestCommand = cargoTestCommand;
@@ -120,8 +119,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
 	}
 
 	private static ILaunchConfiguration getLaunchConfiguration(IResource resource) {
-		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource,
-				"org.eclipse.corrosion.test.CargoTestDelegate"); //$NON-NLS-1$
+		ILaunchConfiguration launchConfiguration = RustLaunchDelegateTools.getLaunchConfiguration(resource, "org.eclipse.corrosion.test.CargoTestDelegate"); //$NON-NLS-1$
 		if (launchConfiguration instanceof ILaunchConfigurationWorkingCopy) {
 			ILaunchConfigurationWorkingCopy wc = (ILaunchConfigurationWorkingCopy) launchConfiguration;
 			wc.setAttribute(RustLaunchDelegateTools.PROJECT_ATTRIBUTE, resource.getProject().getName());