From a79f3080ea40840edf1a6563447a8121fa45000f Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 14 Jul 2020 16:03:06 -0700 Subject: [PATCH 01/35] First stab at caching more RAR info This gives RAR the ability to save off a modified state file and load it for later use. Still to do: 1) Verify that the precomputed cache read in is real. 2) Change logging such that it will log messages instead of errors or warnings if there are problems with deserializing the precomputed cache 3) Comments at relevant points 4) Switch serialization mode to another form (json?) 5) Validation + performance tests 6) Have SDK opt in --- .../net/Microsoft.Build.Tasks.Core.cs | 2 + .../netstandard/Microsoft.Build.Tasks.Core.cs | 2 + .../ResolveAssemblyReference.cs | 25 +++++-- src/Tasks/SystemState.cs | 66 ++++++++++++++++--- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index b269e54a881..c2eaad8de59 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -910,6 +910,8 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblySubsetTables { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblyTables { get { throw null; } set { } } public string[] LatestTargetFrameworkDirectories { get { throw null; } set { } } + public string[] PreComputedCacheFileList { get { throw null; } set { } } + public string PreComputedCacheOutputPath { get { throw null; } set { } } public string ProfileName { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] public Microsoft.Build.Framework.ITaskItem[] RelatedFiles { get { throw null; } } diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index 1babf5ee2d4..60821f9b1db 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -568,6 +568,8 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblySubsetTables { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblyTables { get { throw null; } set { } } public string[] LatestTargetFrameworkDirectories { get { throw null; } set { } } + public string[] PreComputedCacheFileList { get { throw null; } set { } } + public string PreComputedCacheOutputPath { get { throw null; } set { } } public string ProfileName { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] public Microsoft.Build.Framework.ITaskItem[] RelatedFiles { get { throw null; } } diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index c52420b8e19..b877214bef2 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -441,6 +441,10 @@ public string TargetedRuntimeVersion set { _targetedRuntimeVersionRawValue = value; } } + public string PreComputedCacheOutputPath { get; set; } + + public string[] PreComputedCacheFileList { get; set; } + /// /// List of locations to search for assemblyFiles when resolving dependencies. /// The following types of things can be passed in here: @@ -1845,15 +1849,24 @@ private void LogConflict(Reference reference, string fusionName) /// /// Reads the state file (if present) into the cache. /// - private void ReadStateFile() + private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { _cache = (SystemState)StateFileBase.DeserializeCache(_stateFile, Log, typeof(SystemState)); + if (_cache == null) + { + _cache = SystemState.DeserializePrecomputedCaches(PreComputedCacheFileList, Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); + return; + } + // Construct the cache if necessary. if (_cache == null) { _cache = new SystemState(); } + + _cache.SetGetLastWriteTime(getLastWriteTime); + _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); } /// @@ -1861,7 +1874,11 @@ private void ReadStateFile() /// private void WriteStateFile() { - if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) + if (!string.IsNullOrEmpty(PreComputedCacheOutputPath)) + { + _cache.SerializePrecomputedCache(PreComputedCacheOutputPath, Log); + } + else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { _cache.SerializeCache(_stateFile, Log); } @@ -2072,9 +2089,7 @@ ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader } // Load any prior saved state. - ReadStateFile(); - _cache.SetGetLastWriteTime(getLastWriteTime); - _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); + ReadStateFile(getLastWriteTime, installedAssemblyTableInfo); // Cache delegates. getAssemblyName = _cache.CacheDelegate(getAssemblyName); diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 10fe9859407..c5f0d90353f 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -8,11 +8,13 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; using Microsoft.Build.Shared; using Microsoft.Build.Tasks.AssemblyDependency; +using Microsoft.Build.Utilities; namespace Microsoft.Build.Tasks { @@ -115,7 +117,7 @@ private sealed class FileState : ISerializable /// /// The last modified time for this file. /// - private DateTime lastModified; + internal DateTime lastModified; /// /// The fusion name of this file. @@ -201,6 +203,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) internal DateTime LastModified { get { return lastModified; } + set { lastModified = value; } } /// @@ -232,6 +235,8 @@ internal FrameworkName FrameworkNameAttribute get { return frameworkName; } set { frameworkName = value; } } + + internal Guid ModuleVersionID { get; set; } } /// @@ -547,13 +552,58 @@ out fileState.frameworkName frameworkName = fileState.frameworkName; } - /// - /// Cached implementation of GetDirectories. - /// - /// - /// - /// - private string[] GetDirectories(string path, string pattern) + internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + { + SystemState retVal = new SystemState(); + retVal.SetGetLastWriteTime(getLastWriteTime); + retVal.SetInstalledAssemblyInformation(installedAssemblyTableInfo); + retVal.isDirty = true; + + foreach (string stateFile in stateFiles) + { + // Verify that it's a real stateFile; log message but do not error if not + SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType); + foreach (string s in sfBase.instanceLocalFileStateCache.Keys) + { + if (!retVal.instanceLocalFileStateCache.ContainsKey(s)) + { + FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[s]; + // Verify that the assembly is correct + if (File.Exists(s) && Assembly.LoadFile(s).ManifestModule.ModuleVersionId.Equals(fileState.ModuleVersionID)) + { + // Correct file path and timestamp + string fullPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), stateFile)); + fileState.LastModified = retVal.getLastWriteTime(fullPath); + retVal.instanceLocalFileStateCache[fullPath] = fileState; + } + } + } + } + + return retVal; + } + + internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) + { + foreach (string path in instanceLocalFileStateCache.Keys) + { + FileState fileState = (FileState)instanceLocalFileStateCache[path]; + fileState.ModuleVersionID = Assembly.LoadFrom(path).ManifestModule.ModuleVersionId; + instanceLocalFileStateCache.Remove(path); + string relativePath = new Uri(Path.GetDirectoryName(stateFile)).MakeRelativeUri(new Uri(path)).ToString(); + instanceLocalFileStateCache[relativePath] = fileState; + } + + SerializeCache(stateFile, log); + } + + /// + /// Cached implementation of GetDirectories. + /// + /// + /// + /// + private string[] GetDirectories(string path, string pattern) { // Only cache the *. pattern. This is by far the most common pattern // and generalized caching would require a call to Path.Combine which From 1d7355985217b3de3049d383a9d829173bb3d390 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 21 Jul 2020 10:20:42 -0700 Subject: [PATCH 02/35] PR comments pt. 1 --- .../ResolveAssemblyReference.cs | 13 +++------ src/Tasks/Microsoft.Build.Tasks.csproj | 2 +- src/Tasks/SystemState.cs | 27 ++++++++++++++----- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index b877214bef2..5a3ab85eaa0 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1855,18 +1855,13 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ if (_cache == null) { - _cache = SystemState.DeserializePrecomputedCaches(PreComputedCacheFileList, Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); - return; + _cache = SystemState.DeserializePrecomputedCaches(PreComputedCacheFileList ?? new string[0], Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); } - - // Construct the cache if necessary. - if (_cache == null) + else { - _cache = new SystemState(); + _cache.SetGetLastWriteTime(getLastWriteTime); + _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); } - - _cache.SetGetLastWriteTime(getLastWriteTime); - _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); } /// diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index 6275d3bcd30..b13e439a3c7 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -978,6 +978,7 @@ + @@ -996,7 +997,6 @@ - diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index c5f0d90353f..fa027695ebb 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -9,11 +9,14 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; using Microsoft.Build.Shared; using Microsoft.Build.Tasks.AssemblyDependency; +using Microsoft.Build.Tasks.Deployment.ManifestUtilities; using Microsoft.Build.Utilities; namespace Microsoft.Build.Tasks @@ -557,22 +560,28 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta SystemState retVal = new SystemState(); retVal.SetGetLastWriteTime(getLastWriteTime); retVal.SetInstalledAssemblyInformation(installedAssemblyTableInfo); - retVal.isDirty = true; + retVal.isDirty = stateFiles.Length > 0; foreach (string stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType); - foreach (string s in sfBase.instanceLocalFileStateCache.Keys) + foreach (string relativePath in sfBase.instanceLocalFileStateCache.Keys) { - if (!retVal.instanceLocalFileStateCache.ContainsKey(s)) + if (!retVal.instanceLocalFileStateCache.ContainsKey(relativePath)) { - FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[s]; + FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[relativePath]; // Verify that the assembly is correct - if (File.Exists(s) && Assembly.LoadFile(s).ManifestModule.ModuleVersionId.Equals(fileState.ModuleVersionID)) + Guid mvid; + using (var reader = new PEReader(File.OpenRead(relativePath))) + { + var metadataReader = reader.GetMetadataReader(); + mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + } + if (File.Exists(relativePath) && Assembly.Load(File.ReadAllBytes(relativePath)).ManifestModule.ModuleVersionId.Equals(fileState.ModuleVersionID)) { // Correct file path and timestamp - string fullPath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), stateFile)); + string fullPath = Path.GetFullPath(Path.Combine(stateFile, relativePath)); fileState.LastModified = retVal.getLastWriteTime(fullPath); retVal.instanceLocalFileStateCache[fullPath] = fileState; } @@ -588,7 +597,11 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) foreach (string path in instanceLocalFileStateCache.Keys) { FileState fileState = (FileState)instanceLocalFileStateCache[path]; - fileState.ModuleVersionID = Assembly.LoadFrom(path).ManifestModule.ModuleVersionId; + using (var reader = new PEReader(File.OpenRead(path))) + { + var metadataReader = reader.GetMetadataReader(); + fileState.ModuleVersionID = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + } instanceLocalFileStateCache.Remove(path); string relativePath = new Uri(Path.GetDirectoryName(stateFile)).MakeRelativeUri(new Uri(path)).ToString(); instanceLocalFileStateCache[relativePath] = fileState; From 036c5a38dd759b46dc8cb52e9fa244f737a6acf1 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 21 Jul 2020 13:50:05 -0700 Subject: [PATCH 03/35] Resolve first three bullet points --- .../ResolveAssemblyReference.cs | 22 +++++++++++++------ src/Tasks/StateFileBase.cs | 18 +++++++++------ src/Tasks/SystemState.cs | 17 ++++++++++---- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 5a3ab85eaa0..9065ea7c22f 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -441,9 +441,17 @@ public string TargetedRuntimeVersion set { _targetedRuntimeVersionRawValue = value; } } - public string PreComputedCacheOutputPath { get; set; } + /// + /// If not null, serializes a cache to this location. This overrides the usual cache, so only use this if you will + /// not have access to the usual cache at the next build. + /// + public string CacheOutputPath { get; set; } - public string[] PreComputedCacheFileList { get; set; } + /// + /// If not null, uses this set of caches as inputs if RAR cannot find the usual cache in the obj folder. Typically + /// used for demos and first-run scenarios. + /// + public string[] CacheInputPaths { get; set; } /// /// List of locations to search for assemblyFiles when resolving dependencies. @@ -1847,7 +1855,7 @@ private void LogConflict(Reference reference, string fusionName) #region StateFile /// - /// Reads the state file (if present) into the cache. + /// Reads the state file (if present) into the cache. If not present, attempts to read from CacheInputPaths, then creates a new cache if necessary. /// private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { @@ -1855,7 +1863,7 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ if (_cache == null) { - _cache = SystemState.DeserializePrecomputedCaches(PreComputedCacheFileList ?? new string[0], Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); + _cache = SystemState.DeserializePrecomputedCaches(CacheInputPaths ?? new string[0], Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); } else { @@ -1865,13 +1873,13 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ } /// - /// Write out the state file if a state name was supplied and the cache is dirty. + /// If CacheOutputPath is non-null, writes out a cache to that location. Otherwise, writes out the state file if a state name was supplied and the cache is dirty. /// private void WriteStateFile() { - if (!string.IsNullOrEmpty(PreComputedCacheOutputPath)) + if (!string.IsNullOrEmpty(CacheOutputPath)) { - _cache.SerializePrecomputedCache(PreComputedCacheOutputPath, Log); + _cache.SerializePrecomputedCache(CacheOutputPath, Log); } else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index 3d17a4967a4..8a48a986219 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -64,7 +64,7 @@ internal virtual void SerializeCache(string stateFile, TaskLoggingHelper log) /// /// Reads the specified file from disk into a StateFileBase derived object. /// - internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelper log, Type requiredReturnType) + internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelper log, Type requiredReturnType, bool logWarnings = true) { StateFileBase retVal = null; @@ -89,16 +89,20 @@ internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelp // If there is an invalid cast, a message rather than a warning should be emitted. log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); } - - if ((retVal != null) && (!requiredReturnType.IsInstanceOfType(retVal))) + else if (retVal != null && (!requiredReturnType.IsInstanceOfType(retVal))) { - log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, - log.FormatResourceString("General.IncompatibleStateFileType")); + if (logWarnings) + { + log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); + } + else + { + log.LogMessageFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); + } retVal = null; } - // If we get back a valid object and internals were changed, things are likely to be null. Check the version before we use it. - if (retVal != null && retVal._serializedVersion != CurrentSerializationVersion) + else if (retVal != null && retVal._serializedVersion != CurrentSerializationVersion) { log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); retVal = null; diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index fa027695ebb..b663f73c5a3 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.Serialization; @@ -16,7 +15,6 @@ using System.Security.Permissions; using Microsoft.Build.Shared; using Microsoft.Build.Tasks.AssemblyDependency; -using Microsoft.Build.Tasks.Deployment.ManifestUtilities; using Microsoft.Build.Utilities; namespace Microsoft.Build.Tasks @@ -239,6 +237,9 @@ internal FrameworkName FrameworkNameAttribute set { frameworkName = value; } } + /// + /// Get or set the ID of this assembly. Used to verify it is the same version. + /// internal Guid ModuleVersionID { get; set; } } @@ -555,6 +556,9 @@ out fileState.frameworkName frameworkName = fileState.frameworkName; } + /// + /// Reads in cached data from stateFiles to build an initial cache. Avoids logging warnings or errors. + /// internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { SystemState retVal = new SystemState(); @@ -565,7 +569,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta foreach (string stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not - SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType); + SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); foreach (string relativePath in sfBase.instanceLocalFileStateCache.Keys) { if (!retVal.instanceLocalFileStateCache.ContainsKey(relativePath)) @@ -578,7 +582,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta var metadataReader = reader.GetMetadataReader(); mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); } - if (File.Exists(relativePath) && Assembly.Load(File.ReadAllBytes(relativePath)).ManifestModule.ModuleVersionId.Equals(fileState.ModuleVersionID)) + if (File.Exists(relativePath) && mvid.Equals(fileState.ModuleVersionID)) { // Correct file path and timestamp string fullPath = Path.GetFullPath(Path.Combine(stateFile, relativePath)); @@ -592,16 +596,21 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta return retVal; } + /// + /// Modifies this object to be more portable across machines, then writes it to stateFile. + /// internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) { foreach (string path in instanceLocalFileStateCache.Keys) { + // Add MVID to allow us to verify that we are using the same assembly later FileState fileState = (FileState)instanceLocalFileStateCache[path]; using (var reader = new PEReader(File.OpenRead(path))) { var metadataReader = reader.GetMetadataReader(); fileState.ModuleVersionID = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); } + instanceLocalFileStateCache.Remove(path); string relativePath = new Uri(Path.GetDirectoryName(stateFile)).MakeRelativeUri(new Uri(path)).ToString(); instanceLocalFileStateCache[relativePath] = fileState; From 8b3b613b728bb62f2b6ca1e97c267229f5f81078 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 3 Aug 2020 13:28:42 -0700 Subject: [PATCH 04/35] Next steps --- .../net/Microsoft.Build.Tasks.Core.cs | 4 +- .../netstandard/Microsoft.Build.Tasks.Core.cs | 4 +- src/Tasks/Microsoft.Build.Tasks.csproj | 1 + src/Tasks/StateFileBase.cs | 63 ++++++++----------- src/Tasks/SystemState.cs | 12 ++-- 5 files changed, 36 insertions(+), 48 deletions(-) diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index c2eaad8de59..504910a4ea2 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -884,6 +884,8 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] Assemblies { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } } public bool AutoUnify { get { throw null; } set { } } + public string[] CacheInputPaths { get { throw null; } set { } } + public string CacheOutputPath { get { throw null; } set { } } public string[] CandidateAssemblyFiles { get { throw null; } set { } } public bool CopyLocalDependenciesWhenParentReferenceInGac { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] @@ -910,8 +912,6 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblySubsetTables { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblyTables { get { throw null; } set { } } public string[] LatestTargetFrameworkDirectories { get { throw null; } set { } } - public string[] PreComputedCacheFileList { get { throw null; } set { } } - public string PreComputedCacheOutputPath { get { throw null; } set { } } public string ProfileName { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] public Microsoft.Build.Framework.ITaskItem[] RelatedFiles { get { throw null; } } diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index 60821f9b1db..a1d7750c4df 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -542,6 +542,8 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] Assemblies { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } } public bool AutoUnify { get { throw null; } set { } } + public string[] CacheInputPaths { get { throw null; } set { } } + public string CacheOutputPath { get { throw null; } set { } } public string[] CandidateAssemblyFiles { get { throw null; } set { } } public bool CopyLocalDependenciesWhenParentReferenceInGac { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] @@ -568,8 +570,6 @@ public ResolveAssemblyReference() { } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblySubsetTables { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] InstalledAssemblyTables { get { throw null; } set { } } public string[] LatestTargetFrameworkDirectories { get { throw null; } set { } } - public string[] PreComputedCacheFileList { get { throw null; } set { } } - public string PreComputedCacheOutputPath { get { throw null; } set { } } public string ProfileName { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] public Microsoft.Build.Framework.ITaskItem[] RelatedFiles { get { throw null; } } diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index b13e439a3c7..42b7444e5c2 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -986,6 +986,7 @@ + diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index 8a48a986219..3eb868b682c 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -7,6 +7,7 @@ using Microsoft.Build.Utilities; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; +using System.Text.Json; namespace Microsoft.Build.Tasks { @@ -36,16 +37,7 @@ internal virtual void SerializeCache(string stateFile, TaskLoggingHelper log) { if (!string.IsNullOrEmpty(stateFile)) { - if (FileSystems.Default.FileExists(stateFile)) - { - File.Delete(stateFile); - } - - using (var s = new FileStream(stateFile, FileMode.CreateNew)) - { - var formatter = new BinaryFormatter(); - formatter.Serialize(s, this); - } + File.WriteAllText(stateFile, JsonSerializer.Serialize(this)); } } catch (Exception e) @@ -74,39 +66,34 @@ internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelp { if (!string.IsNullOrEmpty(stateFile) && FileSystems.Default.FileExists(stateFile)) { - using (FileStream s = new FileStream(stateFile, FileMode.Open)) + object deserializedObject = JsonSerializer.Deserialize(File.ReadAllText(stateFile), requiredReturnType); + retVal = deserializedObject as StateFileBase; + // If the deserialized object is null then there would be no cast error but retVal would still be null + // only log the message if there would have been a cast error + if (retVal == null && deserializedObject != null) { - var formatter = new BinaryFormatter(); - object deserializedObject = formatter.Deserialize(s); - retVal = deserializedObject as StateFileBase; - - // If the deserialized object is null then there would be no cast error but retVal would still be null - // only log the message if there would have been a cast error - if (retVal == null && deserializedObject != null) - { - // When upgrading to Visual Studio 2008 and running the build for the first time the resource cache files are replaced which causes a cast error due - // to a new version number on the tasks class. "Unable to cast object of type 'Microsoft.Build.Tasks.SystemState' to type 'Microsoft.Build.Tasks.StateFileBase". - // If there is an invalid cast, a message rather than a warning should be emitted. - log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); - } - else if (retVal != null && (!requiredReturnType.IsInstanceOfType(retVal))) + // When upgrading to Visual Studio 2008 and running the build for the first time the resource cache files are replaced which causes a cast error due + // to a new version number on the tasks class. "Unable to cast object of type 'Microsoft.Build.Tasks.SystemState' to type 'Microsoft.Build.Tasks.StateFileBase". + // If there is an invalid cast, a message rather than a warning should be emitted. + log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); + } + else if (retVal != null && (!requiredReturnType.IsInstanceOfType(retVal))) + { + if (logWarnings) { - if (logWarnings) - { - log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); - } - else - { - log.LogMessageFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); - } - retVal = null; + log.LogWarningWithCodeFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); } - // If we get back a valid object and internals were changed, things are likely to be null. Check the version before we use it. - else if (retVal != null && retVal._serializedVersion != CurrentSerializationVersion) + else { - log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); - retVal = null; + log.LogMessageFromResources("General.CouldNotReadStateFile", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); } + retVal = null; + } + // If we get back a valid object and internals were changed, things are likely to be null. Check the version before we use it. + else if (retVal != null && retVal._serializedVersion != CurrentSerializationVersion) + { + log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); + retVal = null; } } } diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index b663f73c5a3..b54c97f8896 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -201,7 +201,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) /// Gets the last modified date. /// /// - internal DateTime LastModified + public DateTime LastModified { get { return lastModified; } set { lastModified = value; } @@ -211,7 +211,7 @@ internal DateTime LastModified /// Get or set the assemblyName. /// /// - internal AssemblyNameExtension Assembly + public AssemblyNameExtension Assembly { get { return assemblyName; } set { assemblyName = value; } @@ -221,7 +221,7 @@ internal AssemblyNameExtension Assembly /// Get or set the runtimeVersion /// /// - internal string RuntimeVersion + public string RuntimeVersion { get { return runtimeVersion; } set { runtimeVersion = value; } @@ -231,7 +231,7 @@ internal string RuntimeVersion /// Get or set the framework name the file was built against /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Could be used in other assemblies")] - internal FrameworkName FrameworkNameAttribute + public FrameworkName FrameworkNameAttribute { get { return frameworkName; } set { frameworkName = value; } @@ -240,13 +240,13 @@ internal FrameworkName FrameworkNameAttribute /// /// Get or set the ID of this assembly. Used to verify it is the same version. /// - internal Guid ModuleVersionID { get; set; } + public Guid ModuleVersionID { get; set; } } /// /// Construct. /// - internal SystemState() + public SystemState() { } From 6160d48cdf18dc7ad407588204fe1f475631f7d6 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 3 Aug 2020 16:01:42 -0700 Subject: [PATCH 05/35] revert to BinaryFormatter --- src/Tasks/StateFileBase.cs | 20 +++++++++++++++++--- src/Tasks/SystemState.cs | 30 +++++++++++++++++------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index 3eb868b682c..ec09003778a 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -37,7 +37,16 @@ internal virtual void SerializeCache(string stateFile, TaskLoggingHelper log) { if (!string.IsNullOrEmpty(stateFile)) { - File.WriteAllText(stateFile, JsonSerializer.Serialize(this)); + if (FileSystems.Default.FileExists(stateFile)) + { + File.Delete(stateFile); + } + + using (var s = new FileStream(stateFile, FileMode.CreateNew)) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(s, this); + } } } catch (Exception e) @@ -59,6 +68,7 @@ internal virtual void SerializeCache(string stateFile, TaskLoggingHelper log) internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelper log, Type requiredReturnType, bool logWarnings = true) { StateFileBase retVal = null; + object deserializedObject = null; // First, we read the cache from disk if one exists, or if one does not exist // then we create one. @@ -66,8 +76,12 @@ internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelp { if (!string.IsNullOrEmpty(stateFile) && FileSystems.Default.FileExists(stateFile)) { - object deserializedObject = JsonSerializer.Deserialize(File.ReadAllText(stateFile), requiredReturnType); - retVal = deserializedObject as StateFileBase; + using (FileStream s = new FileStream(stateFile, FileMode.Open)) + { + var formatter = new BinaryFormatter(); + deserializedObject = formatter.Deserialize(s); + retVal = deserializedObject as StateFileBase; + } // If the deserialized object is null then there would be no cast error but retVal would still be null // only log the message if there would have been a cast error if (retVal == null && deserializedObject != null) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index b54c97f8896..2ff64df7e92 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -577,17 +577,20 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[relativePath]; // Verify that the assembly is correct Guid mvid; - using (var reader = new PEReader(File.OpenRead(relativePath))) + string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); + if (File.Exists(fullPath)) { - var metadataReader = reader.GetMetadataReader(); - mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); - } - if (File.Exists(relativePath) && mvid.Equals(fileState.ModuleVersionID)) - { - // Correct file path and timestamp - string fullPath = Path.GetFullPath(Path.Combine(stateFile, relativePath)); - fileState.LastModified = retVal.getLastWriteTime(fullPath); - retVal.instanceLocalFileStateCache[fullPath] = fileState; + using (var reader = new PEReader(File.OpenRead(fullPath))) + { + var metadataReader = reader.GetMetadataReader(); + mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + } + if (mvid.Equals(fileState.ModuleVersionID)) + { + // Correct file path and timestamp + fileState.LastModified = retVal.getLastWriteTime(fullPath); + retVal.instanceLocalFileStateCache[fullPath] = fileState; + } } } } @@ -601,6 +604,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta /// internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) { + Hashtable newInstanceLocalFileStateCache = new Hashtable(); foreach (string path in instanceLocalFileStateCache.Keys) { // Add MVID to allow us to verify that we are using the same assembly later @@ -611,10 +615,10 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) fileState.ModuleVersionID = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); } - instanceLocalFileStateCache.Remove(path); - string relativePath = new Uri(Path.GetDirectoryName(stateFile)).MakeRelativeUri(new Uri(path)).ToString(); - instanceLocalFileStateCache[relativePath] = fileState; + string relativePath = FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), path); + newInstanceLocalFileStateCache[relativePath] = fileState; } + instanceLocalFileStateCache = newInstanceLocalFileStateCache; SerializeCache(stateFile, log); } From 9b95494d0351a91a2ed82f567c1d73cf751862e7 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 4 Aug 2020 13:18:06 -0700 Subject: [PATCH 06/35] Serialize mvid --- src/Tasks/SystemState.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 2ff64df7e92..face0482fc9 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -172,6 +172,7 @@ internal FileState(SerializationInfo info, StreamingContext context) var frameworkProfile = info.GetString("fmProf"); frameworkName = new FrameworkName(frameworkIdentifier, frameworkNameVersion, frameworkProfile); } + ModuleVersionID = (Guid)info.GetValue("mvid", typeof(Guid)); } /// @@ -195,6 +196,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("fnId", frameworkName.Identifier); info.AddValue("fmProf", frameworkName.Profile); } + info.AddValue("mvid", ModuleVersionID); } /// From 43195f4f368c443643f6a9757a7d025ed19e6a92 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 10 Aug 2020 14:46:13 -0700 Subject: [PATCH 07/35] Design change --- src/Tasks/Resources/Strings.resx | 4 ++++ src/Tasks/Resources/xlf/Strings.cs.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.de.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.en.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.es.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.fr.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.it.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.ja.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.ko.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.pl.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.pt-BR.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.ru.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.tr.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.zh-Hans.xlf | 5 +++++ src/Tasks/Resources/xlf/Strings.zh-Hant.xlf | 5 +++++ src/Tasks/SystemState.cs | 8 +++++++- 16 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index cc1e2699443..8a5c363976b 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -451,6 +451,10 @@ MSB3101: Could not write state file "{0}". {1} {StrBegin="MSB3101: "} + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + MSB3105: The item "{0}" was specified more than once in the "{1}" parameter. Duplicate items are not supported by the "{1}" parameter. {StrBegin="MSB3105: "} diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf index 9598c9d6157..a1798d0b2c0 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -509,6 +509,11 @@ Globální vlastnosti: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Odstraňování vlastností: diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf index 69c726c62a4..e1ab948f4a1 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -509,6 +509,11 @@ Globale Eigenschaften: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Eigenschaften werden entfernt: diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index c9d6885f10c..76bf8ba1679 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -554,6 +554,11 @@ Global Properties: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Removing Properties: diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index e34aa053c3e..2dcef957d51 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -509,6 +509,11 @@ Propiedades globales: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Quitando propiedades: diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index 9720b69bf5f..ae2bcb64137 100644 --- a/src/Tasks/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Resources/xlf/Strings.fr.xlf @@ -509,6 +509,11 @@ Propriétés globales : + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Suppression des propriétés : diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 99cc118cd38..6dd8dc13afb 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -509,6 +509,11 @@ Proprietà globali: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Rimozione proprietà: diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index e209c62b2f7..84d0fd3f898 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -509,6 +509,11 @@ グローバル プロパティ: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: プロパティの削除: diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index 2e70e167585..8226fd59601 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -509,6 +509,11 @@ 전역 속성: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: 속성 제거: diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index 6b53dc24fb7..7eb22e26920 100644 --- a/src/Tasks/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Resources/xlf/Strings.pl.xlf @@ -509,6 +509,11 @@ Właściwości globalne: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Usuwanie właściwości: diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf index 0d31dafd981..5b593c3af62 100644 --- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf @@ -509,6 +509,11 @@ Propriedades globais: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Removendo Propriedades: diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index 29fe42402d0..30b2198656b 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -509,6 +509,11 @@ Глобальные свойства: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Удаление свойств: diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index 5076d987e41..14c98e5c23c 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -509,6 +509,11 @@ Genel Özellikler: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: Özellikler kaldırılıyor: diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf index 2797e21cd0b..de2b681c56e 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -509,6 +509,11 @@ 全局属性: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: 移除属性: diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index 05c4f11672b..e5178f7427d 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -509,6 +509,11 @@ 全域屬性: + + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + {StrBegin="MSB3667: "} + Removing Properties: 正在移除屬性: diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index face0482fc9..d775b5ced7e 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -567,6 +567,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta retVal.SetGetLastWriteTime(getLastWriteTime); retVal.SetInstalledAssemblyInformation(installedAssemblyTableInfo); retVal.isDirty = stateFiles.Length > 0; + HashSet assembliesFound = new HashSet(); foreach (string stateFile in stateFiles) { @@ -574,7 +575,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); foreach (string relativePath in sfBase.instanceLocalFileStateCache.Keys) { - if (!retVal.instanceLocalFileStateCache.ContainsKey(relativePath)) + if (!assembliesFound.Contains(relativePath)) { FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[relativePath]; // Verify that the assembly is correct @@ -592,6 +593,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta // Correct file path and timestamp fileState.LastModified = retVal.getLastWriteTime(fullPath); retVal.instanceLocalFileStateCache[fullPath] = fileState; + assembliesFound.Add(relativePath); } } } @@ -622,6 +624,10 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) } instanceLocalFileStateCache = newInstanceLocalFileStateCache; + if (FileUtilities.FileExistsNoThrow(stateFile)) + { + log.LogWarningWithCodeFromResources("General.StateFileAlreadyPresent", stateFile); + } SerializeCache(stateFile, log); } From 9045d3d68f4990896ce4c9ff774b113b9164d1ce Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 31 Aug 2020 17:17:43 -0700 Subject: [PATCH 08/35] Serialize AssemblyNameExtension It was previously serializable via BinaryFormatter. This makes it serializable via System.Text.Json's JsonConverter. --- src/Shared/AssemblyNameExtension.cs | 248 +++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 6 deletions(-) diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index 758e90ee4e6..cb8140ced3d 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -2,15 +2,19 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Text; -using System.Reflection; -using System.Collections; -using System.Globalization; -using System.Diagnostics; using System.Collections.Generic; using System.Configuration.Assemblies; -using System.Runtime.Serialization; +using System.Globalization; using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +#if !NET35 +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +#endif #if FEATURE_ASSEMBLYLOADCONTEXT using System.Reflection.PortableExecutable; using System.Reflection.Metadata; @@ -996,5 +1000,237 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("immutable", immutable); info.AddValue("remapped", remappedFrom); } + +#if !NET35 + internal class Converter : JsonConverter + { + public override AssemblyNameExtension Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + AssemblyNameExtension ane = new AssemblyNameExtension(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return ane; + } + else if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + else if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + string parameter = reader.GetString(); + reader.Read(); + if (reader.TokenType == JsonTokenType.Null) + { + continue; + } + switch (parameter) + { + case nameof(ane.asAssemblyName): + AssemblyName an = new AssemblyName(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + ane.asAssemblyName = an; + break; + } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + string anParameter = reader.GetString(); + reader.Read(); + if (reader.TokenType == JsonTokenType.Null) + { + continue; + } + switch (anParameter) + { + case nameof(an.Name): + an.Name = reader.GetString(); + break; + case "PublicKey": + an.SetPublicKey(ParseByteArray(ref reader)); + break; + case "PublicKeyToken": + an.SetPublicKeyToken(ParseByteArray(ref reader)); + break; + case nameof(an.Version): + an.Version = Version.Parse(reader.GetString()); + break; + case "Flags": + an.Flags = (AssemblyNameFlags)reader.GetDecimal(); + break; + case "CPUArch": + an.ProcessorArchitecture = (ProcessorArchitecture)reader.GetDecimal(); + break; + case nameof(an.CultureInfo): + an.CultureInfo = new CultureInfo(reader.GetString()); + break; + case "HashAlg": + an.HashAlgorithm = (System.Configuration.Assemblies.AssemblyHashAlgorithm)reader.GetDecimal(); + break; + case "VersionCompat": + an.VersionCompatibility = (AssemblyVersionCompatibility)reader.GetDecimal(); + break; + case nameof(an.CodeBase): + an.CodeBase = reader.GetString(); + break; + case nameof(an.KeyPair): + an.KeyPair = new StrongNameKeyPair(reader.GetString()); + break; + default: + throw new JsonException(); + } + } + break; + case nameof(ane.asString): + ane.asString = reader.GetString(); + break; + case nameof(ane.isSimpleName): + ane.isSimpleName = reader.GetBoolean(); + break; + case nameof(ane.hasProcessorArchitectureInFusionName): + ane.hasProcessorArchitectureInFusionName = reader.GetBoolean(); + break; + case nameof(ane.immutable): + ane.immutable = reader.GetBoolean(); + break; + case nameof(ane.remappedFrom): + ane.remappedFrom = ParseArray(ref reader, this); + break; + } + } + throw new JsonException(); + } + + private HashSet ParseArray(ref Utf8JsonReader reader, JsonConverter converter) + { + // If the array is null + if (reader.TokenType != JsonTokenType.StartArray) + { + return null; + } + HashSet ret = new HashSet(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + return ret; + } + ret.Add(converter.Read(ref reader, typeof(T), new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); + } + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, AssemblyNameExtension asn, JsonSerializerOptions options) + { + writer.WriteStartObject(); + if (asn.asAssemblyName != null) + { + writer.WritePropertyName(nameof(asn.asAssemblyName)); + writer.WriteStartObject(); + writer.WriteString(nameof(asn.asAssemblyName.Name), asn.asAssemblyName.Name); + byte[] publicKey = asn.asAssemblyName.GetPublicKey(); + if (publicKey != null) + { + writer.WritePropertyName("PublicKey"); + writer.WriteStartArray(); + foreach (byte b in asn.asAssemblyName.GetPublicKey()) + { + writer.WriteNumberValue(b); + } + writer.WriteEndArray(); + } + byte[] publicKeyToken = asn.asAssemblyName.GetPublicKeyToken(); + if (publicKeyToken != null) + { + writer.WritePropertyName("PublicKeyToken"); + writer.WriteStartArray(); + foreach (byte b in asn.asAssemblyName.GetPublicKeyToken()) + { + writer.WriteNumberValue(b); + } + writer.WriteEndArray(); + } + if (asn.asAssemblyName.Version != null) + { + writer.WriteString(nameof(asn.asAssemblyName.Version), asn.asAssemblyName.Version.ToString()); + } + writer.WriteNumber("Flags", (int)asn.asAssemblyName.Flags); + writer.WriteNumber("CPUArch", (int)asn.asAssemblyName.ProcessorArchitecture); + if (asn.asAssemblyName.CultureInfo != null) + { + writer.WriteString(nameof(asn.asAssemblyName.CultureInfo), asn.asAssemblyName.CultureInfo.ToString()); + } + writer.WriteNumber("HashAlg", (int)asn.asAssemblyName.HashAlgorithm); + writer.WriteNumber("VersionCompat", (int)asn.asAssemblyName.VersionCompatibility); + writer.WriteString(nameof(asn.asAssemblyName.CodeBase), asn.asAssemblyName.CodeBase); + if (asn.asAssemblyName.KeyPair != null) + { + writer.WriteString(nameof(asn.asAssemblyName.KeyPair), asn.asAssemblyName.KeyPair.ToString()); + } + writer.WriteEndObject(); + } + writer.WriteString(nameof(asn.asString), asn.asString); + writer.WriteBoolean(nameof(asn.isSimpleName), asn.isSimpleName); + writer.WriteBoolean(nameof(asn.hasProcessorArchitectureInFusionName), asn.hasProcessorArchitectureInFusionName); + writer.WriteBoolean(nameof(asn.immutable), asn.immutable); + if (asn.remappedFrom != null) + { + writer.WritePropertyName(nameof(asn.remappedFrom)); + writer.WriteStartArray(); + JsonSerializerOptions aneOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + bool first = true; + foreach (AssemblyNameExtension ane in asn.remappedFrom) + { + if (first) + { + first = false; + } + else + { + writer.WriteStringValue(string.Empty); + } + if (ane is null) + { + writer.WriteNullValue(); + continue; + } + Write(writer, ane, aneOptions); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + } + + private byte[] ParseByteArray(ref Utf8JsonReader reader) + { + // If the array is null + if (reader.TokenType != JsonTokenType.StartArray) + { + return null; + } + List ret = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + return ret.ToArray(); + } + ret.Add(reader.GetByte()); + } + throw new JsonException(); + } + } +#endif } } From ecf24de89e7a54f51ee3b6abccb9e79556bdad05 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 31 Aug 2020 17:18:47 -0700 Subject: [PATCH 09/35] Serialize SystemState Add support for serializing SystemState using System.Text.Json rather than the older BinaryFormatter. --- src/Tasks/SystemState.cs | 333 +++++++++++++++++++++++++++------------ 1 file changed, 231 insertions(+), 102 deletions(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index d775b5ced7e..88421155661 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -10,9 +10,11 @@ using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; -using System.Runtime.Serialization; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; -using System.Security.Permissions; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.Build.Shared; using Microsoft.Build.Tasks.AssemblyDependency; using Microsoft.Build.Utilities; @@ -23,7 +25,7 @@ namespace Microsoft.Build.Tasks /// Class is used to cache system state. /// [Serializable] - internal sealed class SystemState : StateFileBase, ISerializable + internal sealed class SystemState { /// /// Cache at the SystemState instance level. Has the same contents as . @@ -113,18 +115,8 @@ internal sealed class SystemState : StateFileBase, ISerializable /// Class that holds the current file state. /// [Serializable] - private sealed class FileState : ISerializable + private sealed class FileState { - /// - /// The last modified time for this file. - /// - internal DateTime lastModified; - - /// - /// The fusion name of this file. - /// - private AssemblyNameExtension assemblyName; - /// /// The assemblies that this file depends on. /// @@ -140,94 +132,38 @@ private sealed class FileState : ISerializable /// internal FrameworkName frameworkName; - /// - /// The CLR runtime version for the assembly. - /// - internal string runtimeVersion; - /// /// Default construct. /// internal FileState(DateTime lastModified) { - this.lastModified = lastModified; + this.LastModified = lastModified; } /// - /// Deserializing constuctor. + /// Simplified constructor for deserialization. /// - internal FileState(SerializationInfo info, StreamingContext context) + internal FileState() { - ErrorUtilities.VerifyThrowArgumentNull(info, "info"); - - lastModified = new DateTime(info.GetInt64("mod"), (DateTimeKind)info.GetInt32("modk")); - assemblyName = (AssemblyNameExtension)info.GetValue("an", typeof(AssemblyNameExtension)); - dependencies = (AssemblyNameExtension[])info.GetValue("deps", typeof(AssemblyNameExtension[])); - scatterFiles = (string[])info.GetValue("sfiles", typeof(string[])); - runtimeVersion = (string)info.GetValue("rtver", typeof(string)); - if (info.GetBoolean("fn")) - { - var frameworkNameVersion = (Version) info.GetValue("fnVer", typeof(Version)); - var frameworkIdentifier = info.GetString("fnId"); - var frameworkProfile = info.GetString("fmProf"); - frameworkName = new FrameworkName(frameworkIdentifier, frameworkNameVersion, frameworkProfile); - } - ModuleVersionID = (Guid)info.GetValue("mvid", typeof(Guid)); - } - - /// - /// Serialize the contents of the class. - /// - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - ErrorUtilities.VerifyThrowArgumentNull(info, "info"); - - info.AddValue("mod", lastModified.Ticks); - info.AddValue("modk", (int)lastModified.Kind); - info.AddValue("an", assemblyName); - info.AddValue("deps", dependencies); - info.AddValue("sfiles", scatterFiles); - info.AddValue("rtver", runtimeVersion); - info.AddValue("fn", frameworkName != null); - if (frameworkName != null) - { - info.AddValue("fnVer", frameworkName.Version); - info.AddValue("fnId", frameworkName.Identifier); - info.AddValue("fmProf", frameworkName.Profile); - } - info.AddValue("mvid", ModuleVersionID); } /// /// Gets the last modified date. /// /// - public DateTime LastModified - { - get { return lastModified; } - set { lastModified = value; } - } + public DateTime LastModified { get; set; } /// /// Get or set the assemblyName. /// /// - public AssemblyNameExtension Assembly - { - get { return assemblyName; } - set { assemblyName = value; } - } + public AssemblyNameExtension Assembly { get; set; } /// /// Get or set the runtimeVersion /// /// - public string RuntimeVersion - { - get { return runtimeVersion; } - set { runtimeVersion = value; } - } + public string RuntimeVersion { get; set; } /// /// Get or set the framework name the file was built against @@ -245,22 +181,222 @@ public FrameworkName FrameworkNameAttribute public Guid ModuleVersionID { get; set; } } - /// - /// Construct. - /// - public SystemState() + internal sealed class Converter : JsonConverter { + public override SystemState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + SystemState systemState = new SystemState(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return systemState; + } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + systemState.instanceLocalFileStateCache.Add(reader.GetString(), ParseFileState(ref reader)); + } + + throw new JsonException(); + } + + private FileState ParseFileState(ref Utf8JsonReader reader) + { + FileState state = new FileState(); + reader.Read(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + return state; + } + if (reader.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException(); + } + AssemblyNameExtension.Converter converter = new AssemblyNameExtension.Converter(); + string parameter = reader.GetString(); + reader.Read(); + if (reader.TokenType == JsonTokenType.Null) + { + continue; + } + switch (parameter) + { + case nameof(state.dependencies): + state.dependencies = ParseArray(ref reader, converter); + break; + case nameof(state.scatterFiles): + state.scatterFiles = ParseArray(ref reader, s => s); + break; + case nameof(state.LastModified): + state.LastModified = DateTime.Parse(reader.GetString()); + break; + case nameof(state.Assembly): + state.Assembly = converter.Read(ref reader, typeof(AssemblyNameExtension), new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); + break; + case nameof(state.RuntimeVersion): + state.RuntimeVersion = reader.GetString(); + break; + case nameof(state.FrameworkNameAttribute): + string version = string.Empty; + string identifier = string.Empty; + string profile = string.Empty; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + state.FrameworkNameAttribute = new FrameworkName(identifier, Version.Parse(version), profile); + break; + } + switch (reader.GetString()) + { + case "Version": + reader.Read(); + version = reader.GetString(); + break; + case "Identifier": + reader.Read(); + identifier = reader.GetString(); + break; + case "Profile": + reader.Read(); + profile = reader.GetString(); + break; + } + } + break; + case nameof(state.ModuleVersionID): + state.ModuleVersionID = Guid.Parse(reader.GetString()); + break; + default: + throw new JsonException(); + } + } + throw new JsonException(); + } + + private T[] ParseArray(ref Utf8JsonReader reader, JsonConverter converter) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + return null; + } + List list = new List(); + JsonSerializerOptions options = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + return list.ToArray(); + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + list.Add(converter.Read(ref reader, typeof(T), options)); + } + } + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, SystemState stateFile, JsonSerializerOptions options) + { + Hashtable cache = stateFile.instanceLocalFileStateCache; + writer.WriteStartObject(); + JsonSerializerOptions aneOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + AssemblyNameExtension.Converter converter = new AssemblyNameExtension.Converter(); + foreach (string fileInfoKey in cache.Keys) + { + writer.WritePropertyName(fileInfoKey); + FileState fileInfo = (FileState)cache[fileInfoKey]; + writer.WriteStartObject(); + if (fileInfo.dependencies != null) + { + writer.WritePropertyName(nameof(fileInfo.dependencies)); + writer.WriteStartArray(); + for (int i = 0; i < fileInfo.dependencies.Length; i++) + { + if (i != 0) + { + writer.WriteStringValue(string.Empty); + } + converter.Write(writer, fileInfo.dependencies[i], aneOptions); + } + foreach (AssemblyNameExtension e in fileInfo.dependencies) + { + converter.Write(writer, e, aneOptions); + } + writer.WriteEndArray(); + } + if (fileInfo.scatterFiles != null) + { + writer.WritePropertyName(nameof(fileInfo.scatterFiles)); + writer.WriteStartArray(); + foreach (string s in fileInfo.scatterFiles) + { + writer.WriteStringValue(s); + } + writer.WriteEndArray(); + } + writer.WriteString(nameof(fileInfo.LastModified), fileInfo.LastModified.ToString()); + if (fileInfo.Assembly is null) + { + writer.WriteNull(nameof(fileInfo.Assembly)); + } + else + { + writer.WritePropertyName(nameof(fileInfo.Assembly)); + converter.Write(writer, fileInfo.Assembly, aneOptions); + } + writer.WriteString(nameof(fileInfo.RuntimeVersion), fileInfo.RuntimeVersion); + if (fileInfo.FrameworkNameAttribute != null) + { + writer.WritePropertyName(nameof(fileInfo.FrameworkNameAttribute)); + writer.WriteStartObject(); + writer.WriteString("Version", fileInfo.FrameworkNameAttribute.Version.ToString()); + writer.WriteString("Identifier", fileInfo.FrameworkNameAttribute.Identifier); + writer.WriteString("Profile", fileInfo.FrameworkNameAttribute.Profile); + writer.WriteEndObject(); + } + writer.WriteString(nameof(fileInfo.ModuleVersionID), fileInfo.ModuleVersionID.ToString()); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + private T[] ParseArray(ref Utf8JsonReader reader, Func converter) + { + if (reader.TokenType != JsonTokenType.StartArray) + { + return null; + } + List list = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + { + return list.ToArray(); + } + list.Add(converter(reader.GetString())); + } + throw new JsonException(); + } } /// - /// Deserialize the contents of the class. + /// Construct. /// - internal SystemState(SerializationInfo info, StreamingContext context) + public SystemState() { - ErrorUtilities.VerifyThrowArgumentNull(info, "info"); - - instanceLocalFileStateCache = (Hashtable)info.GetValue("fileState", typeof(Hashtable)); - isDirty = false; } /// @@ -278,17 +414,6 @@ AssemblyTableInfo[] installedAssemblyTableInfos redistList = RedistList.GetRedistList(installedAssemblyTableInfos); } - /// - /// Serialize the contents of the class. - /// - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - ErrorUtilities.VerifyThrowArgumentNull(info, "info"); - - info.AddValue("fileState", instanceLocalFileStateCache); - } - /// /// Flag that indicates /// @@ -555,7 +680,7 @@ out fileState.frameworkName dependencies = fileState.dependencies; scatterFiles = fileState.scatterFiles; - frameworkName = fileState.frameworkName; + frameworkName = fileState.FrameworkNameAttribute; } /// @@ -572,12 +697,14 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta foreach (string stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not - SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); - foreach (string relativePath in sfBase.instanceLocalFileStateCache.Keys) + var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + deserializeOptions.Converters.Add(new SystemState.Converter()); + SystemState sysBase = JsonSerializer.Deserialize(File.ReadAllText(stateFile), deserializeOptions); + foreach (string relativePath in sysBase.instanceLocalFileStateCache.Keys) { if (!assembliesFound.Contains(relativePath)) { - FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[relativePath]; + FileState fileState = (FileState)sysBase.instanceLocalFileStateCache[relativePath]; // Verify that the assembly is correct Guid mvid; string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); @@ -628,7 +755,9 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) { log.LogWarningWithCodeFromResources("General.StateFileAlreadyPresent", stateFile); } - SerializeCache(stateFile, log); + JsonSerializerOptions options = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + options.Converters.Add(new SystemState.Converter()); + File.WriteAllText(stateFile, JsonSerializer.Serialize(this, options)); } /// From b25d16087c3d0046ce40df7b2393f26417d76414 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Wed, 2 Sep 2020 14:37:23 -0700 Subject: [PATCH 10/35] Opt into new serialization behavior for all serialization rather than just when serializing and deserializing the precomputed cache --- .../ResolveAssemblyReference.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 9065ea7c22f..211537b094f 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -9,6 +9,8 @@ using System.IO; using System.Reflection; using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; using System.Xml.Linq; using Microsoft.Build.Eventing; @@ -1859,7 +1861,16 @@ private void LogConflict(Reference reference, string fusionName) /// private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { - _cache = (SystemState)StateFileBase.DeserializeCache(_stateFile, Log, typeof(SystemState)); + var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + deserializeOptions.Converters.Add(new SystemState.Converter()); + try + { + _cache = JsonSerializer.Deserialize(File.ReadAllText(_stateFile), deserializeOptions); + } + catch (Exception) + { + // log message + } if (_cache == null) { @@ -1883,7 +1894,9 @@ private void WriteStateFile() } else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { - _cache.SerializeCache(_stateFile, Log); + var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + deserializeOptions.Converters.Add(new SystemState.Converter()); + File.WriteAllText(_stateFile, JsonSerializer.Serialize(_cache, deserializeOptions)); } } #endregion From 560806632da291d575d7251256a1f96cbb853d2b Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 3 Sep 2020 18:07:33 -0700 Subject: [PATCH 11/35] PR comments I have not yet measured how long it takes to read a MVID vs. reading and parsing its references, nor have I made unit tests. --- .../ResolveAssemblyReference.cs | 2 +- src/Tasks/Resources/Strings.resx | 2 +- src/Tasks/Resources/xlf/Strings.cs.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.de.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.en.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.es.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.fr.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.it.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.ja.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.ko.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.pl.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.pt-BR.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.ru.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.tr.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.zh-Hans.xlf | 4 +-- src/Tasks/Resources/xlf/Strings.zh-Hant.xlf | 4 +-- src/Tasks/SystemState.cs | 36 +++++++++++-------- 17 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 9065ea7c22f..a9eabeea1b3 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1863,7 +1863,7 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ if (_cache == null) { - _cache = SystemState.DeserializePrecomputedCaches(CacheInputPaths ?? new string[0], Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); + _cache = SystemState.DeserializePrecomputedCaches(CacheInputPaths ?? Array.Empty(), Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); } else { diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index 8a5c363976b..5e15108b6c0 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -452,7 +452,7 @@ {StrBegin="MSB3101: "} - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf index a1798d0b2c0..d7bea347846 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf index e1ab948f4a1..f588bd1fb08 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index 76bf8ba1679..f31747656e8 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -555,8 +555,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index 2dcef957d51..d0355958c68 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index ae2bcb64137..8b444bec28d 100644 --- a/src/Tasks/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Resources/xlf/Strings.fr.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 6dd8dc13afb..7e27282d990 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index 84d0fd3f898..35d09e9ead6 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index 8226fd59601..62ddad539c5 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index 7eb22e26920..f19378eef8b 100644 --- a/src/Tasks/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Resources/xlf/Strings.pl.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf index 5b593c3af62..c79b95ef75a 100644 --- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index 30b2198656b..0c7b99aff32 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index 14c98e5c23c..ca978d5d887 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf index de2b681c56e..e78f15e2bec 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index e5178f7427d..775c8fa7230 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -510,8 +510,8 @@ - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. - MSB3101: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "_CacheOutputPath" field of RAR. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index d775b5ced7e..408442c2412 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -118,7 +118,7 @@ private sealed class FileState : ISerializable /// /// The last modified time for this file. /// - internal DateTime lastModified; + private DateTime lastModified; /// /// The fusion name of this file. @@ -203,7 +203,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) /// Gets the last modified date. /// /// - public DateTime LastModified + internal DateTime LastModified { get { return lastModified; } set { lastModified = value; } @@ -213,7 +213,7 @@ public DateTime LastModified /// Get or set the assemblyName. /// /// - public AssemblyNameExtension Assembly + internal AssemblyNameExtension Assembly { get { return assemblyName; } set { assemblyName = value; } @@ -223,7 +223,7 @@ public AssemblyNameExtension Assembly /// Get or set the runtimeVersion /// /// - public string RuntimeVersion + internal string RuntimeVersion { get { return runtimeVersion; } set { runtimeVersion = value; } @@ -233,7 +233,7 @@ public string RuntimeVersion /// Get or set the framework name the file was built against /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Could be used in other assemblies")] - public FrameworkName FrameworkNameAttribute + internal FrameworkName FrameworkNameAttribute { get { return frameworkName; } set { frameworkName = value; } @@ -242,13 +242,13 @@ public FrameworkName FrameworkNameAttribute /// /// Get or set the ID of this assembly. Used to verify it is the same version. /// - public Guid ModuleVersionID { get; set; } + internal Guid ModuleVersionID { get; set; } } /// /// Construct. /// - public SystemState() + internal SystemState() { } @@ -572,12 +572,17 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta foreach (string stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not - SystemState sfBase = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); - foreach (string relativePath in sfBase.instanceLocalFileStateCache.Keys) + SystemState sysState = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); + if (sysState == null) { + continue; + } + foreach (DictionaryEntry kvp in sysState.instanceLocalFileStateCache) + { + string relativePath = (string)kvp.Key; if (!assembliesFound.Contains(relativePath)) { - FileState fileState = (FileState)sfBase.instanceLocalFileStateCache[relativePath]; + FileState fileState = (FileState)kvp.Value; // Verify that the assembly is correct Guid mvid; string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); @@ -608,18 +613,19 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta /// internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) { - Hashtable newInstanceLocalFileStateCache = new Hashtable(); - foreach (string path in instanceLocalFileStateCache.Keys) + Hashtable newInstanceLocalFileStateCache = new Hashtable(instanceLocalFileStateCache.Count); + foreach (DictionaryEntry kvp in instanceLocalFileStateCache) { // Add MVID to allow us to verify that we are using the same assembly later - FileState fileState = (FileState)instanceLocalFileStateCache[path]; - using (var reader = new PEReader(File.OpenRead(path))) + string absolutePath = (string)kvp.Key; + FileState fileState = (FileState)kvp.Value; + using (var reader = new PEReader(File.OpenRead(absolutePath))) { var metadataReader = reader.GetMetadataReader(); fileState.ModuleVersionID = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); } - string relativePath = FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), path); + string relativePath = FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), absolutePath); newInstanceLocalFileStateCache[relativePath] = fileState; } instanceLocalFileStateCache = newInstanceLocalFileStateCache; From ccb508da7ec32a57f3e49ee232ddca480fd73a95 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 8 Sep 2020 14:13:52 -0700 Subject: [PATCH 12/35] Make DeserializeCache take a generic StateFileBase --- src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs | 2 +- src/Tasks/ResGenDependencies.cs | 2 +- src/Tasks/ResolveComReference.cs | 2 +- src/Tasks/StateFileBase.cs | 8 ++++---- src/Tasks/SystemState.cs | 2 +- src/Tasks/UnregisterAssembly.cs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index a9eabeea1b3..f26cec62bd3 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1859,7 +1859,7 @@ private void LogConflict(Reference reference, string fusionName) /// private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { - _cache = (SystemState)StateFileBase.DeserializeCache(_stateFile, Log, typeof(SystemState)); + _cache = StateFileBase.DeserializeCache(_stateFile, Log); if (_cache == null) { diff --git a/src/Tasks/ResGenDependencies.cs b/src/Tasks/ResGenDependencies.cs index e174d3e8f0d..dc1351a855a 100644 --- a/src/Tasks/ResGenDependencies.cs +++ b/src/Tasks/ResGenDependencies.cs @@ -167,7 +167,7 @@ internal override void SerializeCache(string stateFile, TaskLoggingHelper log) /// internal static ResGenDependencies DeserializeCache(string stateFile, bool useSourcePath, TaskLoggingHelper log) { - var retVal = (ResGenDependencies)DeserializeCache(stateFile, log, typeof(ResGenDependencies)) ?? new ResGenDependencies(); + var retVal = DeserializeCache(stateFile, log); // Ensure that the cache is properly initialized with respect to how resgen will // resolve linked files within .resx files. ResGen has two different diff --git a/src/Tasks/ResolveComReference.cs b/src/Tasks/ResolveComReference.cs index 63fef7d822d..eb711365760 100644 --- a/src/Tasks/ResolveComReference.cs +++ b/src/Tasks/ResolveComReference.cs @@ -325,7 +325,7 @@ public override bool Execute() allProjectRefs = new List(); allDependencyRefs = new List(); - _timestampCache = (ResolveComReferenceCache)StateFileBase.DeserializeCache(StateFile, Log, typeof(ResolveComReferenceCache)); + _timestampCache = StateFileBase.DeserializeCache(StateFile, Log); if (_timestampCache == null || (_timestampCache != null && !_timestampCache.ToolPathsMatchCachePaths(_tlbimpPath, _aximpPath))) { diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index ec09003778a..af919800b7d 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -65,9 +65,9 @@ internal virtual void SerializeCache(string stateFile, TaskLoggingHelper log) /// /// Reads the specified file from disk into a StateFileBase derived object. /// - internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelper log, Type requiredReturnType, bool logWarnings = true) + internal static T DeserializeCache(string stateFile, TaskLoggingHelper log, bool logWarnings = true) where T : StateFileBase { - StateFileBase retVal = null; + T retVal = null; object deserializedObject = null; // First, we read the cache from disk if one exists, or if one does not exist @@ -80,7 +80,7 @@ internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelp { var formatter = new BinaryFormatter(); deserializedObject = formatter.Deserialize(s); - retVal = deserializedObject as StateFileBase; + retVal = deserializedObject as T; } // If the deserialized object is null then there would be no cast error but retVal would still be null // only log the message if there would have been a cast error @@ -91,7 +91,7 @@ internal static StateFileBase DeserializeCache(string stateFile, TaskLoggingHelp // If there is an invalid cast, a message rather than a warning should be emitted. log.LogMessageFromResources("General.CouldNotReadStateFileMessage", stateFile, log.FormatResourceString("General.IncompatibleStateFileType")); } - else if (retVal != null && (!requiredReturnType.IsInstanceOfType(retVal))) + else if (retVal != null && !(retVal is T)) { if (logWarnings) { diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 408442c2412..a2f7b6251b0 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -572,7 +572,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta foreach (string stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not - SystemState sysState = (SystemState)DeserializeCache(stateFile, log, requiredReturnType, false); + SystemState sysState = DeserializeCache(stateFile, log, false); if (sysState == null) { continue; diff --git a/src/Tasks/UnregisterAssembly.cs b/src/Tasks/UnregisterAssembly.cs index 928fb7aecce..dc67f9a9063 100644 --- a/src/Tasks/UnregisterAssembly.cs +++ b/src/Tasks/UnregisterAssembly.cs @@ -49,7 +49,7 @@ public override bool Execute() if (AssemblyListFile != null) { - cacheFile = (AssemblyRegistrationCache)StateFileBase.DeserializeCache(AssemblyListFile.ItemSpec, Log, typeof(AssemblyRegistrationCache)); + cacheFile = StateFileBase.DeserializeCache(AssemblyListFile.ItemSpec, Log); // no cache file, nothing to do. In case there was a problem reading the cache file, we can't do anything anyway. if (cacheFile == null) From 354de87d4348988be672611b780f71fd4ac64d07 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 8 Sep 2020 14:14:11 -0700 Subject: [PATCH 13/35] Use FileSystems.Default.FileExists --- src/Tasks/SystemState.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index a2f7b6251b0..00412d29a33 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -14,6 +14,7 @@ using System.Runtime.Versioning; using System.Security.Permissions; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyDependency; using Microsoft.Build.Utilities; @@ -586,7 +587,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta // Verify that the assembly is correct Guid mvid; string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); - if (File.Exists(fullPath)) + if (FileSystems.Default.FileExists(fullPath)) { using (var reader = new PEReader(File.OpenRead(fullPath))) { From fd76e0b73da602b5c1e0ba72cc652044d0bd02a0 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 8 Sep 2020 14:18:39 -0700 Subject: [PATCH 14/35] Convert instanceLocalFileStateCache to a dictionary --- src/Tasks/SystemState.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 00412d29a33..6691ac91dd5 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -35,7 +35,7 @@ internal sealed class SystemState : StateFileBase, ISerializable /// /// Cache at the SystemState instance level. It is serialized and reused between instances. /// - private Hashtable instanceLocalFileStateCache = new Hashtable(StringComparer.OrdinalIgnoreCase); + private Dictionary instanceLocalFileStateCache = new Dictionary(StringComparer.OrdinalIgnoreCase); /// @@ -260,7 +260,7 @@ internal SystemState(SerializationInfo info, StreamingContext context) { ErrorUtilities.VerifyThrowArgumentNull(info, "info"); - instanceLocalFileStateCache = (Hashtable)info.GetValue("fileState", typeof(Hashtable)); + instanceLocalFileStateCache = (Dictionary)info.GetValue("fileState", typeof(Dictionary)); isDirty = false; } @@ -578,12 +578,12 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta { continue; } - foreach (DictionaryEntry kvp in sysState.instanceLocalFileStateCache) + foreach (KeyValuePair kvp in sysState.instanceLocalFileStateCache) { - string relativePath = (string)kvp.Key; + string relativePath = kvp.Key; if (!assembliesFound.Contains(relativePath)) { - FileState fileState = (FileState)kvp.Value; + FileState fileState = kvp.Value; // Verify that the assembly is correct Guid mvid; string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); From b1a7695f1291079bbebe6420e67815eea3754bdc Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 22 Sep 2020 14:52:19 -0700 Subject: [PATCH 15/35] Fix generic --- src/Tasks/RegisterAssembly.cs | 3 +-- src/Tasks/SystemState.cs | 11 +++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Tasks/RegisterAssembly.cs b/src/Tasks/RegisterAssembly.cs index ecd34871a69..e68e23bbaf4 100644 --- a/src/Tasks/RegisterAssembly.cs +++ b/src/Tasks/RegisterAssembly.cs @@ -74,8 +74,7 @@ public override bool Execute() if ((AssemblyListFile != null) && (AssemblyListFile.ItemSpec.Length > 0)) { - cacheFile = (AssemblyRegistrationCache)StateFileBase.DeserializeCache(AssemblyListFile.ItemSpec, Log, typeof(AssemblyRegistrationCache)) ?? - new AssemblyRegistrationCache(); + cacheFile = StateFileBase.DeserializeCache(AssemblyListFile.ItemSpec, Log) ?? new AssemblyRegistrationCache(); } bool taskReturnValue = true; diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 6691ac91dd5..654bb1be94c 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -394,8 +394,7 @@ private FileState GetFileState(string path) private FileState ComputeFileStateFromCachesAndDisk(string path) { DateTime lastModified = GetAndCacheLastModified(path); - FileState cachedInstanceFileState = (FileState)instanceLocalFileStateCache[path]; - bool isCachedInInstance = cachedInstanceFileState != null; + bool isCachedInInstance = instanceLocalFileStateCache.TryGetValue(path, out FileState cachedInstanceFileState); bool isCachedInProcess = s_processWideFileStateCache.TryGetValue(path, out FileState cachedProcessFileState); @@ -614,12 +613,12 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta /// internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) { - Hashtable newInstanceLocalFileStateCache = new Hashtable(instanceLocalFileStateCache.Count); - foreach (DictionaryEntry kvp in instanceLocalFileStateCache) + Dictionary newInstanceLocalFileStateCache = new Dictionary(instanceLocalFileStateCache.Count); + foreach (KeyValuePair kvp in instanceLocalFileStateCache) { // Add MVID to allow us to verify that we are using the same assembly later - string absolutePath = (string)kvp.Key; - FileState fileState = (FileState)kvp.Value; + string absolutePath = kvp.Key; + FileState fileState = kvp.Value; using (var reader = new PEReader(File.OpenRead(absolutePath))) { var metadataReader = reader.GetMetadataReader(); From fbe102851c5dbda71ef6bf3663d61231a538ccd0 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 22 Sep 2020 15:28:09 -0700 Subject: [PATCH 16/35] Build --- .../net/Microsoft.Build.Tasks.Core.cs | 2 -- .../netstandard/Microsoft.Build.Tasks.Core.cs | 2 -- src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs | 6 +++--- src/Tasks/SystemState.cs | 9 +++++---- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index 0b6310c25cd..b500e0ea135 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -900,8 +900,6 @@ public ResolveAssemblyReference() { } public string AssemblyInformationCacheOutputPath { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] AssemblyInformationCachePaths { get { throw null; } set { } } public bool AutoUnify { get { throw null; } set { } } - public string[] CacheInputPaths { get { throw null; } set { } } - public string CacheOutputPath { get { throw null; } set { } } public string[] CandidateAssemblyFiles { get { throw null; } set { } } public bool CopyLocalDependenciesWhenParentReferenceInGac { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index 2d63521edbb..4d931ebd3ba 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -645,8 +645,6 @@ public ResolveAssemblyReference() { } public string AssemblyInformationCacheOutputPath { get { throw null; } set { } } public Microsoft.Build.Framework.ITaskItem[] AssemblyInformationCachePaths { get { throw null; } set { } } public bool AutoUnify { get { throw null; } set { } } - public string[] CacheInputPaths { get { throw null; } set { } } - public string CacheOutputPath { get { throw null; } set { } } public string[] CandidateAssemblyFiles { get { throw null; } set { } } public bool CopyLocalDependenciesWhenParentReferenceInGac { get { throw null; } set { } } [Microsoft.Build.Framework.OutputAttribute] diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 7ade206e3db..11c776b4ae6 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1863,7 +1863,7 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ if (_cache == null) { - _cache = SystemState.DeserializePrecomputedCaches(CacheInputPaths ?? Array.Empty(), Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); + _cache = SystemState.DeserializePrecomputedCaches(AssemblyInformationCachePaths ?? Array.Empty(), Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); } else { @@ -1877,9 +1877,9 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ /// private void WriteStateFile() { - if (!string.IsNullOrEmpty(CacheOutputPath)) + if (!string.IsNullOrEmpty(AssemblyInformationCacheOutputPath)) { - _cache.SerializePrecomputedCache(CacheOutputPath, Log); + _cache.SerializePrecomputedCache(AssemblyInformationCacheOutputPath, Log); } else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 0815e2a5449..a1e735bf8e8 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -13,6 +13,7 @@ using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyDependency; @@ -557,7 +558,7 @@ out fileState.frameworkName /// /// Reads in cached data from stateFiles to build an initial cache. Avoids logging warnings or errors. /// - internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { SystemState retVal = new SystemState(); retVal.SetGetLastWriteTime(getLastWriteTime); @@ -565,10 +566,10 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta retVal.isDirty = stateFiles.Length > 0; HashSet assembliesFound = new HashSet(); - foreach (string stateFile in stateFiles) + foreach (ITaskItem stateFile in stateFiles) { // Verify that it's a real stateFile; log message but do not error if not - SystemState sysState = DeserializeCache(stateFile, log, false); + SystemState sysState = DeserializeCache(stateFile.ToString(), log, false); if (sysState == null) { continue; @@ -581,7 +582,7 @@ internal static SystemState DeserializePrecomputedCaches(string[] stateFiles, Ta FileState fileState = kvp.Value; // Verify that the assembly is correct Guid mvid; - string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile), relativePath)); + string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile.ToString()), relativePath)); if (FileSystems.Default.FileExists(fullPath)) { using (var reader = new PEReader(File.OpenRead(fullPath))) From d790150b5b0d1a12a3f92894173540846a9e47b8 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Wed, 23 Sep 2020 10:22:33 -0700 Subject: [PATCH 17/35] Fix bad changes --- src/Tasks/ResGenDependencies.cs | 2 +- src/Tasks/StateFileBase.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Tasks/ResGenDependencies.cs b/src/Tasks/ResGenDependencies.cs index aaafd6ddf46..ccacacb77d5 100644 --- a/src/Tasks/ResGenDependencies.cs +++ b/src/Tasks/ResGenDependencies.cs @@ -167,7 +167,7 @@ internal override void SerializeCache(string stateFile, TaskLoggingHelper log) /// internal static ResGenDependencies DeserializeCache(string stateFile, bool useSourcePath, TaskLoggingHelper log) { - var retVal = DeserializeCache(stateFile, log); + var retVal = DeserializeCache(stateFile, log) ?? new ResGenDependencies(); // Ensure that the cache is properly initialized with respect to how resgen will // resolve linked files within .resx files. ResGen has two different diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index af919800b7d..8fc4ee04b40 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Utilities; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; -using System.Text.Json; namespace Microsoft.Build.Tasks { From a759006ef6b192171673a2bf9bc2253b25acc8a6 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Fri, 2 Oct 2020 15:41:39 -0700 Subject: [PATCH 18/35] Added tests 1 --- .../RARPrecomputedCache_Tests.cs | 69 +++++++++++++++++++ .../ResolveAssemblyReference.cs | 6 +- src/Tasks/SystemState.cs | 4 +- 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs new file mode 100644 index 00000000000..994e8e39c68 --- /dev/null +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Build.Framework; +using Microsoft.Build.UnitTests; +using Microsoft.Build.Utilities; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using Xunit; + +namespace Microsoft.Build.Tasks.UnitTests +{ + public class RARPrecomputedCache_Tests + { + [Fact] + public void TestPrecomputedCache() + { + using (TestEnvironment env = TestEnvironment.Create()) + { + ResolveAssemblyReference t = new ResolveAssemblyReference(); + t._cache = new SystemState(); + t._cache.instanceLocalFileStateCache = new Dictionary() { + { "assembly1", new SystemState.FileState(DateTime.Now) }, + { "assembly2", new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; + TransientTestFile standardCache = env.CreateFile(".cache"); + t.StateFile = standardCache.Path; + t.WriteStateFile(); + File.ReadAllText(standardCache.Path).Length.ShouldBeGreaterThan(0); + + TransientTestFile precomputedCache = env.CreateFile(".cache"); + } + + + t.Assemblies = new ITaskItem[] + { + new TaskItem("Regular"), + }; + + t.Assemblies[0].SetMetadata("HintPath", @"C:\SystemRuntime\Regular.dll"); + + + // build mode + t.FindDependencies = true; + Assert.True( + t.Execute + ( + fileExists, + directoryExists, + getDirectories, + getAssemblyName, + getAssemblyMetadata, +#if FEATURE_WIN32_REGISTRY + getRegistrySubKeyNames, + getRegistrySubKeyDefaultValue, +#endif + getLastWriteTime, + getRuntimeVersion, +#if FEATURE_WIN32_REGISTRY + openBaseKey, +#endif + checkIfAssemblyIsInGac, + isWinMDFile, + readMachineTypeFromPEHeader + ) + ); + } + } +} diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 11c776b4ae6..335fc98dfca 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 @@ -1857,7 +1857,7 @@ private void LogConflict(Reference reference, string fusionName) /// /// Reads the state file (if present) into the cache. If not present, attempts to read from CacheInputPaths, then creates a new cache if necessary. /// - private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { _cache = StateFileBase.DeserializeCache(_stateFile, Log); @@ -1875,7 +1875,7 @@ private void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[ /// /// If CacheOutputPath is non-null, writes out a cache to that location. Otherwise, writes out the state file if a state name was supplied and the cache is dirty. /// - private void WriteStateFile() + internal void WriteStateFile() { if (!string.IsNullOrEmpty(AssemblyInformationCacheOutputPath)) { diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index a1e735bf8e8..4968c9aa691 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -36,7 +36,7 @@ internal sealed class SystemState : StateFileBase, ISerializable /// /// 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 @@ -113,7 +113,7 @@ internal sealed class SystemState : StateFileBase, ISerializable /// Class that holds the current file state. /// [Serializable] - private sealed class FileState : ISerializable + internal sealed class FileState : ISerializable { /// /// The last modified time for this file. From 767f1ccbddd9c72dba9c0ed8959a3c3c2073bac4 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 6 Oct 2020 15:42:39 -0700 Subject: [PATCH 19/35] Finish tests ...although they don't work yet --- .../RARPrecomputedCache_Tests.cs | 84 +++++++++++-------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index 994e8e39c68..c7f5df71aff 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -14,7 +14,7 @@ namespace Microsoft.Build.Tasks.UnitTests public class RARPrecomputedCache_Tests { [Fact] - public void TestPrecomputedCache() + public void TestPrecomputedCacheOutput() { using (TestEnvironment env = TestEnvironment.Create()) { @@ -26,44 +26,62 @@ public void TestPrecomputedCache() TransientTestFile standardCache = env.CreateFile(".cache"); t.StateFile = standardCache.Path; t.WriteStateFile(); - File.ReadAllText(standardCache.Path).Length.ShouldBeGreaterThan(0); + int standardLen = File.ReadAllText(standardCache.Path).Length; + File.Delete(standardCache.Path); + standardLen.ShouldBeGreaterThan(0); - TransientTestFile precomputedCache = env.CreateFile(".cache"); + TransientTestFile precomputedCache = env.CreateFile(standardCache.Path + ".cache", string.Empty); + t.AssemblyInformationCacheOutputPath = precomputedCache.Path; + t.WriteStateFile(); + File.Exists(standardCache.Path).ShouldBeFalse(); + int preLen = File.ReadAllText(precomputedCache.Path).Length; + preLen.ShouldBeGreaterThan(0); + preLen.ShouldNotBe(standardLen); } + } + [Fact] + public void TestPreComputedCacheInputAndOutput() + { + using (TestEnvironment env = TestEnvironment.Create()) { + ResolveAssemblyReference t = new ResolveAssemblyReference(); + t._cache = new SystemState(); + t._cache.instanceLocalFileStateCache = new Dictionary() { + { "assembly1", new SystemState.FileState(DateTime.Now) }, + { "assembly2", new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; + TransientTestFile standardCache = env.CreateFile(".cache"); + t.StateFile = standardCache.Path; + t.WriteStateFile(); - t.Assemblies = new ITaskItem[] - { - new TaskItem("Regular"), - }; - - t.Assemblies[0].SetMetadata("HintPath", @"C:\SystemRuntime\Regular.dll"); + t._cache.instanceLocalFileStateCache.Add("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll", + new SystemState.FileState(DateTime.Now) { + Assembly = null, + RuntimeVersion = "v4.0.30319", + FrameworkNameAttribute = new System.Runtime.Versioning.FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), + scatterFiles = new string[] { "first", "second" } }); + TransientTestFile precomputedCache = env.CreateFile(standardCache.Path + ".cache", string.Empty); + t.AssemblyInformationCacheOutputPath = precomputedCache.Path; + t.WriteStateFile(); + ResolveAssemblyReference u = new ResolveAssemblyReference(); + u.StateFile = standardCache.Path; + u.AssemblyInformationCachePaths = new ITaskItem[] + { + new TaskItem(precomputedCache.Path) + }; - // build mode - t.FindDependencies = true; - Assert.True( - t.Execute - ( - fileExists, - directoryExists, - getDirectories, - getAssemblyName, - getAssemblyMetadata, -#if FEATURE_WIN32_REGISTRY - getRegistrySubKeyNames, - getRegistrySubKeyDefaultValue, -#endif - getLastWriteTime, - getRuntimeVersion, -#if FEATURE_WIN32_REGISTRY - openBaseKey, -#endif - checkIfAssemblyIsInGac, - isWinMDFile, - readMachineTypeFromPEHeader - ) - ); + u.ReadStateFile(File.GetLastWriteTime, Array.Empty()); + u._cache.instanceLocalFileStateCache.ShouldNotContainKey("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"); + File.Delete(standardCache.Path); + u.ReadStateFile(File.GetLastWriteTime, Array.Empty()); + u._cache.instanceLocalFileStateCache.ShouldContainKey("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"); + SystemState.FileState a3 = u._cache.instanceLocalFileStateCache["..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"]; + a3.Assembly.ShouldBeNull(); + a3.RuntimeVersion.ShouldBe("v4.0.30319"); + a3.FrameworkNameAttribute.Version.ShouldBe(Version.Parse("4.7.2")); + a3.scatterFiles.Length.ShouldBe(2); + a3.scatterFiles[1].ShouldBe("second"); + } } } } From 412c21b6339c2609f49f06ce7c8e5e6f1813940e Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 8 Oct 2020 16:54:57 -0700 Subject: [PATCH 20/35] Create delegates to mock system calls --- .../RARPrecomputedCache_Tests.cs | 61 ++++++++++++------- .../ResolveAssemblyReference.cs | 8 +-- src/Tasks/SystemState.cs | 43 +++++++------ 3 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index c7f5df71aff..0571ce4e7eb 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -13,28 +13,41 @@ namespace Microsoft.Build.Tasks.UnitTests { public class RARPrecomputedCache_Tests { + private Dictionary guidStore = new Dictionary(); + + private Guid calculateMvid(string path) + { + if (!guidStore.ContainsKey(path)) + { + guidStore.Add(path, Guid.NewGuid()); + } + return guidStore[path]; + } + [Fact] public void TestPrecomputedCacheOutput() { using (TestEnvironment env = TestEnvironment.Create()) { + TransientTestFile standardCache = env.CreateFile(".cache"); ResolveAssemblyReference t = new ResolveAssemblyReference(); t._cache = new SystemState(); t._cache.instanceLocalFileStateCache = new Dictionary() { - { "assembly1", new SystemState.FileState(DateTime.Now) }, - { "assembly2", new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; - TransientTestFile standardCache = env.CreateFile(".cache"); + { Path.Combine(standardCache.Path, "assembly1"), new SystemState.FileState(DateTime.Now) }, + { Path.Combine(standardCache.Path, "assembly2"), new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; + t._cache.IsDirty = true; t.StateFile = standardCache.Path; - t.WriteStateFile(); + t.WriteStateFile(calculateMvid); int standardLen = File.ReadAllText(standardCache.Path).Length; File.Delete(standardCache.Path); standardLen.ShouldBeGreaterThan(0); - TransientTestFile precomputedCache = env.CreateFile(standardCache.Path + ".cache", string.Empty); - t.AssemblyInformationCacheOutputPath = precomputedCache.Path; - t.WriteStateFile(); + string precomputedPath = standardCache.Path + ".cache"; + t._cache.IsDirty = true; + t.AssemblyInformationCacheOutputPath = precomputedPath; + t.WriteStateFile(calculateMvid); File.Exists(standardCache.Path).ShouldBeFalse(); - int preLen = File.ReadAllText(precomputedCache.Path).Length; + int preLen = File.ReadAllText(precomputedPath).Length; preLen.ShouldBeGreaterThan(0); preLen.ShouldNotBe(standardLen); } @@ -44,38 +57,42 @@ public void TestPrecomputedCacheOutput() public void TestPreComputedCacheInputAndOutput() { using (TestEnvironment env = TestEnvironment.Create()) { + TransientTestFile standardCache = env.CreateFile(".cache"); ResolveAssemblyReference t = new ResolveAssemblyReference(); t._cache = new SystemState(); t._cache.instanceLocalFileStateCache = new Dictionary() { - { "assembly1", new SystemState.FileState(DateTime.Now) }, - { "assembly2", new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; - TransientTestFile standardCache = env.CreateFile(".cache"); + { Path.Combine(standardCache.Path, "assembly1"), new SystemState.FileState(DateTime.Now) }, + { Path.Combine(standardCache.Path, "assembly2"), new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; t.StateFile = standardCache.Path; - t.WriteStateFile(); + t._cache.IsDirty = true; + t.WriteStateFile(calculateMvid); - t._cache.instanceLocalFileStateCache.Add("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll", + string dllName = Path.Combine(Path.GetDirectoryName(standardCache.Path), "randomFolder", "dll.dll"); + t._cache.instanceLocalFileStateCache.Add(dllName, new SystemState.FileState(DateTime.Now) { Assembly = null, RuntimeVersion = "v4.0.30319", FrameworkNameAttribute = new System.Runtime.Versioning.FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), scatterFiles = new string[] { "first", "second" } }); - TransientTestFile precomputedCache = env.CreateFile(standardCache.Path + ".cache", string.Empty); - t.AssemblyInformationCacheOutputPath = precomputedCache.Path; - t.WriteStateFile(); + string precomputedCachePath = standardCache.Path + ".cache"; + t.AssemblyInformationCacheOutputPath = precomputedCachePath; + t._cache.IsDirty = true; + t.WriteStateFile(calculateMvid); ResolveAssemblyReference u = new ResolveAssemblyReference(); u.StateFile = standardCache.Path; u.AssemblyInformationCachePaths = new ITaskItem[] { - new TaskItem(precomputedCache.Path) + new TaskItem(precomputedCachePath) }; - u.ReadStateFile(File.GetLastWriteTime, Array.Empty()); - u._cache.instanceLocalFileStateCache.ShouldNotContainKey("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"); + u.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); + u._cache.instanceLocalFileStateCache.ShouldNotContainKey(dllName); File.Delete(standardCache.Path); - u.ReadStateFile(File.GetLastWriteTime, Array.Empty()); - u._cache.instanceLocalFileStateCache.ShouldContainKey("..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"); - SystemState.FileState a3 = u._cache.instanceLocalFileStateCache["..\\.nuget\\packages\\system.text.encodings.web\\4.7.0\\lib\\netstandard2.0\\System.Text.Encodings.Web.dll"]; + u._cache = null; + u.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); + u._cache.instanceLocalFileStateCache.ShouldContainKey(dllName); + SystemState.FileState a3 = u._cache.instanceLocalFileStateCache[dllName]; a3.Assembly.ShouldBeNull(); a3.RuntimeVersion.ShouldBe("v4.0.30319"); a3.FrameworkNameAttribute.Version.ShouldBe(Version.Parse("4.7.2")); diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 335fc98dfca..54c0c3c0f86 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1857,13 +1857,13 @@ private void LogConflict(Reference reference, string fusionName) /// /// Reads the state file (if present) into the cache. If not present, attempts to read from CacheInputPaths, then creates a new cache if necessary. /// - internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo, Func calculateMvid = null, Func fileExists = null) { _cache = StateFileBase.DeserializeCache(_stateFile, Log); if (_cache == null) { - _cache = SystemState.DeserializePrecomputedCaches(AssemblyInformationCachePaths ?? Array.Empty(), Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo); + _cache = SystemState.DeserializePrecomputedCaches(AssemblyInformationCachePaths ?? Array.Empty(), Log, typeof(SystemState), getLastWriteTime, installedAssemblyTableInfo, calculateMvid, fileExists); } else { @@ -1875,11 +1875,11 @@ internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo /// /// If CacheOutputPath is non-null, writes out a cache to that location. Otherwise, writes out the state file if a state name was supplied and the cache is dirty. /// - internal void WriteStateFile() + internal void WriteStateFile(Func calculateMvid = null) { if (!string.IsNullOrEmpty(AssemblyInformationCacheOutputPath)) { - _cache.SerializePrecomputedCache(AssemblyInformationCacheOutputPath, Log); + _cache.SerializePrecomputedCache(AssemblyInformationCacheOutputPath, Log, calculateMvid); } else if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 4968c9aa691..aa8087eecfc 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; @@ -295,6 +294,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) internal bool IsDirty { get { return isDirty; } + set { isDirty = value; } } /// @@ -558,13 +558,15 @@ out fileState.frameworkName /// /// Reads in cached data from stateFiles to build an initial cache. Avoids logging warnings or errors. /// - internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, TaskLoggingHelper log, Type requiredReturnType, GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo, Func calculateMvid, Func fileExists) { SystemState retVal = new SystemState(); retVal.SetGetLastWriteTime(getLastWriteTime); retVal.SetInstalledAssemblyInformation(installedAssemblyTableInfo); retVal.isDirty = stateFiles.Length > 0; HashSet assembliesFound = new HashSet(); + calculateMvid ??= CalculateMvid; + fileExists ??= FileSystems.Default.FileExists; foreach (ITaskItem stateFile in stateFiles) { @@ -581,22 +583,13 @@ internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, { FileState fileState = kvp.Value; // Verify that the assembly is correct - Guid mvid; string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile.ToString()), relativePath)); - if (FileSystems.Default.FileExists(fullPath)) + if (fileExists(fullPath) && calculateMvid(fullPath).Equals(fileState.ModuleVersionID)) { - using (var reader = new PEReader(File.OpenRead(fullPath))) - { - var metadataReader = reader.GetMetadataReader(); - mvid = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); - } - if (mvid.Equals(fileState.ModuleVersionID)) - { - // Correct file path and timestamp - fileState.LastModified = retVal.getLastWriteTime(fullPath); - retVal.instanceLocalFileStateCache[fullPath] = fileState; - assembliesFound.Add(relativePath); - } + // Correct file path and timestamp + fileState.LastModified = retVal.getLastWriteTime(fullPath); + retVal.instanceLocalFileStateCache[fullPath] = fileState; + assembliesFound.Add(relativePath); } } } @@ -608,19 +601,16 @@ internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, /// /// Modifies this object to be more portable across machines, then writes it to stateFile. /// - internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) + internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log, Func calculateMvid) { Dictionary newInstanceLocalFileStateCache = new Dictionary(instanceLocalFileStateCache.Count); + calculateMvid ??= CalculateMvid; foreach (KeyValuePair kvp in instanceLocalFileStateCache) { // Add MVID to allow us to verify that we are using the same assembly later string absolutePath = kvp.Key; FileState fileState = kvp.Value; - using (var reader = new PEReader(File.OpenRead(absolutePath))) - { - var metadataReader = reader.GetMetadataReader(); - fileState.ModuleVersionID = metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); - } + fileState.ModuleVersionID = calculateMvid(absolutePath); string relativePath = FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), absolutePath); newInstanceLocalFileStateCache[relativePath] = fileState; @@ -634,6 +624,15 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log) SerializeCache(stateFile, log); } + private static Guid CalculateMvid(string path) + { + using (var reader = new PEReader(File.OpenRead(path))) + { + var metadataReader = reader.GetMetadataReader(); + return metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); + } + } + /// /// Cached implementation of GetDirectories. /// From 9f930f438685f685b022ef2cbd3a9486a3dc7748 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 8 Oct 2020 17:21:30 -0700 Subject: [PATCH 21/35] Small fixups --- src/Tasks/SystemState.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 3746c5fe606..7d5dda5f695 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -310,7 +310,7 @@ private T[] ParseArray(ref Utf8JsonReader reader, JsonConverter converter) public override void Write(Utf8JsonWriter writer, SystemState stateFile, JsonSerializerOptions options) { - Hashtable cache = stateFile.instanceLocalFileStateCache; + Dictionary cache = stateFile.instanceLocalFileStateCache; writer.WriteStartObject(); JsonSerializerOptions aneOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; AssemblyNameExtension.Converter converter = new AssemblyNameExtension.Converter(); @@ -699,7 +699,7 @@ internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, // Verify that it's a real stateFile; log message but do not error if not var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; deserializeOptions.Converters.Add(new SystemState.Converter()); - SystemState sysBase = JsonSerializer.Deserialize(File.ReadAllText(stateFile), deserializeOptions); + SystemState sysBase = JsonSerializer.Deserialize(File.ReadAllText(stateFile.ToString()), deserializeOptions); if (sysBase == null) { continue; From 58217f657497623ad60db5c1a62c4df2ea53a084 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 8 Oct 2020 17:25:20 -0700 Subject: [PATCH 22/35] Make constructor internal --- src/Tasks/SystemState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 7d5dda5f695..b6d2992f4e1 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -395,7 +395,7 @@ private T[] ParseArray(ref Utf8JsonReader reader, Func converter) /// /// Construct. /// - public SystemState() + internal SystemState() { } From 2043c3f5bc02a1514db81c892e9a55d2f75f9598 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Wed, 11 Nov 2020 18:19:27 -0800 Subject: [PATCH 23/35] PR feedback --- .../RARPrecomputedCache_Tests.cs | 63 +++++++++++-------- src/Tasks/Resources/Strings.resx | 2 +- src/Tasks/Resources/xlf/Strings.cs.xlf | 4 +- src/Tasks/Resources/xlf/Strings.de.xlf | 4 +- src/Tasks/Resources/xlf/Strings.en.xlf | 4 +- src/Tasks/Resources/xlf/Strings.es.xlf | 4 +- src/Tasks/Resources/xlf/Strings.fr.xlf | 4 +- src/Tasks/Resources/xlf/Strings.it.xlf | 4 +- src/Tasks/Resources/xlf/Strings.ja.xlf | 4 +- src/Tasks/Resources/xlf/Strings.ko.xlf | 4 +- src/Tasks/Resources/xlf/Strings.pl.xlf | 4 +- src/Tasks/Resources/xlf/Strings.pt-BR.xlf | 4 +- src/Tasks/Resources/xlf/Strings.ru.xlf | 4 +- src/Tasks/Resources/xlf/Strings.tr.xlf | 4 +- src/Tasks/Resources/xlf/Strings.zh-Hans.xlf | 4 +- src/Tasks/Resources/xlf/Strings.zh-Hant.xlf | 4 +- 16 files changed, 66 insertions(+), 55 deletions(-) diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index 0571ce4e7eb..8bb097dfc2b 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -30,8 +30,10 @@ public void TestPrecomputedCacheOutput() using (TestEnvironment env = TestEnvironment.Create()) { TransientTestFile standardCache = env.CreateFile(".cache"); - ResolveAssemblyReference t = new ResolveAssemblyReference(); - t._cache = new SystemState(); + ResolveAssemblyReference t = new() + { + _cache = new SystemState() + }; t._cache.instanceLocalFileStateCache = new Dictionary() { { Path.Combine(standardCache.Path, "assembly1"), new SystemState.FileState(DateTime.Now) }, { Path.Combine(standardCache.Path, "assembly2"), new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; @@ -58,46 +60,55 @@ public void TestPreComputedCacheInputAndOutput() { using (TestEnvironment env = TestEnvironment.Create()) { TransientTestFile standardCache = env.CreateFile(".cache"); - ResolveAssemblyReference t = new ResolveAssemblyReference(); - t._cache = new SystemState(); - t._cache.instanceLocalFileStateCache = new Dictionary() { + ResolveAssemblyReference rarWriterTask = new() + { + _cache = new SystemState() + }; + rarWriterTask._cache.instanceLocalFileStateCache = new Dictionary() { { Path.Combine(standardCache.Path, "assembly1"), new SystemState.FileState(DateTime.Now) }, { Path.Combine(standardCache.Path, "assembly2"), new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } } }; - t.StateFile = standardCache.Path; - t._cache.IsDirty = true; - t.WriteStateFile(calculateMvid); + rarWriterTask.StateFile = standardCache.Path; + rarWriterTask._cache.IsDirty = true; + rarWriterTask.WriteStateFile(calculateMvid); string dllName = Path.Combine(Path.GetDirectoryName(standardCache.Path), "randomFolder", "dll.dll"); - t._cache.instanceLocalFileStateCache.Add(dllName, + rarWriterTask._cache.instanceLocalFileStateCache.Add(dllName, new SystemState.FileState(DateTime.Now) { Assembly = null, RuntimeVersion = "v4.0.30319", FrameworkNameAttribute = new System.Runtime.Versioning.FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), scatterFiles = new string[] { "first", "second" } }); string precomputedCachePath = standardCache.Path + ".cache"; - t.AssemblyInformationCacheOutputPath = precomputedCachePath; - t._cache.IsDirty = true; - t.WriteStateFile(calculateMvid); + rarWriterTask.AssemblyInformationCacheOutputPath = precomputedCachePath; + rarWriterTask._cache.IsDirty = true; + rarWriterTask.WriteStateFile(calculateMvid); - ResolveAssemblyReference u = new ResolveAssemblyReference(); - u.StateFile = standardCache.Path; - u.AssemblyInformationCachePaths = new ITaskItem[] + ResolveAssemblyReference rarReaderTask = new(); + rarReaderTask.StateFile = standardCache.Path; + rarReaderTask.AssemblyInformationCachePaths = new ITaskItem[] { new TaskItem(precomputedCachePath) }; - u.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); - u._cache.instanceLocalFileStateCache.ShouldNotContainKey(dllName); + // At this point, we should have created two cache files: one "normal" one and one "precomputed" one. + // When we read the state file the first time, it should read from the caches produced in a normal + // build, partially because we can read it faster. If that cache does not exist, as with the second + // time we try to read the state file, it defaults to reading the "precomputed" cache. In this case, + // the normal cache does not have dll.dll, whereas the precomputed cache does, so it should not be + // present when we read the first time but should be present the second time. Then we verify that the + // information contained in that cache matches what we'd expect. + rarReaderTask.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); + rarReaderTask._cache.instanceLocalFileStateCache.ShouldNotContainKey(dllName); File.Delete(standardCache.Path); - u._cache = null; - u.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); - u._cache.instanceLocalFileStateCache.ShouldContainKey(dllName); - SystemState.FileState a3 = u._cache.instanceLocalFileStateCache[dllName]; - a3.Assembly.ShouldBeNull(); - a3.RuntimeVersion.ShouldBe("v4.0.30319"); - a3.FrameworkNameAttribute.Version.ShouldBe(Version.Parse("4.7.2")); - a3.scatterFiles.Length.ShouldBe(2); - a3.scatterFiles[1].ShouldBe("second"); + rarReaderTask._cache = null; + rarReaderTask.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); + rarReaderTask._cache.instanceLocalFileStateCache.ShouldContainKey(dllName); + SystemState.FileState assembly3 = rarReaderTask._cache.instanceLocalFileStateCache[dllName]; + assembly3.Assembly.ShouldBeNull(); + assembly3.RuntimeVersion.ShouldBe("v4.0.30319"); + assembly3.FrameworkNameAttribute.Version.ShouldBe(Version.Parse("4.7.2")); + assembly3.scatterFiles.Length.ShouldBe(2); + assembly3.scatterFiles[1].ShouldBe("second"); } } } diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index db6c18091f7..d079c772cb3 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -452,7 +452,7 @@ {StrBegin="MSB3101: "} - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf index 563837c2983..acae99833b1 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf index 715c0733ef5..cf703d785a1 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index 44c7f33761d..7700a97fbd5 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -555,8 +555,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index 5f4cc6b1206..c59768d651e 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index d2f869f2deb..c52aed16889 100644 --- a/src/Tasks/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Resources/xlf/Strings.fr.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 543de47e98a..5c300566f6c 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index 83d9e27593c..481fd366f6a 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index 68a8a6e3a7e..db36dd5b3de 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index ac00b1d86de..5807c9d503c 100644 --- a/src/Tasks/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Resources/xlf/Strings.pl.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf index a5bfa091b68..9df4a84c942 100644 --- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index ab898336b0b..e550e9578d2 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index aa243572d65..a8c78d9a603 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf index af18c448974..6d064790bbe 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index 84e099a8dd6..98a63bb5593 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -510,8 +510,8 @@ - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. - MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running RAR normally, do not set the "CacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. + MSB3667: There is already a file at "{0}". If you are trying to create a precomputed cache, ensure that you are building a single project that depends on your assemblies rather than building your assemblies themselves. If you are running the ResolveAssemblyReference task normally, do not set the "AssemblyInformationCacheOutputPath" parameter of the ResolveAssemblyReference task. {StrBegin="MSB3667: "} From 853fd38f0c14b0fcfeb7e760d5d7e502fad31cb4 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 12 Nov 2020 07:57:42 -0800 Subject: [PATCH 24/35] Avoid using overly new feature --- src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index 8bb097dfc2b..e2e917771ca 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -30,7 +30,7 @@ public void TestPrecomputedCacheOutput() using (TestEnvironment env = TestEnvironment.Create()) { TransientTestFile standardCache = env.CreateFile(".cache"); - ResolveAssemblyReference t = new() + ResolveAssemblyReference t = new ResolveAssemblyReference() { _cache = new SystemState() }; @@ -60,7 +60,7 @@ public void TestPreComputedCacheInputAndOutput() { using (TestEnvironment env = TestEnvironment.Create()) { TransientTestFile standardCache = env.CreateFile(".cache"); - ResolveAssemblyReference rarWriterTask = new() + ResolveAssemblyReference rarWriterTask = new ResolveAssemblyReference() { _cache = new SystemState() }; @@ -83,7 +83,7 @@ public void TestPreComputedCacheInputAndOutput() rarWriterTask._cache.IsDirty = true; rarWriterTask.WriteStateFile(calculateMvid); - ResolveAssemblyReference rarReaderTask = new(); + ResolveAssemblyReference rarReaderTask = new ResolveAssemblyReference(); rarReaderTask.StateFile = standardCache.Path; rarReaderTask.AssemblyInformationCachePaths = new ITaskItem[] { From bda6b3720ee66f0012d2dd05da8b3c7fc3aea69e Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 12 Nov 2020 08:17:56 -0800 Subject: [PATCH 25/35] Real ANE --- src/Shared/AssemblyNameExtension.cs | 5 +++++ src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index 34266b18e39..f9b9740a16a 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -321,6 +321,11 @@ internal Version Version CreateAssemblyName(); return asAssemblyName.Version; } + set + { + CreateAssemblyName(); + asAssemblyName.Version = value; + } } /// diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index 0571ce4e7eb..e2f8b86a9d4 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -70,14 +70,17 @@ public void TestPreComputedCacheInputAndOutput() string dllName = Path.Combine(Path.GetDirectoryName(standardCache.Path), "randomFolder", "dll.dll"); t._cache.instanceLocalFileStateCache.Add(dllName, new SystemState.FileState(DateTime.Now) { - Assembly = null, + Assembly = new Shared.AssemblyNameExtension("notDll.dll", false), RuntimeVersion = "v4.0.30319", FrameworkNameAttribute = new System.Runtime.Versioning.FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), scatterFiles = new string[] { "first", "second" } }); + t._cache.instanceLocalFileStateCache[dllName].Assembly.Version = new Version("16.3"); string precomputedCachePath = standardCache.Path + ".cache"; t.AssemblyInformationCacheOutputPath = precomputedCachePath; t._cache.IsDirty = true; t.WriteStateFile(calculateMvid); + // The cache is already written; this change should do nothing. + t._cache.instanceLocalFileStateCache[dllName].Assembly = null; ResolveAssemblyReference u = new ResolveAssemblyReference(); u.StateFile = standardCache.Path; @@ -93,7 +96,8 @@ public void TestPreComputedCacheInputAndOutput() u.ReadStateFile(File.GetLastWriteTime, Array.Empty(), calculateMvid, p => true); u._cache.instanceLocalFileStateCache.ShouldContainKey(dllName); SystemState.FileState a3 = u._cache.instanceLocalFileStateCache[dllName]; - a3.Assembly.ShouldBeNull(); + a3.Assembly.FullName.ShouldBe("notDll.dll"); + a3.Assembly.Version.Major.ShouldBe(16); a3.RuntimeVersion.ShouldBe("v4.0.30319"); a3.FrameworkNameAttribute.Version.ShouldBe(Version.Parse("4.7.2")); a3.scatterFiles.Length.ShouldBe(2); From 37a758301d9b1e36541be8d353bf50b2c1548448 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Thu, 12 Nov 2020 09:10:49 -0800 Subject: [PATCH 26/35] fixed test --- src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs | 2 +- src/Tasks/SystemState.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs index eca0cbf9936..a7dfafbdc1c 100644 --- a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -78,7 +78,7 @@ public void TestPreComputedCacheInputAndOutput() RuntimeVersion = "v4.0.30319", FrameworkNameAttribute = new System.Runtime.Versioning.FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), scatterFiles = new string[] { "first", "second" } }); - t._cache.instanceLocalFileStateCache[dllName].Assembly.Version = new Version("16.3"); + rarWriterTask._cache.instanceLocalFileStateCache[dllName].Assembly.Version = new Version("16.3"); string precomputedCachePath = standardCache.Path + ".cache"; rarWriterTask.AssemblyInformationCacheOutputPath = precomputedCachePath; rarWriterTask._cache.IsDirty = true; diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index b6d2992f4e1..50326b9843f 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -732,6 +732,7 @@ internal static SystemState DeserializePrecomputedCaches(ITaskItem[] stateFiles, /// internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log, Func calculateMvid) { + Dictionary oldInstanceLocalFileStateCache = instanceLocalFileStateCache; Dictionary newInstanceLocalFileStateCache = new Dictionary(instanceLocalFileStateCache.Count); calculateMvid ??= CalculateMvid; foreach (KeyValuePair kvp in instanceLocalFileStateCache) @@ -753,6 +754,7 @@ internal void SerializePrecomputedCache(string stateFile, TaskLoggingHelper log, JsonSerializerOptions options = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; options.Converters.Add(new SystemState.Converter()); File.WriteAllText(stateFile, JsonSerializer.Serialize(this, options)); + instanceLocalFileStateCache = oldInstanceLocalFileStateCache; } private static Guid CalculateMvid(string path) From a5577222b07804e02046e174a4ed5c860e5d3b6b Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Mon, 23 Nov 2020 11:57:58 -0800 Subject: [PATCH 27/35] Update versions The version of Arcade MSBuild currently uses uses Roslyn version 3.3.1. This updates that, but it should be removed when we've updated to a more modern version of arcade. --- eng/Packages.props | 2 +- eng/Versions.props | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/eng/Packages.props b/eng/Packages.props index 4fe2fb90fc4..088b4192263 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -1,7 +1,7 @@ - 5.7.0-rtm.6710 + 5.9.0-preview.1.6870 $(NuGetPackageVersion) $(NuGetPackageVersion) $(NuGetPackageVersion) diff --git a/eng/Versions.props b/eng/Versions.props index 4fb6d230d23..d504494922e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -37,6 +37,7 @@ 3.1.100 + 3.8.0 - @@ -1006,6 +1005,7 @@ + diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index 8763208d60c..b1cd6a3b474 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -1367,6 +1367,9 @@ If this bucket overflows, pls. contact 'vsppbdev'. --> + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + AssemblyFoldersEx location: "{0}" diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf index 87738ba6838..fc9911becaf 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -1557,6 +1557,11 @@ Umístění AssemblyFoldersEx: {0} + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Byla uvažována umístění AssemblyFoldersEx. diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf index 26da37dc629..5b21d56fadc 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -1557,6 +1557,11 @@ Speicherort von AssemblyFoldersEx: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Berücksichtigte Speicherorte von AssemblyFoldersEx. diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index 7283348b631..39e5ebd2364 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -1602,6 +1602,11 @@ AssemblyFoldersEx location: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Considered AssemblyFoldersEx locations. diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index f25cd317d62..ab1b3cbe0be 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -1557,6 +1557,11 @@ Ubicación de AssemblyFoldersEx: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Ubicaciones de AssemblyFoldersEx consideradas. diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index d237c187790..fb43bf3b1b7 100644 --- a/src/Tasks/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Resources/xlf/Strings.fr.xlf @@ -1557,6 +1557,11 @@ Emplacement d'AssemblyFoldersEx : "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Emplacements d'AssemblyFoldersEx envisagés. diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 627586b4659..415e3d3243a 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -1557,6 +1557,11 @@ Percorso AssemblyFoldersEx: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Percorsi AssemblyFoldersEx considerati. diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index adaf0cf558e..193f334d8ac 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -1557,6 +1557,11 @@ AssemblyFoldersEx の場所:"{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. AssemblyFoldersEx の場所が考慮されました。 diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index 6cb5fb6cdb6..be02ec3a346 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -1557,6 +1557,11 @@ AssemblyFoldersEx 위치: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. AssemblyFoldersEx 위치로 간주했습니다. diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index a7e374cb242..bf9f62de208 100644 --- a/src/Tasks/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Resources/xlf/Strings.pl.xlf @@ -1557,6 +1557,11 @@ Lokalizacja klucza rejestru AssemblyFoldersEx: „{0}” + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Wybrano lokalizacje klucza rejestru AssemblyFoldersEx. diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf index a7ae22a85b0..598234c22a2 100644 --- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf @@ -1557,6 +1557,11 @@ Localização de AssemblyFoldersEx: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Localizações de AssemblyFoldersEx consideradas. diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index 9f4541597f8..94793589fa7 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -1557,6 +1557,11 @@ Расположение AssemblyFoldersEx: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. Рассмотрены расположения AssemblyFoldersEx. diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index 2dc326bcd54..f892dd71075 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -1557,6 +1557,11 @@ AssemblyFoldersEx konumu: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. AssemblyFoldersEx konumları dikkate alındı. diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf index c1836ec8099..27bc7c3b15a 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -1557,6 +1557,11 @@ AssemblyFoldersEx 位置:“{0}” + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. 已考虑 AssemblyFoldersEx 位置。 diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index 23e53494450..5ae9d8c0e7a 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -1557,6 +1557,11 @@ AssemblyFoldersEx 位置: "{0}" + + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + Cache deserialization failed, possibly because the user's caches are serialized with a different serialization technology than the MSBuild currently in use uses. + + Considered AssemblyFoldersEx locations. 已考慮 AssemblyFoldersEx 位置。 diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 85c169d4681..75cc8c1cf2d 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -7,19 +7,12 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; -using System.Security.Permissions; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyDependency; -using Microsoft.Build.Utilities; namespace Microsoft.Build.Tasks { From 08aede62329e0f399961c90b8ae964645cac1245 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Tue, 22 Dec 2020 10:32:40 -0800 Subject: [PATCH 32/35] Switch to async --- .../AssemblyDependency/ResolveAssemblyReference.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 8b655a91da4..28d7facb991 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1859,13 +1859,16 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l /// /// Reads the state file (if present) into the cache. If not present, attempts to read from CacheInputPaths, then creates a new cache if necessary. /// - internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal async void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) { var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; deserializeOptions.Converters.Add(new SystemState.Converter()); try { - _cache = JsonSerializer.Deserialize(File.ReadAllText(_stateFile), deserializeOptions); + using (FileStream s = new FileStream(_stateFile, FileMode.Open)) + { + _cache = await JsonSerializer.DeserializeAsync(s, deserializeOptions); + } } catch (Exception) { @@ -1881,13 +1884,16 @@ internal void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo /// /// If CacheOutputPath is non-null, writes out a cache to that location. Otherwise, writes out the state file if a state name was supplied and the cache is dirty. /// - internal void WriteStateFile() + internal async void WriteStateFile() { if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; deserializeOptions.Converters.Add(new SystemState.Converter()); - File.WriteAllText(_stateFile, JsonSerializer.Serialize(_cache, deserializeOptions)); + using (FileStream fs = new FileStream(_stateFile, FileMode.OpenOrCreate)) + { + await JsonSerializer.SerializeAsync(fs, _cache, deserializeOptions); + } } } #endregion From 01a3b56058ea6781ad3f9e2d65ae62a1419cf954 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Wed, 23 Dec 2020 18:27:24 -0800 Subject: [PATCH 33/35] Make tests pass --- .../ResolveAssemblyReferenceTestFixture.cs | 7 +++++-- src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs index 373805aa30f..18fc376129b 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs @@ -3089,8 +3089,6 @@ protected static bool Execute(ResolveAssemblyReference t, bool buildConsistencyC Assert.Equal(cache, t.FilesWritten[0].ItemSpec); } - File.Delete(t.StateFile); - // Check attributes on resolve files. for (int i = 0; i < t.ResolvedFiles.Length; i++) { @@ -3113,6 +3111,11 @@ protected static bool Execute(ResolveAssemblyReference t, bool buildConsistencyC { FileUtilities.DeleteNoThrow(rarCacheFile); } + + if (File.Exists(t.StateFile)) + { + FileUtilities.DeleteNoThrow(t.StateFile); + } } return succeeded; } diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 28d7facb991..63c635bf816 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1859,7 +1859,7 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l /// /// Reads the state file (if present) into the cache. If not present, attempts to read from CacheInputPaths, then creates a new cache if necessary. /// - internal async void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTableInfo[] installedAssemblyTableInfo) + internal async void ReadStateFile() { var deserializeOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; deserializeOptions.Converters.Add(new SystemState.Converter()); @@ -1877,8 +1877,6 @@ internal async void ReadStateFile(GetLastWriteTime getLastWriteTime, AssemblyTab } _cache ??= new SystemState(); - _cache.SetGetLastWriteTime(getLastWriteTime); - _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); } /// @@ -2123,7 +2121,9 @@ ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader } // Load any prior saved state. - ReadStateFile(getLastWriteTime, installedAssemblyTableInfo); + ReadStateFile(); + _cache.SetGetLastWriteTime(getLastWriteTime); + _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); // Cache delegates. getAssemblyName = _cache.CacheDelegate(getAssemblyName); From 63f4b001ffa87020b833af3af6b4bc6a56044e9e Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Fri, 8 Jan 2021 07:41:08 -0800 Subject: [PATCH 34/35] Use frameworkName --- src/Tasks/SystemState.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 75cc8c1cf2d..e87ce6dc7f0 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -244,7 +244,7 @@ private FileState ParseFileState(ref Utf8JsonReader reader) { if (reader.TokenType == JsonTokenType.EndObject) { - state.FrameworkNameAttribute = new FrameworkName(identifier, Version.Parse(version), profile); + state.frameworkName = new FrameworkName(identifier, Version.Parse(version), profile); break; } switch (reader.GetString()) @@ -343,13 +343,13 @@ public override void Write(Utf8JsonWriter writer, SystemState stateFile, JsonSer converter.Write(writer, fileInfo.Assembly, aneOptions); } writer.WriteString(nameof(fileInfo.RuntimeVersion), fileInfo.RuntimeVersion); - if (fileInfo.FrameworkNameAttribute != null) + if (fileInfo.frameworkName != null) { - writer.WritePropertyName(nameof(fileInfo.FrameworkNameAttribute)); + writer.WritePropertyName(nameof(fileInfo.frameworkName)); writer.WriteStartObject(); - writer.WriteString("Version", fileInfo.FrameworkNameAttribute.Version.ToString()); - writer.WriteString("Identifier", fileInfo.FrameworkNameAttribute.Identifier); - writer.WriteString("Profile", fileInfo.FrameworkNameAttribute.Profile); + writer.WriteString("Version", fileInfo.frameworkName.Version.ToString()); + writer.WriteString("Identifier", fileInfo.frameworkName.Identifier); + writer.WriteString("Profile", fileInfo.frameworkName.Profile); writer.WriteEndObject(); } writer.WriteEndObject(); @@ -662,7 +662,7 @@ out fileState.frameworkName dependencies = fileState.dependencies; scatterFiles = fileState.scatterFiles; - frameworkName = fileState.FrameworkNameAttribute; + frameworkName = fileState.frameworkName; } /// From d50dbb4aba8fc0e632e95a4a2403d6381ed79ec5 Mon Sep 17 00:00:00 2001 From: Nathan Mytelka Date: Fri, 8 Jan 2021 08:12:55 -0800 Subject: [PATCH 35/35] Add comment --- src/Shared/AssemblyNameExtension.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index f9b9740a16a..8ae7c03f8f6 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -1031,6 +1031,12 @@ public override AssemblyNameExtension Read(ref Utf8JsonReader reader, Type typeT } string parameter = reader.GetString(); reader.Read(); + // This reader is set up such that each component has its own token type. If we encounter + // the null token just after reader a PropertyName, we know that property is null, and we + // don't need to assign to it. If it is not null, it will be read into the appropriate + // parameter below unless the propertyName specifies that this is "asAssemblyName" or + // "remappedFrom", in which case the value of the property will start with a StartObject + // or StartArray token respectively. We can safely skip over those. if (reader.TokenType == JsonTokenType.Null) { continue;