Refactor process starting and stdout and stderr reading

This commit is contained in:
Tamas Vajk
2023-09-19 14:16:50 +02:00
parent edc93dfeb7
commit c78cd73edf
3 changed files with 62 additions and 51 deletions

View File

@@ -187,12 +187,12 @@ namespace Semmle.Autobuild.Shared
bool IBuildActions.FileExists(string file) => File.Exists(file);
private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary<string, string>? environment, bool redirectStandardOutput)
private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary<string, string>? environment)
{
var pi = new ProcessStartInfo(exe, arguments)
{
UseShellExecute = false,
RedirectStandardOutput = redirectStandardOutput
RedirectStandardOutput = true
};
if (workingDirectory is not null)
pi.WorkingDirectory = workingDirectory;
@@ -204,40 +204,22 @@ namespace Semmle.Autobuild.Shared
int IBuildActions.RunProcess(string exe, string args, string? workingDirectory, System.Collections.Generic.IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError)
{
var pi = GetProcessStartInfo(exe, args, workingDirectory, env, true);
using var p = new Process
{
StartInfo = pi
};
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) => onOutput(e.Data));
p.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => onError(e.Data));
var pi = GetProcessStartInfo(exe, args, workingDirectory, env);
pi.RedirectStandardError = true;
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.WaitForExit();
return p.ExitCode;
return pi.ReadOutput(out _, onOut: s => onOutput(s), onError: s => onError(s));
}
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? environment)
{
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, false);
using var p = Process.Start(pi);
if (p is null)
{
return -1;
}
p.WaitForExit();
return p.ExitCode;
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment);
return pi.ReadOutput(out _, onOut: Console.WriteLine, onError: null);
}
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? environment, out IList<string> stdOut)
{
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, true);
return pi.ReadOutput(out stdOut, printToConsole: false);
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment);
return pi.ReadOutput(out stdOut, onOut: null, onError: null);
}
void IBuildActions.DirectoryDelete(string dir, bool recursive) => Directory.Delete(dir, recursive);

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Semmle.Util;
@@ -24,7 +25,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var startInfo = new ProcessStartInfo(Exec, args)
{
UseShellExecute = false,
RedirectStandardOutput = true
RedirectStandardOutput = true,
RedirectStandardError = true
};
// Set the .NET CLI language to English to avoid localized output.
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en";
@@ -35,7 +37,16 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
progressMonitor.RunningProcess($"{Exec} {args}");
var pi = MakeDotnetStartInfo(args);
var exitCode = pi.ReadOutput(out output, true);
var threadId = $"[{Environment.CurrentManagedThreadId:D3}]";
void onOut(string s)
{
Console.Out.WriteLine($"{threadId} {s}");
}
void onError(string s)
{
Console.Error.WriteLine($"{threadId} {s}");
}
var exitCode = pi.ReadOutput(out output, onOut, onError);
if (exitCode != 0)
{
progressMonitor.CommandFailed(Exec, args, exitCode);

View File

@@ -8,38 +8,56 @@ namespace Semmle.Util
{
/// <summary>
/// Runs this process, and returns the exit code, as well as the contents
/// of stdout in <paramref name="stdout"/>. If <paramref name="printToConsole"/>
/// is true, then stdout is printed to the console and each line is prefixed
/// with the thread id.
/// of stdout in <paramref name="stdout"/>.
/// </summary>
public static int ReadOutput(this ProcessStartInfo pi, out IList<string> stdout, bool printToConsole)
public static int ReadOutput(this ProcessStartInfo pi, out IList<string> stdout, Action<string>? onOut, Action<string>? onError)
{
stdout = new List<string>();
using var process = Process.Start(pi);
if (process is null)
var @out = new List<string>();
using var process = new Process
{
return -1;
}
StartInfo = pi
};
if (pi.RedirectStandardOutput && !pi.UseShellExecute)
if (process.StartInfo.RedirectStandardOutput && !pi.UseShellExecute)
{
string? s;
do
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
{
s = process.StandardOutput.ReadLine();
if (s is not null)
if (e.Data == null)
{
if (printToConsole)
{
Console.WriteLine($"[{Environment.CurrentManagedThreadId:D3}] {s}");
}
stdout.Add(s);
return;
}
}
while (s is not null);
onOut?.Invoke(e.Data);
@out.Add(e.Data);
});
}
if (process.StartInfo.RedirectStandardError && !pi.UseShellExecute)
{
process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) =>
{
if (e.Data == null)
{
return;
}
onError?.Invoke(e.Data);
});
}
process.Start();
if (process.StartInfo.RedirectStandardError)
{
process.BeginErrorReadLine();
}
if (process.StartInfo.RedirectStandardOutput)
{
process.BeginOutputReadLine();
}
process.WaitForExit();
stdout = @out;
return process.ExitCode;
}
}