diff --git a/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs new file mode 100644 index 00000000000..11c0a395500 --- /dev/null +++ b/src/Tasks.UnitTests/RARPrecomputedCache_Tests.cs @@ -0,0 +1,142 @@ +// 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 TestPrecomputedCacheOutput() + { + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFile standardCache = env.CreateFile(".cache"); + ResolveAssemblyReference t = new ResolveAssemblyReference() + { + _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") } } }; + t._cache.IsDirty = true; + t.StateFile = standardCache.Path; + t.WriteStateFile(); + int standardLen = File.ReadAllText(standardCache.Path).Length; + File.Delete(standardCache.Path); + standardLen.ShouldBeGreaterThan(0); + + string precomputedPath = standardCache.Path + ".cache"; + t._cache.IsDirty = true; + t.AssemblyInformationCacheOutputPath = precomputedPath; + t.WriteStateFile(); + File.Exists(standardCache.Path).ShouldBeFalse(); + int preLen = File.ReadAllText(precomputedPath).Length; + preLen.ShouldBeGreaterThan(0); + preLen.ShouldNotBe(standardLen); + } + } + + [Fact] + public void StandardCacheTakesPrecedence() + { + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFile standardCache = env.CreateFile(".cache"); + ResolveAssemblyReference rarWriterTask = new ResolveAssemblyReference() + { + _cache = new SystemState() + }; + rarWriterTask._cache.instanceLocalFileStateCache = new Dictionary(); + rarWriterTask.StateFile = standardCache.Path; + rarWriterTask._cache.IsDirty = true; + // Write standard cache + rarWriterTask.WriteStateFile(); + + string dllName = Path.Combine(Path.GetDirectoryName(standardCache.Path), "randomFolder", "dll.dll"); + 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"; + rarWriterTask.AssemblyInformationCacheOutputPath = precomputedCachePath; + rarWriterTask._cache.IsDirty = true; + // Write precomputed cache + rarWriterTask.WriteStateFile(); + + ResolveAssemblyReference rarReaderTask = new ResolveAssemblyReference(); + rarReaderTask.StateFile = standardCache.Path; + rarReaderTask.AssemblyInformationCachePaths = new ITaskItem[] + { + new TaskItem(precomputedCachePath) + }; + + // At this point, we should have created two cache files: one "normal" one and one "precomputed" one. + // When we read the state file, it should read from the caches produced in a normal build. 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 it. + rarReaderTask.ReadStateFile(p => true); + rarReaderTask._cache.instanceLocalFileStateCache.ShouldNotContainKey(dllName); + } + } + + [Fact] + public void TestPreComputedCacheInputMatchesOutput() + { + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFile precomputedCache = env.CreateFile(".cache"); + ResolveAssemblyReference rarWriterTask = new ResolveAssemblyReference() + { + _cache = new SystemState() + }; + string dllName = Path.Combine(Path.GetDirectoryName(precomputedCache.Path), "randomFolder", "dll.dll"); + rarWriterTask._cache.instanceLocalFileStateCache = new Dictionary() { + { Path.Combine(precomputedCache.Path, "..", "assembly1", "assembly1"), new SystemState.FileState(DateTime.Now) }, + { Path.Combine(precomputedCache.Path, "assembly2"), new SystemState.FileState(DateTime.Now) { Assembly = new Shared.AssemblyNameExtension("hi") } }, + { 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" } } } }; + + rarWriterTask.AssemblyInformationCacheOutputPath = precomputedCache.Path; + rarWriterTask._cache.IsDirty = true; + + // Throws an exception because precomputedCache.Path already exists. + Should.Throw(() => rarWriterTask.WriteStateFile()); + File.Delete(precomputedCache.Path); + rarWriterTask.WriteStateFile(); + + ResolveAssemblyReference rarReaderTask = new ResolveAssemblyReference(); + rarReaderTask.StateFile = precomputedCache.Path.Substring(0, precomputedCache.Path.Length - 6); // Not a real path; should not be used. + rarReaderTask.AssemblyInformationCachePaths = new ITaskItem[] + { + new TaskItem(precomputedCache.Path) + }; + + // At this point, the standard cache does not exist, so it defaults to reading the "precomputed" cache. + // Then we verify that the information contained in that cache matches what we'd expect. + rarReaderTask.ReadStateFile(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/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 9a91b9178a6..f0062776ba5 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 @@ -1883,11 +1883,16 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l /// /// Reads the state file (if present) into the cache. /// - private void ReadStateFile() + internal void ReadStateFile(FileExists fileExists) { _cache = SystemState.DeserializeCacheByTranslator(_stateFile, Log); - // Construct the cache if necessary. + // Construct the cache only if we can't find any caches. + if (_cache == null && AssemblyInformationCachePaths != null && AssemblyInformationCachePaths.Length > 0) + { + _cache = SystemState.DeserializePrecomputedCachesByTranslator(AssemblyInformationCachePaths, Log, fileExists); + } + if (_cache == null) { _cache = new SystemState(); @@ -1897,9 +1902,13 @@ private void ReadStateFile() /// /// Write out the state file if a state name was supplied and the cache is dirty. /// - private void WriteStateFile() + internal void WriteStateFile() { - if (!string.IsNullOrEmpty(_stateFile) && _cache.IsDirty) + if (!String.IsNullOrEmpty(AssemblyInformationCacheOutputPath)) + { + _cache.SerializePrecomputedCacheByTranslator(AssemblyInformationCacheOutputPath, Log); + } + else if (!String.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { _cache.SerializeCacheByTranslator(_stateFile, Log); } @@ -2131,7 +2140,7 @@ ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader } // Load any prior saved state. - ReadStateFile(); + ReadStateFile(fileExists); _cache.SetGetLastWriteTime(getLastWriteTime); _cache.SetInstalledAssemblyInformation(installedAssemblyTableInfo); diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index cfc3d7d49b5..668684ed3c6 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -2226,6 +2226,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. TargetFrameworkMonikerDisplayName="$(TargetFrameworkMonikerDisplayName)" TargetedRuntimeVersion="$(TargetedRuntimeVersion)" StateFile="$(ResolveAssemblyReferencesStateFile)" + AssemblyInformationCachePaths="$(AssemblyInformationCachePaths)" + AssemblyInformationCacheOutputPath="$(AssemblyInformationCacheOutputPath)" InstalledAssemblySubsetTables="@(InstalledAssemblySubsetTables)" TargetFrameworkSubsets="@(_ReferenceInstalledAssemblySubsets)" FullTargetFrameworkSubsetNames="$(FullReferenceAssemblyNames)" diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index a054ea6c65a..e986acba46b 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: "} + + 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: "} + 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 c6ebbc2bf98..42ab02dfb9a 100644 --- a/src/Tasks/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Resources/xlf/Strings.cs.xlf @@ -509,6 +509,11 @@ Globální vlastnosti: + + 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: "} + 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 9d261b2b46f..3d41e13c1f6 100644 --- a/src/Tasks/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Resources/xlf/Strings.de.xlf @@ -509,6 +509,11 @@ Globale Eigenschaften: + + 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: "} + Removing Properties: Eigenschaften werden entfernt: diff --git a/src/Tasks/Resources/xlf/Strings.en.xlf b/src/Tasks/Resources/xlf/Strings.en.xlf index df627557c4a..afa8f0dbdc1 100644 --- a/src/Tasks/Resources/xlf/Strings.en.xlf +++ b/src/Tasks/Resources/xlf/Strings.en.xlf @@ -554,6 +554,11 @@ Global Properties: + + 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: "} + Removing Properties: Removing Properties: diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf index 8a28657818f..e48c8382a9c 100644 --- a/src/Tasks/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Resources/xlf/Strings.es.xlf @@ -509,6 +509,11 @@ Propiedades globales: + + 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: "} + Removing Properties: Quitando propiedades: diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf index 3854fcf0c19..80130e38052 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 : + + 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: "} + 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 14bdb0e8baf..ca5ce8a40ac 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -509,6 +509,11 @@ Proprietà globali: + + 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: "} + Removing Properties: Rimozione proprietà: diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf index a4b07fadb16..bffbc31b3df 100644 --- a/src/Tasks/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Resources/xlf/Strings.ja.xlf @@ -509,6 +509,11 @@ グローバル プロパティ: + + 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: "} + Removing Properties: プロパティの削除: diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf index e801bdd91a6..9273b2677d9 100644 --- a/src/Tasks/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Resources/xlf/Strings.ko.xlf @@ -509,6 +509,11 @@ 전역 속성: + + 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: "} + Removing Properties: 속성 제거: diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf index 4251be9ff36..a7f9a1cb2c5 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: + + 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: "} + 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 1dfff7329b4..3636ae32ec4 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: + + 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: "} + Removing Properties: Removendo Propriedades: diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf index 9e883f29af7..7bfd739d667 100644 --- a/src/Tasks/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Resources/xlf/Strings.ru.xlf @@ -509,6 +509,11 @@ Глобальные свойства: + + 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: "} + Removing Properties: Удаление свойств: diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf index 194e8b3f3e1..0015b1d200f 100644 --- a/src/Tasks/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Resources/xlf/Strings.tr.xlf @@ -509,6 +509,11 @@ Genel Özellikler: + + 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: "} + 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 44e2fce72d4..40069c96e67 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf @@ -509,6 +509,11 @@ 全局属性: + + 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: "} + Removing Properties: 移除属性: diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf index a70ec8fb257..e1aacb326ff 100644 --- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf @@ -509,6 +509,11 @@ 全域屬性: + + 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: "} + Removing Properties: 正在移除屬性: diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index 8c219ff2202..3f5ea428b0c 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Runtime.Versioning; using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyDependency; @@ -35,7 +36,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable /// /// Cache at the SystemState instance level. It is serialized and reused between instances. /// - private Dictionary instanceLocalFileStateCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + internal Dictionary instanceLocalFileStateCache = new Dictionary(StringComparer.OrdinalIgnoreCase); /// /// LastModified information is purely instance-local. It doesn't make sense to @@ -112,7 +113,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable /// Class that holds the current file state. /// [Serializable] - private sealed class FileState : ITranslatable + internal sealed class FileState : ITranslatable { /// /// The last modified time for this file. @@ -337,6 +338,7 @@ public void Translate(ITranslator translator) internal bool IsDirty { get { return isDirty; } + set { isDirty = value; } } /// @@ -596,6 +598,73 @@ out fileState.frameworkName frameworkName = fileState.frameworkName; } + /// + /// Reads in cached data from stateFiles to build an initial cache. Avoids logging warnings or errors. + /// + /// List of locations of caches on disk. + /// How to log + /// Whether a file exists + /// A cache representing key aspects of file states. + internal static SystemState DeserializePrecomputedCachesByTranslator(ITaskItem[] stateFiles, TaskLoggingHelper log, FileExists fileExists) + { + SystemState retVal = new SystemState(); + retVal.isDirty = stateFiles.Length > 0; + HashSet assembliesFound = new HashSet(); + + foreach (ITaskItem stateFile in stateFiles) + { + // Verify that it's a real stateFile. Log message but do not error if not. + SystemState sysState = DeserializeCacheByTranslator(stateFile.ToString(), log); + if (sysState == null) + { + continue; + } + foreach (KeyValuePair kvp in sysState.instanceLocalFileStateCache) + { + string relativePath = kvp.Key; + if (!assembliesFound.Contains(relativePath)) + { + FileState fileState = kvp.Value; + string fullPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(stateFile.ToString()), relativePath)); + if (fileExists(fullPath)) + { + // Correct file path + retVal.instanceLocalFileStateCache[fullPath] = fileState; + assembliesFound.Add(relativePath); + } + } + } + } + + return retVal; + } + + /// + /// Modifies this object to be more portable across machines, then writes it to filePath. + /// + /// Path to which to write the precomputed cache + /// How to log + internal void SerializePrecomputedCacheByTranslator(string stateFile, TaskLoggingHelper log) + { + // Save a copy of instanceLocalFileStateCache so we can restore it later. SerializeCacheByTranslator serializes + // instanceLocalFileStateCache by default, so change that to the relativized form, then change it back. + Dictionary oldFileStateCache = instanceLocalFileStateCache; + instanceLocalFileStateCache = instanceLocalFileStateCache.ToDictionary(kvp => FileUtilities.MakeRelative(Path.GetDirectoryName(stateFile), kvp.Key), kvp => kvp.Value); + + try + { + if (FileUtilities.FileExistsNoThrow(stateFile)) + { + log.LogWarningWithCodeFromResources("General.StateFileAlreadyPresent", stateFile); + } + SerializeCacheByTranslator(stateFile, log); + } + finally + { + instanceLocalFileStateCache = oldFileStateCache; + } + } + /// /// Cached implementation of GetDirectories. ///