diff --git a/src/Build.UnitTests/BackEnd/MockLoggingService.cs b/src/Build.UnitTests/BackEnd/MockLoggingService.cs
index a62b03686d4..e30cc99a0a8 100644
--- a/src/Build.UnitTests/BackEnd/MockLoggingService.cs
+++ b/src/Build.UnitTests/BackEnd/MockLoggingService.cs
@@ -496,7 +496,7 @@ public BuildEventContext CreateProjectCacheBuildEventContext(int submissionId, i
=> new BuildEventContext(0, 0, 0, 0, 0, 0, 0);
///
- public void LogProjectEvaluationStarted(BuildEventContext eventContext, string projectFile)
+ public void LogProjectEvaluationStarted(BuildEventContext eventContext, string projectFile, bool isRestore)
{
}
diff --git a/src/Build/BackEnd/Components/Logging/EvaluationLoggingContext.cs b/src/Build/BackEnd/Components/Logging/EvaluationLoggingContext.cs
index d9cb65d4b93..3592c72faec 100644
--- a/src/Build/BackEnd/Components/Logging/EvaluationLoggingContext.cs
+++ b/src/Build/BackEnd/Components/Logging/EvaluationLoggingContext.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
+using System.Collections.Generic;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Profiler;
@@ -27,9 +28,9 @@ public EvaluationLoggingContext(ILoggingService loggingService, BuildEventContex
IsValid = true;
}
- public void LogProjectEvaluationStarted()
+ public void LogProjectEvaluationStarted(bool isRestore)
{
- LoggingService.LogProjectEvaluationStarted(BuildEventContext, _projectFile);
+ LoggingService.LogProjectEvaluationStarted(BuildEventContext, _projectFile, isRestore);
}
///
diff --git a/src/Build/BackEnd/Components/Logging/ILoggingService.cs b/src/Build/BackEnd/Components/Logging/ILoggingService.cs
index 3e44402a61e..e8138d35a4e 100644
--- a/src/Build/BackEnd/Components/Logging/ILoggingService.cs
+++ b/src/Build/BackEnd/Components/Logging/ILoggingService.cs
@@ -485,8 +485,9 @@ MessageImportance MinimumRequiredMessageImportance
///
/// The event context to use for logging
/// Project file being built
+ /// If the project is currently in restore phase
/// The evaluation event context for the project.
- void LogProjectEvaluationStarted(BuildEventContext eventContext, string projectFile);
+ void LogProjectEvaluationStarted(BuildEventContext eventContext, string projectFile, bool isRestore);
///
/// Logs that a project evaluation has finished
diff --git a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs
index 547554d06d8..eb02baacd0c 100644
--- a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs
+++ b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs
@@ -444,14 +444,15 @@ public BuildEventContext CreateProjectCacheBuildEventContext(
}
///
- public void LogProjectEvaluationStarted(BuildEventContext projectEvaluationEventContext, string projectFile)
+ public void LogProjectEvaluationStarted(BuildEventContext projectEvaluationEventContext, string projectFile, bool isRestore)
{
ProjectEvaluationStartedEventArgs evaluationEvent =
new ProjectEvaluationStartedEventArgs(ResourceUtilities.GetResourceString("EvaluationStarted"),
projectFile)
{
BuildEventContext = projectEvaluationEventContext,
- ProjectFile = projectFile
+ ProjectFile = projectFile,
+ IsRestore = isRestore
};
ProcessLoggingEvent(evaluationEvent);
diff --git a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs
index 1a4693ba685..fb1fbcd50d0 100644
--- a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs
+++ b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs
@@ -1105,9 +1105,9 @@ private async Task BuildProject()
ErrorUtilities.VerifyThrow(_targetBuilder != null, "Target builder is null");
// We consider this the entrypoint for the project build for purposes of BuildCheck processing
-
- var buildCheckManager = (_componentHost.GetComponent(BuildComponentType.BuildCheckManagerProvider) as IBuildCheckManagerProvider)!.Instance;
- buildCheckManager.SetDataSource(BuildCheckDataSource.BuildExecution);
+ var propertyEntry = _requestEntry.RequestConfiguration.GlobalProperties[MSBuildConstants.MSBuildIsRestoring];
+ IBuildCheckManager buildCheckManager = propertyEntry is not null ? null : (_componentHost.GetComponent(BuildComponentType.BuildCheckManagerProvider) as IBuildCheckManagerProvider)!.Instance;
+ buildCheckManager?.SetDataSource(BuildCheckDataSource.BuildExecution);
// Make sure it is null before loading the configuration into the request, because if there is a problem
// we do not wand to have an invalid projectLoggingContext floating around. Also if this is null the error will be
@@ -1121,10 +1121,12 @@ private async Task BuildProject()
// Load the project
if (!_requestEntry.RequestConfiguration.IsLoaded)
{
- buildCheckManager.StartProjectEvaluation(
+
+ buildCheckManager?.StartProjectEvaluation(
BuildCheckDataSource.BuildExecution,
_requestEntry.Request.ParentBuildEventContext,
_requestEntry.RequestConfiguration.ProjectFullPath);
+
_requestEntry.RequestConfiguration.LoadProjectIntoConfiguration(
_componentHost,
@@ -1146,13 +1148,13 @@ private async Task BuildProject()
}
finally
{
- buildCheckManager.EndProjectEvaluation(
+ buildCheckManager?.EndProjectEvaluation(
BuildCheckDataSource.BuildExecution,
_requestEntry.Request.ParentBuildEventContext);
}
_projectLoggingContext = _nodeLoggingContext.LogProjectStarted(_requestEntry);
- buildCheckManager.StartProjectRequest(
+ buildCheckManager?.StartProjectRequest(
BuildCheckDataSource.BuildExecution,
_requestEntry.Request.ParentBuildEventContext);
@@ -1223,7 +1225,7 @@ private async Task BuildProject()
}
finally
{
- buildCheckManager.EndProjectRequest(
+ buildCheckManager?.EndProjectRequest(
BuildCheckDataSource.BuildExecution,
_requestEntry.Request.ParentBuildEventContext);
}
diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckConnectorLogger.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckConnectorLogger.cs
index 361c30c4200..02e3b9cf78c 100644
--- a/src/Build/BuildCheck/Infrastructure/BuildCheckConnectorLogger.cs
+++ b/src/Build/BuildCheck/Infrastructure/BuildCheckConnectorLogger.cs
@@ -8,6 +8,7 @@
using Microsoft.Build.BuildCheck.Acquisition;
using Microsoft.Build.Experimental.BuildCheck;
using Microsoft.Build.Framework;
+using Microsoft.Build.Shared;
using static Microsoft.Build.BuildCheck.Infrastructure.BuildCheckManagerProvider;
namespace Microsoft.Build.BuildCheck.Infrastructure;
@@ -31,6 +32,8 @@ internal BuildCheckConnectorLogger(
public string? Parameters { get; set; }
+ private bool isRestore = false;
+
public void Initialize(IEventSource eventSource)
{
eventSource.AnyEventRaised += EventSource_AnyEventRaised;
@@ -48,6 +51,11 @@ public void Shutdown()
private void HandleProjectEvaluationFinishedEvent(ProjectEvaluationFinishedEventArgs eventArgs)
{
+ if (isRestore)
+ {
+ return;
+ }
+
if (!IsMetaProjFile(eventArgs.ProjectFile))
{
_buildCheckManager.ProcessEvaluationFinishedEventArgs(
@@ -60,6 +68,16 @@ private void HandleProjectEvaluationFinishedEvent(ProjectEvaluationFinishedEvent
private void HandleProjectEvaluationStartedEvent(ProjectEvaluationStartedEventArgs eventArgs)
{
+ if (eventArgs.IsRestore)
+ {
+ isRestore = true;
+ return;
+ }
+ if (isRestore)
+ {
+ isRestore = false;
+ }
+
if (!IsMetaProjFile(eventArgs.ProjectFile))
{
_buildCheckManager.StartProjectEvaluation(BuildCheckDataSource.EventArgs, eventArgs.BuildEventContext!, eventArgs.ProjectFile!);
@@ -100,8 +118,22 @@ private void EventSource_BuildFinished(object sender, BuildFinishedEventArgs e)
{
{ typeof(ProjectEvaluationFinishedEventArgs), (BuildEventArgs e) => HandleProjectEvaluationFinishedEvent((ProjectEvaluationFinishedEventArgs) e) },
{ typeof(ProjectEvaluationStartedEventArgs), (BuildEventArgs e) => HandleProjectEvaluationStartedEvent((ProjectEvaluationStartedEventArgs) e) },
- { typeof(ProjectStartedEventArgs), (BuildEventArgs e) => _buildCheckManager.StartProjectRequest(BuildCheckDataSource.EventArgs, e.BuildEventContext!) },
- { typeof(ProjectFinishedEventArgs), (BuildEventArgs e) => _buildCheckManager.EndProjectRequest(BuildCheckDataSource.EventArgs, e.BuildEventContext!) },
+ { typeof(ProjectStartedEventArgs), (BuildEventArgs e) =>
+ {
+ if (!isRestore)
+ {
+ _buildCheckManager.StartProjectRequest(BuildCheckDataSource.EventArgs, e.BuildEventContext!);
+ }
+ }
+ },
+ { typeof(ProjectFinishedEventArgs), (BuildEventArgs e) =>
+ {
+ if (!isRestore)
+ {
+ _buildCheckManager.EndProjectRequest(BuildCheckDataSource.EventArgs, e.BuildEventContext!);
+ }
+ }
+ },
{ typeof(BuildCheckTracingEventArgs), (BuildEventArgs e) => _stats.Merge(((BuildCheckTracingEventArgs)e).TracingData, (span1, span2) => span1 + span2) },
{ typeof(BuildCheckAcquisitionEventArgs), (BuildEventArgs e) => _buildCheckManager.ProcessAnalyzerAcquisition(((BuildCheckAcquisitionEventArgs)e).ToAnalyzerAcquisitionData(), e.BuildEventContext!) },
};
diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs
index 85447378533..9c411aa528d 100644
--- a/src/Build/Evaluation/Evaluator.cs
+++ b/src/Build/Evaluation/Evaluator.cs
@@ -626,7 +626,7 @@ private void Evaluate()
}
}
- _evaluationLoggingContext.LogProjectEvaluationStarted();
+ _evaluationLoggingContext.LogProjectEvaluationStarted(_data.GlobalPropertiesDictionary[MSBuildConstants.MSBuildIsRestoring] is not null); ;
ErrorUtilities.VerifyThrow(_data.EvaluationId != BuildEventContext.InvalidEvaluationId, "Evaluation should produce an evaluation ID");
diff --git a/src/BuildCheck.UnitTests/EndToEndTests.cs b/src/BuildCheck.UnitTests/EndToEndTests.cs
index 9351612060f..85f5f495d4b 100644
--- a/src/BuildCheck.UnitTests/EndToEndTests.cs
+++ b/src/BuildCheck.UnitTests/EndToEndTests.cs
@@ -35,32 +35,60 @@ public EndToEndTests(ITestOutputHelper output)
[InlineData(false, false)]
public void SampleAnalyzerIntegrationTest(bool buildInOutOfProcessNode, bool analysisRequested)
{
- string contents = $"""
-
-
+ TransientTestFile projectFile = SetupTestFiles();
+ _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", buildInOutOfProcessNode ? "1" : "0");
+ _env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "1");
+ string output = RunnerUtilities.ExecBootstrapedMSBuild(
+ $"{Path.GetFileName(projectFile.Path)} /m:1 /p:BuildProjectReferences=false -nr:False -restore" +
+ (analysisRequested ? " -analyze" : string.Empty), out bool success, false, _env.Output);
+ _env.Output.WriteLine(output);
+ success.ShouldBeTrue();
+ // The conflicting outputs warning appears - but only if analysis was requested
+ if (analysisRequested)
+ {
+ output.ShouldContain("BC0101");
+ }
+ else
+ {
+ output.ShouldNotContain("BC0101");
+ }
+ }
+
+ [Fact]
+ public void NoRunOnRestore()
+ {
+ TransientTestFile projectFile = SetupTestFiles();
+ string output = RunnerUtilities.ExecBootstrapedMSBuild(
+ $"{Path.GetFileName(projectFile.Path)} /m:1 -nr:False -analyze -t:restore", out bool success);
+ _env.Output.WriteLine(output);
+ success.ShouldBeTrue();
+ output.ShouldNotContain("BC0101");
+ }
+
+ private TransientTestFile SetupTestFiles()
+ {
+ {
+ string contents = $"""
+
- Exe
- net8.0
- enable
- enable
+ Exe
+ net8.0
+ enable
+ enable
-
+
- Test
+ Test
-
+
-
+
-
-
-
-
-
+
""";
- string contents2 = $"""
+ string contents2 = $"""
@@ -77,24 +105,21 @@ public void SampleAnalyzerIntegrationTest(bool buildInOutOfProcessNode, bool ana
-
-
-
-
""";
- TransientTestFolder workFolder = _env.CreateFolder(createFolder: true);
- TransientTestFile projectFile = _env.CreateFile(workFolder, "FooBar.csproj", contents);
- TransientTestFile projectFile2 = _env.CreateFile(workFolder, "FooBar-Copy.csproj", contents2);
- // var cache = new SimpleProjectRootElementCache();
- // ProjectRootElement xml = ProjectRootElement.OpenProjectOrSolution(projectFile.Path, /*unused*/null, /*unused*/null, cache, false /*Not explicitly loaded - unused*/);
+ TransientTestFolder workFolder = _env.CreateFolder(createFolder: true);
+ TransientTestFile projectFile = _env.CreateFile(workFolder, "FooBar.csproj", contents);
+ TransientTestFile projectFile2 = _env.CreateFile(workFolder, "FooBar-Copy.csproj", contents2);
+
+ // var cache = new SimpleProjectRootElementCache();
+ // ProjectRootElement xml = ProjectRootElement.OpenProjectOrSolution(projectFile.Path, /*unused*/null, /*unused*/null, cache, false /*Not explicitly loaded - unused*/);
- TransientTestFile config = _env.CreateFile(workFolder, "editorconfig.json",
- /*lang=json,strict*/
- """
+ TransientTestFile config = _env.CreateFile(workFolder, "editorconfig.json",
+ /*lang=json,strict*/
+ """
{
"BC0101": {
"IsEnabled": true,
@@ -112,26 +137,11 @@ public void SampleAnalyzerIntegrationTest(bool buildInOutOfProcessNode, bool ana
}
""");
- // OSX links /var into /private, which makes Path.GetTempPath() return "/var..." but Directory.GetCurrentDirectory return "/private/var...".
- // This discrepancy breaks path equality checks in analyzers if we pass to MSBuild full path to the initial project.
- // See if there is a way of fixing it in the engine - tracked: https://github.com/orgs/dotnet/projects/373/views/1?pane=issue&itemId=55702688.
- _env.SetCurrentDirectory(Path.GetDirectoryName(projectFile.Path));
-
- _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", buildInOutOfProcessNode ? "1" : "0");
- _env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "1");
- string output = RunnerUtilities.ExecBootstrapedMSBuild(
- $"{Path.GetFileName(projectFile.Path)} /m:1 -nr:False -restore" +
- (analysisRequested ? " -analyze" : string.Empty), out bool success, false, _env.Output);
- _env.Output.WriteLine(output);
- success.ShouldBeTrue();
- // The conflicting outputs warning appears - but only if analysis was requested
- if (analysisRequested)
- {
- output.ShouldContain("BC0101");
- }
- else
- {
- output.ShouldNotContain("BC0101");
+ // OSX links /var into /private, which makes Path.GetTempPath() return "/var..." but Directory.GetCurrentDirectory return "/private/var...".
+ // This discrepancy breaks path equality checks in analyzers if we pass to MSBuild full path to the initial project.
+ // See if there is a way of fixing it in the engine - tracked: https://github.com/orgs/dotnet/projects/373/views/1?pane=issue&itemId=55702688.
+ _env.SetCurrentDirectory(Path.GetDirectoryName(projectFile.Path));
+ return (projectFile);
}
}
}
diff --git a/src/Framework/ProjectEvaluationStartedEventArgs.cs b/src/Framework/ProjectEvaluationStartedEventArgs.cs
index 6d231fe1428..06bb2ceb994 100644
--- a/src/Framework/ProjectEvaluationStartedEventArgs.cs
+++ b/src/Framework/ProjectEvaluationStartedEventArgs.cs
@@ -30,5 +30,10 @@ public ProjectEvaluationStartedEventArgs(string? message, params object[]? messa
/// Gets or sets the full path of the project that started evaluation.
///
public string? ProjectFile { get; set; }
+
+ ///
+ /// Gets or sets is the project is currently on restore phase.
+ ///
+ public bool IsRestore { get; internal set; }
}
}