6
6
using System . Collections . Generic ;
7
7
using System . Diagnostics ;
8
8
using System . Linq ;
9
- using System . Threading ;
10
9
using Microsoft . Build . BackEnd ;
11
10
using Microsoft . Build . BackEnd . Logging ;
12
11
using Microsoft . Build . BuildCheck . Infrastructure ;
@@ -59,7 +58,7 @@ public void ShutdownComponent()
59
58
{
60
59
_instance ? . Shutdown ( ) ;
61
60
_instance = null ;
62
- }
61
+ }
63
62
64
63
internal sealed class BuildCheckManager : IBuildCheckManager , IBuildEngineDataRouter , IResultReporter
65
64
{
@@ -94,7 +93,7 @@ public void SetDataSource(BuildCheckDataSource buildCheckDataSource)
94
93
{
95
94
_enabledDataSources [ ( int ) buildCheckDataSource ] = true ;
96
95
RegisterBuiltInChecks ( buildCheckDataSource ) ;
97
- }
96
+ }
98
97
stopwatch . Stop ( ) ;
99
98
_tracingReporter . AddSetDataSourceStats ( stopwatch . Elapsed ) ;
100
99
}
@@ -344,11 +343,11 @@ public void RemoveThrottledChecks(ICheckContext checkContext)
344
343
private void RemoveCheck ( CheckFactoryContext checkToRemove )
345
344
{
346
345
_checkRegistry . Remove ( checkToRemove ) ;
347
-
346
+
348
347
if ( checkToRemove . MaterializedCheck is not null )
349
348
{
350
349
_buildCheckCentralContext . DeregisterCheck ( checkToRemove . MaterializedCheck ) ;
351
- _ruleTelemetryData . AddRange ( checkToRemove . MaterializedCheck . GetRuleTelemetryData ( ) ) ;
350
+ _ruleTelemetryData . AddRange ( checkToRemove . MaterializedCheck . GetRuleTelemetryData ( ) ) ;
352
351
checkToRemove . MaterializedCheck . Check . Dispose ( ) ;
353
352
}
354
353
}
@@ -372,6 +371,18 @@ public void ProcessEvaluationFinishedEventArgs(
372
371
FileClassifier . Shared . RegisterKnownImmutableLocations ( getPropertyValue ) ;
373
372
}
374
373
374
+ // run it here to avoid the missed imports that can be reported before the checks registration
375
+ if ( _deferredProjectEvalIdToImportedProjects . TryGetValue ( checkContext . BuildEventContext . EvaluationId , out HashSet < string > ? importedProjects ) )
376
+ {
377
+ if ( importedProjects != null && TryGetProjectFullPath ( checkContext . BuildEventContext , out string projectPath ) )
378
+ {
379
+ foreach ( string importedProject in importedProjects )
380
+ {
381
+ _buildEventsProcessor . ProcessProjectImportedEventArgs ( checkContext , projectPath , importedProject ) ;
382
+ }
383
+ }
384
+ }
385
+
375
386
_buildEventsProcessor
376
387
. ProcessEvaluationFinishedEventArgs ( checkContext , evaluationFinishedEventArgs , propertiesLookup ) ;
377
388
}
@@ -392,6 +403,16 @@ public void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext,
392
403
}
393
404
}
394
405
406
+ public void ProcessProjectImportedEventArgs ( ICheckContext checkContext , ProjectImportedEventArgs projectImportedEventArgs )
407
+ {
408
+ if ( string . IsNullOrEmpty ( projectImportedEventArgs . ImportedProjectFile ) )
409
+ {
410
+ return ;
411
+ }
412
+
413
+ PropagateImport ( checkContext . BuildEventContext . EvaluationId , projectImportedEventArgs . ProjectFile , projectImportedEventArgs . ImportedProjectFile ) ;
414
+ }
415
+
395
416
public void ProcessTaskStartedEventArgs (
396
417
ICheckContext checkContext ,
397
418
TaskStartedEventArgs taskStartedEventArgs )
@@ -414,6 +435,7 @@ public void ProcessTaskParameterEventArgs(
414
435
. ProcessTaskParameterEventArgs ( checkContext , taskParameterEventArgs ) ;
415
436
416
437
private readonly List < BuildCheckRuleTelemetryData > _ruleTelemetryData = [ ] ;
438
+
417
439
public BuildCheckTracingData CreateCheckTracingStats ( )
418
440
{
419
441
foreach ( CheckFactoryContext checkFactoryContext in _checkRegistry )
@@ -445,6 +467,8 @@ public void FinalizeProcessing(LoggingContext loggingContext)
445
467
private readonly ConcurrentDictionary < int , string > _projectsByInstanceId = new ( ) ;
446
468
private readonly ConcurrentDictionary < int , string > _projectsByEvaluationId = new ( ) ;
447
469
470
+ private readonly Dictionary < int , HashSet < string > > _deferredProjectEvalIdToImportedProjects = new ( ) ;
471
+
448
472
/// <summary>
449
473
/// This method fetches the project full path from the context id.
450
474
/// This is needed because the full path is needed for configuration and later for fetching configured checks
@@ -515,6 +539,10 @@ public void ProcessProjectEvaluationStarted(
515
539
string projectFullPath )
516
540
{
517
541
_projectsByEvaluationId [ checkContext . BuildEventContext . EvaluationId ] = projectFullPath ;
542
+ if ( ! _deferredProjectEvalIdToImportedProjects . ContainsKey ( checkContext . BuildEventContext . EvaluationId ) )
543
+ {
544
+ _deferredProjectEvalIdToImportedProjects . Add ( checkContext . BuildEventContext . EvaluationId , [ projectFullPath ] ) ;
545
+ }
518
546
}
519
547
520
548
/*
@@ -523,7 +551,6 @@ public void ProcessProjectEvaluationStarted(
523
551
*
524
552
*/
525
553
526
-
527
554
public void EndProjectEvaluation ( BuildEventContext buildEventContext )
528
555
{
529
556
}
@@ -548,6 +575,24 @@ public void StartProjectRequest(ICheckContext checkContext, string projectFullPa
548
575
}
549
576
550
577
private readonly Dictionary < int , List < BuildEventArgs > > _deferredEvalDiagnostics = new ( ) ;
578
+
579
+ /// <summary>
580
+ /// Propagates a newly imported project file to all projects that import the original project file.
581
+ /// This method ensures that if Project A imports Project B, and Project B now imports Project C,
582
+ /// then Project A will also show Project C as an import.
583
+ /// </summary>
584
+ /// <param name="evaluationId">The evaluation id is associated with the root project path.</param>
585
+ /// <param name="originalProjectFile">The path of the project file that is importing a new project.</param>
586
+ /// <param name="newImportedProjectFile">The path of the newly imported project file.</param>
587
+ private void PropagateImport ( int evaluationId , string originalProjectFile , string newImportedProjectFile )
588
+ {
589
+ if ( _deferredProjectEvalIdToImportedProjects . TryGetValue ( evaluationId , out HashSet < string > ? importedProjects )
590
+ && importedProjects . Contains ( originalProjectFile ) )
591
+ {
592
+ importedProjects . Add ( newImportedProjectFile ) ;
593
+ }
594
+ }
595
+
551
596
void IResultReporter . ReportResult ( BuildEventArgs eventArgs , ICheckContext checkContext )
552
597
{
553
598
// If we do not need to decide on promotability/demotability of warnings or we are ready to decide on those
0 commit comments