| | | 1 | | using System.Diagnostics; |
| | | 2 | | |
| | | 3 | | namespace ZhEaIsNsAaBn.DbSeeder.Tool; |
| | | 4 | | |
| | | 5 | | /// <summary>Runs <c>dotnet</c>: captured (msbuild, build) or on the real terminal (the command itself).</summary> |
| | | 6 | | internal sealed class ToolProcess |
| | | 7 | | { |
| | | 8 | | private readonly TextWriter _log; |
| | | 9 | | private readonly bool _verbose; |
| | | 10 | | |
| | | 11 | | |
| | 0 | 12 | | public ToolProcess( TextWriter log, bool verbose ) |
| | | 13 | | { |
| | 0 | 14 | | _log = log; |
| | 0 | 15 | | _verbose = verbose; |
| | 0 | 16 | | } |
| | | 17 | | |
| | | 18 | | |
| | | 19 | | /// The dotnet that runs this tool when the SDK says so, else the one on PATH. |
| | 0 | 20 | | public static string Dotnet => System.Environment.GetEnvironmentVariable( "DOTNET_HOST_PATH" ) is { Length: > 0 } ho |
| | | 21 | | |
| | | 22 | | |
| | | 23 | | /// <summary>Runs <c>dotnet <paramref name="args"/></c> with stdout + stderr captured.</summary> |
| | | 24 | | public async Task<(int Code, string Output)> CaptureAsync( IReadOnlyList<string> args ) |
| | | 25 | | { |
| | 0 | 26 | | var start = Start( args, null ); |
| | 0 | 27 | | start.RedirectStandardOutput = true; |
| | 0 | 28 | | start.RedirectStandardError = true; |
| | | 29 | | // A reused MSBuild node (or the MSBuild server) inherits the redirected output and lives on for minutes: |
| | | 30 | | // reading the output to its end would wait for it. |
| | 0 | 31 | | start.Environment[ "MSBUILDDISABLENODEREUSE" ] = "1"; |
| | 0 | 32 | | start.Environment[ "DOTNET_CLI_USE_MSBUILD_SERVER" ] = "0"; |
| | | 33 | | |
| | 0 | 34 | | Trace( start ); |
| | | 35 | | |
| | 0 | 36 | | using var process = Process.Start( start ) ?? throw new ToolException( "dotnet could not be started." ); |
| | | 37 | | |
| | 0 | 38 | | var stdout = process.StandardOutput.ReadToEndAsync(); |
| | 0 | 39 | | var stderr = process.StandardError.ReadToEndAsync(); |
| | | 40 | | |
| | 0 | 41 | | await process.WaitForExitAsync().ConfigureAwait( false ); |
| | | 42 | | |
| | 0 | 43 | | return ( process.ExitCode, ( await stdout.ConfigureAwait( false ) ) + ( await stderr.ConfigureAwait( false ) ) ) |
| | 0 | 44 | | } |
| | | 45 | | |
| | | 46 | | /// <summary> |
| | | 47 | | /// Runs <c>dotnet <paramref name="args"/></c> on the inherited terminal (nothing redirected: prompts and colours wo |
| | | 48 | | /// stdin stays redirected), in <paramref name="workingDirectory"/>. Ctrl+C reaches it directly; this process just w |
| | | 49 | | /// </summary> |
| | | 50 | | public async Task<int> RunAsync( IReadOnlyList<string> args, string workingDirectory, IReadOnlyDictionary<string, st |
| | | 51 | | { |
| | 0 | 52 | | var start = Start( args, workingDirectory ); |
| | | 53 | | |
| | 0 | 54 | | foreach ( var (name, value) in environment ) { start.Environment[ name ] = value; } |
| | | 55 | | |
| | 0 | 56 | | Trace( start ); |
| | | 57 | | |
| | 0 | 58 | | using var process = Process.Start( start ) ?? throw new ToolException( "dotnet could not be started." ); |
| | | 59 | | |
| | 0 | 60 | | ConsoleCancelEventHandler wait = ( _, e ) => e.Cancel = true; |
| | 0 | 61 | | Console.CancelKeyPress += wait; |
| | | 62 | | |
| | | 63 | | try |
| | | 64 | | { |
| | 0 | 65 | | await process.WaitForExitAsync().ConfigureAwait( false ); |
| | | 66 | | |
| | 0 | 67 | | return process.ExitCode; |
| | | 68 | | } |
| | 0 | 69 | | finally { Console.CancelKeyPress -= wait; } |
| | 0 | 70 | | } |
| | | 71 | | |
| | | 72 | | |
| | | 73 | | private static ProcessStartInfo Start( IReadOnlyList<string> args, string? workingDirectory ) |
| | | 74 | | { |
| | 0 | 75 | | var start = new ProcessStartInfo( Dotnet ) { UseShellExecute = false }; |
| | | 76 | | |
| | 0 | 77 | | foreach ( var arg in args ) { start.ArgumentList.Add( arg ); } |
| | | 78 | | |
| | 0 | 79 | | if ( workingDirectory is not null ) { start.WorkingDirectory = workingDirectory; } |
| | | 80 | | |
| | 0 | 81 | | return start; |
| | | 82 | | } |
| | | 83 | | |
| | | 84 | | private void Trace( ProcessStartInfo start ) |
| | | 85 | | { |
| | 0 | 86 | | if ( _verbose ) { _log.WriteLine( $"> {start.FileName} {string.Join( " ", start.ArgumentList.Select( a => a.Cont |
| | 0 | 87 | | } |
| | | 88 | | } |