Skip to content

Commit

Permalink
Shared EvaluationContext for graph construction (#9680)
Browse files Browse the repository at this point in the history
Fix #9678 by creating a single shared `EvaluationContext` in the `Graph`
object and using it when creating `ProjectInstance`s using the default
factory.

This is similar to how NuGet static-graph restore already works: https://github.com/NuGet/NuGet.Client/blob/b83566ec2369c4e9fd07e6f95d734dfe370a1e66/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs#L885

Since we're evaluating the projects in the graph in parallel and "all at
once", the shared caches in the `EvaluationContext` should be a solid
improvement.
  • Loading branch information
rainersigwald authored Apr 24, 2024
1 parent 3d6a5af commit 47ba51c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 5 deletions.
6 changes: 4 additions & 2 deletions src/Build.UnitTests/Graph/ProjectGraph_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Text.RegularExpressions;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Evaluation.Context;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
Expand Down Expand Up @@ -107,10 +108,11 @@ public void ConstructWithSingleNodeWithProjectInstanceFactory()
(projectPath, globalProperties, projectCollection) =>
{
factoryCalled = true;
return ProjectGraph.DefaultProjectInstanceFactory(
return ProjectGraph.StaticProjectInstanceFactory(
projectPath,
globalProperties,
projectCollection);
projectCollection,
EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated));
});
projectGraph.ProjectNodes.Count.ShouldBe(1);
projectGraph.ProjectNodes.First().ProjectInstance.FullPath.ShouldBe(entryProject.Path);
Expand Down
30 changes: 27 additions & 3 deletions src/Build/Graph/ProjectGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Microsoft.Build.Collections;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Evaluation.Context;
using Microsoft.Build.Eventing;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Execution;
Expand Down Expand Up @@ -58,6 +59,8 @@ public delegate ProjectInstance ProjectInstanceFactoryFunc(

private readonly Lazy<IReadOnlyCollection<ProjectGraphNode>> _projectNodesTopologicallySorted;

private readonly EvaluationContext _evaluationContext = null;

private GraphBuilder.GraphEdges Edges { get; }

internal GraphBuilder.GraphEdges TestOnly_Edges => Edges;
Expand Down Expand Up @@ -422,7 +425,11 @@ public ProjectGraph(

var measurementInfo = BeginMeasurement();

projectInstanceFactory ??= DefaultProjectInstanceFactory;
if (projectInstanceFactory is null)
{
_evaluationContext = EvaluationContext.Create(EvaluationContext.SharingPolicy.Shared);
projectInstanceFactory = DefaultProjectInstanceFactory;
}

var graphBuilder = new GraphBuilder(
entryPoints,
Expand Down Expand Up @@ -825,16 +832,33 @@ private static ImmutableList<string> ExpandDefaultTargets(ImmutableList<string>
return targets;
}

internal static ProjectInstance DefaultProjectInstanceFactory(
internal ProjectInstance DefaultProjectInstanceFactory(
string projectPath,
Dictionary<string, string> globalProperties,
ProjectCollection projectCollection)
{
Debug.Assert(_evaluationContext is not null);

return StaticProjectInstanceFactory(
projectPath,
globalProperties,
projectCollection,
_evaluationContext);
}

internal static ProjectInstance StaticProjectInstanceFactory(
string projectPath,
Dictionary<string, string> globalProperties,
ProjectCollection projectCollection,
EvaluationContext evaluationContext)
{
return new ProjectInstance(
projectPath,
globalProperties,
MSBuildConstants.CurrentToolsVersion,
projectCollection);
subToolsetVersion: null,
projectCollection,
evaluationContext);
}

private struct ProjectGraphBuildRequest : IEquatable<ProjectGraphBuildRequest>
Expand Down
17 changes: 17 additions & 0 deletions src/Build/Instance/ProjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,23 @@ public ProjectInstance(string projectFile, IDictionary<string, string> globalPro
{
}

/// <summary>
/// Creates a ProjectInstance directly.
/// No intermediate Project object is created.
/// This is ideal if the project is simply going to be built, and not displayed or edited.
/// </summary>
/// <param name="projectFile">The path to the project file.</param>
/// <param name="globalProperties">The global properties to use.</param>
/// <param name="toolsVersion">The tools version. May be <see langword="null"/>.</param>
/// <param name="subToolsetVersion">The sub-toolset version, used in tandem with <paramref name="toolsVersion"/> to determine the set of toolset properties. May be <see langword="null"/>.</param>
/// <param name="projectCollection">Project collection</param>
/// <param name="context">Context to evaluate inside, potentially sharing caches with other evaluations.</param>
/// <param name="interactive">Indicates if loading the project is allowed to interact with the user.</param>
internal ProjectInstance(string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, EvaluationContext context, bool interactive = false)
: this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: context, directoryCacheFactory: null, interactive: interactive)
{
}

/// <summary>
/// Creates a ProjectInstance directly.
/// No intermediate Project object is created.
Expand Down

0 comments on commit 47ba51c

Please sign in to comment.