diff --git a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs index 443871defb6..78cd0f31dc1 100644 --- a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs +++ b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs @@ -130,26 +130,44 @@ public void IsolatedContextShouldNotSupportBeingPassedAFileSystem() Should.Throw(() => EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fileSystem)); } - [Fact] - public void EvaluationShouldUseDirectoryCache() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void EvaluationShouldUseDirectoryCache(bool useProjectInstance) { - var projectFile = _env.CreateFile("1.proj", @" ".Cleanup()).Path; + var projectFile = _env.CreateFile("1.proj", @" ".Cleanup()).Path; var projectCollection = _env.CreateProjectCollection().Collection; var directoryCacheFactory = new Helpers.LoggingDirectoryCacheFactory(); - var project = Project.FromFile( - projectFile, - new ProjectOptions - { - ProjectCollection = projectCollection, - DirectoryCacheFactory = directoryCacheFactory, - }); + int expectedEvaluationId; + if (useProjectInstance) + { + var projectInstance = ProjectInstance.FromFile( + projectFile, + new ProjectOptions + { + ProjectCollection = projectCollection, + DirectoryCacheFactory = directoryCacheFactory, + }); + expectedEvaluationId = projectInstance.EvaluationId; + } + else + { + var project = Project.FromFile( + projectFile, + new ProjectOptions + { + ProjectCollection = projectCollection, + DirectoryCacheFactory = directoryCacheFactory, + }); + expectedEvaluationId = project.LastEvaluationId; + } directoryCacheFactory.DirectoryCaches.Count.ShouldBe(1); var directoryCache = directoryCacheFactory.DirectoryCaches[0]; - directoryCache.EvaluationId.ShouldBe(project.LastEvaluationId); + directoryCache.EvaluationId.ShouldBe(expectedEvaluationId); directoryCache.ExistenceChecks.OrderBy(kvp => kvp.Key).ShouldBe( new Dictionary diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index 09d2321cad4..106b1ca08ee 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -1809,18 +1809,6 @@ internal void VerifyThrowInvalidOperationNotImported(ProjectRootElement otherXml ErrorUtilities.VerifyThrowInvalidOperation(ReferenceEquals(Xml, otherXml), "OM_CannotModifyEvaluatedObjectInImportedFile", otherXml.Location.File); } - /// - /// Returns as provided by the passed when creating the - /// project, specific for a given evaluation ID. - /// - /// The evaluation ID for which the cache is requested. - /// An implementation, or null if this project has no - /// associated with it or it returned null. - internal IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId) - { - return _directoryCacheFactory?.GetDirectoryCacheForEvaluation(evaluationId); - } - /// /// Internal project evaluation implementation. /// @@ -3737,6 +3725,7 @@ private void Reevaluate( loggingServiceForEvaluation, new ProjectItemFactory(Owner), ProjectCollection, + Owner._directoryCacheFactory, ProjectCollection.ProjectRootElementCache, s_buildEventContext, evaluationContext.SdkResolverService, diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index 35097b0aeda..a80109d4361 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -202,6 +202,7 @@ private Evaluator( PropertyDictionary environmentProperties, IItemFactory itemFactory, IToolsetProvider toolsetProvider, + IDirectoryCacheFactory directoryCacheFactory, ProjectRootElementCacheBase projectRootElementCache, ISdkResolverService sdkResolverService, int submissionId, @@ -231,7 +232,7 @@ private Evaluator( // If the host wishes to provide a directory cache for this evaluation, create a new EvaluationContext with the right file system. _evaluationContext = evaluationContext; - IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); + IDirectoryCache directoryCache = directoryCacheFactory?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId); if (directoryCache is not null) { IFileSystem fileSystem = new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache); @@ -308,6 +309,7 @@ internal static void Evaluate( ILoggingService loggingService, IItemFactory itemFactory, IToolsetProvider toolsetProvider, + IDirectoryCacheFactory directoryCacheFactory, ProjectRootElementCacheBase projectRootElementCache, BuildEventContext buildEventContext, ISdkResolverService sdkResolverService, @@ -326,6 +328,7 @@ internal static void Evaluate( environmentProperties, itemFactory, toolsetProvider, + directoryCacheFactory, projectRootElementCache, sdkResolverService, submissionId, diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 9a301afd793..de51b01c7fa 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -19,6 +19,7 @@ using Microsoft.Build.Definition; using Microsoft.Build.Evaluation; using Microsoft.Build.Evaluation.Context; +using Microsoft.Build.FileSystem; using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; @@ -241,7 +242,7 @@ public ProjectInstance(string projectFile, IDictionary globalPro /// Project collection /// A new project instance public ProjectInstance(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection) - : this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, interactive: false) + : this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, directoryCacheFactory: null, interactive: false) { } @@ -260,9 +261,11 @@ public ProjectInstance(string projectFile, IDictionary globalPro /// Project collection /// Project load settings /// The context to use for evaluation. + /// The directory cache factory to use for file I/O. /// Indicates if loading the project is allowed to interact with the user. /// A new project instance - private ProjectInstance(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, bool interactive) + private ProjectInstance(string projectFile, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, + ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, bool interactive) { ErrorUtilities.VerifyThrowArgumentLength(projectFile, nameof(projectFile)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion)); @@ -279,7 +282,8 @@ private ProjectInstance(string projectFile, IDictionary globalPr BuildEventContext buildEventContext = new BuildEventContext(buildParameters.NodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId); ProjectRootElement xml = ProjectRootElement.OpenProjectOrSolution(projectFile, globalProperties, toolsVersion, buildParameters.ProjectRootElementCache, true /*Explicitly Loaded*/); - Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version provided */, buildParameters, projectCollection.LoggingService, buildEventContext, projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext); + Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version provided */, buildParameters, projectCollection.LoggingService, buildEventContext, + projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext, directoryCacheFactory: directoryCacheFactory); } /// @@ -327,7 +331,7 @@ public ProjectInstance(ProjectRootElement xml, IDictionary globa /// Project collection /// A new project instance public ProjectInstance(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection) - : this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, interactive: false) + : this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, directoryCacheFactory: null, interactive: false) { } @@ -399,9 +403,11 @@ public ProjectInstance(Project project, ProjectInstanceSettings settings) /// Project collection /// Project load settings /// The context to use for evaluation. + /// The directory cache factory to use for file I/O. /// Indicates if loading the project is allowed to interact with the user. /// A new project instance - private ProjectInstance(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, bool interactive) + private ProjectInstance(ProjectRootElement xml, IDictionary globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, + ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, bool interactive) { BuildEventContext buildEventContext = new BuildEventContext(0, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId); @@ -410,7 +416,8 @@ private ProjectInstance(ProjectRootElement xml, IDictionary glob Interactive = interactive }; - Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version specified */, buildParameters, projectCollection.LoggingService, buildEventContext, projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext); + Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version specified */, buildParameters, projectCollection.LoggingService, buildEventContext, + projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext, directoryCacheFactory: directoryCacheFactory); } /// @@ -755,6 +762,7 @@ public static ProjectInstance FromFile(string file, ProjectOptions options) options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection, options.LoadSettings, options.EvaluationContext, + options.DirectoryCacheFactory, options.Interactive); } @@ -773,6 +781,7 @@ public static ProjectInstance FromProjectRootElement(ProjectRootElement rootElem options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection, options.LoadSettings, options.EvaluationContext, + options.DirectoryCacheFactory, options.Interactive); } @@ -2702,7 +2711,8 @@ private void Initialize( ISdkResolverService sdkResolverService = null, int submissionId = BuildEventContext.InvalidSubmissionId, ProjectLoadSettings? projectLoadSettings = null, - EvaluationContext evaluationContext = null) + EvaluationContext evaluationContext = null, + IDirectoryCacheFactory directoryCacheFactory = null) { ErrorUtilities.VerifyThrowArgumentNull(xml, nameof(xml)); ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(explicitToolsVersion, "toolsVersion"); @@ -2792,8 +2802,8 @@ private void Initialize( evaluationContext = evaluationContext?.ContextForNewProject() ?? EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated); Evaluator.Evaluate( - this, - null, + data: this, + project: null, xml, projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */ buildParameters.MaxNodeCount, @@ -2801,6 +2811,7 @@ private void Initialize( loggingService, new ProjectItemInstanceFactory(this), buildParameters.ToolsetProvider, + directoryCacheFactory, ProjectRootElementCache, buildEventContext, sdkResolverService ?? evaluationContext.SdkResolverService, /* Use override ISdkResolverService if specified */