/// <summary> /// Uploads the files for the program being debugged to the Raspberry, replacing /// any existing files. /// </summary> /// <param name="programName">The program name</param> /// <param name="assemblyName">The addembly name.</param> /// <param name="publishedBinaryFolder">Path to the workstation folder holding the program files.</param> /// <returns><c>true</c> on success.</returns> public async Task <bool> UploadProgramAsync(string programName, string assemblyName, string publishedBinaryFolder) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(programName), nameof(programName)); Covenant.Requires <ArgumentException>(!programName.Contains(' '), nameof(programName)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(assemblyName), nameof(assemblyName)); Covenant.Requires <ArgumentException>(!assemblyName.Contains(' '), nameof(assemblyName)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(publishedBinaryFolder), nameof(publishedBinaryFolder)); Covenant.Requires <ArgumentNullException>(Directory.Exists(publishedBinaryFolder), nameof(publishedBinaryFolder)); // We're going to ZIP the program files locally and then transfer the zipped // files to the Raspberry to be expanded there. var debugFolder = LinuxPath.Combine(PackageHelper.RemoteDebugBinaryRoot(Username), programName); var groupScript = string.Empty; if (!string.IsNullOrEmpty(projectSettings.TargetGroup)) { groupScript = $@" # Add the program assembly to the user specified target group (if any). This # defaults to [gpio] so users will be able to access the GPIO pins. if ! chgrp {projectSettings.TargetGroup} {debugFolder}/{assemblyName} ; then exit 1 fi "; } var uploadScript = $@" # Ensure that the debug folder exists. if ! mkdir -p {debugFolder} ; then exit 1 fi # Clear all existing program files. if ! rm -rf {debugFolder}/* ; then exit 1 fi # Unzip the binary and other files to the debug folder. if ! unzip program.zip -d {debugFolder} ; then exit 1 fi # The program assembly needs execute permissions. if ! chmod 770 {debugFolder}/{assemblyName} ; then exit 1 fi {groupScript} exit 0 "; // I'm not going to do a progress dialog because this should be fast. try { LogInfo($"Uploading program to: [{debugFolder}]"); var bundle = new CommandBundle(uploadScript); bundle.AddZip("program.zip", publishedBinaryFolder); var response = RunCommand(bundle); if (response.ExitCode == 0) { LogInfo($"Program uploaded"); return(await Task.FromResult(true)); } else { LogError(response.AllText); return(await Task.FromResult(false)); } } catch (Exception e) { LogException(e); return(await Task.FromResult(false)); } }
/// <summary> /// Creates the temporary launch settings file we'll use starting <b>vsdbg</b> on /// the Raspberry for this command. /// </summary> /// <param name="connectionInfo">The connection information.</param> /// <param name="projectProperties">The project properties.</param> /// <returns>The <see cref="TempFile"/> referencing the created launch file.</returns> private async Task <TempFile> CreateLaunchSettingsAsync(ConnectionInfo connectionInfo, ProjectProperties projectProperties) { Covenant.Requires <ArgumentNullException>(connectionInfo != null, nameof(connectionInfo)); Covenant.Requires <ArgumentNullException>(projectProperties != null, nameof(projectProperties)); var systemRoot = Environment.GetFolderPath(Environment.SpecialFolder.Windows); var debugFolder = LinuxPath.Combine(PackageHelper.RemoteDebugBinaryRoot(connectionInfo.User), projectProperties.Name); // NOTE: // // We're having the remote [vsdbg] debugger launch our program as: // // dotnet program.dll args // // where: // // dotnet - is the fully qualified path to the dotnet SDK tool on the remote machine // program.dll - is the fully qualified path to our program DLL // args - are the arguments to be passed to our program // // This means that we need add [program.dll] as the first argument, followed // by the program arguments. var args = new JArray(); args.Add(LinuxPath.Combine(debugFolder, projectProperties.AssemblyName + ".dll")); foreach (var arg in projectProperties.CommandLineArgs) { args.Add(arg); } var environmentVariables = new JObject(); foreach (var variable in projectProperties.EnvironmentVariables) { environmentVariables.Add(variable.Key, variable.Value); } // For ASPNET apps, set the [ASPNETCORE_URLS] environment variable // to [http://0.0.0.0:PORT] so that the app running on the Raspberry will // be reachable from the development workstation. Note that we don't // support HTTPS at this time. if (projectProperties.IsAspNet) { environmentVariables["ASPNETCORE_URLS"] = $"http://0.0.0.0:{projectProperties.AspPort}"; } // Construct the debug launch JSON file. var engineLogging = string.Empty; #if DISABLED // Uncomment this to have the remote debugger log the traffic it // sees from Visual Studio for debugging purposes. The log file // is persisted to the program folder on the Raspberry. engineLogging = $"--engineLogging={debugFolder}/__vsdbg-log.txt"; #endif var settings = new JObject ( new JProperty("version", "0.2.1"), new JProperty("adapter", Path.Combine(systemRoot, "Sysnative", "OpenSSH", "ssh.exe")), new JProperty("adapterArgs", $"-i \"{connectionInfo.PrivateKeyPath}\" -o \"StrictHostKeyChecking no\" {connectionInfo.User}@{connectionInfo.Host} {PackageHelper.RemoteDebuggerPath} --interpreter=vscode {engineLogging}"), new JProperty("configurations", new JArray ( new JObject ( new JProperty("name", "Debug on Raspberry"), new JProperty("type", "coreclr"), new JProperty("request", "launch"), new JProperty("program", PackageHelper.RemoteDotnetCommand), new JProperty("args", args), new JProperty("cwd", debugFolder), new JProperty("stopAtEntry", "false"), new JProperty("console", "internalConsole"), new JProperty("env", environmentVariables) ) ) ) ); var tempFile = new TempFile(".launch.json"); using (var stream = new FileStream(tempFile.Path, FileMode.CreateNew, FileAccess.ReadWrite)) { await stream.WriteAsync(Encoding.UTF8.GetBytes(settings.ToString())); } return(tempFile); }