/// <summary> /// Runs the specified command in a separate project /// </summary> /// <param name="type">Command type</param> /// <param name="command">Command to run</param> private Task <string> RunCommand(string type, string command) { var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); pane.WriteLine($"Running {type}:"); pane.WriteLine(command); var tcs = new TaskCompletionSource <string>(); var cmdProcess = new Process { StartInfo = new ProcessStartInfo { FileName = "cmd.exe", Arguments = $"/C {command}", CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, WorkingDirectory = SpectNetPackage.Default.ActiveProject.ProjectDir } }; Task.Factory.StartNew(() => { try { cmdProcess.Start(); // --- Wait up to 5 minutes to run the process cmdProcess.WaitForExit(300000); if (!cmdProcess.HasExited) { cmdProcess.Kill(); tcs.SetException(new InvalidOperationException("Task did not complete within timeout.")); } else { var exitCode = cmdProcess.ExitCode; var output = cmdProcess.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(output)) { pane.Write(output); } pane.WriteLine($"Executing {type} completed with exit code {exitCode}."); tcs.SetResult(output.Length == 0 ? null : output); } } catch (Exception ex) { tcs.SetException(ex); } finally { cmdProcess.Dispose(); } }); return(tcs.Task); }
/// <summary> /// Gets the active project's configuration /// </summary> /// <returns> /// Configuration information /// </returns> public SpectrumProjectConfiguration GetSpectrumProjectConfiguration() { try { var configFileName = Path.Combine(SpectNetPackage.Default.ActiveProject.ProjectDir, SpectrumProject.PROJECT_CONFIG_FILE); if (!File.Exists(configFileName)) { return(null); } var configContents = File.ReadAllText(configFileName); return(JsonConvert.DeserializeObject <SpectrumProjectConfiguration>(configContents)); } catch (Exception ex) { var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); pane.WriteLine($"Cannot obtain ZX Spectrum project configuration: {ex.Message}"); return(null); } }
/// <summary> /// Compiles the program code /// </summary> /// <returns>True, if compilation successful; otherwise, false</returns> public async Task <bool> CompileCode() { // --- Execute pre-build event var codeManager = SpectNetPackage.Default.CodeManager; PrebuildError = null; PostbuildError = null; CleanupError = null; PreexportError = null; PostexportError = null; var eventOutput = await codeManager.RunPreBuildEvent(ItemFullPath); if (eventOutput != null) { PrebuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } var start = DateTime.Now; var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); await pane.WriteLineAsync(_compiler.ServiceName); var output = await _compiler.CompileDocument(ItemFullPath, PrepareAssemblerOptions()); var duration = (DateTime.Now - start).TotalMilliseconds; await pane.WriteLineAsync($"Compile time: {duration}ms"); var compiled = output != null; if (compiled) { // --- Sign that compilation was successful HostPackage.DebugInfoProvider.CompiledOutput = Output = output; CreateCompilationListFile(_hierarchy, _itemId); } HostPackage.CodeManager.RaiseCompilationCompleted(output); // --- Execute post-build event if (compiled && output.Errors.Count == 0) { // --- Export if required if (!string.IsNullOrEmpty(SpectNetPackage.Default.Options.ExportOnCompile)) { var parts = SpectNetPackage.Default.Options.ExportOnCompile.Split(';'); var name = Path.GetFileNameWithoutExtension(ItemPath) ?? "MyCode"; var vm = new ExportZ80ProgramViewModel { Name = name, Filename = Path.Combine(Path.GetDirectoryName(SpectNetPackage.Default.ActiveProject.Root.FullName), EXPORT_PATH, name) }; if (parts.Length > 0) { // --- Validate format var format = parts[0].Trim().ToLower(); if (format == "tap") { vm.Format = ExportFormat.Tap; } else if (format == "tzx") { vm.Format = ExportFormat.Tzx; } else if (format == "hex") { vm.Format = ExportFormat.IntelHex; } else { PostbuildError = $"Invalid export format: {parts[0]}"; } } if (PostbuildError == null && parts.Length > 1) { var flag = parts[1].Trim(); if (flag == "" || flag == "1") { vm.AutoStart = true; } else if (flag == "0") { vm.AutoStart = false; } else { PostbuildError = $"Invalid export Auto-Start flag (use 0 or 1): {parts[1]}"; } } if (PostbuildError == null && parts.Length > 2) { var flag = parts[2].Trim(); if (flag == "" || flag == "1") { vm.ApplyClear = true; } else if (flag == "0") { vm.ApplyClear = false; } else { PostbuildError = $"Invalid export Apply CLEAR flag (use 0 or 1): {parts[2]}"; } } vm.SingleBlock = true; vm.AddToProject = false; if (PostbuildError == null && parts.Length > 3) { var flag = parts[3].Trim(); if (flag == "" || flag == "0") { vm.AddPause0 = false; } else if (flag == "1") { vm.AddPause0 = true; } else { PostbuildError = $"Invalid export Add PAUSE flag (use 0 or 1): {parts[3]}"; } } if (PostbuildError == null && parts.Length > 4 && parts[4].Trim() != "") { if (ushort.TryParse(parts[4], out var startAddr)) { vm.StartAddress = startAddr.ToString(); } else { PostbuildError = $"Invalid Start Address (use 0-65535): {parts[4]}"; } } if (PostbuildError == null && parts.Length > 5 && parts[5].Trim() != "") { if (ushort.TryParse(parts[5], out var border)) { if (border >= 0 && border <= 7) { vm.Border = border.ToString(); } else { PostbuildError = $"Invalid Border value (use 0-7): {parts[5]}"; } } else { PostbuildError = $"Invalid Border value (use 0-7): {parts[5]}"; } } if (PostbuildError == null && parts.Length > 6) { vm.ScreenFile = parts[6]; } if (PostbuildError == null) { ExportProgramCommand.ExportCompiledCode(Output, vm); } } // --- Run post-build tasks eventOutput = await codeManager.RunPostBuildEvent(ItemFullPath); if (eventOutput != null) { PostbuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } } else { CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); } return(compiled); }
/// <summary> /// Compiles the program code /// </summary> /// <returns>True, if compilation successful; otherwise, false</returns> public async Task <bool> CompileCode() { // --- Execute pre-build event var codeManager = SpectNetPackage.Default.CodeManager; PrebuildError = null; PostbuildError = null; CleanupError = null; PreexportError = null; PostexportError = null; var eventOutput = await codeManager.RunPreBuildEvent(ItemFullPath); if (eventOutput != null) { PrebuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } var start = DateTime.Now; var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); await pane.WriteLineAsync(_compiler.ServiceName); var output = await _compiler.CompileDocument(ItemFullPath, PrepareAssemblerOptions()); var duration = (DateTime.Now - start).TotalMilliseconds; await pane.WriteLineAsync($"Compile time: {duration}ms"); var compiled = output != null; if (compiled) { // --- Sign that compilation was successful HostPackage.DebugInfoProvider.CompiledOutput = Output = output; CreateCompilationListFile(_hierarchy, _itemId); } HostPackage.CodeManager.RaiseCompilationCompleted(output); // --- Execute post-build event if (compiled && output.Errors.Count == 0) { eventOutput = await codeManager.RunPostBuildEvent(ItemFullPath); if (eventOutput != null) { PostbuildError = eventOutput; CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); return(false); } } else { CleanupError = await codeManager.RunBuildErrorEvent(ItemFullPath); DisplayBuildErrors(); } return(compiled); }
/// <summary> /// Logs a message to the SpectNetIDE output pane /// </summary> /// <param name="message">Message to log</param> public static void Log(string message) { var pane = OutputWindow.GetPane <SpectNetIdeOutputPane>(); pane.WriteLine(message); }
public Task <ZxbResult> RunAsync(ZxbOptions options, bool log = false) { var tcs = new TaskCompletionSource <ZxbResult>(); var zxbProcess = new Process { StartInfo = new ProcessStartInfo { FileName = Path.Combine(_zxbPath ?? string.Empty, "zxb.exe"), Arguments = options.ToString(), CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false } }; _ = Task.Factory.StartNew(() => { try { if (log) { var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); pane.WriteLine($"Starting ZXB with: {zxbProcess.StartInfo.Arguments}"); } var output = new StringBuilder(); var error = new StringBuilder(); using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) { zxbProcess.OutputDataReceived += (sender, e) => { if (e.Data == null) { outputWaitHandle.Set(); } else { output.AppendLine(e.Data); } }; zxbProcess.ErrorDataReceived += (sender, e) => { if (e.Data == null) { errorWaitHandle.Set(); } else { error.AppendLine(e.Data); } }; zxbProcess.Start(); zxbProcess.BeginOutputReadLine(); zxbProcess.BeginErrorReadLine(); if (zxbProcess.WaitForExit(_timeOut) && outputWaitHandle.WaitOne(_timeOut) && errorWaitHandle.WaitOne(_timeOut)) { var exitCode = zxbProcess.ExitCode; tcs.SetResult(new ZxbResult(exitCode, error.ToString())); } else { zxbProcess.Kill(); tcs.SetException(new InvalidOperationException("ZXB task did not complete within timeout.")); } } } catch (Exception ex) { tcs.SetException(ex); } finally { zxbProcess.Dispose(); } }); return(tcs.Task); }
/// <summary> /// Responds to the event when the Z80 assembler releases a message /// </summary> private void OnAssemblerMessage(object sender, AssemblerMessageArgs e) { var pane = OutputWindow.GetPane <Z80AssemblerOutputPane>(); pane.WriteLine(e.Message); }