/******************************************************************************* * Copyright (c) 2015 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package com.github.rustdt.ide.debug.ui; import static com.github.rustdt.ide.core.operations.RustSDKPreferences.SDK_PATH_Acessor; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.debug.internal.core.sourcelookup.MapEntrySourceContainer; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence_7_7; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.IMIBackend; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.sourcelookup.ISourceContainer; import com.github.rustdt.ide.core.operations.RustSDKPreferences; import melnorme.lang.ide.core.EclipseCore; import melnorme.lang.ide.core.LangCore; import melnorme.lang.ide.core.utils.ResourceUtils; import melnorme.lang.ide.debug.core.AbstractLangDebugLaunchConfigurationDelegate; import melnorme.lang.ide.debug.core.GdbLaunchDelegateExtension; import melnorme.lang.ide.debug.core.LangSourceLookupDirector; import melnorme.lang.ide.debug.core.services.LangDebugServicesExtensions; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.fields.validation.ValidationException; import melnorme.utilbox.misc.FileUtil; import melnorme.utilbox.misc.Location; import melnorme.utilbox.misc.MiscUtil; import melnorme.utilbox.status.StatusException; public class RustDebugLaunchConfigurationDelegate extends AbstractLangDebugLaunchConfigurationDelegate { @Override protected GdbLaunch doCreateGdbLaunch(ILaunchConfiguration configuration, String mode, ISourceLocator locator) { return new RustGdbLaunch(configuration, mode, locator); } @Override protected LangDebugServicesExtensions doCreateServicesExtensions(IDsfDebugServicesFactory parentServiceFactory) { return new LangDebugServicesExtensions(parentServiceFactory) { @Override public IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) { return new GDBBackend_Rust(session, lc); } @Override protected Sequence getCompleteInitializationSequence__GDBControl_7_7__ext(DsfSession session, Map<String, Object> attributes, RequestMonitorWithProgress rm) { return new FinalLaunchSequence_7_7_Rust(session, attributes, rm); } }; } @Override protected GdbLaunchDelegateExtension createGdbLaunchDelegate() { return new RustGdbLaunchDelegateExt(); } public static class GDBBackend_Rust extends GDBBackend_Lang { protected final Location prettyPrintLoc; public GDBBackend_Rust(DsfSession session, ILaunchConfiguration lc) { super(session, lc); this.prettyPrintLoc = initGDBPrettyPrintLoc(); } protected Location initGDBPrettyPrintLoc() { try { Location loc = SDK_PATH_Acessor.getSDKLocation(project).resolve_fromValid("rustc/lib/rustlib/etc/"); if(loc.toFile().exists()) { return loc; } loc = SDK_PATH_Acessor.getSDKLocation(project).resolve_fromValid("bin/rustlib/etc/"); if(loc.toFile().exists()) { return loc; } return null; // Maybe throw an error? } catch (ValidationException ve) { LangCore.logWarning(ve.getMessage(), ve.getCause()); return null; } } @Override protected String[] getGDBCommandLineArray() { ArrayList2<String> gdbCmdLine = ArrayList2.create(super.getGDBCommandLineArray()); if(prettyPrintLoc != null) { if(!MiscUtil.OS_IS_WINDOWS) { gdbCmdLine.addElements("-d", prettyPrintLoc.toString()); gdbCmdLine.addElements("-iex", "add-auto-load-safe-path " + prettyPrintLoc.toString()); } else { // GDB pretty printers auto-load isn't working directly in Windows, // so they will be loaded directly later, in initializtion sequence. } } return gdbCmdLine.toArray(String.class); } @Override protected void customizeEnvironment(HashMap<String, String> envMap) { if(prettyPrintLoc == null) { return; } String pythonPath = envMap.get("PYTHONPATH"); pythonPath = pythonPath == null ? "" : pythonPath + File.pathSeparator; pythonPath += prettyPrintLoc.toPathString(); envMap.put("PYTHONPATH", pythonPath); } } /* ----------------- ----------------- */ public static class FinalLaunchSequence_7_7_Rust extends FinalLaunchSequence_7_7 { protected IGDBControl fCommandControl; protected CommandFactory fCommandFactory; public FinalLaunchSequence_7_7_Rust(DsfSession session, Map<String, java.lang.Object> attributes, RequestMonitorWithProgress rm) { super(session, attributes, rm); } @Execute @Override public void stepInitializeFinalLaunchSequence_7_7(RequestMonitor rm) { DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); fCommandControl = tracker.getService(IGDBControl.class); tracker.dispose(); if(fCommandControl == null) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain control service", null)); rm.done(); return; } fCommandFactory = fCommandControl.getCommandFactory(); super.stepInitializeFinalLaunchSequence_7_7(rm); } @Execute @Override public void stepSourceGDBInitFile(RequestMonitor rm) { try { java.nio.file.Path tempFile = WindowsGdbTempFileHelper.getTempFile(); fCommandControl.queueCommand( fCommandFactory.createCLISource(fCommandControl.getContext(), tempFile.toString()), new DataRequestMonitor<MIInfo>(getExecutor(), rm) { @Override protected void handleCompleted() { // rm.setStatus(getStatus()); rm.done(); } } ); super.stepSourceGDBInitFile(rm); } catch(IOException e) { rm.setStatus(EclipseCore.createErrorStatus("Could not create GDB init temp file", e)); rm.done(); } } } public static class WindowsGdbTempFileHelper { private static java.nio.file.Path tempFile; protected static synchronized java.nio.file.Path getTempFile() throws IOException { if(tempFile == null) { tempFile = Files.createTempFile("rustdt_gdbinit_windows_", ""); Runtime.getRuntime().addShutdownHook(new Thread(() -> tempFile.toFile().delete())); String contents = "python\n" + "print \"--Registering Rust pretty-printers for Windows--\"\n" + "import gdb_rust_pretty_printing\n"+ "gdb_rust_pretty_printing.register_printers(gdb)\n"+ "print \"--DONE--\"\n" + "end\n"; FileUtil.writeStringToFile(tempFile.toFile(), contents, melnorme.utilbox.misc.StringUtil.UTF8); } return tempFile; } } /* ----------------- Add source lookup for Rust std lib ----------------- */ protected class RustGdbLaunchDelegateExt extends GdbLaunchDelegateExt { public RustGdbLaunchDelegateExt() { } @Override protected LangSourceLookupDirector createSourceLookupDirector(ILaunchConfiguration lc, DsfSession session) { IProject project = getProject(lc); // can be null if(project == null || project.getLocation() == null) { return super.createSourceLookupDirector(lc, session); } IPath projectLoc = project.getLocation(); return new LangSourceLookupDirector(session) { @Override protected void customizeDefaultSourceContainers(ArrayList2<ISourceContainer> containers) { Location sdkSrcLocation; try { sdkSrcLocation = RustSDKPreferences.SDK_SRC_PATH3.getDerivedValue(project); } catch(StatusException e) { LangCore.logWarning("Error setting up source mapping for Rust `src` path.", e); return; } // So, this seems to be the mapping that Rust standard lib sources get in debug info. // I guess this could change in the future, need to watch out for that. containers.add(new MapEntrySourceContainer( projectLoc.append("../src").toOSString(), ResourceUtils.epath(sdkSrcLocation) )); } }; } } }