@@ -82,8 +82,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken)
8282 {
8383 var rootProjectOptions = _context . RootProjectOptions ;
8484
85- var ( buildSucceeded , buildOutput , _) = await BuildProjectAsync ( rootProjectOptions . ProjectPath , rootProjectOptions . BuildArguments , iterationCancellationToken ) ;
86- BuildOutput . ReportBuildOutput ( _context . BuildLogger , buildOutput , buildSucceeded , projectDisplay : rootProjectOptions . ProjectPath ) ;
85+ var buildSucceeded = await BuildProjectAsync ( rootProjectOptions . ProjectPath , rootProjectOptions . BuildArguments , iterationCancellationToken ) ;
8786 if ( ! buildSucceeded )
8887 {
8988 continue ;
@@ -189,7 +188,7 @@ void FileChangedCallback(ChangedPath change)
189188
190189 fileChangedCallback = FileChangedCallback ;
191190 fileWatcher . OnFileChange += fileChangedCallback ;
192- ReportWatchingForChanges ( ) ;
191+ _context . Logger . Log ( MessageDescriptor . WaitingForChanges ) ;
193192
194193 // Hot Reload loop - exits when the root process needs to be restarted.
195194 bool extendTimeout = false ;
@@ -328,15 +327,19 @@ void FileChangedCallback(ChangedPath change)
328327 fileWatcher . SuppressEvents = true ;
329328 try
330329 {
331- var buildResults = await Task . WhenAll (
332- projectsToRebuild . Select ( projectPath => BuildProjectAsync ( projectPath , rootProjectOptions . BuildArguments , iterationCancellationToken ) ) ) ;
333-
334- foreach ( var ( success , output , projectPath ) in buildResults )
330+ // Build projects sequentially to avoid failed attempts to overwrite dependent project outputs.
331+ // TODO: Ideally, dotnet build would be able to build multiple projects. https://github.com/dotnet/sdk/issues/51311
332+ var success = true ;
333+ foreach ( var projectPath in projectsToRebuild )
335334 {
336- BuildOutput . ReportBuildOutput ( _context . BuildLogger , output , success , projectPath ) ;
335+ success = await BuildProjectAsync ( projectPath , rootProjectOptions . BuildArguments , iterationCancellationToken ) ;
336+ if ( ! success )
337+ {
338+ break ;
339+ }
337340 }
338341
339- if ( buildResults . All ( result => result . success ) )
342+ if ( success )
340343 {
341344 break ;
342345 }
@@ -772,12 +775,6 @@ internal static IEnumerable<ChangedPath> NormalizePathChanges(IEnumerable<Change
772775 . Where ( item => item != null )
773776 . Select ( item => item ! . Value ) ;
774777
775- private void ReportWatchingForChanges ( )
776- {
777- _context . Logger . Log ( MessageDescriptor . WaitingForChanges
778- . WithSeverityWhen ( MessageSeverity . Output , _context . EnvironmentOptions . TestFlags . HasFlag ( TestFlags . ElevateWaitingForChangesMessageSeverity ) ) ) ;
779- }
780-
781778 private void ReportFileChanges ( IReadOnlyList < ChangedFile > changedFiles )
782779 {
783780 Report ( kind : ChangeKind . Add ) ;
@@ -823,6 +820,9 @@ private async ValueTask<EvaluationResult> EvaluateRootProjectAsync(bool restore,
823820 {
824821 cancellationToken . ThrowIfCancellationRequested ( ) ;
825822
823+ _context . Logger . LogInformation ( "Evaluating projects ..." ) ;
824+ var stopwatch = Stopwatch . StartNew ( ) ;
825+
826826 var result = EvaluationResult . TryCreate (
827827 _context . RootProjectOptions . ProjectPath ,
828828 _context . RootProjectOptions . BuildArguments ,
@@ -832,6 +832,8 @@ private async ValueTask<EvaluationResult> EvaluateRootProjectAsync(bool restore,
832832 restore ,
833833 cancellationToken ) ;
834834
835+ _context . Logger . LogInformation ( "Evaluation completed in {Time}s." , stopwatch . Elapsed . TotalSeconds . ToString ( "0.0" ) ) ;
836+
835837 if ( result != null )
836838 {
837839 return result ;
@@ -846,31 +848,43 @@ await FileWatcher.WaitForFileChangeAsync(
846848 }
847849 }
848850
849- private async Task < ( bool success , ImmutableArray < OutputLine > output , string projectPath ) > BuildProjectAsync (
850- string projectPath , IReadOnlyList < string > buildArguments , CancellationToken cancellationToken )
851+ private async Task < bool > BuildProjectAsync ( string projectPath , IReadOnlyList < string > buildArguments , CancellationToken cancellationToken )
851852 {
852- var buildOutput = new List < OutputLine > ( ) ;
853+ List < OutputLine > ? capturedOutput = _context . EnvironmentOptions . TestFlags != TestFlags . None ? [ ] : null ;
853854
854855 var processSpec = new ProcessSpec
855856 {
856857 Executable = _context . EnvironmentOptions . MuxerPath ,
857858 WorkingDirectory = Path . GetDirectoryName ( projectPath ) ! ,
858859 IsUserApplication = false ,
859- OnOutput = line =>
860- {
861- lock ( buildOutput )
860+
861+ // Capture output if running in a test environment.
862+ // If the output is not captured dotnet build will show live build progress.
863+ OnOutput = capturedOutput != null
864+ ? line =>
862865 {
863- buildOutput . Add ( line ) ;
866+ lock ( capturedOutput )
867+ {
868+ capturedOutput . Add ( line ) ;
869+ }
864870 }
865- } ,
871+ : null ,
872+
866873 // pass user-specified build arguments last to override defaults:
867874 Arguments = [ "build" , projectPath , "-consoleLoggerParameters:NoSummary;Verbosity=minimal" , .. buildArguments ]
868875 } ;
869876
870877 _context . BuildLogger . Log ( MessageDescriptor . Building , projectPath ) ;
871878
872- var exitCode = await _context . ProcessRunner . RunAsync ( processSpec , _context . Logger , launchResult : null , cancellationToken ) ;
873- return ( exitCode == 0 , buildOutput . ToImmutableArray ( ) , projectPath ) ;
879+ var success = await _context . ProcessRunner . RunAsync ( processSpec , _context . Logger , launchResult : null , cancellationToken ) == 0 ;
880+
881+ if ( capturedOutput != null )
882+ {
883+ _context . BuildLogger . Log ( success ? MessageDescriptor . BuildSucceeded : MessageDescriptor . BuildFailed , projectPath ) ;
884+ BuildOutput . ReportBuildOutput ( _context . BuildLogger , capturedOutput , success ) ;
885+ }
886+
887+ return success ;
874888 }
875889
876890 private string GetRelativeFilePath ( string path )
0 commit comments