using System.Collections.Generic; using System.Diagnostics; /// /// Class encompassing the optional settings for running processes. /// public class RunOptions { /// /// The working directory of the process. /// public string WorkingDirectory { get; set; } /// /// Container logging the StandardOutput content. /// public IList StandardOutputListing { get; set; } /// /// Desired maximum time-out for the process /// public int TimeOut { get; set; } } /// /// Wrapper for the exit code and state. /// Used to query the result of an execution with method calls. /// public struct ExitStatus { private int _code; private bool _timeOut; /// /// Default constructor when the execution finished. /// /// The exit code public ExitStatus(int code) { this._code = code; this._timeOut = false; } /// /// Default constructor when the execution potentially timed out. /// /// The exit code /// True if the execution timed out public ExitStatus(int code, bool timeOut) { this._code = code; this._timeOut = timeOut; } /// /// Flag signalling that the execution timed out. /// public bool DidTimeOut { get { return _timeOut; } } /// /// Implicit conversion from ExitStatus to the exit code. /// /// The exit status /// The exit code public static implicit operator int(ExitStatus exitStatus) { return exitStatus._code; } /// /// Trigger Exception for non-zero exit code. /// /// The message to use in the Exception /// The exit status for further queries public ExitStatus ExceptionOnError(string errorMessage) { if (this._code != 0) { throw new Exception(errorMessage); } return this; } } /// /// Run the given executable with the given arguments. /// /// Executable to run /// Arguments /// The exit status for further queries ExitStatus Run(string exec, string args) { return Run(exec, args, new RunOptions()); } /// /// Run the given executable with the given arguments. /// /// Executable to run /// Arguments /// Working directory /// The exit status for further queries ExitStatus Run(string exec, string args, string workingDirectory) { return Run(exec, args, new RunOptions() { WorkingDirectory = workingDirectory }); } /// /// Run the given executable with the given arguments. /// /// Executable to run /// Arguments /// Optional settings /// The exit status for further queries ExitStatus Run(string exec, string args, RunOptions runOptions) { var workingDirectory = runOptions.WorkingDirectory ?? System.IO.Directory.GetCurrentDirectory(); var process = System.Diagnostics.Process.Start( new ProcessStartInfo(exec, args) { WorkingDirectory = workingDirectory, UseShellExecute = false, RedirectStandardOutput = runOptions.StandardOutputListing != null }); if (runOptions.StandardOutputListing != null) { process.OutputDataReceived += (s, e) => { if (e.Data != null) { runOptions.StandardOutputListing.Add(e.Data); } }; process.BeginOutputReadLine(); } if (runOptions.TimeOut == 0) { process.WaitForExit(); return new ExitStatus(process.ExitCode); } else { bool finished = process.WaitForExit(runOptions.TimeOut); if (finished) { return new ExitStatus(process.ExitCode); } else { KillProcessTree(process); return new ExitStatus(0, true); } } } /// /// Run restore with the given arguments /// /// Executable to run /// Arguments /// Optional settings /// The exit status for further queries ExitStatus RunRestore(string exec, string args, string workingDirectory) { Information("Restoring packages...."); var p = StartAndReturnProcess(exec, new ProcessSettings { Arguments = args, RedirectStandardOutput = true, WorkingDirectory = workingDirectory }); p.WaitForExit(); var exitCode = p.GetExitCode(); if (exitCode == 0) { Information("Package restore successful!"); } else { Error(string.Join("\n", p.GetStandardOutput())); } return new ExitStatus(exitCode); } /// /// Kill the given process and all its child processes. /// /// Root process public void KillProcessTree(Process process) { // Child processes are not killed on Windows by default // Use TASKKILL to kill the process hierarchy rooted in the process if (IsRunningOnWindows()) { StartProcess($"TASKKILL", new ProcessSettings { Arguments = $"/PID {process.Id} /T /F", }); } else { process.Kill(); } }