Skip to content

Commit

Permalink
Improve output reporting, fix potential race condition of output redi…
Browse files Browse the repository at this point in the history
…rection
  • Loading branch information
tmat committed Dec 20, 2024
1 parent b65e3c6 commit c7e93f0
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 21 deletions.
26 changes: 13 additions & 13 deletions src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
};
}

if (!await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken))
var (buildSucceeded, buildOutput, _) = await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken);
BuildUtilities.ReportBuildOutput(Context.Reporter, buildOutput, buildSucceeded, projectDisplay: rootProjectOptions.ProjectPath);
if (!buildSucceeded)
{
// error has been reported:
continue;
}

Expand Down Expand Up @@ -334,7 +335,12 @@ void FileChangedCallback(ChangedPath change)
var buildResults = await Task.WhenAll(
projectsToRebuild.Values.Select(projectPath => BuildProjectAsync(projectPath, rootProjectOptions.BuildArguments, iterationCancellationToken)));

if (buildResults.All(success => success))
foreach (var (success, output, projectPath) in buildResults)
{
BuildUtilities.ReportBuildOutput(Context.Reporter, output, success, projectPath);
}

if (buildResults.All(result => result.success))
{
break;
}
Expand Down Expand Up @@ -815,7 +821,8 @@ await FileWatcher.WaitForFileChangeAsync(
}
}

private async Task<bool> BuildProjectAsync(string projectPath, IReadOnlyList<string> buildArguments, CancellationToken cancellationToken)
private async Task<(bool success, ImmutableArray<OutputLine> output, string projectPath)> BuildProjectAsync(
string projectPath, IReadOnlyList<string> buildArguments, CancellationToken cancellationToken)
{
var buildOutput = new List<OutputLine>();

Expand All @@ -834,17 +841,10 @@ private async Task<bool> BuildProjectAsync(string projectPath, IReadOnlyList<str
Arguments = ["build", projectPath, "-consoleLoggerParameters:NoSummary;Verbosity=minimal", .. buildArguments]
};

Context.Reporter.Output($"Building '{projectPath}' ...");
Context.Reporter.Output($"Building {projectPath} ...");

var exitCode = await ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: false, launchResult: null, cancellationToken);
BuildUtilities.ReportBuildOutput(Context.Reporter, buildOutput, verboseOutput: exitCode == 0);

if (exitCode == 0)
{
Context.Reporter.Output("Build succeeded.");
}

return exitCode == 0;
return (exitCode == 0, buildOutput.ToImmutableArray(), projectPath);
}

private string GetRelativeFilePath(string path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ internal class MSBuildFileSetFactory(
reporter.Output($"MSBuild output from target '{TargetName}':");
}

BuildUtilities.ReportBuildOutput(reporter, capturedOutput, verboseOutput: success);
BuildUtilities.ReportBuildOutput(reporter, capturedOutput, success, projectDisplay: null);
if (!success)
{
return null;
Expand Down
9 changes: 9 additions & 0 deletions src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public static async Task<int> RunAsync(ProcessSpec processSpec, IReporter report
try
{
await process.WaitForExitAsync(processTerminationToken);

// ensures that all process output has been reported:
try
{
process.WaitForExit();
}
catch
{
}
}
catch (OperationCanceledException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"dotnet-watch": {
"commandName": "Project",
"commandLineArgs": "--verbose /bl:DotnetRun.binlog",
"workingDirectory": "$(RepoRoot)src\\Assets\\TestProjects\\BlazorWasmWithLibrary\\blazorwasm",
"workingDirectory": "C:\\sdk5\\artifacts\\tmp\\Debug\\Aspire_ApplyD---C6DC4E42\\WatchAspire.AppHost",
"environmentVariables": {
"DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)",
"DCP_IDE_REQUEST_TIMEOUT_SECONDS": "100000",
Expand Down
17 changes: 14 additions & 3 deletions src/BuiltInTools/dotnet-watch/Utilities/BuildUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@ namespace Microsoft.DotNet.Watch;

internal static partial class BuildUtilities
{
private const string BuildEmoji = "🔨";
private static readonly Regex s_buildDiagnosticRegex = GetBuildDiagnosticRegex();

[GeneratedRegex(@"[^:]+: (error|warning) [A-Za-z]+[0-9]+: .+")]
private static partial Regex GetBuildDiagnosticRegex();

public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine> buildOutput, bool verboseOutput)
public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine> buildOutput, bool success, string? projectDisplay)
{
const string BuildEmoji = "🔨";
if (projectDisplay != null)
{
if (success)
{
reporter.Output($"Build succeeded: {projectDisplay}", BuildEmoji);
}
else
{
reporter.Output($"Build failed: {projectDisplay}", BuildEmoji);
}
}

foreach (var (line, isError) in buildOutput)
{
Expand All @@ -33,7 +44,7 @@ public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine>
reporter.Warn(line);
}
}
else if (verboseOutput)
else if (success)
{
reporter.Verbose(line, BuildEmoji);
}
Expand Down
4 changes: 2 additions & 2 deletions test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ public async Task Aspire()
App.AssertOutputContains($"[WatchAspire.ApiService ({tfm})] Exited");
}

App.AssertOutputContains($"dotnet watch ⌚ Building '{serviceProjectPath}' ...");
App.AssertOutputContains($"dotnet watch ⌚ Building {serviceProjectPath} ...");
App.AssertOutputContains("error CS0246: The type or namespace name 'WeatherForecast' could not be found");
App.Process.ClearOutput();

Expand All @@ -693,7 +693,7 @@ public async Task Aspire()

await App.AssertOutputLineStartsWith($"dotnet watch ⌚ [WatchAspire.ApiService ({tfm})] Capabilities");

App.AssertOutputContains("dotnet watch Build succeeded.");
App.AssertOutputContains($"dotnet watch 🔨 Build succeeded: {serviceProjectPath}");
App.AssertOutputContains("dotnet watch 🔥 Project baselines updated.");
App.AssertOutputContains($"dotnet watch ⭐ Starting project: {serviceProjectPath}");

Expand Down
2 changes: 1 addition & 1 deletion test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private void OnData(object sender, DataReceivedEventArgs args)
line = line.StripTerminalLoggerProgressIndicators();
}

WriteTestOutput($"{DateTime.Now}: post: '{line}'");
WriteTestOutput(line);
_source.Post(line);
}

Expand Down

0 comments on commit c7e93f0

Please sign in to comment.