Skip to content

Commit eaaeee2

Browse files
committed
Improve output reporting, fix potential race condition of output redirection
1 parent 536f467 commit eaaeee2

File tree

7 files changed

+41
-21
lines changed

7 files changed

+41
-21
lines changed

src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs

+13-13
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
119119
};
120120
}
121121

122-
if (!await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken))
122+
var (buildSucceeded, buildOutput, _) = await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken);
123+
BuildUtilities.ReportBuildOutput(Context.Reporter, buildOutput, buildSucceeded, projectDisplay: rootProjectOptions.ProjectPath);
124+
if (!buildSucceeded)
123125
{
124-
// error has been reported:
125126
continue;
126127
}
127128

@@ -325,7 +326,12 @@ void FileChangedCallback(string path, ChangeKind kind)
325326
var buildResults = await Task.WhenAll(
326327
projectsToRebuild.Values.Select(projectPath => BuildProjectAsync(projectPath, rootProjectOptions.BuildArguments, iterationCancellationToken)));
327328

328-
if (buildResults.All(success => success))
329+
foreach (var (success, output, projectPath) in buildResults)
330+
{
331+
BuildUtilities.ReportBuildOutput(Context.Reporter, output, success, projectPath);
332+
}
333+
334+
if (buildResults.All(result => result.success))
329335
{
330336
break;
331337
}
@@ -675,7 +681,8 @@ await FileWatcher.WaitForFileChangeAsync(
675681
}
676682
}
677683

678-
private async Task<bool> BuildProjectAsync(string projectPath, IReadOnlyList<string> buildArguments, CancellationToken cancellationToken)
684+
private async Task<(bool success, ImmutableArray<OutputLine> output, string projectPath)> BuildProjectAsync(
685+
string projectPath, IReadOnlyList<string> buildArguments, CancellationToken cancellationToken)
679686
{
680687
var buildOutput = new List<OutputLine>();
681688

@@ -694,17 +701,10 @@ private async Task<bool> BuildProjectAsync(string projectPath, IReadOnlyList<str
694701
Arguments = ["build", projectPath, "-consoleLoggerParameters:NoSummary;Verbosity=minimal", .. buildArguments]
695702
};
696703

697-
Context.Reporter.Output($"Building '{projectPath}' ...");
704+
Context.Reporter.Output($"Building {projectPath} ...");
698705

699706
var exitCode = await ProcessRunner.RunAsync(processSpec, Context.Reporter, isUserApplication: false, launchResult: null, cancellationToken);
700-
BuildUtilities.ReportBuildOutput(Context.Reporter, buildOutput, verboseOutput: exitCode == 0);
701-
702-
if (exitCode == 0)
703-
{
704-
Context.Reporter.Output("Build succeeded.");
705-
}
706-
707-
return exitCode == 0;
707+
return (exitCode == 0, buildOutput.ToImmutableArray(), projectPath);
708708
}
709709

710710
private string GetRelativeFilePath(string path)

src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ internal class MSBuildFileSetFactory(
6363
reporter.Output($"MSBuild output from target '{TargetName}':");
6464
}
6565

66-
BuildUtilities.ReportBuildOutput(reporter, capturedOutput, verboseOutput: success);
66+
BuildUtilities.ReportBuildOutput(reporter, capturedOutput, success, projectDisplay: null);
6767
if (!success)
6868
{
6969
return null;

src/BuiltInTools/dotnet-watch/Internal/ProcessRunner.cs

+9
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ public static async Task<int> RunAsync(ProcessSpec processSpec, IReporter report
8787
try
8888
{
8989
await process.WaitForExitAsync(processTerminationToken);
90+
91+
// ensures that all process output has been reported:
92+
try
93+
{
94+
process.WaitForExit();
95+
}
96+
catch
97+
{
98+
}
9099
}
91100
catch (OperationCanceledException)
92101
{

src/BuiltInTools/dotnet-watch/Properties/launchSettings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"dotnet-watch": {
44
"commandName": "Project",
55
"commandLineArgs": "--verbose /bl:DotnetRun.binlog",
6-
"workingDirectory": "$(RepoRoot)src\\Assets\\TestProjects\\BlazorWasmWithLibrary\\blazorwasm",
6+
"workingDirectory": "C:\\sdk5\\artifacts\\tmp\\Debug\\Aspire_ApplyD---C6DC4E42\\WatchAspire.AppHost",
77
"environmentVariables": {
88
"DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)",
99
"DCP_IDE_REQUEST_TIMEOUT_SECONDS": "100000",

src/BuiltInTools/dotnet-watch/Utilities/BuildUtilities.cs

+14-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,25 @@ namespace Microsoft.DotNet.Watch;
77

88
internal static partial class BuildUtilities
99
{
10+
private const string BuildEmoji = "🔨";
1011
private static readonly Regex s_buildDiagnosticRegex = GetBuildDiagnosticRegex();
1112

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

15-
public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine> buildOutput, bool verboseOutput)
16+
public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine> buildOutput, bool success, string? projectDisplay)
1617
{
17-
const string BuildEmoji = "🔨";
18+
if (projectDisplay != null)
19+
{
20+
if (success)
21+
{
22+
reporter.Output($"Build succeeded: {projectDisplay}", BuildEmoji);
23+
}
24+
else
25+
{
26+
reporter.Output($"Build failed: {projectDisplay}", BuildEmoji);
27+
}
28+
}
1829

1930
foreach (var (line, isError) in buildOutput)
2031
{
@@ -33,7 +44,7 @@ public static void ReportBuildOutput(IReporter reporter, IEnumerable<OutputLine>
3344
reporter.Warn(line);
3445
}
3546
}
36-
else if (verboseOutput)
47+
else if (success)
3748
{
3849
reporter.Verbose(line, BuildEmoji);
3950
}

test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ public async Task Aspire()
637637
App.AssertOutputContains($"[WatchAspire.ApiService ({tfm})] Exited");
638638
}
639639

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

@@ -648,7 +648,7 @@ public async Task Aspire()
648648

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

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

test/dotnet-watch.Tests/Utilities/AwaitableProcess.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ private void OnData(object sender, DataReceivedEventArgs args)
129129
line = line.StripTerminalLoggerProgressIndicators();
130130
}
131131

132-
WriteTestOutput($"{DateTime.Now}: post: '{line}'");
132+
WriteTestOutput(line);
133133
_source.Post(line);
134134
}
135135

0 commit comments

Comments
 (0)