diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index 0e21ea5682..3303e62a05 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Xml; using Microsoft.Build.Collections; using Microsoft.Build.Construction; @@ -458,13 +459,44 @@ namespace Microsoft.Build.Evaluation { lock (_locker) { - LinkedList oldStrongCache = _strongCache; - _weakCache = new WeakValueDictionary(StringComparer.OrdinalIgnoreCase); - _strongCache = new LinkedList(); - - foreach (ProjectRootElement projectRootElement in oldStrongCache) + if (Traits.Instance.EscapeHatches.AlwaysDoImmutableFilesUpToDateCheck) { - RaiseProjectRootElementRemovedFromStrongCache(projectRootElement); + LinkedList oldStrongCache = _strongCache; + _weakCache = new WeakValueDictionary(StringComparer.OrdinalIgnoreCase); + _strongCache = new LinkedList(); + + foreach (ProjectRootElement projectRootElement in oldStrongCache) + { + RaiseProjectRootElementRemovedFromStrongCache(projectRootElement); + } + } + else + { + // Manually iterate through LinkedList so we can remove items during this iteration + for (var listNode = _strongCache.First; listNode != null;) + { + var nextNode = listNode.Next; + + ProjectRootElement projectRootElement = listNode.Value; + // Do not remove cache of files from immutable locations. + // Those are mostly SDK project files and will be most probably needed in next builds. + if (!FileClassifier.Shared.IsNonModifiable(projectRootElement.FullPath)) + { + _weakCache.Remove(projectRootElement.FullPath); + _strongCache.Remove(listNode); + RaiseProjectRootElementRemovedFromStrongCache(projectRootElement); + } + + listNode = nextNode; + } + + // From weak list remove all which is not in strong list anymore + IList toBeRemovedFromWeakRefs = _weakCache.Keys.Except(_strongCache.Select(i => i.FullPath)).ToList(); + foreach (string victim in toBeRemovedFromWeakRefs) + { + _weakCache.Remove(victim); + } + _weakCache.Scavenge(); } } }