Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose API for imported projects checks #10761

Merged
merged 12 commits into from
Oct 19, 2024
2 changes: 2 additions & 0 deletions src/Build/BuildCheck/API/IBuildCheckRegistrationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface IBuildCheckRegistrationContext
void RegisterEnvironmentVariableReadAction(Action<BuildCheckDataContext<EnvironmentVariableCheckData>> environmentVariableAction);

void RegisterBuildFinishedAction(Action<BuildCheckDataContext<BuildFinishedCheckData>> buildFinishedAction);

void RegisterProjectImportedAction(Action<BuildCheckDataContext<ProjectImportedCheckData>> projectImportedAction);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Experimental.BuildCheck;
using Microsoft.Build.Experimental.BuildCheck.Acquisition;
using Microsoft.Build.Experimental.BuildCheck.Utilities;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;

Expand Down Expand Up @@ -44,6 +40,7 @@ internal BuildCheckBuildEventHandler(
{ typeof(TaskFinishedEventArgs), (BuildEventArgs e) => HandleTaskFinishedEvent((TaskFinishedEventArgs)e) },
{ typeof(TaskParameterEventArgs), (BuildEventArgs e) => HandleTaskParameterEvent((TaskParameterEventArgs)e) },
{ typeof(BuildFinishedEventArgs), (BuildEventArgs e) => HandleBuildFinishedEvent((BuildFinishedEventArgs)e) },
{ typeof(ProjectImportedEventArgs), (BuildEventArgs e) => HandleProjectImportedEvent((ProjectImportedEventArgs)e) },
};

// During restore we'll wait only for restore to be done.
Expand Down Expand Up @@ -92,6 +89,7 @@ private void HandleProjectEvaluationStartedEvent(ProjectEvaluationStartedEventAr
BuildCheckDataSource.EventArgs,
checkContext,
eventArgs.ProjectFile!);

_buildCheckManager.ProcessProjectEvaluationStarted(
checkContext,
eventArgs.ProjectFile!);
Expand Down Expand Up @@ -141,6 +139,11 @@ private void HandleEnvironmentVariableReadEvent(EnvironmentVariableReadEventArgs
_checkContextFactory.CreateCheckContext(GetBuildEventContext(eventArgs)),
eventArgs);

private void HandleProjectImportedEvent(ProjectImportedEventArgs eventArgs)
=> _buildCheckManager.ProcessProjectImportedEventArgs(
_checkContextFactory.CreateCheckContext(GetBuildEventContext(eventArgs)),
eventArgs);

private bool IsMetaProjFile(string? projectFile) => projectFile?.EndsWith(".metaproj", StringComparison.OrdinalIgnoreCase) == true;

private readonly BuildCheckTracingData _tracingData = new BuildCheckTracingData();
Expand Down
21 changes: 15 additions & 6 deletions src/Build/BuildCheck/Infrastructure/BuildCheckCentralContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ private record CallbackRegistry(
List<(CheckWrapper, Action<BuildCheckDataContext<PropertyWriteData>>)> PropertyWriteActions,
List<(CheckWrapper, Action<BuildCheckDataContext<ProjectRequestProcessingDoneData>>)> ProjectRequestProcessingDoneActions,
List<(CheckWrapper, Action<BuildCheckDataContext<BuildFinishedCheckData>>)> BuildFinishedActions,
List<(CheckWrapper, Action<BuildCheckDataContext<EnvironmentVariableCheckData>>)> EnvironmentVariableCheckDataActions)
List<(CheckWrapper, Action<BuildCheckDataContext<EnvironmentVariableCheckData>>)> EnvironmentVariableCheckDataActions,
List<(CheckWrapper, Action<BuildCheckDataContext<ProjectImportedCheckData>>)> ProjectImportedCheckDataActions)
{
public CallbackRegistry()
: this([], [], [], [], [], [], [], [])
: this([], [], [], [], [], [], [], [], [])
{
}

Expand Down Expand Up @@ -62,6 +63,7 @@ internal void DeregisterCheck(CheckWrapper check)
internal bool HasPropertyReadActions => _globalCallbacks.PropertyReadActions.Count > 0;

internal bool HasPropertyWriteActions => _globalCallbacks.PropertyWriteActions.Count > 0;

internal bool HasBuildFinishedActions => _globalCallbacks.BuildFinishedActions.Count > 0;

internal void RegisterEnvironmentVariableReadAction(CheckWrapper check, Action<BuildCheckDataContext<EnvironmentVariableCheckData>> environmentVariableAction)
Expand Down Expand Up @@ -90,6 +92,9 @@ internal void RegisterProjectRequestProcessingDoneAction(CheckWrapper check, Act
internal void RegisterBuildFinishedAction(CheckWrapper check, Action<BuildCheckDataContext<BuildFinishedCheckData>> buildFinishedAction)
=> RegisterAction(check, buildFinishedAction, _globalCallbacks.BuildFinishedActions);

internal void RegisterProjectImportedAction(CheckWrapper check, Action<BuildCheckDataContext<ProjectImportedCheckData>> projectImportedAction)
=> RegisterAction(check, projectImportedAction, _globalCallbacks.ProjectImportedCheckDataActions);

private void RegisterAction<T>(
CheckWrapper wrappedCheck,
Action<BuildCheckDataContext<T>> handler,
Expand Down Expand Up @@ -167,10 +172,14 @@ internal void RunProjectProcessingDoneActions(
internal void RunBuildFinishedActions(
BuildFinishedCheckData buildFinishedCheckData,
ICheckContext checkContext,
Action<CheckWrapper, ICheckContext, CheckConfigurationEffective[], BuildCheckResult>
resultHandler)
=> RunRegisteredActions(_globalCallbacks.BuildFinishedActions, buildFinishedCheckData,
checkContext, resultHandler);
Action<CheckWrapper, ICheckContext, CheckConfigurationEffective[], BuildCheckResult> resultHandler)
=> RunRegisteredActions(_globalCallbacks.BuildFinishedActions, buildFinishedCheckData, checkContext, resultHandler);

internal void RunProjectImportedActions(
ProjectImportedCheckData projectImportedCheckData,
ICheckContext checkContext,
Action<CheckWrapper, ICheckContext, CheckConfigurationEffective[], BuildCheckResult> resultHandler)
=> RunRegisteredActions(_globalCallbacks.ProjectImportedCheckDataActions, projectImportedCheckData, checkContext, resultHandler);

private void RunRegisteredActions<T>(
List<(CheckWrapper, Action<BuildCheckDataContext<T>>)> registeredCallbacks,
Expand Down
28 changes: 11 additions & 17 deletions src/Build/BuildCheck/Infrastructure/BuildCheckForwardingLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Experimental.BuildCheck.Acquisition;
using Microsoft.Build.Framework;
using static Microsoft.Build.Experimental.BuildCheck.Infrastructure.BuildCheckManagerProvider;

namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure;

Expand All @@ -20,7 +14,7 @@ namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure;
/// In the future we may need more specific behavior.
/// </summary>
/// <remarks>
/// Ensure that events filtering is in sync with <see cref="BuildCheckConnectorLogger"/>
/// Ensure that events filtering is in sync with <see cref="BuildCheckConnectorLogger"/>.
/// </remarks>
internal class BuildCheckForwardingLogger : IForwardingLogger
{
Expand All @@ -33,10 +27,10 @@ internal class BuildCheckForwardingLogger : IForwardingLogger
public string? Parameters { get; set; }

/// <summary>
/// Set of events to be forwarded to <see cref="BuildCheckConnectorLogger"/>
/// Set of events to be forwarded to <see cref="BuildCheckConnectorLogger"/>.
/// </summary>
private HashSet<Type> _eventsToForward = new HashSet<Type>
{
private HashSet<Type> _eventsToForward =
[
typeof(EnvironmentVariableReadEventArgs),
typeof(BuildSubmissionStartedEventArgs),
typeof(ProjectEvaluationFinishedEventArgs),
Expand All @@ -47,15 +41,13 @@ internal class BuildCheckForwardingLogger : IForwardingLogger
typeof(BuildCheckAcquisitionEventArgs),
typeof(TaskStartedEventArgs),
typeof(TaskFinishedEventArgs),
typeof(TaskParameterEventArgs)
};
typeof(TaskParameterEventArgs),
typeof(ProjectImportedEventArgs),
];

public void Initialize(IEventSource eventSource, int nodeCount) => Initialize(eventSource);

public void Initialize(IEventSource eventSource)
{
eventSource.AnyEventRaised += EventSource_AnyEventRaised;
}
public void Initialize(IEventSource eventSource) => eventSource.AnyEventRaised += EventSource_AnyEventRaised;

public void EventSource_AnyEventRaised(object sender, BuildEventArgs buildEvent)
{
Expand All @@ -65,5 +57,7 @@ public void EventSource_AnyEventRaised(object sender, BuildEventArgs buildEvent)
}
}

public void Shutdown() { }
public void Shutdown()
{
}
}
57 changes: 51 additions & 6 deletions src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.Build.BackEnd;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.BuildCheck.Infrastructure;
Expand Down Expand Up @@ -59,7 +58,7 @@ public void ShutdownComponent()
{
_instance?.Shutdown();
_instance = null;
}
}

internal sealed class BuildCheckManager : IBuildCheckManager, IBuildEngineDataRouter, IResultReporter
{
Expand Down Expand Up @@ -94,7 +93,7 @@ public void SetDataSource(BuildCheckDataSource buildCheckDataSource)
{
_enabledDataSources[(int)buildCheckDataSource] = true;
RegisterBuiltInChecks(buildCheckDataSource);
}
}
stopwatch.Stop();
_tracingReporter.AddSetDataSourceStats(stopwatch.Elapsed);
}
Expand Down Expand Up @@ -344,11 +343,11 @@ public void RemoveThrottledChecks(ICheckContext checkContext)
private void RemoveCheck(CheckFactoryContext checkToRemove)
{
_checkRegistry.Remove(checkToRemove);

if (checkToRemove.MaterializedCheck is not null)
{
_buildCheckCentralContext.DeregisterCheck(checkToRemove.MaterializedCheck);
_ruleTelemetryData.AddRange(checkToRemove.MaterializedCheck.GetRuleTelemetryData());
_ruleTelemetryData.AddRange(checkToRemove.MaterializedCheck.GetRuleTelemetryData());
checkToRemove.MaterializedCheck.Check.Dispose();
}
}
Expand All @@ -372,6 +371,18 @@ public void ProcessEvaluationFinishedEventArgs(
FileClassifier.Shared.RegisterKnownImmutableLocations(getPropertyValue);
}

// run it here to avoid the missed imports that can be reported before the checks registration
if (_deferredProjectEvalIdToImportedProjects.TryGetValue(checkContext.BuildEventContext.EvaluationId, out HashSet<string>? importedProjects))
{
if (importedProjects != null && TryGetProjectFullPath(checkContext.BuildEventContext, out string projectPath))
{
foreach (string importedProject in importedProjects)
{
_buildEventsProcessor.ProcessProjectImportedEventArgs(checkContext, projectPath, importedProject);
}
}
}

_buildEventsProcessor
.ProcessEvaluationFinishedEventArgs(checkContext, evaluationFinishedEventArgs, propertiesLookup);
}
Expand All @@ -392,6 +403,16 @@ public void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext,
}
}

public void ProcessProjectImportedEventArgs(ICheckContext checkContext, ProjectImportedEventArgs projectImportedEventArgs)
YuliiaKovalova marked this conversation as resolved.
Show resolved Hide resolved
{
if (string.IsNullOrEmpty(projectImportedEventArgs.ImportedProjectFile))
{
return;
}

PropagateImport(checkContext.BuildEventContext.EvaluationId, projectImportedEventArgs.ProjectFile, projectImportedEventArgs.ImportedProjectFile);
}

public void ProcessTaskStartedEventArgs(
ICheckContext checkContext,
TaskStartedEventArgs taskStartedEventArgs)
Expand All @@ -414,6 +435,7 @@ public void ProcessTaskParameterEventArgs(
.ProcessTaskParameterEventArgs(checkContext, taskParameterEventArgs);

private readonly List<BuildCheckRuleTelemetryData> _ruleTelemetryData = [];

public BuildCheckTracingData CreateCheckTracingStats()
{
foreach (CheckFactoryContext checkFactoryContext in _checkRegistry)
Expand Down Expand Up @@ -445,6 +467,8 @@ public void FinalizeProcessing(LoggingContext loggingContext)
private readonly ConcurrentDictionary<int, string> _projectsByInstanceId = new();
private readonly ConcurrentDictionary<int, string> _projectsByEvaluationId = new();

private readonly Dictionary<int, HashSet<string>> _deferredProjectEvalIdToImportedProjects = new();

/// <summary>
/// This method fetches the project full path from the context id.
/// This is needed because the full path is needed for configuration and later for fetching configured checks
Expand Down Expand Up @@ -515,6 +539,10 @@ public void ProcessProjectEvaluationStarted(
string projectFullPath)
{
_projectsByEvaluationId[checkContext.BuildEventContext.EvaluationId] = projectFullPath;
if (!_deferredProjectEvalIdToImportedProjects.ContainsKey(checkContext.BuildEventContext.EvaluationId))
{
_deferredProjectEvalIdToImportedProjects.Add(checkContext.BuildEventContext.EvaluationId, [projectFullPath]);
}
}

/*
Expand All @@ -523,7 +551,6 @@ public void ProcessProjectEvaluationStarted(
*
*/


public void EndProjectEvaluation(BuildEventContext buildEventContext)
{
}
Expand All @@ -548,6 +575,24 @@ public void StartProjectRequest(ICheckContext checkContext, string projectFullPa
}

private readonly Dictionary<int, List<BuildEventArgs>> _deferredEvalDiagnostics = new();

/// <summary>
/// Propagates a newly imported project file to all projects that import the original project file.
/// This method ensures that if Project A imports Project B, and Project B now imports Project C,
/// then Project A will also show Project C as an import.
/// </summary>
/// <param name="evaluationId">The evaluation id is associated with the root project path.</param>
/// <param name="originalProjectFile">The path of the project file that is importing a new project.</param>
/// <param name="newImportedProjectFile">The path of the newly imported project file.</param>
private void PropagateImport(int evaluationId, string originalProjectFile, string newImportedProjectFile)
{
if (_deferredProjectEvalIdToImportedProjects.TryGetValue(evaluationId, out HashSet<string>? importedProjects)
&& importedProjects.Contains(originalProjectFile))
{
importedProjects.Add(newImportedProjectFile);
}
}

void IResultReporter.ReportResult(BuildEventArgs eventArgs, ICheckContext checkContext)
{
// If we do not need to decide on promotability/demotability of warnings or we are ready to decide on those
Expand Down
10 changes: 10 additions & 0 deletions src/Build/BuildCheck/Infrastructure/BuildEventsProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ internal void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext
_buildCheckCentralContext.RunEnvironmentVariableActions(checkData, checkContext, ReportResult);
}

/// <summary>
/// The method handles events associated with the ProjectImportedEventArgs.
/// </summary>
internal void ProcessProjectImportedEventArgs(ICheckContext checkContext, string projectPath, string importedProjectPath)
{
ProjectImportedCheckData checkData = new(importedProjectPath, projectPath, checkContext.BuildEventContext?.ProjectInstanceId);

_buildCheckCentralContext.RunProjectImportedActions(checkData, checkContext, ReportResult);
}

internal void ProcessBuildDone(ICheckContext checkContext)
{
if (!_buildCheckCentralContext.HasBuildFinishedActions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading;
using Microsoft.Build.Experimental.BuildCheck;
using Microsoft.Build.Experimental.BuildCheck.Checks;

namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure;

internal sealed class CheckRegistrationContext(CheckWrapper checkWrapper, BuildCheckCentralContext buildCheckCentralContext) : IInternalCheckRegistrationContext
internal sealed class CheckRegistrationContext(CheckWrapper checkWrapper, BuildCheckCentralContext buildCheckCentralContext)
: IInternalCheckRegistrationContext
{
public void RegisterEnvironmentVariableReadAction(Action<BuildCheckDataContext<EnvironmentVariableCheckData>> environmentVariableAction) =>
buildCheckCentralContext.RegisterEnvironmentVariableReadAction(checkWrapper, environmentVariableAction);
Expand All @@ -33,4 +32,7 @@ public void RegisterProjectRequestProcessingDoneAction(Action<BuildCheckDataCont

public void RegisterBuildFinishedAction(Action<BuildCheckDataContext<BuildFinishedCheckData>> buildFinishedAction)
=> buildCheckCentralContext.RegisterBuildFinishedAction(checkWrapper, buildFinishedAction);

public void RegisterProjectImportedAction(Action<BuildCheckDataContext<ProjectImportedCheckData>> projectImportedAction) =>
buildCheckCentralContext.RegisterProjectImportedAction(checkWrapper, projectImportedAction);
}
Loading