From ea75a75ce71e4e50aea4bb20f7658384fec6b8c5 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> Date: Sat, 19 Oct 2024 10:32:33 +0200 Subject: [PATCH] Expose API for imported projects checks (#10761) --- .../API/IBuildCheckRegistrationContext.cs | 2 + .../BuildCheckBuildEventHandler.cs | 11 ++-- .../BuildCheckCentralContext.cs | 21 +++++-- .../BuildCheckForwardingLogger.cs | 28 ++++----- .../BuildCheckManagerProvider.cs | 57 +++++++++++++++++-- .../Infrastructure/BuildEventsProcessor.cs | 10 ++++ .../CheckRegistrationContext.cs | 8 ++- .../Infrastructure/CheckScopeClassifier.cs | 37 +++++------- .../Infrastructure/IBuildCheckManager.cs | 7 +-- .../Infrastructure/NullBuildCheckManager.cs | 16 ++++-- .../BuildCheck/OM/ProjectImportedCheckData.cs | 24 ++++++++ src/Build/CompatibilitySuppressions.xml | 25 +++++++- src/Build/ElementLocation/ElementLocation.cs | 11 ++-- .../MockBuildCheckRegistrationContext.cs | 2 + .../Company.CheckTemplate.csproj | 18 +++--- 15 files changed, 188 insertions(+), 89 deletions(-) create mode 100644 src/Build/BuildCheck/OM/ProjectImportedCheckData.cs diff --git a/src/Build/BuildCheck/API/IBuildCheckRegistrationContext.cs b/src/Build/BuildCheck/API/IBuildCheckRegistrationContext.cs index 4e164d177c3..7bf44e40de2 100644 --- a/src/Build/BuildCheck/API/IBuildCheckRegistrationContext.cs +++ b/src/Build/BuildCheck/API/IBuildCheckRegistrationContext.cs @@ -16,4 +16,6 @@ public interface IBuildCheckRegistrationContext void RegisterEnvironmentVariableReadAction(Action> environmentVariableAction); void RegisterBuildFinishedAction(Action> buildFinishedAction); + + void RegisterProjectImportedAction(Action> projectImportedAction); } diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckBuildEventHandler.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckBuildEventHandler.cs index 6e011090046..e14d7849f4f 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckBuildEventHandler.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckBuildEventHandler.cs @@ -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; @@ -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. @@ -92,6 +89,7 @@ private void HandleProjectEvaluationStartedEvent(ProjectEvaluationStartedEventAr BuildCheckDataSource.EventArgs, checkContext, eventArgs.ProjectFile!); + _buildCheckManager.ProcessProjectEvaluationStarted( checkContext, eventArgs.ProjectFile!); @@ -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(); diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckCentralContext.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckCentralContext.cs index 5e25c3e0a48..d9ca747fc60 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckCentralContext.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckCentralContext.cs @@ -29,10 +29,11 @@ private record CallbackRegistry( List<(CheckWrapper, Action>)> PropertyWriteActions, List<(CheckWrapper, Action>)> ProjectRequestProcessingDoneActions, List<(CheckWrapper, Action>)> BuildFinishedActions, - List<(CheckWrapper, Action>)> EnvironmentVariableCheckDataActions) + List<(CheckWrapper, Action>)> EnvironmentVariableCheckDataActions, + List<(CheckWrapper, Action>)> ProjectImportedCheckDataActions) { public CallbackRegistry() - : this([], [], [], [], [], [], [], []) + : this([], [], [], [], [], [], [], [], []) { } @@ -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> environmentVariableAction) @@ -90,6 +92,9 @@ internal void RegisterProjectRequestProcessingDoneAction(CheckWrapper check, Act internal void RegisterBuildFinishedAction(CheckWrapper check, Action> buildFinishedAction) => RegisterAction(check, buildFinishedAction, _globalCallbacks.BuildFinishedActions); + internal void RegisterProjectImportedAction(CheckWrapper check, Action> projectImportedAction) + => RegisterAction(check, projectImportedAction, _globalCallbacks.ProjectImportedCheckDataActions); + private void RegisterAction( CheckWrapper wrappedCheck, Action> handler, @@ -167,10 +172,14 @@ internal void RunProjectProcessingDoneActions( internal void RunBuildFinishedActions( BuildFinishedCheckData buildFinishedCheckData, ICheckContext checkContext, - Action - resultHandler) - => RunRegisteredActions(_globalCallbacks.BuildFinishedActions, buildFinishedCheckData, - checkContext, resultHandler); + Action resultHandler) + => RunRegisteredActions(_globalCallbacks.BuildFinishedActions, buildFinishedCheckData, checkContext, resultHandler); + + internal void RunProjectImportedActions( + ProjectImportedCheckData projectImportedCheckData, + ICheckContext checkContext, + Action resultHandler) + => RunRegisteredActions(_globalCallbacks.ProjectImportedCheckDataActions, projectImportedCheckData, checkContext, resultHandler); private void RunRegisteredActions( List<(CheckWrapper, Action>)> registeredCallbacks, diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckForwardingLogger.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckForwardingLogger.cs index 491fe92d700..c9b8d0219e9 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckForwardingLogger.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckForwardingLogger.cs @@ -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; @@ -20,7 +14,7 @@ namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure; /// In the future we may need more specific behavior. /// /// -/// Ensure that events filtering is in sync with +/// Ensure that events filtering is in sync with . /// internal class BuildCheckForwardingLogger : IForwardingLogger { @@ -33,10 +27,10 @@ internal class BuildCheckForwardingLogger : IForwardingLogger public string? Parameters { get; set; } /// - /// Set of events to be forwarded to + /// Set of events to be forwarded to . /// - private HashSet _eventsToForward = new HashSet - { + private HashSet _eventsToForward = + [ typeof(EnvironmentVariableReadEventArgs), typeof(BuildSubmissionStartedEventArgs), typeof(ProjectEvaluationFinishedEventArgs), @@ -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) { @@ -65,5 +57,7 @@ public void EventSource_AnyEventRaised(object sender, BuildEventArgs buildEvent) } } - public void Shutdown() { } + public void Shutdown() + { + } } diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs index 13925ad6d5f..bf2c5fda3a5 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs @@ -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; @@ -59,7 +58,7 @@ public void ShutdownComponent() { _instance?.Shutdown(); _instance = null; - } + } internal sealed class BuildCheckManager : IBuildCheckManager, IBuildEngineDataRouter, IResultReporter { @@ -94,7 +93,7 @@ public void SetDataSource(BuildCheckDataSource buildCheckDataSource) { _enabledDataSources[(int)buildCheckDataSource] = true; RegisterBuiltInChecks(buildCheckDataSource); - } + } stopwatch.Stop(); _tracingReporter.AddSetDataSourceStats(stopwatch.Elapsed); } @@ -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(); } } @@ -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? 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); } @@ -392,6 +403,16 @@ public void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext, } } + public void ProcessProjectImportedEventArgs(ICheckContext checkContext, ProjectImportedEventArgs projectImportedEventArgs) + { + if (string.IsNullOrEmpty(projectImportedEventArgs.ImportedProjectFile)) + { + return; + } + + PropagateImport(checkContext.BuildEventContext.EvaluationId, projectImportedEventArgs.ProjectFile, projectImportedEventArgs.ImportedProjectFile); + } + public void ProcessTaskStartedEventArgs( ICheckContext checkContext, TaskStartedEventArgs taskStartedEventArgs) @@ -414,6 +435,7 @@ public void ProcessTaskParameterEventArgs( .ProcessTaskParameterEventArgs(checkContext, taskParameterEventArgs); private readonly List _ruleTelemetryData = []; + public BuildCheckTracingData CreateCheckTracingStats() { foreach (CheckFactoryContext checkFactoryContext in _checkRegistry) @@ -445,6 +467,8 @@ public void FinalizeProcessing(LoggingContext loggingContext) private readonly ConcurrentDictionary _projectsByInstanceId = new(); private readonly ConcurrentDictionary _projectsByEvaluationId = new(); + private readonly Dictionary> _deferredProjectEvalIdToImportedProjects = new(); + /// /// 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 @@ -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]); + } } /* @@ -523,7 +551,6 @@ public void ProcessProjectEvaluationStarted( * */ - public void EndProjectEvaluation(BuildEventContext buildEventContext) { } @@ -548,6 +575,24 @@ public void StartProjectRequest(ICheckContext checkContext, string projectFullPa } private readonly Dictionary> _deferredEvalDiagnostics = new(); + + /// + /// 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. + /// + /// The evaluation id is associated with the root project path. + /// The path of the project file that is importing a new project. + /// The path of the newly imported project file. + private void PropagateImport(int evaluationId, string originalProjectFile, string newImportedProjectFile) + { + if (_deferredProjectEvalIdToImportedProjects.TryGetValue(evaluationId, out HashSet? 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 diff --git a/src/Build/BuildCheck/Infrastructure/BuildEventsProcessor.cs b/src/Build/BuildCheck/Infrastructure/BuildEventsProcessor.cs index 7a932c671f9..3ab917a4850 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildEventsProcessor.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildEventsProcessor.cs @@ -96,6 +96,16 @@ internal void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext _buildCheckCentralContext.RunEnvironmentVariableActions(checkData, checkContext, ReportResult); } + /// + /// The method handles events associated with the ProjectImportedEventArgs. + /// + 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) diff --git a/src/Build/BuildCheck/Infrastructure/CheckRegistrationContext.cs b/src/Build/BuildCheck/Infrastructure/CheckRegistrationContext.cs index 1a0565a50fb..4ec9cd0a3a5 100644 --- a/src/Build/BuildCheck/Infrastructure/CheckRegistrationContext.cs +++ b/src/Build/BuildCheck/Infrastructure/CheckRegistrationContext.cs @@ -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> environmentVariableAction) => buildCheckCentralContext.RegisterEnvironmentVariableReadAction(checkWrapper, environmentVariableAction); @@ -33,4 +32,7 @@ public void RegisterProjectRequestProcessingDoneAction(Action> buildFinishedAction) => buildCheckCentralContext.RegisterBuildFinishedAction(checkWrapper, buildFinishedAction); + + public void RegisterProjectImportedAction(Action> projectImportedAction) => + buildCheckCentralContext.RegisterProjectImportedAction(checkWrapper, projectImportedAction); } diff --git a/src/Build/BuildCheck/Infrastructure/CheckScopeClassifier.cs b/src/Build/BuildCheck/Infrastructure/CheckScopeClassifier.cs index 2185f509a26..2dc126665e5 100644 --- a/src/Build/BuildCheck/Infrastructure/CheckScopeClassifier.cs +++ b/src/Build/BuildCheck/Infrastructure/CheckScopeClassifier.cs @@ -8,12 +8,9 @@ namespace Microsoft.Build.BuildCheck.Infrastructure; -internal static class CheckScopeClassifier +public static class CheckScopeClassifier { - static CheckScopeClassifier() - { - FileClassifier.Shared.OnImmutablePathsInitialized += SubscribeImmutablePathsInitialized; - } + static CheckScopeClassifier() => FileClassifier.Shared.OnImmutablePathsInitialized += SubscribeImmutablePathsInitialized; internal static event Action? NotifyOnScopingReadiness; @@ -22,7 +19,7 @@ static CheckScopeClassifier() /// /// Notifies the subscribers that the scoping is ready. /// - internal static Func IsScopingReady => (scope) => (scope is EvaluationCheckScope.ProjectFileOnly or EvaluationCheckScope.All) || IsScopingInitialized; + public static Func IsScopingReady => (scope) => (scope is EvaluationCheckScope.ProjectFileOnly or EvaluationCheckScope.All) || IsScopingInitialized; /// /// Indicates whether given location is in the observed scope, based on currently built project path. @@ -32,7 +29,7 @@ static CheckScopeClassifier() /// /// /// - internal static bool IsActionInObservedScope( + public static bool IsActionInObservedScope( EvaluationCheckScope scope, IMSBuildElementLocation? location, string projectFileFullPath) @@ -46,26 +43,18 @@ internal static bool IsActionInObservedScope( /// /// /// - internal static bool IsActionInObservedScope( + public static bool IsActionInObservedScope( EvaluationCheckScope scope, string? filePathOfEvent, - string projectFileFullPath) - { - switch (scope) + string projectFileFullPath) => scope switch { - case EvaluationCheckScope.ProjectFileOnly: - return filePathOfEvent == projectFileFullPath; - case EvaluationCheckScope.WorkTreeImports: - return - filePathOfEvent != null && - !FileClassifier.Shared.IsNonModifiable(filePathOfEvent) && - !IsGeneratedNugetImport(filePathOfEvent); - case EvaluationCheckScope.All: - return true; - default: - throw new ArgumentOutOfRangeException(nameof(scope), scope, null); - } - } + EvaluationCheckScope.ProjectFileOnly => filePathOfEvent == projectFileFullPath, + EvaluationCheckScope.WorkTreeImports => filePathOfEvent != null + && !FileClassifier.Shared.IsNonModifiable(filePathOfEvent) + && !IsGeneratedNugetImport(filePathOfEvent), + EvaluationCheckScope.All => true, + _ => throw new ArgumentOutOfRangeException(nameof(scope), scope, null), + }; private static bool IsGeneratedNugetImport(string file) => file.EndsWith("nuget.g.props", StringComparison.OrdinalIgnoreCase) diff --git a/src/Build/BuildCheck/Infrastructure/IBuildCheckManager.cs b/src/Build/BuildCheck/Infrastructure/IBuildCheckManager.cs index 552c49dac83..8971403ed22 100644 --- a/src/Build/BuildCheck/Infrastructure/IBuildCheckManager.cs +++ b/src/Build/BuildCheck/Infrastructure/IBuildCheckManager.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Experimental.BuildCheck.Acquisition; using Microsoft.Build.Framework; @@ -61,6 +59,8 @@ void ProcessTaskParameterEventArgs( void ProcessCheckAcquisition(CheckAcquisitionData acquisitionData, ICheckContext checksContext); + void ProcessProjectImportedEventArgs(ICheckContext checkContext, ProjectImportedEventArgs projectImportedEventArgs); + BuildCheckTracingData CreateCheckTracingStats(); void FinalizeProcessing(LoggingContext loggingContext); @@ -69,8 +69,7 @@ void ProcessTaskParameterEventArgs( // but as well from the ConnectorLogger - as even if interleaved, it gives the info // to manager about what checks need to be materialized and configuration fetched. // No unloading of checks is yet considered - once loaded it stays for whole build. - - + // Project might be encountered first time in some node, but be already evaluated in another - so StartProjectEvaluation won't happen // - but we still need to know about it, hence the dedicated event. void ProjectFirstEncountered(BuildCheckDataSource buildCheckDataSource, ICheckContext analysisContext, string projectFullPath); diff --git a/src/Build/BuildCheck/Infrastructure/NullBuildCheckManager.cs b/src/Build/BuildCheck/Infrastructure/NullBuildCheckManager.cs index 294700ef5fc..bcbe2075e84 100644 --- a/src/Build/BuildCheck/Infrastructure/NullBuildCheckManager.cs +++ b/src/Build/BuildCheck/Infrastructure/NullBuildCheckManager.cs @@ -1,11 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Experimental.BuildCheck.Acquisition; -using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Framework; namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure; @@ -82,11 +79,18 @@ public void EndProjectRequest(ICheckContext checkContext, string projectFullPath public BuildCheckTracingData CreateCheckTracingStats() => new BuildCheckTracingData(); public void ProcessPropertyRead(PropertyReadInfo propertyReadInfo, CheckLoggingContext buildEventContext) - { } + { + } public void ProcessPropertyWrite(PropertyWriteInfo propertyWriteInfo, CheckLoggingContext buildEventContext) - { } + { + } public void ProcessEnvironmentVariableReadEventArgs(ICheckContext checkContext, EnvironmentVariableReadEventArgs projectEvaluationEventArgs) - { } + { + } + + public void ProcessProjectImportedEventArgs(ICheckContext checkContext, ProjectImportedEventArgs projectImportedEventArgs) + { + } } diff --git a/src/Build/BuildCheck/OM/ProjectImportedCheckData.cs b/src/Build/BuildCheck/OM/ProjectImportedCheckData.cs new file mode 100644 index 00000000000..e4172f52215 --- /dev/null +++ b/src/Build/BuildCheck/OM/ProjectImportedCheckData.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Experimental.BuildCheck; + +namespace Microsoft.Build.Experimental.BuildCheck; + +/// +/// Represents data for a check related to an imported project in MSBuild. +/// +/// +/// This class extends the base class to include +/// information specific to imported projects. +/// +public class ProjectImportedCheckData : CheckData +{ + internal ProjectImportedCheckData(string importedProjectFile, string projectFilePath, int? projectConfigurationId) + : base(projectFilePath, projectConfigurationId) => ImportedProjectFileFullPath = importedProjectFile; + + /// + /// Gets the file path of the imported project. + /// + public string ImportedProjectFileFullPath { get; } +} diff --git a/src/Build/CompatibilitySuppressions.xml b/src/Build/CompatibilitySuppressions.xml index 05317adadab..9335aedb11f 100644 --- a/src/Build/CompatibilitySuppressions.xml +++ b/src/Build/CompatibilitySuppressions.xml @@ -1,4 +1,25 @@  - + - + + CP0006 + M:Microsoft.Build.Experimental.BuildCheck.IBuildCheckRegistrationContext.RegisterProjectImportedAction(System.Action{Microsoft.Build.Experimental.BuildCheck.BuildCheckDataContext{Microsoft.Build.Experimental.BuildCheck.ProjectImportedCheckData}}) + lib/net472/Microsoft.Build.dll + lib/net472/Microsoft.Build.dll + true + + + CP0006 + M:Microsoft.Build.Experimental.BuildCheck.IBuildCheckRegistrationContext.RegisterProjectImportedAction(System.Action{Microsoft.Build.Experimental.BuildCheck.BuildCheckDataContext{Microsoft.Build.Experimental.BuildCheck.ProjectImportedCheckData}}) + lib/net9.0/Microsoft.Build.dll + lib/net9.0/Microsoft.Build.dll + true + + + CP0006 + M:Microsoft.Build.Experimental.BuildCheck.IBuildCheckRegistrationContext.RegisterProjectImportedAction(System.Action{Microsoft.Build.Experimental.BuildCheck.BuildCheckDataContext{Microsoft.Build.Experimental.BuildCheck.ProjectImportedCheckData}}) + ref/net9.0/Microsoft.Build.dll + ref/net9.0/Microsoft.Build.dll + true + + \ No newline at end of file diff --git a/src/Build/ElementLocation/ElementLocation.cs b/src/Build/ElementLocation/ElementLocation.cs index c9bf42b48b2..8ca5594c946 100644 --- a/src/Build/ElementLocation/ElementLocation.cs +++ b/src/Build/ElementLocation/ElementLocation.cs @@ -185,19 +185,16 @@ internal static ElementLocation Create(string file) /// In AG there are 600 locations that have a file but zero line and column. /// In theory yet another derived class could be made for these to save 4 bytes each. /// - internal static ElementLocation Create(string file, int line, int column) + public static ElementLocation Create(string file, int line, int column) { if (string.IsNullOrEmpty(file) && line == 0 && column == 0) { return EmptyLocation; } - if (line <= 65535 && column <= 65535) - { - return new ElementLocation.SmallElementLocation(file, line, column); - } - - return new ElementLocation.RegularElementLocation(file, line, column); + return line <= 65535 && column <= 65535 + ? new ElementLocation.SmallElementLocation(file, line, column) + : new ElementLocation.RegularElementLocation(file, line, column); } /// diff --git a/src/BuildCheck.UnitTests/MockBuildCheckRegistrationContext.cs b/src/BuildCheck.UnitTests/MockBuildCheckRegistrationContext.cs index 954f69ed050..ced7415118d 100644 --- a/src/BuildCheck.UnitTests/MockBuildCheckRegistrationContext.cs +++ b/src/BuildCheck.UnitTests/MockBuildCheckRegistrationContext.cs @@ -55,5 +55,7 @@ private void ResultHandler(CheckWrapper wrapper, ICheckContext context, CheckCon => Results.Add(result); public void RegisterEnvironmentVariableReadAction(Action> environmentVariableAction) => throw new NotImplementedException(); + + public void RegisterProjectImportedAction(Action> projectImportedAction) => throw new NotImplementedException(); } } diff --git a/template_feed/content/Microsoft.CheckTemplate/Company.CheckTemplate.csproj b/template_feed/content/Microsoft.CheckTemplate/Company.CheckTemplate.csproj index 009b2ad1fe0..fd1808174d6 100644 --- a/template_feed/content/Microsoft.CheckTemplate/Company.CheckTemplate.csproj +++ b/template_feed/content/Microsoft.CheckTemplate/Company.CheckTemplate.csproj @@ -20,26 +20,24 @@ - + - + - - <_PackagesToPack Remove="@(_PackagesToPack)" Condition="%(NuGetPackageId) == 'NETStandard.Library'" /> - <_PackagesToPack Remove="@(_PackagesToPack)" Condition="%(_PackagesToPack.IncludeInPackage) != 'true'" /> + + <_PackagesToPack Remove="@(_PackagesToPack)" Condition="'%(_PackagesToPack.IncludeInPackage)' != 'true'" /> - - + + - - + +