/// <summary> /// Determines whether the current project is Raspberry compatible, has Raspberry project /// settings and is enabled for debugging and returns the name of the target connection when /// these conditions are met. /// </summary> /// <returns> /// <c>null</c> if the project does not have Raspberry settings or is not an eligible /// .NET Core project, <see cref="ProjectSettings.DefaultConnectionName"/> when the project /// targets the default Raspberry connection, otherwise the name of the specific target /// connection will be returned. /// </returns> private string GetConnectionName() { ThreadHelper.ThrowIfNotOnUIThread(); if (dte.Solution == null) { return(null); } var project = PackageHelper.GetStartupProject(dte.Solution); if (project == null) { return(null); } var projectProperties = ProjectProperties.CopyFrom(dte.Solution, project); if (!projectProperties.IsRaspberryCompatible) { return(null); } var projectSettings = PackageHelper.GetProjectSettings(dte.Solution, project); if (projectSettings == null || !projectSettings.EnableRemoteDebugging) { return(null); } return(projectSettings.RemoteDebugTarget ?? ProjectSettings.DefaultConnectionName); }
/// <summary> /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event args.</param> #pragma warning disable VSTHRD100 private async void Execute(object sender, EventArgs e) #pragma warning restore VSTHRD100 { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); if (!await DebugHelper.EnsureOpenSshAsync()) { return; } var project = DebugHelper.GetTargetProject(dte); if (project == null) { return; } var projectProperties = ProjectProperties.CopyFrom(dte.Solution, project); if (!await DebugHelper.PublishProjectWithUIAsync(dte, dte.Solution, project, projectProperties)) { return; } var connectionInfo = DebugHelper.GetDebugConnectionInfo(projectProperties); if (connectionInfo == null) { return; } // Identify the most recent SDK installed on the workstation that has the same // major and minor version numbers as the project. We'll ensure that the same // SDK is installed on the Raspberry (further below). var targetSdk = DebugHelper.GetTargetSdk(projectProperties); if (targetSdk == null) { return; } // Establish a Raspberry connection to handle some things before we start the debugger. var connection = await DebugHelper.InitializeConnectionAsync(connectionInfo, targetSdk, projectProperties, PackageHelper.GetProjectSettings(dte.Solution, project)); if (connection == null) { return; } using (connection) { // Generate a temporary [launch.json] file and launch the debugger. using (var tempFile = await CreateLaunchSettingsAsync(connectionInfo, projectProperties)) { dte.ExecuteCommand("DebugAdapterHost.Launch", $"/LaunchJson:\"{tempFile.Path}\""); } // Launch the browser for ASPNET apps if requested. Note that we're going to do this // on a background task to poll the Raspberry, waiting for the app to create the create // the LISTENING socket. if (projectProperties.IsAspNet && projectProperties.AspLaunchBrowser) { var baseUri = $"http://{connectionInfo.Host}:{projectProperties.AspPort}"; var launchReady = false; await NeonHelper.WaitForAsync( async() => { if (dte.Mode != vsIDEMode.vsIDEModeDebug) { // The developer must have stopped debugging before the ASPNET // application was able to begin servicing requests. return(true); } try { var appListeningScript = $@" if lsof -i -P -n | grep --quiet 'TCP \*:{projectProperties.AspPort} (LISTEN)' ; then exit 0 else exit 1 fi "; var response = connection.SudoCommand(CommandBundle.FromScript(appListeningScript)); if (response.ExitCode != 0) { return(false); } // Wait just a bit longer to give the application a chance to // perform any additional initialization. await Task.Delay(TimeSpan.FromSeconds(1)); launchReady = true; return(true); } catch { return(false); } }, timeout : TimeSpan.FromSeconds(30), pollInterval : TimeSpan.FromSeconds(0.5)); if (launchReady) { NeonHelper.OpenBrowser($"{baseUri}{projectProperties.AspRelativeBrowserUri}"); } } } }