using System.ComponentModel; using System.Diagnostics; namespace Build.Tools; /// /// Runs one external tool and lets it write straight to the terminal. /// /// /// The output is deliberately not captured. A gate which swallows the output of a failing test run /// and then prints "failed" leaves the person who has to fix it with nothing to go on, while the /// tools it runs already say everything worth saying -- which test, which line, which lint. /// public static class CommandRunner { /// /// What a tool which could not be started at all reports. /// /// /// Anything but zero counts as a failure, so the exact number matters only in that it is not /// one a tool would plausibly return itself. /// public const int COULD_NOT_START = 127; /// /// Runs a tool and waits for it. /// /// Where the tool should run. /// The tool, as it is called on the PATH. /// What to pass it. /// The exit code of the tool, or COULD_NOT_START when it never ran. public static async Task RunAsync(string workingDirectory, string fileName, string arguments) { Console.WriteLine($"- Running '{fileName} {arguments}' in '{workingDirectory}' ..."); var startInfo = new ProcessStartInfo { FileName = fileName, Arguments = arguments, WorkingDirectory = workingDirectory, UseShellExecute = false, }; try { using var process = Process.Start(startInfo); if (process is null) { Console.WriteLine($"- Error: '{fileName}' did not start, and the system did not say why."); return COULD_NOT_START; } await process.WaitForExitAsync(); return process.ExitCode; } catch (Win32Exception exception) { Console.WriteLine($"- Error: '{fileName}' could not be started: {exception.Message}"); Console.WriteLine($" Is '{fileName}' installed and on the PATH?"); return COULD_NOT_START; } } }