diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs
index 4844d757611..59273724f1d 100644
--- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs
+++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs
@@ -49,7 +49,7 @@ public class ResolveAssemblyReference : TaskExtension
///
/// Cache of system state information, used to optimize performance.
///
- private SystemState _cache = null;
+ internal SystemState _cache = null;
///
/// Construct
@@ -1884,23 +1884,27 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l
///
/// Reads the state file (if present) into the cache.
///
- private void ReadStateFile()
+ internal void ReadStateFile(FileExists fileExists)
{
_cache = SystemState.DeserializeCacheByTranslator(_stateFile, Log);
// Construct the cache if necessary.
if (_cache == null)
{
- _cache = new SystemState();
+ _cache = SystemState.DeserializePrecomputedCachesByTranslator(AssemblyInformationCachePaths ?? Array.Empty(), Log, fileExists);
}
}
///
/// Write out the state file if a state name was supplied and the cache is dirty.
///
- private void WriteStateFile()
+ internal void WriteStateFile()
{
- if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty)
+ if (!String.IsNullOrEmpty(AssemblyInformationCacheOutputPath))
+ {
+ _cache.SerializePrecomputedCacheByTranslator(AssemblyInformationCacheOutputPath, Log);
+ }
+ else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty)
{
_cache.SerializeCacheByTranslator(_stateFile, Log);
}
@@ -2132,7 +2136,7 @@ ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader
}
// Load any prior saved state.
- ReadStateFile();
+ ReadStateFile(fileExists);
_cache.SetGetLastWriteTime(getLastWriteTime);
_cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo);
diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs
index b4f422959a2..fa5e8de3517 100644
--- a/src/Tasks/SystemState.cs
+++ b/src/Tasks/SystemState.cs
@@ -10,6 +10,7 @@
using System.Linq;
using System.Runtime.Versioning;
using Microsoft.Build.BackEnd;
+using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Shared.FileSystem;
using Microsoft.Build.Tasks.AssemblyDependency;
@@ -35,7 +36,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable
///
/// Cache at the SystemState instance level. It is serialized and reused between instances.
///
- private Dictionary instanceLocalFileStateCache = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ internal Dictionary instanceLocalFileStateCache = new Dictionary(StringComparer.OrdinalIgnoreCase);
///
/// LastModified information is purely instance-local. It doesn't make sense to
@@ -71,7 +72,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable
///
/// True if the contents have changed.
///
- private bool isDirty;
+ internal bool isDirty;
///
/// Delegate used internally.
@@ -112,7 +113,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable
/// Class that holds the current file state.
///
[Serializable]
- private sealed class FileState : ITranslatable
+ internal sealed class FileState : ITranslatable
{
///
/// The last modified time for this file.
@@ -276,7 +277,7 @@ internal void SerializeCacheByTranslator(string stateFile, TaskLoggingHelper log
/// Read the contents of this object out to the specified file.
/// TODO: once all classes derived from StateFileBase adopt the new serialization, we should consider moving this into the base class
///
- internal static SystemState DeserializeCacheByTranslator(string stateFile, TaskLoggingHelper log)
+ internal static SystemState DeserializeCacheByTranslator(string stateFile, TaskLoggingHelper log, bool logWarning = true)
{
// First, we read the cache from disk if one exists, or if one does not exist, we create one.
try
@@ -309,7 +310,15 @@ internal static SystemState DeserializeCacheByTranslator(string stateFile, TaskL
// any exception imaginable. Catch them all here.
// Not being able to deserialize the cache is not an error, but we let the user know anyway.
// Don't want to hold up processing just because we couldn't read the file.
- log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, e.Message);
+ if (logWarning)
+ {
+ log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, e.Message);
+ }
+ else
+ {
+ log.LogMessageFromResources("General.CouldNotReadStateFile", stateFile, e.Message);
+ }
+ return null;
}
return null;
@@ -337,6 +346,7 @@ public void Translate(ITranslator translator)
internal bool IsDirty
{
get { return isDirty; }
+ set { isDirty = value; }
}
///
@@ -596,6 +606,69 @@ out fileState.frameworkName
frameworkName = fileState.frameworkName;
}
+ ///
+ /// Reads in cached data from stateFiles to build an initial cache. Avoids logging warnings or errors.
+ ///
+ /// List of locations of caches on disk.
+ /// How to log
+ /// Whether a file exists
+ ///
+ internal static SystemState DeserializePrecomputedCachesByTranslator(ITaskItem[] stateFiles, TaskLoggingHelper log, FileExists fileExists)
+ {
+ SystemState retVal = new SystemState();
+ retVal.isDirty = stateFiles.Length > 0;
+ HashSet assembliesFound = new HashSet();
+
+ foreach (ITaskItem stateFile in stateFiles)
+ {
+ // Verify that it's a real stateFile. Log message but do not error if not.
+ SystemState sysState = DeserializeCacheByTranslator(stateFile.ToString(), log, false);
+ if (sysState == null)
+ {
+ continue;
+ }
+ foreach (KeyValuePair kvp in sysState.instanceLocalFileStateCache)
+ {
+ string relativePath = kvp.Key;
+ if (!assembliesFound.Contains(relativePath))
+ {
+ FileState fileState = kvp.Value;
+ string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile.ToString()), relativePath));
+ if (fileExists(fullPath))
+ {
+ // Correct file path
+ retVal.instanceLocalFileStateCache[fullPath] = fileState;
+ assembliesFound.Add(relativePath);
+ }
+ }
+ }
+ }
+
+ return retVal;
+ }
+
+ ///
+ /// Modifies this object to be more portable across machines, then writes it to filePath.
+ ///
+ /// Path to which to write the precomputed cache
+ /// How to log
+ internal void SerializePrecomputedCacheByTranslator(string stateFile, TaskLoggingHelper log)
+ {
+ Dictionary newInstanceLocalFileStateCache = new Dictionary(instanceLocalFileStateCache.Count);
+ foreach (KeyValuePair kvp in instanceLocalFileStateCache)
+ {
+ string relativePath = FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), kvp.Key);
+ newInstanceLocalFileStateCache[relativePath] = kvp.Value;
+ }
+ instanceLocalFileStateCache = newInstanceLocalFileStateCache;
+
+ if (FileUtilities.FileExistsNoThrow(stateFile))
+ {
+ log.LogWarningWithCodeFromResources("General.StateFileAlreadyPresent", stateFile);
+ }
+ SerializeCacheByTranslator(stateFile, log);
+ }
+
///
/// Cached implementation of GetDirectories.
///