Skip to content

Commit

Permalink
build: debug
Browse files Browse the repository at this point in the history
  • Loading branch information
marklechtermann committed May 8, 2023
1 parent a15e55a commit 1d0995d
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/code-style.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ jobs:
- name: Check dotnet version
run: dotnet --version

- name: Check code format (editorconfig)
run: dotnet run --project .\test\test.csproj

- name: Check code format (editorconfig)
run: dotnet format --verify-no-changes
194 changes: 194 additions & 0 deletions test/ProcessRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.CodeAnalysis.Tools.Utilities
{
public readonly struct ProcessResult
{
public Process Process { get; }
public int ExitCode { get; }
public ReadOnlyCollection<string> OutputLines { get; }
public ReadOnlyCollection<string> ErrorLines { get; }

public ProcessResult(Process process, int exitCode, ReadOnlyCollection<string> outputLines, ReadOnlyCollection<string> errorLines)
{
Process = process;
ExitCode = exitCode;
OutputLines = outputLines;
ErrorLines = errorLines;
}
}

public readonly struct ProcessInfo
{
public Process Process { get; }
public ProcessStartInfo StartInfo { get; }
public Task<ProcessResult> Result { get; }

public int Id => Process.Id;

public ProcessInfo(Process process, ProcessStartInfo startInfo, Task<ProcessResult> result)
{
Process = process;
StartInfo = startInfo;
Result = result;
}
}

public static class ProcessRunner
{
public static void OpenFile(string file)
{
if (File.Exists(file))
{
Process.Start(file);
}
}

public static ProcessInfo CreateProcess(
string executable,
string arguments,
bool lowPriority = false,
string? workingDirectory = null,
bool captureOutput = false,
bool displayWindow = true,
Dictionary<string, string>? environmentVariables = null,
Action<Process>? onProcessStartHandler = null,
CancellationToken cancellationToken = default) =>
CreateProcess(
CreateProcessStartInfo(executable, arguments, workingDirectory, captureOutput, displayWindow, environmentVariables),
lowPriority: lowPriority,
onProcessStartHandler: onProcessStartHandler,
cancellationToken: cancellationToken);

public static ProcessInfo CreateProcess(
ProcessStartInfo processStartInfo,
bool lowPriority = false,
Action<Process>? onProcessStartHandler = null,
CancellationToken cancellationToken = default)
{
var errorLines = new List<string>();
var outputLines = new List<string>();
var process = new Process();
var tcs = new TaskCompletionSource<ProcessResult>();

process.EnableRaisingEvents = true;
process.StartInfo = processStartInfo;

process.OutputDataReceived += (s, e) =>
{
if (e.Data != null)
{
outputLines.Add(e.Data);
}
};

process.ErrorDataReceived += (s, e) =>
{
if (e.Data != null)
{
errorLines.Add(e.Data);
}
};

process.Exited += (s, e) =>
{
// We must call WaitForExit to make sure we've received all OutputDataReceived/ErrorDataReceived calls
// or else we'll be returning a list we're still modifying. For paranoia, we'll start a task here rather
// than enter right back into the Process type and start a wait which isn't guaranteed to be safe.
Task.Run(() =>
{
process.WaitForExit();
var result = new ProcessResult(
process,
process.ExitCode,
new ReadOnlyCollection<string>(outputLines),
new ReadOnlyCollection<string>(errorLines));
tcs.TrySetResult(result);
});
};

_ = cancellationToken.Register(() =>
{
if (tcs.TrySetCanceled())
{
// If the underlying process is still running, we should kill it
if (!process.HasExited)
{
try
{
process.Kill();
}
catch (InvalidOperationException)
{
// Ignore, since the process is already dead
}
}
}
});

process.Start();
onProcessStartHandler?.Invoke(process);

if (lowPriority)
{
process.PriorityClass = ProcessPriorityClass.BelowNormal;
}

if (processStartInfo.RedirectStandardOutput)
{
process.BeginOutputReadLine();
}

if (processStartInfo.RedirectStandardError)
{
process.BeginErrorReadLine();
}

return new ProcessInfo(process, processStartInfo, tcs.Task);
}

public static ProcessStartInfo CreateProcessStartInfo(
string executable,
string arguments,
string? workingDirectory = null,
bool captureOutput = false,
bool displayWindow = true,
Dictionary<string, string>? environmentVariables = null)
{
var processStartInfo = new ProcessStartInfo(executable, arguments);

if (!string.IsNullOrEmpty(workingDirectory))
{
processStartInfo.WorkingDirectory = workingDirectory;
}

if (environmentVariables != null)
{
foreach (var pair in environmentVariables)
{
processStartInfo.EnvironmentVariables[pair.Key] = pair.Value;
}
}

if (captureOutput)
{
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
}
else
{
processStartInfo.CreateNoWindow = !displayWindow;
processStartInfo.UseShellExecute = displayWindow;
}

return processStartInfo;
}
}
}
22 changes: 22 additions & 0 deletions test/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.CodeAnalysis.Tools.Utilities;

internal class Program
{
private static int Main(string[] args)
{
TryGetDotNetCliVersion(out string a);
Console.WriteLine(a);

return 0;
}


internal static bool TryGetDotNetCliVersion(out string dotnetVersion)
{
var processInfo = ProcessRunner.CreateProcess("dotnet", "--version", captureOutput: true, displayWindow: false);
var versionResult = processInfo.Result.GetAwaiter().GetResult();

dotnetVersion = versionResult.OutputLines[0].Trim();
return true;
}
}
10 changes: 10 additions & 0 deletions test/test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>

0 comments on commit 1d0995d

Please sign in to comment.