From e4609e1cd5cde7a0393b3633aca3b58fd1be4ac9 Mon Sep 17 00:00:00 2001 From: mingkuang Date: Thu, 27 Apr 2023 23:11:59 +0800 Subject: [PATCH] =?UTF-8?q?Fea=20#5,=20Compile=E6=94=AF=E6=8C=81=E5=A2=9E?= =?UTF-8?q?=E9=87=8F=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- Build.proj | 1 - MSBuildCppTasks.sln | 22 +- Microsoft.Build.CPPTasks/GetOutOfDateItems.cs | 343 ++++ Microsoft.Build.CPPTasks/Helpers.cs | 49 + .../Microsoft.Build.CPPTasks.projitems | 4 + .../MsbuildTaskUtilities.cs | 80 + Microsoft.Build.CPPTasks/TrackedVCToolTask.cs | 1048 ++++++++++++ Microsoft.Build.CPPTasks/VCToolTask.cs | 39 +- .../GetOutOfDateItems.cs | 51 - .../Microsoft.Build.CppTasks.Common.csproj | 20 +- ...crosoft.Build.CPPTasks.Strings.Designer.cs | 1325 +++++++++++++++ .../ImmutableFilesTimestampCache.cs | 25 + .../Microsoft.Build.Framework.projitems | 15 + .../Microsoft.Build.Framework.shproj | 13 + Microsoft.Build.Framework/NativeMethods.cs | 64 + Microsoft.Build.Shared/ErrorUtilities.cs | 356 +++- Microsoft.Build.Shared/EscapingUtilities.cs | 178 ++ Microsoft.Build.Shared/ExceptionHandling.cs | 51 + Microsoft.Build.Shared/FileMatcher.cs | 1430 +++++++++++++++++ .../FileSystem/FileSystems.cs | 51 + .../FileSystem/ManagedFileSystem.cs | 85 + Microsoft.Build.Shared/FileTracker.cs | 89 + Microsoft.Build.Shared/FileUtilities.cs | 275 ++++ Microsoft.Build.Shared/FileUtilitiesRegex.cs | 110 ++ Microsoft.Build.Shared/MSBuildConstants.cs | 11 + .../Microsoft.Build.Shared.projitems | 12 + .../ReuseableStringBuilder.cs | 144 ++ Microsoft.Build.Shared/StringBuilderCache.cs | 43 + Microsoft.Build.Shared/VCUtilities.cs | 22 + .../CanonicalTrackedFilesHelper.cs | 66 + .../CanonicalTrackedInputFiles.cs | 737 +++++++++ .../CanonicalTrackedOutputFiles.cs | 516 ++++++ Microsoft.Build.Utilities/DependencyFilter.cs | 8 + .../DependencyTableCache.cs | 125 ++ .../DependencyTableCacheEntry.cs | 35 + .../Microsoft.Build.Utilities.projitems | 20 + .../Microsoft.Build.Utilities.shproj | 13 + .../TrackedDependencies.cs | 65 + VCTargets/v170/Microsoft.CppBuild.targets | 4 + YY.Build.Linux.Tasks/GCC/Ar.cs | 1 + YY.Build.Linux.Tasks/GCC/Compile.cs | 517 +++++- YY.Build.Linux.Tasks/GCC/GCCMapReader.cs | 116 ++ YY.Build.Linux.Tasks/GCC/Ld.cs | 1 + .../Targets/YY.Linux.Cross.targets | 18 +- .../YY.Build.Linux.Tasks.csproj | 21 - .../YY.Build.Linux.Tasks.projitems | 25 + .../YY.Build.Linux.Tasks.shproj | 13 + 48 files changed, 8134 insertions(+), 126 deletions(-) create mode 100644 Microsoft.Build.CPPTasks/GetOutOfDateItems.cs create mode 100644 Microsoft.Build.CPPTasks/Helpers.cs create mode 100644 Microsoft.Build.CPPTasks/MsbuildTaskUtilities.cs create mode 100644 Microsoft.Build.CPPTasks/TrackedVCToolTask.cs delete mode 100644 Microsoft.Build.CppTasks.Common/GetOutOfDateItems.cs create mode 100644 Microsoft.Build.Framework/ImmutableFilesTimestampCache.cs create mode 100644 Microsoft.Build.Framework/Microsoft.Build.Framework.projitems create mode 100644 Microsoft.Build.Framework/Microsoft.Build.Framework.shproj create mode 100644 Microsoft.Build.Framework/NativeMethods.cs create mode 100644 Microsoft.Build.Shared/EscapingUtilities.cs create mode 100644 Microsoft.Build.Shared/ExceptionHandling.cs create mode 100644 Microsoft.Build.Shared/FileMatcher.cs create mode 100644 Microsoft.Build.Shared/FileSystem/FileSystems.cs create mode 100644 Microsoft.Build.Shared/FileSystem/ManagedFileSystem.cs create mode 100644 Microsoft.Build.Shared/FileTracker.cs create mode 100644 Microsoft.Build.Shared/FileUtilities.cs create mode 100644 Microsoft.Build.Shared/FileUtilitiesRegex.cs create mode 100644 Microsoft.Build.Shared/MSBuildConstants.cs create mode 100644 Microsoft.Build.Shared/ReuseableStringBuilder.cs create mode 100644 Microsoft.Build.Shared/StringBuilderCache.cs create mode 100644 Microsoft.Build.Shared/VCUtilities.cs create mode 100644 Microsoft.Build.Utilities/CanonicalTrackedFilesHelper.cs create mode 100644 Microsoft.Build.Utilities/CanonicalTrackedInputFiles.cs create mode 100644 Microsoft.Build.Utilities/CanonicalTrackedOutputFiles.cs create mode 100644 Microsoft.Build.Utilities/DependencyFilter.cs create mode 100644 Microsoft.Build.Utilities/DependencyTableCache.cs create mode 100644 Microsoft.Build.Utilities/DependencyTableCacheEntry.cs create mode 100644 Microsoft.Build.Utilities/Microsoft.Build.Utilities.projitems create mode 100644 Microsoft.Build.Utilities/Microsoft.Build.Utilities.shproj create mode 100644 Microsoft.Build.Utilities/TrackedDependencies.cs create mode 100644 YY.Build.Linux.Tasks/GCC/GCCMapReader.cs delete mode 100644 YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.csproj create mode 100644 YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.projitems create mode 100644 YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.shproj diff --git a/.gitignore b/.gitignore index 9491a2f..d16e668 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,5 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +launchSettings.json diff --git a/Build.proj b/Build.proj index 5f735e7..0ed51f0 100644 --- a/Build.proj +++ b/Build.proj @@ -36,7 +36,6 @@ MSBuild.exe "Build.proj" - diff --git a/MSBuildCppTasks.sln b/MSBuildCppTasks.sln index 402ae6d..4d9ff0f 100644 --- a/MSBuildCppTasks.sln +++ b/MSBuildCppTasks.sln @@ -5,12 +5,16 @@ VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Build.CppTasks.Common", "Microsoft.Build.CppTasks.Common\Microsoft.Build.CppTasks.Common.csproj", "{DE6F93AA-4289-46FC-BBA2-98ED16BD1AF2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YY.Build.Linux.Tasks", "YY.Build.Linux.Tasks\YY.Build.Linux.Tasks.csproj", "{099B1749-31F9-45D9-9F63-7B5661CC0F1B}" -EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.Build.CPPTasks", "Microsoft.Build.CPPTasks\Microsoft.Build.CPPTasks.shproj", "{687DCB03-A1DD-48F2-9A6A-5A7AC4B6F0AE}" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.Build.Shared", "Microsoft.Build.Shared\Microsoft.Build.Shared.shproj", "{20CD5D23-441C-4EFF-8277-F9724E686C3F}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.Build.Utilities", "Microsoft.Build.Utilities\Microsoft.Build.Utilities.shproj", "{B3183EC4-AFD6-47B6-93C5-FFB42F82AF96}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.Build.Framework", "Microsoft.Build.Framework\Microsoft.Build.Framework.shproj", "{4BA607BE-24E1-46F8-A210-EED2B04C836B}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "YY.Build.Linux.Tasks", "YY.Build.Linux.Tasks\YY.Build.Linux.Tasks.shproj", "{8D4550A9-DEFB-4705-9DCA-873B81497C9D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,10 +25,6 @@ Global {DE6F93AA-4289-46FC-BBA2-98ED16BD1AF2}.Debug|Any CPU.Build.0 = Debug|Any CPU {DE6F93AA-4289-46FC-BBA2-98ED16BD1AF2}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE6F93AA-4289-46FC-BBA2-98ED16BD1AF2}.Release|Any CPU.Build.0 = Release|Any CPU - {099B1749-31F9-45D9-9F63-7B5661CC0F1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {099B1749-31F9-45D9-9F63-7B5661CC0F1B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {099B1749-31F9-45D9-9F63-7B5661CC0F1B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {099B1749-31F9-45D9-9F63-7B5661CC0F1B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -33,9 +33,15 @@ Global SolutionGuid = {C1862E59-07CA-42CE-89EE-4F2BBBB21670} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution - Microsoft.Build.CPPTasks\Microsoft.Build.CPPTasks.projitems*{099b1749-31f9-45d9-9f63-7b5661cc0f1b}*SharedItemsImports = 5 - Microsoft.Build.Shared\Microsoft.Build.Shared.projitems*{099b1749-31f9-45d9-9f63-7b5661cc0f1b}*SharedItemsImports = 5 Microsoft.Build.Shared\Microsoft.Build.Shared.projitems*{20cd5d23-441c-4eff-8277-f9724e686c3f}*SharedItemsImports = 13 + Microsoft.Build.Framework\Microsoft.Build.Framework.projitems*{4ba607be-24e1-46f8-a210-eed2b04c836b}*SharedItemsImports = 13 Microsoft.Build.CPPTasks\Microsoft.Build.CPPTasks.projitems*{687dcb03-a1dd-48f2-9a6a-5a7ac4b6f0ae}*SharedItemsImports = 13 + YY.Build.Linux.Tasks\YY.Build.Linux.Tasks.projitems*{8d4550a9-defb-4705-9dca-873b81497c9d}*SharedItemsImports = 13 + Microsoft.Build.Utilities\Microsoft.Build.Utilities.projitems*{b3183ec4-afd6-47b6-93c5-ffb42f82af96}*SharedItemsImports = 13 + Microsoft.Build.CPPTasks\Microsoft.Build.CPPTasks.projitems*{de6f93aa-4289-46fc-bba2-98ed16bd1af2}*SharedItemsImports = 5 + Microsoft.Build.Framework\Microsoft.Build.Framework.projitems*{de6f93aa-4289-46fc-bba2-98ed16bd1af2}*SharedItemsImports = 5 + Microsoft.Build.Shared\Microsoft.Build.Shared.projitems*{de6f93aa-4289-46fc-bba2-98ed16bd1af2}*SharedItemsImports = 5 + Microsoft.Build.Utilities\Microsoft.Build.Utilities.projitems*{de6f93aa-4289-46fc-bba2-98ed16bd1af2}*SharedItemsImports = 5 + YY.Build.Linux.Tasks\YY.Build.Linux.Tasks.projitems*{de6f93aa-4289-46fc-bba2-98ed16bd1af2}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/Microsoft.Build.CPPTasks/GetOutOfDateItems.cs b/Microsoft.Build.CPPTasks/GetOutOfDateItems.cs new file mode 100644 index 0000000..3bf1552 --- /dev/null +++ b/Microsoft.Build.CPPTasks/GetOutOfDateItems.cs @@ -0,0 +1,343 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Resources; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.CPPTasks +{ + public class GetOutOfDateItems : Microsoft.Build.Utilities.Task + { + private const string DependsOnAnotherItemOutputsMetadataName = "DependsOnAnotherItemOutput"; + + private const string InitializingValue = "Initializing"; + + public ITaskItem[] Sources { get; set; } + + [Required] + public string OutputsMetadataName { get; set; } + + public string DependenciesMetadataName { get; set; } + + public string CommandMetadataName { get; set; } + + [Required] + public string TLogDirectory { get; set; } + + [Required] + public string TLogNamePrefix { get; set; } + + public bool CheckForInterdependencies { get; set; } + + public bool TrackFileAccess { get; set; } + + [Output] + public ITaskItem[] OutOfDateSources { get; set; } + + [Output] + public bool HasInterdependencies { get; set; } + + public GetOutOfDateItems() + : base(new ResourceManager("Microsoft.Build.CPPTasks.Strings", Assembly.GetExecutingAssembly())) + { + CheckForInterdependencies = false; + HasInterdependencies = false; + DependenciesMetadataName = null; + CommandMetadataName = null; + TrackFileAccess = true; + } + + public override bool Execute() + { + //IL_00fd: Unknown result type (might be due to invalid IL or missing references) + //IL_0104: Expected O, but got Unknown + //IL_011f: Unknown result type (might be due to invalid IL or missing references) + //IL_0126: Expected O, but got Unknown + if (!TrackFileAccess) + { + if (Sources != null) + { + List list = new List(); + ITaskItem[] sources = Sources; + foreach (ITaskItem sourceItem in sources) + { + list.Add(new TaskItem(sourceItem)); + } + OutOfDateSources = list.ToArray(); + } + return true; + } + TaskItem taskItem = new TaskItem(Path.Combine(TLogDirectory, TLogNamePrefix + ".read.1.tlog")); + TaskItem taskItem2 = new TaskItem(Path.Combine(TLogDirectory, TLogNamePrefix + ".write.1.tlog")); + TaskItem taskItem3 = new TaskItem(Path.Combine(TLogDirectory, TLogNamePrefix + ".command.1.tlog")); + string metadata = taskItem.GetMetadata("FullPath"); + string metadata2 = taskItem2.GetMetadata("FullPath"); + string metadata3 = taskItem3.GetMetadata("FullPath"); + List list2 = new List(); + if (Sources != null) + { + CanonicalTrackedOutputFiles val = new CanonicalTrackedOutputFiles((ITask)this, new ITaskItem[1] { taskItem2 }, true); + CanonicalTrackedInputFiles val2 = new CanonicalTrackedInputFiles((ITask)this, new ITaskItem[1] { taskItem }, Sources, new ITaskItem[0], val, true, false); + ITaskItem[] collection = val2.ComputeSourcesNeedingCompilation(false); + HashSet hashSet = new HashSet(collection); + StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder2 = new StringBuilder(); + StringBuilder stringBuilder3 = new StringBuilder(); + Dictionary dictionary = ReadCommandLines(metadata3); + Dictionary> dictionary2 = new Dictionary>(); + Dictionary> dictionary3 = new Dictionary>(); + ITaskItem[] sources2 = Sources; + foreach (ITaskItem taskItem4 in sources2) + { + string metadata4 = taskItem4.GetMetadata("FullPath"); + string text = metadata4/*.ToUpperInvariant()*/; + string text2 = (string.IsNullOrEmpty(CommandMetadataName) ? string.Empty : taskItem4.GetMetadata(CommandMetadataName).Trim()); + string[] collection2 = (string.IsNullOrEmpty(DependenciesMetadataName) ? new string[0] : MsbuildTaskUtilities.GetWildcardExpandedFileListFromMetadata(base.BuildEngine, taskItem4, DependenciesMetadataName, base.Log)); + HashSet hashSet2 = new HashSet(collection2, StringComparer.OrdinalIgnoreCase); + hashSet2.Remove(metadata4); + string[] wildcardExpandedFileListFromMetadata = MsbuildTaskUtilities.GetWildcardExpandedFileListFromMetadata(base.BuildEngine, taskItem4, OutputsMetadataName, base.Log); + HashSet hashSet3 = new HashSet(wildcardExpandedFileListFromMetadata, StringComparer.OrdinalIgnoreCase); + dictionary2[taskItem4] = hashSet2; + dictionary3[taskItem4] = hashSet3; + if (!hashSet.Contains(taskItem4)) + { + bool flag = false; + if (!string.IsNullOrEmpty(CommandMetadataName)) + { + flag = true; + if (dictionary.TryGetValue(metadata4, out var value)) + { + if (value is List list3) + { + foreach (string item in list3) + { + if (text2.Equals(item)) + { + flag = false; + break; + } + } + } + else + { + string value2 = (string)value; + if (text2.Equals(value2)) + { + flag = false; + } + } + if (flag) + { + hashSet.Add(taskItem4); + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingSourceCommandLineChanged", metadata4); + } + } + else + { + hashSet.Add(taskItem4); + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingSourceCommandLineChanged", metadata4); + } + } + if (!flag && !string.IsNullOrEmpty(DependenciesMetadataName)) + { + if (!IsTheSameFileSet(val2.DependencyTable, metadata4, hashSet2.ToArray(), dependencyTableIncludesSourceFile: true)) + { + hashSet.Add(taskItem4); + base.Log.LogMessageFromResources(MessageImportance.Low, "GetOutOfDateItems.RebuildingSourceDependenciesChanged", metadata4); + } + if (!IsTheSameFileSet(val.DependencyTable, metadata4, hashSet3.ToArray(), dependencyTableIncludesSourceFile: false)) + { + hashSet.Add(taskItem4); + base.Log.LogMessageFromResources(MessageImportance.Low, "GetOutOfDateItems.RebuildingSourceOutputsChanged", metadata4); + } + } + } + stringBuilder.Append("^"); + stringBuilder.AppendLine(text); + stringBuilder.AppendLine(text2); + stringBuilder2.Append("^"); + stringBuilder2.AppendLine(text); + foreach (string item2 in hashSet2) + { + if (!string.Equals(item2, text, StringComparison.OrdinalIgnoreCase)) + { + stringBuilder2.AppendLine(item2); + } + } + stringBuilder3.Append("^"); + stringBuilder3.AppendLine(text); + foreach (string item3 in hashSet3) + { + stringBuilder3.AppendLine(item3); + } + } + if (hashSet.Count > 0) + { + Dictionary dictionary4 = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (CheckForInterdependencies) + { + ITaskItem[] sources3 = Sources; + foreach (ITaskItem taskItem5 in sources3) + { + foreach (string item4 in dictionary3[taskItem5]) + { + dictionary4[item4] = taskItem5; + } + } + } + ITaskItem[] sources4 = Sources; + foreach (ITaskItem taskItem6 in sources4) + { + CheckIfItemDependsOnOtherItemOutputs(taskItem6, dictionary2, dictionary4, hashSet); + if (hashSet.Contains(taskItem6)) + { + list2.Add(new TaskItem(taskItem6)); + } + } + Directory.CreateDirectory(Path.GetDirectoryName(metadata)); + File.WriteAllText(metadata3, stringBuilder.ToString()); + File.WriteAllText(metadata, stringBuilder2.ToString()); + File.WriteAllText(metadata2, stringBuilder3.ToString()); + } + } + else + { + try + { + if (File.Exists(metadata)) + { + File.Delete(metadata); + } + if (File.Exists(metadata2)) + { + File.Delete(metadata2); + } + if (File.Exists(metadata3)) + { + File.Delete(metadata3); + } + } + catch (Exception ex) + { + base.Log.LogWarningWithCodeFromResources("CannotDeleteTlogs", TLogNamePrefix, ex.Message); + } + } + OutOfDateSources = list2.ToArray(); + return true; + } + + private bool CheckIfItemDependsOnOtherItemOutputs(ITaskItem item, Dictionary> itemDependencies, Dictionary allOutputs, HashSet outOfDateItemHash) + { + if (allOutputs.Count > 0 && (!outOfDateItemHash.Contains(item) || string.IsNullOrEmpty(item.GetMetadata("DependsOnAnotherItemOutput")))) + { + item.SetMetadata("DependsOnAnotherItemOutput", "Initializing"); + foreach (string item2 in itemDependencies[item]) + { + if (!allOutputs.TryGetValue(item2, out var value)) + { + continue; + } + string metadata = value.GetMetadata("DependsOnAnotherItemOutput"); + if (!string.Equals(metadata, "Initializing")) + { + if (outOfDateItemHash.Contains(value)) + { + base.Log.LogMessageFromResources(MessageImportance.Normal, "GetOutOfDateItems.ItemDependsOnAnotherItemOutput", item.GetMetadata("FullPath"), item2, value.GetMetadata("FullPath")); + item.SetMetadata("DependsOnAnotherItemOutput", "true"); + outOfDateItemHash.Add(item); + HasInterdependencies = true; + return true; + } + if (CheckIfItemDependsOnOtherItemOutputs(value, itemDependencies, allOutputs, outOfDateItemHash)) + { + return true; + } + } + } + item.SetMetadata("DependsOnAnotherItemOutput", "false"); + } + return false; + } + + private Dictionary ReadCommandLines(string tlogCommandFile) + { + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (File.Exists(tlogCommandFile)) + { + string[] array = File.ReadAllLines(tlogCommandFile); + string text = null; + StringBuilder stringBuilder = new StringBuilder(); + string[] array2 = array; + foreach (string text2 in array2) + { + if (string.IsNullOrEmpty(text2) || text2.StartsWith("#")) + { + continue; + } + if (text2.StartsWith("^")) + { + AddCommandLine(dictionary, text, stringBuilder.ToString()); + stringBuilder.Clear(); + text = text2.Substring(1); + } + else if (!string.IsNullOrEmpty(text)) + { + if (stringBuilder.Length != 0) + { + stringBuilder.Append("\r\n"); + } + stringBuilder.Append(text2); + } + } + AddCommandLine(dictionary, text, stringBuilder.ToString()); + } + return dictionary; + } + + private void AddCommandLine(Dictionary commandLines, string sourceFile, string command) + { + if (string.IsNullOrEmpty(sourceFile)) + { + return; + } + if (commandLines.TryGetValue(sourceFile, out var value)) + { + List list = value as List; + if (list == null) + { + list = new List(); + list.Add((string)value); + commandLines[sourceFile] = list; + } + list.Add(command); + } + else + { + commandLines[sourceFile] = command; + } + } + + private bool IsTheSameFileSet(Dictionary> dependencyTable, string primaryFile, string[] newFileSet, bool dependencyTableIncludesSourceFile) + { + if (!dependencyTable.TryGetValue(primaryFile, out var value)) + { + return false; + } + if (value.Count != newFileSet.GetLength(0) + (dependencyTableIncludesSourceFile ? 1 : 0)) + { + return false; + } + foreach (string key in newFileSet) + { + if (!value.ContainsKey(key)) + { + return false; + } + } + return true; + } + } +} diff --git a/Microsoft.Build.CPPTasks/Helpers.cs b/Microsoft.Build.CPPTasks/Helpers.cs new file mode 100644 index 0000000..7173f40 --- /dev/null +++ b/Microsoft.Build.CPPTasks/Helpers.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.CPPTasks +{ + public sealed class Helpers + { + private Helpers() + { + } + + public static string GetOutputFileName(string sourceFile, string outputFileOrDir, string outputExtension) + { + string text; + if (string.IsNullOrEmpty(outputFileOrDir)) + { + text = sourceFile; + if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(outputExtension)) + { + text = Path.ChangeExtension(text, outputExtension); + } + } + else + { + text = outputFileOrDir; + char c = outputFileOrDir[outputFileOrDir.Length - 1]; + if (c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar) + { + text = Path.Combine(text, Path.GetFileName(sourceFile)); + text = Path.ChangeExtension(text, outputExtension); + } + else + { + string extension = Path.GetExtension(text); + if (string.IsNullOrEmpty(extension) && !string.IsNullOrEmpty(outputExtension)) + { + text = Path.ChangeExtension(text, outputExtension); + } + } + } + if (Path.IsPathRooted(text)) + { + text = Path.GetFullPath(text); + } + return text; + } + } +} diff --git a/Microsoft.Build.CPPTasks/Microsoft.Build.CPPTasks.projitems b/Microsoft.Build.CPPTasks/Microsoft.Build.CPPTasks.projitems index d875813..1473b85 100644 --- a/Microsoft.Build.CPPTasks/Microsoft.Build.CPPTasks.projitems +++ b/Microsoft.Build.CPPTasks/Microsoft.Build.CPPTasks.projitems @@ -10,8 +10,12 @@ + + + + \ No newline at end of file diff --git a/Microsoft.Build.CPPTasks/MsbuildTaskUtilities.cs b/Microsoft.Build.CPPTasks/MsbuildTaskUtilities.cs new file mode 100644 index 0000000..717757c --- /dev/null +++ b/Microsoft.Build.CPPTasks/MsbuildTaskUtilities.cs @@ -0,0 +1,80 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Build.Tasks; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.CPPTasks +{ + internal static class MsbuildTaskUtilities + { + public static char[] semicolonSeparator = new char[1] { ';' }; + + public static string[] GetWildcardExpandedFileListFromMetadata(IBuildEngine buildEngine, ITaskItem item, string metadataName, TaskLoggingHelper log = null, bool convertToUpperCase = true) + { + string metadata = item.GetMetadata(metadataName); + string warningResource = null; + if (metadataName == "AdditionalInputs") + { + warningResource = "CustomBuild.InvalidDependency"; + } + else if (metadataName == "Outputs") + { + warningResource = "CustomBuild.InvalidOutput"; + } + return GetWildcardExpandedFileList(buildEngine, metadata, log, warningResource, item.ItemSpec, convertToUpperCase); + } + + public static string[] GetWildcardExpandedFileList(IBuildEngine buildEngine, string value, TaskLoggingHelper log = null, string warningResource = null, string itemName = null, bool convertToUpperCase = true) + { + List list = new List(); + CreateItem createItem = new CreateItem(); + createItem.BuildEngine = buildEngine; + if (!string.IsNullOrEmpty(value)) + { + string[] array = value.Split(semicolonSeparator); + List list2 = new List(); + string[] array2 = array; + foreach (string text in array2) + { + string text2 = text.Trim(); + if (!string.IsNullOrEmpty(text2)) + { + list2.Add(new TaskItem(text2)); + } + } + createItem.Include = list2.ToArray(); + createItem.Execute(); + ITaskItem[] include = createItem.Include; + foreach (ITaskItem taskItem in include) + { + try + { + string text3 = taskItem.GetMetadata("FullPath"); + //if (convertToUpperCase) + //{ + // text3 = text3.ToUpperInvariant(); + //} + list.Add(text3); + } + catch (Exception ex) + { + if (log != null && warningResource != null && itemName != null) + { + log.LogWarningWithCodeFromResources(warningResource, itemName, taskItem.ItemSpec); + } + ex.RethrowIfCritical(); + } + } + } + return list.ToArray(); + } + + public static string FileNameFromHash(string content) + { + return VCUtilities.GetHashString(content); + } + } +} diff --git a/Microsoft.Build.CPPTasks/TrackedVCToolTask.cs b/Microsoft.Build.CPPTasks/TrackedVCToolTask.cs new file mode 100644 index 0000000..ca8c5bd --- /dev/null +++ b/Microsoft.Build.CPPTasks/TrackedVCToolTask.cs @@ -0,0 +1,1048 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.Build.CPPTasks +{ + public abstract class TrackedVCToolTask : VCToolTask + { + private bool skippedExecution; + + private CanonicalTrackedInputFiles sourceDependencies; + + private CanonicalTrackedOutputFiles sourceOutputs; + + private bool trackFileAccess; + + private bool trackCommandLines = true; + + private bool minimalRebuildFromTracking; + + private bool deleteOutputBeforeExecute; + + private string rootSource; + + private ITaskItem[] tlogReadFiles; + + private ITaskItem[] tlogWriteFiles; + + private ITaskItem tlogCommandFile; + + private ITaskItem[] sourcesCompiled; + + private ITaskItem[] trackedInputFilesToIgnore; + + private ITaskItem[] trackedOutputFilesToIgnore; + + private ITaskItem[] excludedInputPaths = new TaskItem[0]; + + private string pathOverride; + + private static readonly char[] NewlineArray = Environment.NewLine.ToCharArray(); + + private static readonly Regex extraNewlineRegex = new Regex("(\\r?\\n)?(\\r?\\n)+"); + + protected abstract string TrackerIntermediateDirectory { get; } + + protected abstract ITaskItem[] TrackedInputFiles { get; } + + protected CanonicalTrackedInputFiles SourceDependencies + { + get + { + return sourceDependencies; + } + set + { + sourceDependencies = value; + } + } + + protected CanonicalTrackedOutputFiles SourceOutputs + { + get + { + return sourceOutputs; + } + set + { + sourceOutputs = value; + } + } + + [Output] + public bool SkippedExecution + { + get + { + return skippedExecution; + } + set + { + skippedExecution = value; + } + } + + public string RootSource + { + get + { + return rootSource; + } + set + { + rootSource = value; + } + } + + protected virtual bool TrackReplaceFile => false; + + protected virtual string[] ReadTLogNames + { + get + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ToolExe); + return new string[4] + { + fileNameWithoutExtension + ".read.*.tlog", + fileNameWithoutExtension + ".*.read.*.tlog", + fileNameWithoutExtension + "-*.read.*.tlog", + GetType().FullName + ".read.*.tlog" + }; + } + } + + protected virtual string[] WriteTLogNames + { + get + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ToolExe); + return new string[4] + { + fileNameWithoutExtension + ".write.*.tlog", + fileNameWithoutExtension + ".*.write.*.tlog", + fileNameWithoutExtension + "-*.write.*.tlog", + GetType().FullName + ".write.*.tlog" + }; + } + } + + protected virtual string[] DeleteTLogNames + { + get + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ToolExe); + return new string[4] + { + fileNameWithoutExtension + ".delete.*.tlog", + fileNameWithoutExtension + ".*.delete.*.tlog", + fileNameWithoutExtension + "-*.delete.*.tlog", + GetType().FullName + ".delete.*.tlog" + }; + } + } + + protected virtual string CommandTLogName + { + get + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ToolExe); + return fileNameWithoutExtension + ".command.1.tlog"; + } + } + + public ITaskItem[] TLogReadFiles + { + get + { + return tlogReadFiles; + } + set + { + tlogReadFiles = value; + } + } + + public ITaskItem[] TLogWriteFiles + { + get + { + return tlogWriteFiles; + } + set + { + tlogWriteFiles = value; + } + } + + public ITaskItem[] TLogDeleteFiles { get; set; } + + public ITaskItem TLogCommandFile + { + get + { + return tlogCommandFile; + } + set + { + tlogCommandFile = value; + } + } + + public bool TrackFileAccess + { + get + { + return trackFileAccess; + } + set + { + trackFileAccess = value; + } + } + + public bool TrackCommandLines + { + get + { + return trackCommandLines; + } + set + { + trackCommandLines = value; + } + } + + public bool PostBuildTrackingCleanup { get; set; } + + public bool EnableExecuteTool { get; set; } + + public bool MinimalRebuildFromTracking + { + get + { + return minimalRebuildFromTracking; + } + set + { + minimalRebuildFromTracking = value; + } + } + + public virtual bool AttributeFileTracking => false; + + [Output] + public ITaskItem[] SourcesCompiled + { + get + { + return sourcesCompiled; + } + set + { + sourcesCompiled = value; + } + } + + public ITaskItem[] TrackedOutputFilesToIgnore + { + get + { + return trackedOutputFilesToIgnore; + } + set + { + trackedOutputFilesToIgnore = value; + } + } + + public ITaskItem[] TrackedInputFilesToIgnore + { + get + { + return trackedInputFilesToIgnore; + } + set + { + trackedInputFilesToIgnore = value; + } + } + + public bool DeleteOutputOnExecute + { + get + { + return deleteOutputBeforeExecute; + } + set + { + deleteOutputBeforeExecute = value; + } + } + + public bool DeleteOutputBeforeExecute + { + get + { + return deleteOutputBeforeExecute; + } + set + { + deleteOutputBeforeExecute = value; + } + } + + protected virtual bool MaintainCompositeRootingMarkers => false; + + protected virtual bool UseMinimalRebuildOptimization => false; + + public virtual string SourcesPropertyName => "Sources"; + + // protected virtual ExecutableType? ToolType => null; + + public string ToolArchitecture { get; set; } + + public string TrackerFrameworkPath { get; set; } + + public string TrackerSdkPath { get; set; } + + public ITaskItem[] ExcludedInputPaths + { + get + { + return excludedInputPaths; + } + set + { + List list = new List(value); + excludedInputPaths = list.ToArray(); + } + } + + public string PathOverride + { + get + { + return pathOverride; + } + set + { + pathOverride = value; + } + } + + protected TrackedVCToolTask(System.Resources.ResourceManager taskResources) + : base(taskResources) + { + PostBuildTrackingCleanup = true; + EnableExecuteTool = true; + } + + protected virtual void AssignDefaultTLogPaths() + { + string trackerIntermediateDirectory = TrackerIntermediateDirectory; + if (TLogReadFiles == null) + { + string[] readTLogNames = ReadTLogNames; + TLogReadFiles = new ITaskItem[readTLogNames.Length]; + for (int i = 0; i < readTLogNames.Length; i++) + { + TLogReadFiles[i] = new TaskItem(Path.Combine(trackerIntermediateDirectory, readTLogNames[i])); + } + } + if (TLogWriteFiles == null) + { + string[] writeTLogNames = WriteTLogNames; + TLogWriteFiles = new ITaskItem[writeTLogNames.Length]; + for (int j = 0; j < writeTLogNames.Length; j++) + { + TLogWriteFiles[j] = new TaskItem(Path.Combine(trackerIntermediateDirectory, writeTLogNames[j])); + } + } + if (TLogDeleteFiles == null) + { + string[] deleteTLogNames = DeleteTLogNames; + TLogDeleteFiles = new ITaskItem[deleteTLogNames.Length]; + for (int k = 0; k < deleteTLogNames.Length; k++) + { + TLogDeleteFiles[k] = new TaskItem(Path.Combine(trackerIntermediateDirectory, deleteTLogNames[k])); + } + } + if (TLogCommandFile == null) + { + TLogCommandFile = new TaskItem(Path.Combine(trackerIntermediateDirectory, CommandTLogName)); + } + } + + protected override bool SkipTaskExecution() + { + return ComputeOutOfDateSources(); + } + + protected internal virtual bool ComputeOutOfDateSources() + { + if (MinimalRebuildFromTracking || TrackFileAccess) + { + AssignDefaultTLogPaths(); + } + if (MinimalRebuildFromTracking && !ForcedRebuildRequired()) + { + sourceOutputs = new CanonicalTrackedOutputFiles(this, TLogWriteFiles); + sourceDependencies = new CanonicalTrackedInputFiles(this, TLogReadFiles, TrackedInputFiles, ExcludedInputPaths, sourceOutputs, UseMinimalRebuildOptimization, MaintainCompositeRootingMarkers); + ITaskItem[] sourcesOutOfDateThroughTracking = SourceDependencies.ComputeSourcesNeedingCompilation(searchForSubRootsInCompositeRootingMarkers: false); + List sourcesWithChangedCommandLines = GenerateSourcesOutOfDateDueToCommandLine(); + SourcesCompiled = MergeOutOfDateSourceLists(sourcesOutOfDateThroughTracking, sourcesWithChangedCommandLines); + if (SourcesCompiled.Length == 0) + { + SkippedExecution = true; + return SkippedExecution; + } + SourcesCompiled = AssignOutOfDateSources(SourcesCompiled); + SourceDependencies.RemoveEntriesForSource(SourcesCompiled); + SourceDependencies.SaveTlog(); + if (DeleteOutputOnExecute) + { + DeleteFiles(sourceOutputs.OutputsForSource(SourcesCompiled, searchForSubRootsInCompositeRootingMarkers: false)); + } + sourceOutputs.RemoveEntriesForSource(SourcesCompiled); + sourceOutputs.SaveTlog(); + } + else + { + SourcesCompiled = TrackedInputFiles; + if (SourcesCompiled == null || SourcesCompiled.Length == 0) + { + SkippedExecution = true; + return SkippedExecution; + } + } + if ((TrackFileAccess || TrackCommandLines) && string.IsNullOrEmpty(RootSource)) + { + RootSource = FileTracker.FormatRootingMarker(SourcesCompiled); + } + SkippedExecution = false; + return SkippedExecution; + } + + protected virtual ITaskItem[] AssignOutOfDateSources(ITaskItem[] sources) + { + return sources; + } + + protected virtual bool ForcedRebuildRequired() + { + string text = null; + try + { + text = TLogCommandFile.GetMetadata("FullPath"); + } + catch (Exception ex) + { + if (!(ex is InvalidOperationException) && !(ex is NullReferenceException)) + { + throw; + } + base.Log.LogWarningWithCodeFromResources("TrackedVCToolTask.RebuildingDueToInvalidTLog", ex.Message); + return true; + } + if (!File.Exists(text)) + { + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingNoCommandTLog", TLogCommandFile.GetMetadata("FullPath")); + return true; + } + return false; + } + + protected virtual List GenerateSourcesOutOfDateDueToCommandLine() + { + IDictionary dictionary = MapSourcesToCommandLines(); + List list = new List(); + if (!TrackCommandLines) + { + return list; + } + if (dictionary.Count == 0) + { + ITaskItem[] trackedInputFiles = TrackedInputFiles; + foreach (ITaskItem item in trackedInputFiles) + { + list.Add(item); + } + } + else if (MaintainCompositeRootingMarkers) + { + string text = ApplyPrecompareCommandFilter(GenerateCommandLine(CommandLineFormat.ForTracking)); + string value = null; + if (dictionary.TryGetValue(FileTracker.FormatRootingMarker(TrackedInputFiles), out value)) + { + value = ApplyPrecompareCommandFilter(value); + if (value == null || !text.Equals(value, StringComparison.Ordinal)) + { + ITaskItem[] trackedInputFiles2 = TrackedInputFiles; + foreach (ITaskItem item2 in trackedInputFiles2) + { + list.Add(item2); + } + } + } + else + { + ITaskItem[] trackedInputFiles3 = TrackedInputFiles; + foreach (ITaskItem item3 in trackedInputFiles3) + { + list.Add(item3); + } + } + } + else + { + string text2 = SourcesPropertyName ?? "Sources"; + string text3 = GenerateCommandLineExceptSwitches(new string[1] { text2 }, CommandLineFormat.ForTracking); + ITaskItem[] trackedInputFiles4 = TrackedInputFiles; + foreach (ITaskItem taskItem in trackedInputFiles4) + { + string text4 = ApplyPrecompareCommandFilter(text3 + " " + taskItem.GetMetadata("FullPath")/*.ToUpperInvariant()*/); + string value2 = null; + if (dictionary.TryGetValue(FileTracker.FormatRootingMarker(taskItem), out value2)) + { + value2 = ApplyPrecompareCommandFilter(value2); + if (value2 == null || !text4.Equals(value2, StringComparison.Ordinal)) + { + list.Add(taskItem); + } + } + else + { + list.Add(taskItem); + } + } + } + return list; + } + + protected ITaskItem[] MergeOutOfDateSourceLists(ITaskItem[] sourcesOutOfDateThroughTracking, List sourcesWithChangedCommandLines) + { + if (sourcesWithChangedCommandLines.Count == 0) + { + return sourcesOutOfDateThroughTracking; + } + if (sourcesOutOfDateThroughTracking.Length == 0) + { + if (sourcesWithChangedCommandLines.Count == TrackedInputFiles.Length) + { + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingAllSourcesCommandLineChanged"); + } + else + { + foreach (ITaskItem sourcesWithChangedCommandLine in sourcesWithChangedCommandLines) + { + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingSourceCommandLineChanged", sourcesWithChangedCommandLine.GetMetadata("FullPath")); + } + } + return sourcesWithChangedCommandLines.ToArray(); + } + if (sourcesOutOfDateThroughTracking.Length == TrackedInputFiles.Length) + { + return TrackedInputFiles; + } + if (sourcesWithChangedCommandLines.Count == TrackedInputFiles.Length) + { + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingAllSourcesCommandLineChanged"); + return TrackedInputFiles; + } + Dictionary dictionary = new Dictionary(); + foreach (ITaskItem key in sourcesOutOfDateThroughTracking) + { + dictionary[key] = false; + } + foreach (ITaskItem sourcesWithChangedCommandLine2 in sourcesWithChangedCommandLines) + { + if (!dictionary.ContainsKey(sourcesWithChangedCommandLine2)) + { + dictionary.Add(sourcesWithChangedCommandLine2, value: true); + } + } + List list = new List(); + ITaskItem[] trackedInputFiles = TrackedInputFiles; + foreach (ITaskItem taskItem in trackedInputFiles) + { + bool value = false; + if (dictionary.TryGetValue(taskItem, out value)) + { + list.Add(taskItem); + if (value) + { + base.Log.LogMessageFromResources(MessageImportance.Low, "TrackedVCToolTask.RebuildingSourceCommandLineChanged", taskItem.GetMetadata("FullPath")); + } + } + } + return list.ToArray(); + } + + protected IDictionary MapSourcesToCommandLines() + { + IDictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + string metadata = TLogCommandFile.GetMetadata("FullPath"); + if (File.Exists(metadata)) + { + using (StreamReader streamReader = File.OpenText(metadata)) + { + bool flag = false; + string text = string.Empty; + for (string text2 = streamReader.ReadLine(); text2 != null; text2 = streamReader.ReadLine()) + { + if (text2.Length == 0) + { + flag = true; + break; + } + if (text2[0] == '^') + { + if (text2.Length == 1) + { + flag = true; + break; + } + text = text2.Substring(1); + } + else + { + string value = null; + if (!dictionary.TryGetValue(text, out value)) + { + dictionary[text] = text2; + } + else + { + IDictionary dictionary2 = dictionary; + string key = text; + dictionary2[key] = dictionary2[key] + "\r\n" + text2; + } + } + } + if (flag) + { + base.Log.LogWarningWithCodeFromResources("TrackedVCToolTask.RebuildingDueToInvalidTLogContents", metadata); + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + return dictionary; + } + } + return dictionary; + } + + protected void WriteSourcesToCommandLinesTable(IDictionary sourcesToCommandLines) + { + string metadata = TLogCommandFile.GetMetadata("FullPath"); + Directory.CreateDirectory(Path.GetDirectoryName(metadata)); + using StreamWriter streamWriter = new StreamWriter(metadata, append: false, Encoding.Unicode); + foreach (KeyValuePair sourcesToCommandLine in sourcesToCommandLines) + { + streamWriter.WriteLine("^" + sourcesToCommandLine.Key); + streamWriter.WriteLine(ApplyPrecompareCommandFilter(sourcesToCommandLine.Value)); + } + } + + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) + { + int num = 0; + if (EnableExecuteTool) + { + try + { + num = TrackerExecuteTool(pathToTool, responseFileCommands, commandLineCommands); + } + finally + { + PrintMessage(ParseLine(null), base.StandardOutputImportanceToUse); + if (PostBuildTrackingCleanup) + { + num = PostExecuteTool(num); + } + } + } + return num; + } + + protected virtual int PostExecuteTool(int exitCode) + { + if (MinimalRebuildFromTracking || TrackFileAccess) + { + SourceOutputs = new CanonicalTrackedOutputFiles(TLogWriteFiles); + SourceDependencies = new CanonicalTrackedInputFiles(TLogReadFiles, TrackedInputFiles, ExcludedInputPaths, SourceOutputs, useMinimalRebuildOptimization: false, MaintainCompositeRootingMarkers); + string[] array = null; + IDictionary dictionary = MapSourcesToCommandLines(); + if (exitCode != 0) + { + SourceOutputs.RemoveEntriesForSource(SourcesCompiled); + SourceOutputs.SaveTlog(); + SourceDependencies.RemoveEntriesForSource(SourcesCompiled); + SourceDependencies.SaveTlog(); + if (TrackCommandLines) + { + if (MaintainCompositeRootingMarkers) + { + dictionary.Remove(RootSource); + } + else + { + ITaskItem[] array2 = SourcesCompiled; + foreach (ITaskItem source in array2) + { + dictionary.Remove(FileTracker.FormatRootingMarker(source)); + } + } + WriteSourcesToCommandLinesTable(dictionary); + } + } + else + { + AddTaskSpecificOutputs(SourcesCompiled, SourceOutputs); + RemoveTaskSpecificOutputs(SourceOutputs); + SourceOutputs.RemoveDependenciesFromEntryIfMissing(SourcesCompiled); + if (MaintainCompositeRootingMarkers) + { + array = SourceOutputs.RemoveRootsWithSharedOutputs(SourcesCompiled); + string[] array3 = array; + foreach (string rootingMarker in array3) + { + SourceDependencies.RemoveEntryForSourceRoot(rootingMarker); + } + } + if (TrackedOutputFilesToIgnore != null && TrackedOutputFilesToIgnore.Length != 0) + { + Dictionary trackedOutputFilesToRemove = new Dictionary(StringComparer.OrdinalIgnoreCase); + ITaskItem[] array4 = TrackedOutputFilesToIgnore; + foreach (ITaskItem taskItem in array4) + { + string key = taskItem.GetMetadata("FullPath")/*.ToUpperInvariant()*/; + if (!trackedOutputFilesToRemove.ContainsKey(key)) + { + trackedOutputFilesToRemove.Add(key, taskItem); + } + } + SourceOutputs.SaveTlog((string fullTrackedPath) => (!trackedOutputFilesToRemove.ContainsKey(fullTrackedPath/*.ToUpperInvariant()*/)) ? true : false); + } + else + { + SourceOutputs.SaveTlog(); + } + DeleteEmptyFile(TLogWriteFiles); + RemoveTaskSpecificInputs(SourceDependencies); + SourceDependencies.RemoveDependenciesFromEntryIfMissing(SourcesCompiled); + if (TrackedInputFilesToIgnore != null && TrackedInputFilesToIgnore.Length != 0) + { + Dictionary trackedInputFilesToRemove = new Dictionary(StringComparer.OrdinalIgnoreCase); + ITaskItem[] array5 = TrackedInputFilesToIgnore; + foreach (ITaskItem taskItem2 in array5) + { + string key2 = taskItem2.GetMetadata("FullPath")/*.ToUpperInvariant()*/; + if (!trackedInputFilesToRemove.ContainsKey(key2)) + { + trackedInputFilesToRemove.Add(key2, taskItem2); + } + } + SourceDependencies.SaveTlog((string fullTrackedPath) => (!trackedInputFilesToRemove.ContainsKey(fullTrackedPath)) ? true : false); + } + else + { + SourceDependencies.SaveTlog(); + } + DeleteEmptyFile(TLogReadFiles); + DeleteFiles(TLogDeleteFiles); + if (TrackCommandLines) + { + if (MaintainCompositeRootingMarkers) + { + string value = GenerateCommandLine(CommandLineFormat.ForTracking); + dictionary[RootSource] = value; + if (array != null) + { + string[] array6 = array; + foreach (string key3 in array6) + { + dictionary.Remove(key3); + } + } + } + else + { + string text = SourcesPropertyName ?? "Sources"; + string text2 = GenerateCommandLineExceptSwitches(new string[1] { text }, CommandLineFormat.ForTracking); + ITaskItem[] array7 = SourcesCompiled; + foreach (ITaskItem taskItem3 in array7) + { + dictionary[FileTracker.FormatRootingMarker(taskItem3)] = text2 + " " + taskItem3.GetMetadata("FullPath")/*.ToUpperInvariant()*/; + } + } + WriteSourcesToCommandLinesTable(dictionary); + } + } + } + return exitCode; + } + + protected virtual void RemoveTaskSpecificOutputs(CanonicalTrackedOutputFiles compactOutputs) + { + } + + protected virtual void RemoveTaskSpecificInputs(CanonicalTrackedInputFiles compactInputs) + { + } + + protected virtual void AddTaskSpecificOutputs(ITaskItem[] sources, CanonicalTrackedOutputFiles compactOutputs) + { + } + + protected override void LogPathToTool(string toolName, string pathToTool) + { + base.LogPathToTool(toolName, base.ResolvedPathToTool); + } + + protected virtual void SaveTracking() + { + // 微软没有此函数,自己重写的版本,保存跟踪文件,增量编译使用。 + } + + protected int TrackerExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) + { + string dllName = null; + string text = null; + bool flag = TrackFileAccess; + string text2 = Environment.ExpandEnvironmentVariables(pathToTool); + string text3 = Environment.ExpandEnvironmentVariables(commandLineCommands); + + // 微软的方案严重不适合Linux,因为tracker什么的再Linux等非Windows 平台都是没有的。 + // 因此这方面重写。 + + var ErrorCode = base.ExecuteTool(text2, responseFileCommands, text3); + + if(ErrorCode == 0 && (MinimalRebuildFromTracking || TrackFileAccess)) + { + // 将数据生成数据会回写到Write文件。 + SaveTracking(); + } + + return ErrorCode; +#if __ + try + { + string text4; + if (flag) + { + ExecutableType result = ExecutableType.SameAsCurrentProcess; + if (!string.IsNullOrEmpty(ToolArchitecture)) + { + if (!Enum.TryParse(ToolArchitecture, out result)) + { + base.Log.LogErrorWithCodeFromResources("General.InvalidValue", "ToolArchitecture", GetType().Name); + return -1; + } + } + else if (ToolType.HasValue) + { + result = ToolType.Value; + } + if ((result == ExecutableType.Native32Bit || result == ExecutableType.Native64Bit) && Microsoft.Build.Shared.NativeMethodsShared.Is64bitApplication(text2, out var is64bit)) + { + result = (is64bit ? ExecutableType.Native64Bit : ExecutableType.Native32Bit); + } + try + { + text4 = FileTracker.GetTrackerPath(result, TrackerSdkPath); + if (text4 == null) + { + base.Log.LogErrorFromResources("Error.MissingFile", "tracker.exe"); + } + } + catch (Exception e) + { + if (Microsoft.Build.Shared.ExceptionHandling.NotExpectedException(e)) + { + throw; + } + base.Log.LogErrorWithCodeFromResources("General.InvalidValue", "TrackerSdkPath", GetType().Name); + return -1; + } + try + { + dllName = FileTracker.GetFileTrackerPath(result, TrackerFrameworkPath); + } + catch (Exception e2) + { + if (Microsoft.Build.Shared.ExceptionHandling.NotExpectedException(e2)) + { + throw; + } + base.Log.LogErrorWithCodeFromResources("General.InvalidValue", "TrackerFrameworkPath", GetType().Name); + return -1; + } + } + else + { + text4 = text2; + } + if (!string.IsNullOrEmpty(text4)) + { + Microsoft.Build.Shared.ErrorUtilities.VerifyThrowInternalRooted(text4); + string commandLineCommands2; + if (flag) + { + string text5 = FileTracker.TrackerArguments(text2, text3, dllName, TrackerIntermediateDirectory, RootSource, base.CancelEventName); + base.Log.LogMessageFromResources(MessageImportance.Low, "Native_TrackingCommandMessage"); + string message = text4 + (AttributeFileTracking ? " /a " : " ") + (TrackReplaceFile ? "/f " : "") + text5 + " " + responseFileCommands; + base.Log.LogMessage(MessageImportance.Low, message); + text = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + using (StreamWriter streamWriter = new StreamWriter(text, append: false, Encoding.Unicode)) + { + streamWriter.Write(FileTracker.TrackerResponseFileArguments(dllName, TrackerIntermediateDirectory, RootSource, base.CancelEventName)); + } + commandLineCommands2 = (AttributeFileTracking ? "/a @\"" : "@\"") + text + "\"" + (TrackReplaceFile ? " /f " : "") + FileTracker.TrackerCommandArguments(text2, text3); + } + else + { + commandLineCommands2 = text3; + } + return base.ExecuteTool(text4, responseFileCommands, commandLineCommands2); + } + return -1; + } + finally + { + if (text != null) + { + DeleteTempFile(text); + } + } +#endif + } + + protected override void ProcessStarted() + { + } + + public virtual string ApplyPrecompareCommandFilter(string value) + { + return extraNewlineRegex.Replace(value, "$2"); + } + + public static string RemoveSwitchFromCommandLine(string removalWord, string cmdString, bool removeMultiple = false) + { + int num = 0; + while ((num = cmdString.IndexOf(removalWord, num, StringComparison.Ordinal)) >= 0) + { + if (num == 0 || cmdString[num - 1] == ' ') + { + int num2 = cmdString.IndexOf(' ', num); + if (num2 >= 0) + { + num2++; + } + else + { + num2 = cmdString.Length; + num--; + } + cmdString = cmdString.Remove(num, num2 - num); + if (!removeMultiple) + { + break; + } + } + num++; + if (num >= cmdString.Length) + { + break; + } + } + return cmdString; + } + + protected static int DeleteFiles(ITaskItem[] filesToDelete) + { + if (filesToDelete == null) + { + return 0; + } + ITaskItem[] array = TrackedDependencies.ExpandWildcards(filesToDelete); + if (array.Length == 0) + { + return 0; + } + int num = 0; + ITaskItem[] array2 = array; + foreach (ITaskItem taskItem in array2) + { + try + { + FileInfo fileInfo = new FileInfo(taskItem.ItemSpec); + if (fileInfo.Exists) + { + fileInfo.Delete(); + num++; + } + } + catch (Exception ex) + { + if (ex is SecurityException || ex is ArgumentException || ex is UnauthorizedAccessException || ex is PathTooLongException || ex is NotSupportedException) + { + continue; + } + throw; + } + } + return num; + } + + protected static int DeleteEmptyFile(ITaskItem[] filesToDelete) + { + if (filesToDelete == null) + { + return 0; + } + ITaskItem[] array = TrackedDependencies.ExpandWildcards(filesToDelete); + if (array.Length == 0) + { + return 0; + } + int num = 0; + ITaskItem[] array2 = array; + foreach (ITaskItem taskItem in array2) + { + bool flag = false; + try + { + FileInfo fileInfo = new FileInfo(taskItem.ItemSpec); + if (fileInfo.Exists) + { + if (fileInfo.Length <= 4) + { + flag = true; + } + if (flag) + { + fileInfo.Delete(); + num++; + } + } + } + catch (Exception ex) + { + if (ex is SecurityException || ex is ArgumentException || ex is UnauthorizedAccessException || ex is PathTooLongException || ex is NotSupportedException) + { + continue; + } + throw; + } + } + return num; + } + } + +} diff --git a/Microsoft.Build.CPPTasks/VCToolTask.cs b/Microsoft.Build.CPPTasks/VCToolTask.cs index df3b098..db18d58 100644 --- a/Microsoft.Build.CPPTasks/VCToolTask.cs +++ b/Microsoft.Build.CPPTasks/VCToolTask.cs @@ -188,7 +188,8 @@ public Dictionary ActiveToolSwitchesValues protected bool IgnoreUnknownSwitchValues { get; set; } - protected VCToolTask() + protected VCToolTask(ResourceManager taskResources) + : base(taskResources) { #if __REMOVE cancelEventName = "MSBuildConsole_CancelEvent" + Guid.NewGuid().ToString("N"); @@ -275,6 +276,11 @@ protected virtual string GenerateCommandLineCommands(CommandLineFormat format, E return GenerateCommandLineCommandsExceptSwitches(new string[0], format, escapeFormat); } + protected virtual bool GenerateCostomCommandsAccordingToType(CommandLineBuilder builder, string switchName, bool dummyForBackwardCompatibility, CommandLineFormat format = CommandLineFormat.ForBuildLog, EscapeFormat escapeFormat = EscapeFormat.Default) + { + return false; + } + protected virtual string /*GenerateResponseFileCommandsExceptSwitches*/GenerateCommandLineCommandsExceptSwitches(string[] switchesToRemove, CommandLineFormat format = CommandLineFormat.ForBuildLog, EscapeFormat escapeFormat = EscapeFormat.Default) { bool flag = false; @@ -308,6 +314,10 @@ protected virtual string GenerateCommandLineCommands(CommandLineFormat format, E GenerateCommandsAccordingToType(commandLineBuilder, toolSwitch, dummyForBackwardCompatibility: false, format, escapeFormat); } } + else if(GenerateCostomCommandsAccordingToType(commandLineBuilder, switchOrder, dummyForBackwardCompatibility : false, format, escapeFormat)) + { + // 已经处理 + } else if (string.Equals(switchOrder, "additionaloptions", StringComparison.OrdinalIgnoreCase)) { BuildAdditionalArgs(commandLineBuilder); @@ -360,6 +370,7 @@ public override void Cancel() #if __REMOVE VCTaskNativeMethods.SetEvent(cancelEvent); #endif + base.Cancel(); } protected bool VerifyRequiredArgumentsArePresent(ToolSwitch property, bool throwOnError) @@ -595,9 +606,7 @@ protected virtual void GenerateCommandsAccordingToType(CommandLineBuilder builde catch (Exception ex) { base.Log.LogErrorFromResources("GenerateCommandLineError", toolSwitch.Name, toolSwitch.ValueAsString, ex.Message); -#if __REMOVE ex.RethrowIfCritical(); -#endif } } @@ -890,10 +899,10 @@ private static void EmitTaskItemArraySwitch(CommandLineBuilder builder, ToolSwit for (int j = 0; j < toolSwitch.TaskItemArray.Length; j++) { array[j] = new TaskItem(Environment.ExpandEnvironmentVariables(toolSwitch.TaskItemArray[j].ItemSpec)); - if (format == CommandLineFormat.ForTracking) - { - array[j].ItemSpec = array[j].ItemSpec.ToUpperInvariant(); - } + //if (format == CommandLineFormat.ForTracking) + //{ + // array[j].ItemSpec = array[j].ItemSpec.ToUpperInvariant(); + //} } builder.AppendSwitchIfNotNull(toolSwitch.SwitchValue, array, toolSwitch.Separator); } @@ -927,10 +936,10 @@ private static void EmitFileSwitch(CommandLineBuilder builder, ToolSwitch toolSw { string text = Environment.ExpandEnvironmentVariables(toolSwitch.Value); text = text.Trim(); - if (format == CommandLineFormat.ForTracking) - { - text = text.ToUpperInvariant(); - } + //if (format == CommandLineFormat.ForTracking) + //{ + // text = text.ToUpperInvariant(); + //} if (!text.StartsWith("\"", StringComparison.Ordinal)) { text = "\"" + text; @@ -968,10 +977,10 @@ private static void EmitStringArraySwitch(CommandLineBuilder builder, ToolSwitch string text = ((!toolSwitch.StringList[i].StartsWith("\"", StringComparison.Ordinal) || !toolSwitch.StringList[i].EndsWith("\"", StringComparison.Ordinal)) ? Environment.ExpandEnvironmentVariables(toolSwitch.StringList[i]) : Environment.ExpandEnvironmentVariables(toolSwitch.StringList[i].Substring(1, toolSwitch.StringList[i].Length - 2))); if (!string.IsNullOrEmpty(text)) { - if (format == CommandLineFormat.ForTracking) - { - text = text.ToUpperInvariant(); - } + //if (format == CommandLineFormat.ForTracking) + //{ + // text = text.ToUpperInvariant(); + //} if (escapeFormat.HasFlag(EscapeFormat.EscapeTrailingSlash) && text.IndexOfAny(anyOf) == -1 && text.EndsWith("\\", StringComparison.Ordinal) && !text.EndsWith("\\\\", StringComparison.Ordinal)) { text += "\\"; diff --git a/Microsoft.Build.CppTasks.Common/GetOutOfDateItems.cs b/Microsoft.Build.CppTasks.Common/GetOutOfDateItems.cs deleted file mode 100644 index 5b8e7cb..0000000 --- a/Microsoft.Build.CppTasks.Common/GetOutOfDateItems.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.Build.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.Build.CppTasks.Common -{ - // todo - public class GetOutOfDateItems : Microsoft.Build.Utilities.Task - { - public ITaskItem[] Sources { get; set; } - - [Required] - public string OutputsMetadataName { get; set; } - - public string DependenciesMetadataName { get; set; } - - public string CommandMetadataName { get; set; } - - [Required] - public string TLogDirectory { get; set; } - - [Required] - public string TLogNamePrefix { get; set; } - - public bool CheckForInterdependencies { get; set; } - - public bool TrackFileAccess { get; set; } - - [Output] - public ITaskItem[] OutOfDateSources { get; set; } - - [Output] - public bool HasInterdependencies { get; set; } - - public GetOutOfDateItems() - { - CheckForInterdependencies = false; - HasInterdependencies = false; - DependenciesMetadataName = null; - CommandMetadataName = null; - TrackFileAccess = true; - } - public override bool Execute() - { - return true; - } - } -} diff --git a/Microsoft.Build.CppTasks.Common/Microsoft.Build.CppTasks.Common.csproj b/Microsoft.Build.CppTasks.Common/Microsoft.Build.CppTasks.Common.csproj index 3c8f1f8..d0ad889 100644 --- a/Microsoft.Build.CppTasks.Common/Microsoft.Build.CppTasks.Common.csproj +++ b/Microsoft.Build.CppTasks.Common/Microsoft.Build.CppTasks.Common.csproj @@ -8,19 +8,17 @@ True + False + portable True + portable - - - - - - + @@ -39,4 +37,14 @@ + + + + + + + + + + diff --git a/Microsoft.Build.CppTasks.Common/Properties/Microsoft.Build.CPPTasks.Strings.Designer.cs b/Microsoft.Build.CppTasks.Common/Properties/Microsoft.Build.CPPTasks.Strings.Designer.cs index 497503f..03f83ba 100644 --- a/Microsoft.Build.CppTasks.Common/Properties/Microsoft.Build.CPPTasks.Strings.Designer.cs +++ b/Microsoft.Build.CppTasks.Common/Properties/Microsoft.Build.CPPTasks.Strings.Designer.cs @@ -59,5 +59,1330 @@ internal Microsoft_Build_CPPTasks_Strings() { resourceCulture = value; } } + + /// + /// 查找类似 Element <{0}> has an invalid value of "{1}". 的本地化字符串。 + /// + internal static string ArgumentOutOfRange { + get { + return ResourceManager.GetString("ArgumentOutOfRange", resourceCulture); + } + } + + /// + /// 查找类似 Switch <{0}> requires an argument. 的本地化字符串。 + /// + internal static string ArgumentRequired { + get { + return ResourceManager.GetString("ArgumentRequired", resourceCulture); + } + } + + /// + /// 查找类似 {0}Reference '{1}'='{2}' is ignored as '{1}'='{3}' already exists. 的本地化字符串。 + /// + internal static string BMIAlreadyReferenced { + get { + return ResourceManager.GetString("BMIAlreadyReferenced", resourceCulture); + } + } + + /// + /// 查找类似 Building header unit {0} as {1} imports it. 的本地化字符串。 + /// + internal static string BuildingHeaderUnit { + get { + return ResourceManager.GetString("BuildingHeaderUnit", resourceCulture); + } + } + + /// + /// 查找类似 MSB8078: Cannot delete old '{0}' tlog files: {1} 的本地化字符串。 + /// + internal static string CannotDeleteTlogs { + get { + return ResourceManager.GetString("CannotDeleteTlogs", resourceCulture); + } + } + + /// + /// 查找类似 Forcing recompile of all source files due to missing PDB "{0}". 的本地化字符串。 + /// + internal static string CL_RebuildingNoPDB { + get { + return ResourceManager.GetString("CL.RebuildingNoPDB", resourceCulture); + } + } + + /// + /// 查找类似 Expected "{0}" but got "{1}". 的本地化字符串。 + /// + internal static string CommandLineDiffer { + get { + return ResourceManager.GetString("CommandLineDiffer", resourceCulture); + } + } + + /// + /// 查找类似 Compiling... 的本地化字符串。 + /// + internal static string Compiling { + get { + return ResourceManager.GetString("Compiling", resourceCulture); + } + } + + /// + /// 查找类似 MSB8017: A circular dependency has been detected while executing custom build commands for item "{0}". This may cause incremental build to work incorrectly. 的本地化字符串。 + /// + internal static string CustomBuild_CircularDepedencyDetected { + get { + return ResourceManager.GetString("CustomBuild.CircularDepedencyDetected", resourceCulture); + } + } + + /// + /// 查找类似 MSB8066: Custom build for '{0}' exited with code {1}. 的本地化字符串。 + /// + internal static string CustomBuild_ExitCodeError { + get { + return ResourceManager.GetString("CustomBuild.ExitCodeError", resourceCulture); + } + } + + /// + /// 查找类似 MSB8062: Custom build for item "{0}" specifies invalid path "{1}" as an additional dependency. This may cause incremental build to work incorrectly. 的本地化字符串。 + /// + internal static string CustomBuild_InvalidDependency { + get { + return ResourceManager.GetString("CustomBuild.InvalidDependency", resourceCulture); + } + } + + /// + /// 查找类似 MSB8063: Custom build for item "{0}" specifies invalid path "{1}" as an output. This may cause incremental build to work incorrectly. 的本地化字符串。 + /// + internal static string CustomBuild_InvalidOutput { + get { + return ResourceManager.GetString("CustomBuild.InvalidOutput", resourceCulture); + } + } + + /// + /// 查找类似 MSB8064: Custom build for item "{0}" succeeded, but specified dependency "{1}" does not exist. This may cause incremental build to work incorrectly. 的本地化字符串。 + /// + internal static string CustomBuild_MissingDependency { + get { + return ResourceManager.GetString("CustomBuild.MissingDependency", resourceCulture); + } + } + + /// + /// 查找类似 MSB8065: Custom build for item "{0}" succeeded, but specified output "{1}" has not been created. This may cause incremental build to work incorrectly. 的本地化字符串。 + /// + internal static string CustomBuild_MissingOutput { + get { + return ResourceManager.GetString("CustomBuild.MissingOutput", resourceCulture); + } + } + + /// + /// 查找类似 MSB8018: No outputs specified for item "{0}". Its custom build command will be skipped. 的本地化字符串。 + /// + internal static string CustomBuild_NoOutputs { + get { + return ResourceManager.GetString("CustomBuild.NoOutputs", resourceCulture); + } + } + + /// + /// 查找类似 Performing Custom Build Step 的本地化字符串。 + /// + internal static string CustomBuildStepMessage { + get { + return ResourceManager.GetString("CustomBuildStepMessage", resourceCulture); + } + } + + /// + /// 查找类似 Required file "{0}" is missing. 的本地化字符串。 + /// + internal static string Error_MissingFile { + get { + return ResourceManager.GetString("Error.MissingFile", resourceCulture); + } + } + + /// + /// 查找类似 Unable to open file {0} because {1} 的本地化字符串。 + /// + internal static string FileNotOpen { + get { + return ResourceManager.GetString("FileNotOpen", resourceCulture); + } + } + + /// + /// 查找类似 MSB3098: "{1}" task received an invalid value for the "{0}" parameter. 的本地化字符串。 + /// + internal static string General_InvalidValue { + get { + return ResourceManager.GetString("General.InvalidValue", resourceCulture); + } + } + + /// + /// 查找类似 Error generating command line switch for '{0}' with value '{1}': {2} 的本地化字符串。 + /// + internal static string GenerateCommandLineError { + get { + return ResourceManager.GetString("GenerateCommandLineError", resourceCulture); + } + } + + /// + /// 查找类似 Remote deployment might be slow/inefficient. {0} 的本地化字符串。 + /// + internal static string GenerateDesktopDeployRecipeFileException { + get { + return ResourceManager.GetString("GenerateDesktopDeployRecipeFileException", resourceCulture); + } + } + + /// + /// 查找类似 "The build of '{0}' depends on '{1}' which is produced by the build of '{2}'. The items cannot be built in parallel." 的本地化字符串。 + /// + internal static string GetOutOfDateItems_ItemDependsOnAnotherItemOutput { + get { + return ResourceManager.GetString("GetOutOfDateItems.ItemDependsOnAnotherItemOutput", resourceCulture); + } + } + + /// + /// 查找类似 Source file '{0}' is not up-to-date: list of dependencies has changed since the last build. 的本地化字符串。 + /// + internal static string GetOutOfDateItems_RebuildingSourceDependenciesChanged { + get { + return ResourceManager.GetString("GetOutOfDateItems.RebuildingSourceDependenciesChanged", resourceCulture); + } + } + + /// + /// 查找类似 Source file '{0}' is not up-to-date: list of outputs has changed since the last build. 的本地化字符串。 + /// + internal static string GetOutOfDateItems_RebuildingSourceOutputsChanged { + get { + return ResourceManager.GetString("GetOutOfDateItems.RebuildingSourceOutputsChanged", resourceCulture); + } + } + + /// + /// 查找类似 Element <{0}> has invalid type of "{1}". 的本地化字符串。 + /// + internal static string ImproperType { + get { + return ResourceManager.GetString("ImproperType", resourceCulture); + } + } + + /// + /// 查找类似 Internal Error has occurred. Most likely a bug. 的本地化字符串。 + /// + internal static string InternalError { + get { + return ResourceManager.GetString("InternalError", resourceCulture); + } + } + + /// + /// 查找类似 Expecting "{0}". 的本地化字符串。 + /// + internal static string InvalidType { + get { + return ResourceManager.GetString("InvalidType", resourceCulture); + } + } + + /// + /// 查找类似 MSB3511: "{0}" is an invalid value for the "Importance" parameter. Valid values are: High, Normal and Low. 的本地化字符串。 + /// + internal static string Message_InvalidImportance { + get { + return ResourceManager.GetString("Message.InvalidImportance", resourceCulture); + } + } + + /// + /// 查找类似 To improve incremental build performance for managed components, please make sure that the 'VC++ Directories->Reference Directories' points to all the paths which contain the referenced managed assemblies. 的本地化字符串。 + /// + internal static string MetaGenInfoReferenceDirectories { + get { + return ResourceManager.GetString("MetaGenInfoReferenceDirectories", resourceCulture); + } + } + + /// + /// 查找类似 Element <{0}> does not contain the required attribute "{1}". 的本地化字符串。 + /// + internal static string MissingAttribute { + get { + return ResourceManager.GetString("MissingAttribute", resourceCulture); + } + } + + /// + /// 查找类似 Missing required argument <{0}> for property <{1}>. 的本地化字符串。 + /// + internal static string MissingRequiredArgument { + get { + return ResourceManager.GetString("MissingRequiredArgument", resourceCulture); + } + } + + /// + /// 查找类似 Missing required argument <{0}> for property <{1}> which is set to {2}. 的本地化字符串。 + /// + internal static string MissingRequiredArgumentWithValue { + get { + return ResourceManager.GetString("MissingRequiredArgumentWithValue", resourceCulture); + } + } + + /// + /// 查找类似 Xml file must start with the root element <{0}>. 的本地化字符串。 + /// + internal static string MissingRootElement { + get { + return ResourceManager.GetString("MissingRootElement", resourceCulture); + } + } + + /// + /// 查找类似 MSB8074: Cannot read Module Dependencies file {0}: {1} The build order might be incorrect. 的本地化字符串。 + /// + internal static string ModuleDependencies_JsonReadingError { + get { + return ResourceManager.GetString("ModuleDependencies.JsonReadingError", resourceCulture); + } + } + + /// + /// 查找类似 MSB8067: Unexpected number of sources '{0}' in Module Dependencies file '{1}'. Only one source data is currenty supported. 的本地化字符串。 + /// + internal static string ModuleDependencies_UnexpectedJsonData { + get { + return ResourceManager.GetString("ModuleDependencies.UnexpectedJsonData", resourceCulture); + } + } + + /// + /// 查找类似 Unable to move file "{0}" to "{1}". {2} 的本地化字符串。 + /// + internal static string Move_Error { + get { + return ResourceManager.GetString("Move.Error", resourceCulture); + } + } + + /// + /// 查找类似 Moving file from "{0}" to "{1}". 的本地化字符串。 + /// + internal static string Move_FileComment { + get { + return ResourceManager.GetString("Move.FileComment", resourceCulture); + } + } + + /// + /// 查找类似 {0} will run on {1} out of {2} file(s) in {4} batches. Startup phase took {3}ms. 的本地化字符串。 + /// + internal static string MultiTool_AddDone { + get { + return ResourceManager.GetString("MultiTool.AddDone", resourceCulture); + } + } + + /// + /// 查找类似 Adding source "{0}". 的本地化字符串。 + /// + internal static string MultiTool_AddSource { + get { + return ResourceManager.GetString("MultiTool.AddSource", resourceCulture); + } + } + + /// + /// 查找类似 Adding source "{0}" with dependency on "{1}". 的本地化字符串。 + /// + internal static string MultiTool_AddSourceWithDep { + get { + return ResourceManager.GetString("MultiTool.AddSourceWithDep", resourceCulture); + } + } + + /// + /// 查找类似 Building with "{0}". 的本地化字符串。 + /// + internal static string MultiTool_BuildingWith { + get { + return ResourceManager.GetString("MultiTool.BuildingWith", resourceCulture); + } + } + + /// + /// 查找类似 Cannot build the following source files because there is a cyclic dependency between them: {0}. 的本地化字符串。 + /// + internal static string MultiTool_CyclicDependency { + get { + return ResourceManager.GetString("MultiTool.CyclicDependency", resourceCulture); + } + } + + /// + /// 查找类似 MultiToolTask has encounted an issue scheduling task because one or more tasks still remains but none could be started. Please check the inputs and their dependency to avoid cirular loops. 的本地化字符串。 + /// + internal static string MultiTool_DependencyIssue { + get { + return ResourceManager.GetString("MultiTool.DependencyIssue", resourceCulture); + } + } + + /// + /// 查找类似 Cleanup phase took {0}ms. 的本地化字符串。 + /// + internal static string MultiTool_PostExecuteToolTiming { + get { + return ResourceManager.GetString("MultiTool.PostExecuteToolTiming", resourceCulture); + } + } + + /// + /// 查找类似 All Sources must have the same TrackerLogDirectory ({0},{1}). 的本地化字符串。 + /// + internal static string MultiTool_SameTrackerLogDirectory { + get { + return ResourceManager.GetString("MultiTool.SameTrackerLogDirectory", resourceCulture); + } + } + + /// + /// 查找类似 Source "{0}" doesn't match previous command line. + ///*** Previous command line: '{1}' + ///*** Current command line: '{2}' 的本地化字符串。 + /// + internal static string MultiTool_SourceNotMatchCommand { + get { + return ResourceManager.GetString("MultiTool.SourceNotMatchCommand", resourceCulture); + } + } + + /// + /// 查找类似 Source "{0}" is out of date. 的本地化字符串。 + /// + internal static string MultiTool_SourceOutOfDate { + get { + return ResourceManager.GetString("MultiTool.SourceOutOfDate", resourceCulture); + } + } + + /// + /// 查找类似 {0} task found. 的本地化字符串。 + /// + internal static string MultiTool_TaskFound { + get { + return ResourceManager.GetString("MultiTool.TaskFound", resourceCulture); + } + } + + /// + /// 查找类似 Task '{0}' took {1}ms. 的本地化字符串。 + /// + internal static string MultiTool_TaskStatus { + get { + return ResourceManager.GetString("MultiTool.TaskStatus", resourceCulture); + } + } + + /// + /// 查找类似 Tracking command: 的本地化字符串。 + /// + internal static string Native_TrackingCommandMessage { + get { + return ResourceManager.GetString("Native_TrackingCommandMessage", resourceCulture); + } + } + + /// + /// 查找类似 {0}Referencing '{1}'='{2}'. 的本地化字符串。 + /// + internal static string ReferencingBMI { + get { + return ResourceManager.GetString("ReferencingBMI", resourceCulture); + } + } + + /// + /// 查找类似 Scanning sources for module dependencies... 的本地化字符串。 + /// + internal static string Scanning { + get { + return ResourceManager.GetString("Scanning", resourceCulture); + } + } + + /// + /// 查找类似 MSB5003: Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} 的本地化字符串。 + /// + internal static string Shared_FailedCreatingTempFile { + get { + return ResourceManager.GetString("Shared.FailedCreatingTempFile", resourceCulture); + } + } + + /// + /// 查找类似 Parameter "{0}" cannot be null. 的本地化字符串。 + /// + internal static string Shared_ParameterCannotBeNull { + get { + return ResourceManager.GetString("Shared.ParameterCannotBeNull", resourceCulture); + } + } + + /// + /// 查找类似 Parameter "{0}" cannot have zero length. 的本地化字符串。 + /// + internal static string Shared_ParameterCannotHaveZeroLength { + get { + return ResourceManager.GetString("Shared.ParameterCannotHaveZeroLength", resourceCulture); + } + } + + /// + /// 查找类似 Parameters "{0}" and "{1}" must have the same number of elements. 的本地化字符串。 + /// + internal static string Shared_ParametersMustHaveTheSameLength { + get { + return ResourceManager.GetString("Shared.ParametersMustHaveTheSameLength", resourceCulture); + } + } + + /// + /// 查找类似 {0} depends on {1} 的本地化字符串。 + /// + internal static string SourceDependsOnSource { + get { + return ResourceManager.GetString("SourceDependsOnSource", resourceCulture); + } + } + + /// + /// 查找类似 MSB8071: Cannot parse tool output '{0}' with regex '{1}': {2} 的本地化字符串。 + /// + internal static string TrackedVCToolTask_CannotParseToolOutput { + get { + return ResourceManager.GetString("TrackedVCToolTask.CannotParseToolOutput", resourceCulture); + } + } + + /// + /// 查找类似 MSB8016: Can not turn on Unicode output for "{0}". Some Unicode characters will be improperly displayed. 的本地化字符串。 + /// + internal static string TrackedVCToolTask_CreateUnicodeOutputPipeFailed { + get { + return ResourceManager.GetString("TrackedVCToolTask.CreateUnicodeOutputPipeFailed", resourceCulture); + } + } + + /// + /// 查找类似 All source files are not up-to-date: command line has changed since the last build. 的本地化字符串。 + /// + internal static string TrackedVCToolTask_RebuildingAllSourcesCommandLineChanged { + get { + return ResourceManager.GetString("TrackedVCToolTask.RebuildingAllSourcesCommandLineChanged", resourceCulture); + } + } + + /// + /// 查找类似 MSB8014: All source files are not up-to-date: forcing a rebuild of all sources due to an error with the tracking logs. {0} 的本地化字符串。 + /// + internal static string TrackedVCToolTask_RebuildingDueToInvalidTLog { + get { + return ResourceManager.GetString("TrackedVCToolTask.RebuildingDueToInvalidTLog", resourceCulture); + } + } + + /// + /// 查找类似 MSB8015: All source files are not up-to-date: forcing a rebuild of all source files due to the contents of '{0}' being invalid. 的本地化字符串。 + /// + internal static string TrackedVCToolTask_RebuildingDueToInvalidTLogContents { + get { + return ResourceManager.GetString("TrackedVCToolTask.RebuildingDueToInvalidTLogContents", resourceCulture); + } + } + + /// + /// 查找类似 All source files are not up-to-date: missing command TLog "{0}". 的本地化字符串。 + /// + internal static string TrackedVCToolTask_RebuildingNoCommandTLog { + get { + return ResourceManager.GetString("TrackedVCToolTask.RebuildingNoCommandTLog", resourceCulture); + } + } + + /// + /// 查找类似 Source file "{0}" is not up-to-date: command line has changed since the last build. 的本地化字符串。 + /// + internal static string TrackedVCToolTask_RebuildingSourceCommandLineChanged { + get { + return ResourceManager.GetString("TrackedVCToolTask.RebuildingSourceCommandLineChanged", resourceCulture); + } + } + + /// + /// 查找类似 "The following files previously generated for unity build were deleted as they are no longer used: {0}. 的本地化字符串。 + /// + internal static string Unity_CleanupOldUnityFiles { + get { + return ResourceManager.GetString("Unity_CleanupOldUnityFiles", resourceCulture); + } + } + + /// + /// 查找类似 "Minimum Number of Sources in a Unity File" ({0}) cannot be more than "Maximum Number of Sources in a Unity File" ({1}). 的本地化字符串。 + /// + internal static string Unity_MinMoreThanMax { + get { + return ResourceManager.GetString("Unity_MinMoreThanMax", resourceCulture); + } + } + + /// + /// 查找类似 MSB8001: "{0}" is an invalid value for the "Code" parameter. 的本地化字符串。 + /// + internal static string VCMessage_InvalidCode { + get { + return ResourceManager.GetString("VCMessage.InvalidCode", resourceCulture); + } + } + + /// + /// 查找类似 MSB8000: "{0}" is an invalid value for the "Type" parameter. Valid values are: Warning and Error. 的本地化字符串。 + /// + internal static string VCMessage_InvalidType { + get { + return ResourceManager.GetString("VCMessage.InvalidType", resourceCulture); + } + } + + /// + /// 查找类似 MSB8002: Specified platform toolset ({0}) is not compatible with the targeted .NET Framework version ({1}). Please set TargetFrameworkVersion to one of the supported values ('v2.0', 'v3.0', 'v3.5'). 的本地化字符串。 + /// + internal static string VCMessage_MSB8002 { + get { + return ResourceManager.GetString("VCMessage.MSB8002", resourceCulture); + } + } + + /// + /// 查找类似 MSB8003: The {0} property is not defined. Some build tools may not be found. 的本地化字符串。 + /// + internal static string VCMessage_MSB8003 { + get { + return ResourceManager.GetString("VCMessage.MSB8003", resourceCulture); + } + } + + /// + /// 查找类似 MSB8004: {0} Directory does not end with a trailing slash. This build instance will add the slash as it is required to allow proper evaluation of the {1} Directory. 的本地化字符串。 + /// + internal static string VCMessage_MSB8004 { + get { + return ResourceManager.GetString("VCMessage.MSB8004", resourceCulture); + } + } + + /// + /// 查找类似 MSB8005: The property '{0}' doesn't exist. Skipping... 的本地化字符串。 + /// + internal static string VCMessage_MSB8005 { + get { + return ResourceManager.GetString("VCMessage.MSB8005", resourceCulture); + } + } + + /// + /// 查找类似 MSB8006: The Platform for project '{0}' is invalid. Platform='{1}'. This error may also appear if some other project is trying to follow a project-to-project reference to this project, this project has been unloaded or is not included in the solution, and the referencing project does not build using the same or an equivalent Platform. 的本地化字符串。 + /// + internal static string VCMessage_MSB8006 { + get { + return ResourceManager.GetString("VCMessage.MSB8006", resourceCulture); + } + } + + /// + /// 查找类似 MSB8007: The Platform for project '{0}' is invalid. Platform='{1}'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Platform that doesn't exist for this project. 的本地化字符串。 + /// + internal static string VCMessage_MSB8007 { + get { + return ResourceManager.GetString("VCMessage.MSB8007", resourceCulture); + } + } + + /// + /// 查找类似 MSB8008: Specified platform toolset ({0}) is not installed or invalid. Please make sure that a supported PlatformToolset value is selected. 的本地化字符串。 + /// + internal static string VCMessage_MSB8008 { + get { + return ResourceManager.GetString("VCMessage.MSB8008", resourceCulture); + } + } + + /// + /// 查找类似 MSB8009: .NET Framework 2.0/3.0/3.5 target the v90 platform toolset. Please make sure that Visual Studio 2008 is installed on the machine. 的本地化字符串。 + /// + internal static string VCMessage_MSB8009 { + get { + return ResourceManager.GetString("VCMessage.MSB8009", resourceCulture); + } + } + + /// + /// 查找类似 MSB8010: Specified platform toolset (v90) requires Visual Studio 2008. Please make sure that Visual Studio 2008 is installed on the machine. 的本地化字符串。 + /// + internal static string VCMessage_MSB8010 { + get { + return ResourceManager.GetString("VCMessage.MSB8010", resourceCulture); + } + } + + /// + /// 查找类似 MSB8011: Failed to register output. Please try enabling Per-user Redirection or register the component from a command prompt with elevated permissions. 的本地化字符串。 + /// + internal static string VCMessage_MSB8011 { + get { + return ResourceManager.GetString("VCMessage.MSB8011", resourceCulture); + } + } + + /// + /// 查找类似 MSB8012: {0}({1}) does not match the {2}'s OutputFile property value ({3}). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in %({4}.OutputFile). 的本地化字符串。 + /// + internal static string VCMessage_MSB8012 { + get { + return ResourceManager.GetString("VCMessage.MSB8012", resourceCulture); + } + } + + /// + /// 查找类似 MSB8013: This project doesn't contain the Configuration and Platform combination of {0}. 的本地化字符串。 + /// + internal static string VCMessage_MSB8013 { + get { + return ResourceManager.GetString("VCMessage.MSB8013", resourceCulture); + } + } + + /// + /// 查找类似 MSB8016: This project does not support the current Configuration Type ({0}). 的本地化字符串。 + /// + internal static string VCMessage_MSB8016 { + get { + return ResourceManager.GetString("VCMessage.MSB8016", resourceCulture); + } + } + + /// + /// 查找类似 MSB8019: This build is consuming a component "{0}" that is not packaged because the component is not coming from a Windows Store app project "{1}". 的本地化字符串。 + /// + internal static string VCMessage_MSB8019 { + get { + return ResourceManager.GetString("VCMessage.MSB8019", resourceCulture); + } + } + + /// + /// 查找类似 MSB8020: The build tools for {0} (Platform Toolset = '{1}') cannot be found. To build using the {1} build tools, please install {0} build tools. Alternatively, you may upgrade to the current Visual Studio tools by selecting the Project menu or right-click the solution, and then selecting "Retarget solution". 的本地化字符串。 + /// + internal static string VCMessage_MSB8020 { + get { + return ResourceManager.GetString("VCMessage.MSB8020", resourceCulture); + } + } + + /// + /// 查找类似 MSB8020: The build tools for '{2}' application Type {0} (Platform Toolset = '{1}') cannot be found. Please install the workflow for this application type and include the build tools for the 的本地化字符串。 + /// + internal static string VCMessage_MSB8020_AppType { + get { + return ResourceManager.GetString("VCMessage.MSB8020_AppType", resourceCulture); + } + } + + /// + /// 查找类似 MSB8021: The value '{0}' of the variable '{1}' is incompatible with the value '{2}' of the variable '{3}'. 的本地化字符串。 + /// + internal static string VCMessage_MSB8021 { + get { + return ResourceManager.GetString("VCMessage.MSB8021", resourceCulture); + } + } + + /// + /// 查找类似 MSB8022: Building Desktop applications for the {0} platform is not supported. 的本地化字符串。 + /// + internal static string VCMessage_MSB8022 { + get { + return ResourceManager.GetString("VCMessage.MSB8022", resourceCulture); + } + } + + /// + /// 查找类似 MSB8023: Execution path ({0}) could not be found. 的本地化字符串。 + /// + internal static string VCMessage_MSB8023 { + get { + return ResourceManager.GetString("VCMessage.MSB8023", resourceCulture); + } + } + + /// + /// 查找类似 MSB8024: Using static version of the C++ runtime library is not supported. 的本地化字符串。 + /// + internal static string VCMessage_MSB8024 { + get { + return ResourceManager.GetString("VCMessage.MSB8024", resourceCulture); + } + } + + /// + /// 查找类似 MSB8025: Using legacy manifest embedding because of {0} on Manifest Tool is set. 的本地化字符串。 + /// + internal static string VCMessage_MSB8025 { + get { + return ResourceManager.GetString("VCMessage.MSB8025", resourceCulture); + } + } + + /// + /// 查找类似 MSB8026: Static analysis is not supported with the current platform toolset. 的本地化字符串。 + /// + internal static string VCMessage_MSB8026 { + get { + return ResourceManager.GetString("VCMessage.MSB8026", resourceCulture); + } + } + + /// + /// 查找类似 MSB8027: Two or more files with the name of {0} will produce outputs to the same location. This can lead to an incorrect build result. The files involved are {1}. 的本地化字符串。 + /// + internal static string VCMessage_MSB8027 { + get { + return ResourceManager.GetString("VCMessage.MSB8027", resourceCulture); + } + } + + /// + /// 查找类似 MSB8028: The intermediate directory ({1}) contains files shared from another project ({0}). This can lead to incorrect clean and rebuild behavior. 的本地化字符串。 + /// + internal static string VCMessage_MSB8028 { + get { + return ResourceManager.GetString("VCMessage.MSB8028", resourceCulture); + } + } + + /// + /// 查找类似 MSB8029: The Intermediate directory or Output directory cannot reside under the Temporary directory as it could lead to issues with incremental build. 的本地化字符串。 + /// + internal static string VCMessage_MSB8029 { + get { + return ResourceManager.GetString("VCMessage.MSB8029", resourceCulture); + } + } + + /// + /// 查找类似 MSB8030: The linker switch "Minimum Required Version" requires "SubSystem" to be set. Without "SubSystem", the "Minimum Required Version" would not be passed to linker and could prevent to the output binary from running on older Operating Systems. 的本地化字符串。 + /// + internal static string VCMessage_MSB8030 { + get { + return ResourceManager.GetString("VCMessage.MSB8030", resourceCulture); + } + } + + /// + /// 查找类似 MSB8031: Building an MFC project for a non-Unicode character set is deprecated. You must change the project property to Unicode or download an additional library. See https://go.microsoft.com/fwlink/p/?LinkId=286820 for more information. 的本地化字符串。 + /// + internal static string VCMessage_MSB8031 { + get { + return ResourceManager.GetString("VCMessage.MSB8031", resourceCulture); + } + } + + /// + /// 查找类似 MSB8032: The Platform or PlatformToolset is not available from a 64bit environment. Consider building from 32bit environment instead. 的本地化字符串。 + /// + internal static string VCMessage_MSB8032 { + get { + return ResourceManager.GetString("VCMessage.MSB8032", resourceCulture); + } + } + + /// + /// 查找类似 {0} -> {1} (Partial PDB) 的本地化字符串。 + /// + internal static string VCMessage_MSB8033 { + get { + return ResourceManager.GetString("VCMessage.MSB8033", resourceCulture); + } + } + + /// + /// 查找类似 MSB8033: Cannot determine a remote location corresponding to {0} directory on local machine. Some files might not be found during remote build. Please change the properties to use relative paths or add a local-remote folders mapping in Tools - Options - Cross Platform – C++ - iOS - Mapping. 的本地化字符串。 + /// + internal static string VCMessage_MSB8033_RemoteBuild_MissingDirectoryMapping { + get { + return ResourceManager.GetString("VCMessage.MSB8033.RemoteBuild.MissingDirectoryMapping", resourceCulture); + } + } + + /// + /// 查找类似 {0} -> {1} (Full PDB) 的本地化字符串。 + /// + internal static string VCMessage_MSB8034 { + get { + return ResourceManager.GetString("VCMessage.MSB8034", resourceCulture); + } + } + + /// + /// 查找类似 {0} -> {1} (Partial -> Full PDB) 的本地化字符串。 + /// + internal static string VCMessage_MSB8035 { + get { + return ResourceManager.GetString("VCMessage.MSB8035", resourceCulture); + } + } + + /// + /// 查找类似 MSB8035: Root folder for local relative paths is not defined, but relative path {0} is used. Please change it to a full path or define root folder. 的本地化字符串。 + /// + internal static string VCMessage_MSB8035_RemoteBuild_MissingRootFolderForLocalRelativePaths { + get { + return ResourceManager.GetString("VCMessage.MSB8035.RemoteBuild.MissingRootFolderForLocalRelativePaths", resourceCulture); + } + } + + /// + /// 查找类似 MSB8036: The Windows SDK version {0} was not found. Install the required version of Windows SDK or change the SDK version in the project property pages or by right-clicking the solution and selecting "Retarget solution". 的本地化字符串。 + /// + internal static string VCMessage_MSB8036 { + get { + return ResourceManager.GetString("VCMessage.MSB8036", resourceCulture); + } + } + + /// + /// 查找类似 MSB8037: The Windows SDK version {0} for Desktop C++ {1} Apps was not found. Install the required version of Windows SDK or change the SDK version in the project property pages or by right-clicking the solution and selecting "Retarget solution". 的本地化字符串。 + /// + internal static string VCMessage_MSB8037 { + get { + return ResourceManager.GetString("VCMessage.MSB8037", resourceCulture); + } + } + + /// + /// 查找类似 MSB8038: Platform Toolset is not defined. Please select one of the available Platform Toolsets in the Project Properties UI. 的本地化字符串。 + /// + internal static string VCMessage_MSB8038 { + get { + return ResourceManager.GetString("VCMessage.MSB8038", resourceCulture); + } + } + + /// + /// 查找类似 Warning: Full debug symbol generation from partial PDBs is not supported for static libraries. 的本地化字符串。 + /// + internal static string VCMessage_MSB8039 { + get { + return ResourceManager.GetString("VCMessage.MSB8039", resourceCulture); + } + } + + /// + /// 查找类似 MSB8040: Spectre-mitigated libraries are required for this project. Install them from the Visual Studio installer (Individual components tab) for any toolsets and architectures being used. Learn more: https://aka.ms/Ofhn4c 的本地化字符串。 + /// + internal static string VCMessage_MSB8040 { + get { + return ResourceManager.GetString("VCMessage.MSB8040", resourceCulture); + } + } + + /// + /// 查找类似 MSB8041: {0} libraries are required for this project. Install them from the Visual Studio installer (Individual Components tab) for any toolsets and architectures being used. 的本地化字符串。 + /// + internal static string VCMessage_MSB8041 { + get { + return ResourceManager.GetString("VCMessage.MSB8041", resourceCulture); + } + } + + /// + /// 查找类似 MSB8042: {0} libraries with Spectre Mitigations are required for this project. Install them from the Visual Studio installer (Individual components tab) for any toolsets and architectures being used. Learn more: https://aka.ms/Ofhn4c 的本地化字符串。 + /// + internal static string VCMessage_MSB8042 { + get { + return ResourceManager.GetString("VCMessage.MSB8042", resourceCulture); + } + } + + /// + /// 查找类似 MSB8051: Support for targeting Windows XP is deprecated and will not be present in future releases of Visual Studio. Please see https://go.microsoft.com/fwlink/?linkid=2023588 for more information. 的本地化字符串。 + /// + internal static string VCMessage_MSB8051 { + get { + return ResourceManager.GetString("VCMessage.MSB8051", resourceCulture); + } + } + + /// + /// 查找类似 MSB8053: /clr (common language runtime) not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8053 { + get { + return ResourceManager.GetString("VCMessage.MSB8053", resourceCulture); + } + } + + /// + /// 查找类似 MSB8054: /ZW (WinRT language extensions) not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8054 { + get { + return ResourceManager.GetString("VCMessage.MSB8054", resourceCulture); + } + } + + /// + /// 查找类似 MSB8055: /openmp (OpenMP language extensions) not supported not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8055 { + get { + return ResourceManager.GetString("VCMessage.MSB8055", resourceCulture); + } + } + + /// + /// 查找类似 MSB8056: /experimental:module not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8056 { + get { + return ResourceManager.GetString("VCMessage.MSB8056", resourceCulture); + } + } + + /// + /// 查找类似 MSB8057: 'C++ Clang tools for Windows (15.0.1 - x64/x86)' component is not installed in Visual Studio. Open the 'Visual Studio Installer' and install 'C++ Clang tools for Windows (15.0.1 - x64/x86)' under the 'Desktop development with C++' workload. Learn more: https://aka.ms/AA55s5p 的本地化字符串。 + /// + internal static string VCMessage_MSB8057 { + get { + return ResourceManager.GetString("VCMessage.MSB8057", resourceCulture); + } + } + + /// + /// 查找类似 MSB8058: -fsanitize=address (Enable Address Sanitizer) not supported by platform '{0}'. 的本地化字符串。 + /// + internal static string VCMessage_MSB8058 { + get { + return ResourceManager.GetString("VCMessage.MSB8058", resourceCulture); + } + } + + /// + /// 查找类似 MSB8059: -fsanitize=address (Enable Address Sanitizer) is incompatible with option '{0}'. 的本地化字符串。 + /// + internal static string VCMessage_MSB8059 { + get { + return ResourceManager.GetString("VCMessage.MSB8059", resourceCulture); + } + } + + /// + /// 查找类似 MSB8060: Microsoft Code Analysis is not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8060 { + get { + return ResourceManager.GetString("VCMessage.MSB8060", resourceCulture); + } + } + + /// + /// 查找类似 MSB8061: No tool enabled for Code Analysis. 的本地化字符串。 + /// + internal static string VCMessage_MSB8061 { + get { + return ResourceManager.GetString("VCMessage.MSB8061", resourceCulture); + } + } + + /// + /// 查找类似 MSB8072: /std:c++latest (MSVC preview - features from the latest C++ working draft ) not supported by clang-cl. 的本地化字符串。 + /// + internal static string VCMessage_MSB8072 { + get { + return ResourceManager.GetString("VCMessage.MSB8072", resourceCulture); + } + } + + /// + /// 查找类似 MSB8073: Cannot find LLVM toolset version '{0}': the folder '{1}' does not exist. Please make sure LLVM toolset version '{0}' is installed or choose another toolset version. Learn more: https://aka.ms/AA55s5p 的本地化字符串。 + /// + internal static string VCMessage_MSB8073 { + get { + return ResourceManager.GetString("VCMessage.MSB8073", resourceCulture); + } + } + + /// + /// 查找类似 MSB8075: We do not support building UWP AppContainer applications for ARM64EC. 的本地化字符串。 + /// + internal static string VCMessage_MSB8075 { + get { + return ResourceManager.GetString("VCMessage.MSB8075", resourceCulture); + } + } + + /// + /// 查找类似 MSB8076: Only ARM, ARM64 and ARM64EC platforms are supported. 的本地化字符串。 + /// + internal static string VCMessage_MSB8076 { + get { + return ResourceManager.GetString("VCMessage.MSB8076", resourceCulture); + } + } + + /// + /// 查找类似 MSB8077: Some files are set to compile as C++/CLI, but 'Enable CLR Support for Individual Files' property is not defined. See 'Advanced Property Page' documentation for more details. 的本地化字符串。 + /// + internal static string VCMessage_MSB8077 { + get { + return ResourceManager.GetString("VCMessage.MSB8077", resourceCulture); + } + } + + /// + /// 查找类似 Adding the project to the build agent... 的本地化字符串。 + /// + internal static string VCRemote_AddingProject { + get { + return ResourceManager.GetString("VCRemote.AddingProject", resourceCulture); + } + } + + /// + /// 查找类似 Adding the sources to the build agent... 的本地化字符串。 + /// + internal static string VCRemote_AddingSources { + get { + return ResourceManager.GetString("VCRemote.AddingSources", resourceCulture); + } + } + + /// + /// 查找类似 Remote build failed, a remote machine has not been configured for build or the connection was lost. If a machine was not paired, please pair it by using the instructions in this link: {0} 的本地化字符串。 + /// + internal static string VCRemote_BuildFailed { + get { + return ResourceManager.GetString("VCRemote.BuildFailed", resourceCulture); + } + } + + /// + /// 查找类似 Remote build failed, please see the Output Window - Build output for more details. 的本地化字符串。 + /// + internal static string VCRemote_BuildFailedSeeOutputWindow { + get { + return ResourceManager.GetString("VCRemote.BuildFailedSeeOutputWindow", resourceCulture); + } + } + + /// + /// 查找类似 Building the project on the build agent... 的本地化字符串。 + /// + internal static string VCRemote_BuildingProject { + get { + return ResourceManager.GetString("VCRemote.BuildingProject", resourceCulture); + } + } + + /// + /// 查找类似 Building the source maps... 的本地化字符串。 + /// + internal static string VCRemote_BuildingSourceMap { + get { + return ResourceManager.GetString("VCRemote.BuildingSourceMap", resourceCulture); + } + } + + /// + /// 查找类似 {0}.vcxproj -> host: {1} output: {2}/{3}{4} 的本地化字符串。 + /// + internal static string VCRemote_BuildOutput { + get { + return ResourceManager.GetString("VCRemote.BuildOutput", resourceCulture); + } + } + + /// + /// 查找类似 Build started on the build agent. 的本地化字符串。 + /// + internal static string VCRemote_BuildStarted { + get { + return ResourceManager.GetString("VCRemote.BuildStarted", resourceCulture); + } + } + + /// + /// 查找类似 Copying remote build output to: {0}. 的本地化字符串。 + /// + internal static string VCRemote_CopyingBuildOutput { + get { + return ResourceManager.GetString("VCRemote.CopyingBuildOutput", resourceCulture); + } + } + + /// + /// 查找类似 The file '{0}' will not be added to the build agent because it is invalid. 的本地化字符串。 + /// + internal static string VCRemote_FileInvalid { + get { + return ResourceManager.GetString("VCRemote.FileInvalid", resourceCulture); + } + } + + /// + /// 查找类似 The file '{0}' will not be added to the build agent because it does not exist. 的本地化字符串。 + /// + internal static string VCRemote_FileNotExist { + get { + return ResourceManager.GetString("VCRemote.FileNotExist", resourceCulture); + } + } + + /// + /// 查找类似 Finished adding the project to the build agent. 的本地化字符串。 + /// + internal static string VCRemote_FinishedAddingProject { + get { + return ResourceManager.GetString("VCRemote.FinishedAddingProject", resourceCulture); + } + } + + /// + /// 查找类似 Finished adding the sources to the build agent. 的本地化字符串。 + /// + internal static string VCRemote_FinishedAddingSources { + get { + return ResourceManager.GetString("VCRemote.FinishedAddingSources", resourceCulture); + } + } + + /// + /// 查找类似 Finished building the project on the build agent. 的本地化字符串。 + /// + internal static string VCRemote_FinishedBuildingProject { + get { + return ResourceManager.GetString("VCRemote.FinishedBuildingProject", resourceCulture); + } + } + + /// + /// 查找类似 Finished copying remote build output. 的本地化字符串。 + /// + internal static string VCRemote_FinishedCopyingBuildOutput { + get { + return ResourceManager.GetString("VCRemote.FinishedCopyingBuildOutput", resourceCulture); + } + } + + /// + /// 查找类似 Remote output directory: {0} 的本地化字符串。 + /// + internal static string VCRemote_RemoteOutputDirectory { + get { + return ResourceManager.GetString("VCRemote.RemoteOutputDirectory", resourceCulture); + } + } + + /// + /// 查找类似 Remote project: {0} 的本地化字符串。 + /// + internal static string VCRemote_RemoteProject { + get { + return ResourceManager.GetString("VCRemote.RemoteProject", resourceCulture); + } + } + + /// + /// 查找类似 Remote project directory: {0} 的本地化字符串。 + /// + internal static string VCRemote_RemoteProjectDirectory { + get { + return ResourceManager.GetString("VCRemote.RemoteProjectDirectory", resourceCulture); + } + } + + /// + /// 查找类似 Waiting for the build to finish on the build agent... 的本地化字符串。 + /// + internal static string VCRemote_WaitingForBuildToFinish { + get { + return ResourceManager.GetString("VCRemote.WaitingForBuildToFinish", resourceCulture); + } + } + + /// + /// 查找类似 MSB8052: MSVC Toolset Version '{0}' is not compatible with '{1}' Platform Toolset. Please either change Platform Toolset to {2} or MSVC Toolset Version (VCToolsVersion property) to the version with the format '{3}*.*'. To use default MSVC Toolset Version for a given Platform Toolset don't set VCToolsVersion property. 的本地化字符串。 + /// + internal static string VCToolsVersionDoesNotMatchPlatformToolset { + get { + return ResourceManager.GetString("VCToolsVersionDoesNotMatchPlatformToolset", resourceCulture); + } + } + + /// + /// 查找类似 MSB8070: Cannot find MSVC toolset version '{0}': the folder '{1}' does not exist. Please make sure MSVC toolset version '{0}' is installed or choose another toolset version. 的本地化字符串。 + /// + internal static string VCToolsVersionNotInstaled { + get { + return ResourceManager.GetString("VCToolsVersionNotInstaled", resourceCulture); + } + } + + /// + /// 查找类似 Unable to create Xaml task. Compilation failed. {0} 的本地化字符串。 + /// + internal static string XamlTaskCreationFailed { + get { + return ResourceManager.GetString("XamlTaskCreationFailed", resourceCulture); + } + } + + /// + /// 查找类似 Unable to parse Xaml task. {0} 的本地化字符串。 + /// + internal static string XamlTaskParseFailed { + get { + return ResourceManager.GetString("XamlTaskParseFailed", resourceCulture); + } + } + + /// + /// 查找类似 Xml Error: {0}. 的本地化字符串。 + /// + internal static string XmlError { + get { + return ResourceManager.GetString("XmlError", resourceCulture); + } + } } } diff --git a/Microsoft.Build.Framework/ImmutableFilesTimestampCache.cs b/Microsoft.Build.Framework/ImmutableFilesTimestampCache.cs new file mode 100644 index 0000000..ee7fd15 --- /dev/null +++ b/Microsoft.Build.Framework/ImmutableFilesTimestampCache.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Framework +{ + internal class ImmutableFilesTimestampCache + { + private readonly ConcurrentDictionary _cache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + public static ImmutableFilesTimestampCache Shared { get; } = new ImmutableFilesTimestampCache(); + + + public bool TryGetValue(string fullPath, out DateTime lastModified) + { + return _cache.TryGetValue(fullPath, out lastModified); + } + + public void TryAdd(string fullPath, DateTime lastModified) + { + _cache.TryAdd(fullPath, lastModified); + } + } +} diff --git a/Microsoft.Build.Framework/Microsoft.Build.Framework.projitems b/Microsoft.Build.Framework/Microsoft.Build.Framework.projitems new file mode 100644 index 0000000..4a3ab94 --- /dev/null +++ b/Microsoft.Build.Framework/Microsoft.Build.Framework.projitems @@ -0,0 +1,15 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 4ba607be-24e1-46f8-a210-eed2b04c836b + + + Microsoft.Build.Framework + + + + + + \ No newline at end of file diff --git a/Microsoft.Build.Framework/Microsoft.Build.Framework.shproj b/Microsoft.Build.Framework/Microsoft.Build.Framework.shproj new file mode 100644 index 0000000..9c74e9b --- /dev/null +++ b/Microsoft.Build.Framework/Microsoft.Build.Framework.shproj @@ -0,0 +1,13 @@ + + + + 4ba607be-24e1-46f8-a210-eed2b04c836b + 14.0 + + + + + + + + diff --git a/Microsoft.Build.Framework/NativeMethods.cs b/Microsoft.Build.Framework/NativeMethods.cs new file mode 100644 index 0000000..9a86824 --- /dev/null +++ b/Microsoft.Build.Framework/NativeMethods.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Framework +{ + internal class NativeMethods + { + static DateTime LastWriteFileUtcTime(string path) + { + DateTime result = DateTime.MinValue; +#if __ + if (IsWindows) + { + if (Traits.Instance.EscapeHatches.AlwaysUseContentTimestamp) + { + return GetContentLastWriteFileUtcTime(path); + } + WIN32_FILE_ATTRIBUTE_DATA lpFileInformation = default(WIN32_FILE_ATTRIBUTE_DATA); + if (GetFileAttributesEx(path, 0, ref lpFileInformation) && (lpFileInformation.fileAttributes & 0x10) == 0) + { + result = DateTime.FromFileTimeUtc((long)(((ulong)lpFileInformation.ftLastWriteTimeHigh << 32) | lpFileInformation.ftLastWriteTimeLow)); + if ((lpFileInformation.fileAttributes & 0x400) == 1024 && !Traits.Instance.EscapeHatches.UseSymlinkTimeInsteadOfTargetTime) + { + result = GetContentLastWriteFileUtcTime(path); + } + } + return result; + } +#endif + if (!File.Exists(path)) + { + return DateTime.MinValue; + } + return File.GetLastWriteTimeUtc(path); + } + + internal static DateTime GetLastWriteFileUtcTime(string fullPath) + { +#if __ + if (Traits.Instance.EscapeHatches.AlwaysDoImmutableFilesUpToDateCheck) + { + return LastWriteFileUtcTime(fullPath); + } + bool flag = FileClassifier.Shared.IsNonModifiable(fullPath); +#else + bool flag = true; +#endif + + if (flag && ImmutableFilesTimestampCache.Shared.TryGetValue(fullPath, out var lastModified)) + { + return lastModified; + } + DateTime dateTime = LastWriteFileUtcTime(fullPath); + if (flag && dateTime != DateTime.MinValue) + { + ImmutableFilesTimestampCache.Shared.TryAdd(fullPath, dateTime); + } + return dateTime; + + } + + } +} diff --git a/Microsoft.Build.Shared/ErrorUtilities.cs b/Microsoft.Build.Shared/ErrorUtilities.cs index 716a952..a85c5e1 100644 --- a/Microsoft.Build.Shared/ErrorUtilities.cs +++ b/Microsoft.Build.Shared/ErrorUtilities.cs @@ -1,18 +1,122 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Threading; +using Microsoft.Build.Shared; namespace Microsoft.Build.Shared { - public class ErrorUtilities + internal static class ErrorUtilities { - internal static void VerifyThrow(bool condition, string unformattedMessage, object arg0) +#if __ + private static readonly bool s_throwExceptions = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDDONOTTHROWINTERNAL")); + + private static readonly bool s_enableMSBuildDebugTracing = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDENABLEDEBUGTRACING")); +#else + private static readonly bool s_throwExceptions = true; +#endif + public static void DebugTraceMessage(string category, string formatstring, params object[] parameters) { - if (!condition) +#if __ + if (s_enableMSBuildDebugTracing) + { + if (parameters != null) + { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, formatstring, parameters), category); + } + else + { + Trace.WriteLine(formatstring, category); + } + } +#endif + } + + internal static void VerifyThrowInternalError(bool condition, string message, params object[] args) + { + if (s_throwExceptions && !condition) + { + throw new Exception(message); + } + } + + internal static void ThrowInternalError(string message, params object[] args) + { + if (s_throwExceptions) + { + throw new Exception(message); + } + } + + internal static void ThrowInternalError(string message, Exception innerException, params object[] args) + { + if (s_throwExceptions) + { + throw new Exception(message, innerException); + } + } + + internal static void ThrowInternalErrorUnreachable() + { + if (s_throwExceptions) { - throw new Exception(unformattedMessage); + throw new Exception("Unreachable?"); + } + } + + internal static void VerifyThrowInternalErrorUnreachable(bool condition) + { + if (s_throwExceptions && !condition) + { + throw new Exception("Unreachable?"); + } + } + + internal static void ThrowIfTypeDoesNotImplementToString(object param) + { + } + + internal static void VerifyThrowInternalNull(object parameter, string parameterName) + { + if (parameter == null) + { + ThrowInternalError("{0} unexpectedly null", parameterName); + } + } + + internal static void VerifyThrowInternalLockHeld(object locker) + { + if (!Monitor.IsEntered(locker)) + { + ThrowInternalError("Lock should already have been taken"); + } + } + + internal static void VerifyThrowInternalLength(string parameterValue, string parameterName) + { + VerifyThrowInternalNull(parameterValue, parameterName); + if (parameterValue.Length == 0) + { + ThrowInternalError("{0} unexpectedly empty", parameterName); + } + } + + public static void VerifyThrowInternalLength(T[] parameterValue, string parameterName) + { + VerifyThrowInternalNull(parameterValue, parameterName); + if (parameterValue.Length == 0) + { + ThrowInternalError("{0} unexpectedly empty", parameterName); + } + } + + internal static void VerifyThrowInternalRooted(string value) + { + if (!Path.IsPathRooted(value)) + { + ThrowInternalError("{0} unexpectedly not a rooted path", value); } } @@ -24,9 +128,243 @@ internal static void VerifyThrow(bool condition, string unformattedMessage) } } - internal static void ThrowInternalError(string message, Exception innerException, params object[] args) + internal static void VerifyThrow(bool condition, string unformattedMessage, object arg0) + { + if (!condition) + { + ThrowInternalError(unformattedMessage, arg0); + } + } + + internal static void VerifyThrow(bool condition, string unformattedMessage, object arg0, object arg1) + { + if (!condition) + { + ThrowInternalError(unformattedMessage, arg0, arg1); + } + } + + internal static void VerifyThrow(bool condition, string unformattedMessage, object arg0, object arg1, object arg2) + { + if (!condition) + { + ThrowInternalError(unformattedMessage, arg0, arg1, arg2); + } + } + + internal static void VerifyThrow(bool condition, string unformattedMessage, object arg0, object arg1, object arg2, object arg3) + { + if (!condition) + { + ThrowInternalError(unformattedMessage, arg0, arg1, arg2, arg3); + } + } + + internal static void ThrowInvalidOperation(string resourceName, params object[] args) + { + if (s_throwExceptions) + { + throw new InvalidOperationException(resourceName); + } + } + + internal static void VerifyThrowInvalidOperation(bool condition, string resourceName) + { + if (!condition) + { + ThrowInvalidOperation(resourceName, null); + } + } + + internal static void VerifyThrowInvalidOperation(bool condition, string resourceName, object arg0) { - throw new Exception(message); + if (!condition) + { + ThrowInvalidOperation(resourceName, arg0); + } + } + + internal static void VerifyThrowInvalidOperation(bool condition, string resourceName, object arg0, object arg1) + { + if (!condition) + { + ThrowInvalidOperation(resourceName, arg0, arg1); + } + } + + internal static void VerifyThrowInvalidOperation(bool condition, string resourceName, object arg0, object arg1, object arg2) + { + if (!condition) + { + ThrowInvalidOperation(resourceName, arg0, arg1, arg2); + } + } + + internal static void VerifyThrowInvalidOperation(bool condition, string resourceName, object arg0, object arg1, object arg2, object arg3) + { + if (!condition) + { + ThrowInvalidOperation(resourceName, arg0, arg1, arg2, arg3); + } + } + + internal static void ThrowArgument(string resourceName, params object[] args) + { + ThrowArgument(null, resourceName, args); + } + + private static void ThrowArgument(Exception innerException, string resourceName, params object[] args) + { + if (s_throwExceptions) + { + throw new ArgumentException(resourceName, innerException); + } + } + + internal static void VerifyThrowArgument(bool condition, string resourceName) + { + VerifyThrowArgument(condition, null, resourceName); + } + + internal static void VerifyThrowArgument(bool condition, string resourceName, object arg0) + { + VerifyThrowArgument(condition, null, resourceName, arg0); + } + + internal static void VerifyThrowArgument(bool condition, string resourceName, object arg0, object arg1) + { + VerifyThrowArgument(condition, null, resourceName, arg0, arg1); + } + + internal static void VerifyThrowArgument(bool condition, string resourceName, object arg0, object arg1, object arg2) + { + VerifyThrowArgument(condition, null, resourceName, arg0, arg1, arg2); + } + + internal static void VerifyThrowArgument(bool condition, string resourceName, object arg0, object arg1, object arg2, object arg3) + { + VerifyThrowArgument(condition, null, resourceName, arg0, arg1, arg2, arg3); + } + + internal static void VerifyThrowArgument(bool condition, Exception innerException, string resourceName) + { + if (!condition) + { + ThrowArgument(innerException, resourceName, null); + } + } + + internal static void VerifyThrowArgument(bool condition, Exception innerException, string resourceName, object arg0) + { + if (!condition) + { + ThrowArgument(innerException, resourceName, arg0); + } + } + + internal static void VerifyThrowArgument(bool condition, Exception innerException, string resourceName, object arg0, object arg1) + { + if (!condition) + { + ThrowArgument(innerException, resourceName, arg0, arg1); + } + } + + internal static void VerifyThrowArgument(bool condition, Exception innerException, string resourceName, object arg0, object arg1, object arg2) + { + if (!condition) + { + ThrowArgument(innerException, resourceName, arg0, arg1, arg2); + } + } + + internal static void VerifyThrowArgument(bool condition, Exception innerException, string resourceName, object arg0, object arg1, object arg2, object arg3) + { + if (!condition) + { + ThrowArgument(innerException, resourceName, arg0, arg1, arg2, arg3); + } + } + + internal static void ThrowArgumentOutOfRange(string parameterName) + { + if (s_throwExceptions) + { + throw new ArgumentOutOfRangeException(parameterName); + } + } + + internal static void VerifyThrowArgumentOutOfRange(bool condition, string parameterName) + { + if (!condition) + { + ThrowArgumentOutOfRange(parameterName); + } + } + + internal static void VerifyThrowArgumentLength(string parameter, string parameterName) + { + VerifyThrowArgumentNull(parameter, parameterName); + if (parameter.Length == 0 && s_throwExceptions) + { + throw new ArgumentException("Shared.ParameterCannotHaveZeroLength" + parameterName); + } + } + + internal static void VerifyThrowArgumentLength(IReadOnlyCollection parameter, string parameterName) + { + VerifyThrowArgumentNull(parameter, parameterName); + if (parameter.Count == 0 && s_throwExceptions) + { + throw new ArgumentException("Shared.ParameterCannotHaveZeroLength" + parameterName); + } + } + + internal static void VerifyThrowArgumentLengthIfNotNull(IReadOnlyCollection parameter, string parameterName) + { + if (parameter != null && parameter.Count == 0 && s_throwExceptions) + { + throw new ArgumentException( "Shared.ParameterCannotHaveZeroLength" + parameterName); + } + } + + internal static void VerifyThrowArgumentLengthIfNotNull(string parameter, string parameterName) + { + if (parameter != null && parameter.Length == 0 && s_throwExceptions) + { + throw new ArgumentException("Shared.ParameterCannotHaveZeroLength" + parameterName); + } + } + + internal static void VerifyThrowArgumentNull(object parameter, string parameterName) + { + VerifyThrowArgumentNull(parameter, parameterName, "Shared.ParameterCannotBeNull"); + } + + internal static void VerifyThrowArgumentNull(object parameter, string parameterName, string resourceName) + { + if (parameter == null && s_throwExceptions) + { + throw new ArgumentNullException(resourceName + parameterName, (Exception?)null); + } + } + + internal static void VerifyThrowArgumentArraysSameLength(Array parameter1, Array parameter2, string parameter1Name, string parameter2Name) + { + VerifyThrowArgumentNull(parameter1, parameter1Name); + VerifyThrowArgumentNull(parameter2, parameter2Name); + if (parameter1.Length != parameter2.Length && s_throwExceptions) + { + throw new ArgumentException("Shared.ParametersMustHaveTheSameLength" + parameter1Name, parameter2Name); + } + } + + internal static void VerifyThrowObjectDisposed(bool condition, string objectName) + { + if (s_throwExceptions && !condition) + { + throw new ObjectDisposedException(objectName); + } } } } diff --git a/Microsoft.Build.Shared/EscapingUtilities.cs b/Microsoft.Build.Shared/EscapingUtilities.cs new file mode 100644 index 0000000..c55d762 --- /dev/null +++ b/Microsoft.Build.Shared/EscapingUtilities.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Build.Shared; +using Microsoft.NET.StringTools; + +namespace Microsoft.Build.Shared +{ + internal static class EscapingUtilities + { + private static Dictionary s_unescapedToEscapedStrings = new Dictionary(StringComparer.Ordinal); + + private static readonly char[] s_charsToEscape = new char[9] { '%', '*', '?', '@', '$', '(', ')', ';', '\'' }; + + private static bool TryDecodeHexDigit(char character, out int value) + { + if (character >= '0' && character <= '9') + { + value = character - 48; + return true; + } + if (character >= 'A' && character <= 'F') + { + value = character - 65 + 10; + return true; + } + if (character >= 'a' && character <= 'f') + { + value = character - 97 + 10; + return true; + } + value = 0; + return false; + } + + internal static string UnescapeAll(string escapedString, bool trim = false) + { + if (string.IsNullOrEmpty(escapedString)) + { + return escapedString; + } + int num = escapedString.IndexOf('%'); + if (num == -1) + { + if (!trim) + { + return escapedString; + } + return escapedString.Trim(); + } + StringBuilder stringBuilder = StringBuilderCache.Acquire(escapedString.Length); + int i = 0; + int num2 = escapedString.Length; + if (trim) + { + for (; i < escapedString.Length && char.IsWhiteSpace(escapedString[i]); i++) + { + } + if (i == escapedString.Length) + { + return string.Empty; + } + while (char.IsWhiteSpace(escapedString[num2 - 1])) + { + num2--; + } + } + while (num != -1) + { + if (num <= num2 - 3 && TryDecodeHexDigit(escapedString[num + 1], out var value) && TryDecodeHexDigit(escapedString[num + 2], out var value2)) + { + stringBuilder.Append(escapedString, i, num - i); + char value3 = (char)((value << 4) + value2); + stringBuilder.Append(value3); + i = num + 3; + } + num = escapedString.IndexOf('%', num + 1); + } + stringBuilder.Append(escapedString, i, num2 - i); + return StringBuilderCache.GetStringAndRelease(stringBuilder); + } + + internal static string EscapeWithCaching(string unescapedString) + { + return EscapeWithOptionalCaching(unescapedString, cache: true); + } + + internal static string Escape(string unescapedString) + { + return EscapeWithOptionalCaching(unescapedString, cache: false); + } + + private static string EscapeWithOptionalCaching(string unescapedString, bool cache) + { + if (string.IsNullOrEmpty(unescapedString) || !ContainsReservedCharacters(unescapedString)) + { + return unescapedString; + } + if (cache) + { + lock (s_unescapedToEscapedStrings) + { + if (s_unescapedToEscapedStrings.TryGetValue(unescapedString, out var value)) + { + return value; + } + } + } + StringBuilder stringBuilder = StringBuilderCache.Acquire(unescapedString.Length * 2); + AppendEscapedString(stringBuilder, unescapedString); + if (!cache) + { + return StringBuilderCache.GetStringAndRelease(stringBuilder); + } + string text = Strings.WeakIntern(stringBuilder.ToString()); + StringBuilderCache.Release(stringBuilder); + lock (s_unescapedToEscapedStrings) + { + s_unescapedToEscapedStrings[unescapedString] = text; + return text; + } + } + + private static bool ContainsReservedCharacters(string unescapedString) + { + return -1 != unescapedString.IndexOfAny(s_charsToEscape); + } + + internal static bool ContainsEscapedWildcards(string escapedString) + { + if (escapedString.Length < 3) + { + return false; + } + for (int num = escapedString.IndexOf('%', 0, escapedString.Length - 2); num != -1; num = escapedString.IndexOf('%', num + 1, escapedString.Length - (num + 1) - 2)) + { + if (escapedString[num + 1] == '2' && (escapedString[num + 2] == 'a' || escapedString[num + 2] == 'A')) + { + return true; + } + if (escapedString[num + 1] == '3' && (escapedString[num + 2] == 'f' || escapedString[num + 2] == 'F')) + { + return true; + } + } + return false; + } + + private static char HexDigitChar(int x) + { + return (char)(x + ((x < 10) ? 48 : 87)); + } + + private static void AppendEscapedChar(StringBuilder sb, char ch) + { + sb.Append('%'); + sb.Append(HexDigitChar((int)ch / 16)); + sb.Append(HexDigitChar(ch & 0xF)); + } + + private static void AppendEscapedString(StringBuilder sb, string unescapedString) + { + int num = 0; + while (true) + { + int num2 = unescapedString.IndexOfAny(s_charsToEscape, num); + if (num2 == -1) + { + break; + } + sb.Append(unescapedString, num, num2 - num); + AppendEscapedChar(sb, unescapedString[num2]); + num = num2 + 1; + } + sb.Append(unescapedString, num, unescapedString.Length - num); + } + } +} diff --git a/Microsoft.Build.Shared/ExceptionHandling.cs b/Microsoft.Build.Shared/ExceptionHandling.cs new file mode 100644 index 0000000..0d6a5bc --- /dev/null +++ b/Microsoft.Build.Shared/ExceptionHandling.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Runtime.ExceptionServices; +using System.Security; +using System.Text; + +namespace Microsoft.Build.Shared +{ + internal static class ExceptionHandling + { + internal static bool IsCriticalException(Exception e) + { + if (e is OutOfMemoryException || e is StackOverflowException || e is ThreadAbortException || e is ThreadInterruptedException || e is AccessViolationException /*|| e is InternalErrorException*/) + { + return true; + } + if (e is AggregateException ex && ex.InnerExceptions.Any((Exception innerException) => IsCriticalException(innerException))) + { + return true; + } + return false; + } + + internal static bool NotExpectedException(Exception e) + { + return !IsIoRelatedException(e); + } + + internal static bool IsIoRelatedException(Exception e) + { + if (!(e is UnauthorizedAccessException) && !(e is NotSupportedException) && (!(e is ArgumentException) || e is ArgumentNullException) && !(e is SecurityException)) + { + return e is IOException; + } + return true; + } + + internal static void Rethrow(this Exception e) + { + ExceptionDispatchInfo.Capture(e).Throw(); + } + + internal static void RethrowIfCritical(this Exception ex) + { + if (IsCriticalException(ex)) + { + ex.Rethrow(); + } + } + } +} diff --git a/Microsoft.Build.Shared/FileMatcher.cs b/Microsoft.Build.Shared/FileMatcher.cs new file mode 100644 index 0000000..b308906 --- /dev/null +++ b/Microsoft.Build.Shared/FileMatcher.cs @@ -0,0 +1,1430 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.IO.Enumeration; +using System.Linq; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; +using Microsoft.VisualBasic.FileIO; + +namespace Microsoft.Build.Shared +{ + internal class FileMatcher + { + private class TaskOptions + { + public readonly int MaxTasks; + + public int AvailableTasks; + + public int MaxTasksPerIteration; + + public TaskOptions(int maxTasks) + { + MaxTasks = maxTasks; + } + } + + + private struct RecursiveStepResult + { + public string RemainingWildcardDirectory; + + public bool ConsiderFiles; + + public bool NeedsToProcessEachFile; + + public string DirectoryPattern; + + public bool NeedsDirectoryRecursion; + } + + private enum SearchAction + { + RunSearch, + ReturnFileSpec, + ReturnEmptyList + } + + + internal enum FileSystemEntity + { + Files, + Directories, + FilesAndDirectories + } + + private class FilesSearchData + { + public string Filespec { get; } + + public string DirectoryPattern { get; } + + public Regex RegexFileMatch { get; } + + public bool NeedsRecursion { get; } + + public FilesSearchData(string filespec, string directoryPattern, Regex regexFileMatch, bool needsRecursion) + { + Filespec = filespec; + DirectoryPattern = directoryPattern; + RegexFileMatch = regexFileMatch; + NeedsRecursion = needsRecursion; + } + } + + internal sealed class Result + { + internal bool isLegalFileSpec; + + internal bool isMatch; + + internal bool isFileSpecRecursive; + + internal string wildcardDirectoryPart = string.Empty; + + internal Result() + { + } + } + + private struct RecursionState + { + public string BaseDirectory; + + public string RemainingWildcardDirectory; + + public bool IsInsideMatchingDirectory; + + public FilesSearchData SearchData; + + public bool IsLookingForMatchingDirectory + { + get + { + if (SearchData.DirectoryPattern != null) + { + return !IsInsideMatchingDirectory; + } + return false; + } + } + } + + private static readonly string s_directorySeparator = new string(Path.DirectorySeparatorChar, 1); + + private static readonly string s_thisDirectory = "." + s_directorySeparator; + + public static FileMatcher Default = new FileMatcher(FileSystems.Default); + + private static readonly char[] s_wildcardCharacters = new char[2] { '*', '?' }; + + internal delegate IReadOnlyList GetFileSystemEntries(FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory); + + private readonly ConcurrentDictionary> _cachedGlobExpansions; + + private readonly Lazy> _cachedGlobExpansionsLock = new Lazy>(() => new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); + + private static readonly Lazy>> s_cachedGlobExpansions = new Lazy>>(() => new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase)); + + private static readonly Lazy> s_cachedGlobExpansionsLock = new Lazy>(() => new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); + + private readonly IFileSystem _fileSystem; + + private readonly GetFileSystemEntries _getFileSystemEntries; + + internal static readonly char[] directorySeparatorCharacters = FileUtilities.Slashes; + + private static readonly char[] s_invalidPathChars = Path.GetInvalidPathChars(); + + public FileMatcher(IFileSystem fileSystem, ConcurrentDictionary> fileEntryExpansionCache = null) + : this(fileSystem, (FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) => GetAccessibleFileSystemEntries(fileSystem, entityType, path, pattern, projectDirectory, stripProjectDirectory).ToArray(), fileEntryExpansionCache) + { + } + + internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemEntries, ConcurrentDictionary> getFileSystemDirectoryEntriesCache = null) + { + if (/*Traits.Instance.MSBuildCacheFileEnumerations*/false) + { + _cachedGlobExpansions = s_cachedGlobExpansions.Value; + _cachedGlobExpansionsLock = s_cachedGlobExpansionsLock; + } + else + { + _cachedGlobExpansions = getFileSystemDirectoryEntriesCache; + } + _fileSystem = fileSystem; + _getFileSystemEntries = ((getFileSystemDirectoryEntriesCache == null) ? getFileSystemEntries : ((GetFileSystemEntries)delegate (FileSystemEntity type, string path, string pattern, string directory, bool stripProjectDirectory) + { +#if __ + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10)) + { + string key = type switch + { + FileSystemEntity.Files => "F", + FileSystemEntity.Directories => "D", + FileSystemEntity.FilesAndDirectories => "A", + _ => throw new NotImplementedException(), + } + ";" + path; + IReadOnlyList orAdd = getFileSystemDirectoryEntriesCache.GetOrAdd(key, (string s) => getFileSystemEntries(type, path, "*", directory, stripProjectDirectory: false)); + IEnumerable enumerable2; + if (pattern == null || IsAllFilesWildcard(pattern)) + { + IEnumerable enumerable = orAdd; + enumerable2 = enumerable; + } + else + { + enumerable2 = orAdd.Where((string o) => IsMatch(Path.GetFileName(o), pattern)); + } + IEnumerable enumerable3 = enumerable2; + if (!stripProjectDirectory) + { + return enumerable3.ToArray(); + } + return RemoveProjectDirectory(enumerable3, directory).ToArray(); + } +#endif + return (type == FileSystemEntity.Directories) ? getFileSystemDirectoryEntriesCache.GetOrAdd("D;" + path + ";" + (pattern ?? "*"), (string s) => getFileSystemEntries(type, path, pattern, directory, stripProjectDirectory).ToArray()) : getFileSystemEntries(type, path, pattern, directory, stripProjectDirectory); + })); + } + + internal static bool HasWildcards(string filespec) + { + return -1 != filespec.LastIndexOfAny(s_wildcardCharacters); + } + + private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem fileSystem, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) + { + path = FileUtilities.FixFilePath(path); + switch (entityType) + { + case FileSystemEntity.Files: + return GetAccessibleFiles(fileSystem, path, pattern, projectDirectory, stripProjectDirectory); + case FileSystemEntity.Directories: + return GetAccessibleDirectories(fileSystem, path, pattern); + case FileSystemEntity.FilesAndDirectories: + return GetAccessibleFilesAndDirectories(fileSystem, path, pattern); + default: + ErrorUtilities.VerifyThrow(condition: false, "Unexpected filesystem entity type."); + return Array.Empty(); + } + } + + private static IReadOnlyList GetAccessibleFilesAndDirectories(IFileSystem fileSystem, string path, string pattern) + { + if (fileSystem.DirectoryExists(path)) + { + try + { + return (ShouldEnforceMatching(pattern) ? (from o in fileSystem.EnumerateFileSystemEntries(path, pattern) + where IsMatch(Path.GetFileName(o), pattern) + select o) : fileSystem.EnumerateFileSystemEntries(path, pattern)).ToArray(); + } + catch (UnauthorizedAccessException) + { + } + catch (SecurityException) + { + } + } + return Array.Empty(); + } + + private static bool ShouldEnforceMatching(string searchPattern) + { + if (searchPattern == null) + { + return false; + } + if (searchPattern.IndexOf("?.", StringComparison.Ordinal) == -1 && (Path.GetExtension(searchPattern).Length != 4 || searchPattern.IndexOf('*') == -1)) + { + return searchPattern.EndsWith("?", StringComparison.Ordinal); + } + return true; + } + + private static IReadOnlyList GetAccessibleFiles(IFileSystem fileSystem, string path, string filespec, string projectDirectory, bool stripProjectDirectory) + { + try + { + string path2 = ((path.Length == 0) ? s_thisDirectory : path); + IEnumerable enumerable; + if (filespec == null) + { + enumerable = fileSystem.EnumerateFiles(path2); + } + else + { + enumerable = fileSystem.EnumerateFiles(path2, filespec); + if (ShouldEnforceMatching(filespec)) + { + enumerable = enumerable.Where((string o) => IsMatch(Path.GetFileName(o), filespec)); + } + } + if (stripProjectDirectory) + { + enumerable = RemoveProjectDirectory(enumerable, projectDirectory); + } + else if (!path.StartsWith(s_thisDirectory, StringComparison.Ordinal)) + { + enumerable = RemoveInitialDotSlash(enumerable); + } + return enumerable.ToArray(); + } + catch (SecurityException) + { + return Array.Empty(); + } + catch (UnauthorizedAccessException) + { + return Array.Empty(); + } + } + + private static IReadOnlyList GetAccessibleDirectories(IFileSystem fileSystem, string path, string pattern) + { + try + { + IEnumerable enumerable = null; + if (pattern == null) + { + enumerable = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path); + } + else + { + enumerable = fileSystem.EnumerateDirectories((path.Length == 0) ? s_thisDirectory : path, pattern); + if (ShouldEnforceMatching(pattern)) + { + enumerable = enumerable.Where((string o) => IsMatch(Path.GetFileName(o), pattern)); + } + } + if (!path.StartsWith(s_thisDirectory, StringComparison.Ordinal)) + { + enumerable = RemoveInitialDotSlash(enumerable); + } + return enumerable.ToArray(); + } + catch (SecurityException) + { + return Array.Empty(); + } + catch (UnauthorizedAccessException) + { + return Array.Empty(); + } + } + + private static IEnumerable RemoveInitialDotSlash(IEnumerable paths) + { + foreach (string path in paths) + { + if (path.StartsWith(s_thisDirectory, StringComparison.Ordinal)) + { + yield return path.Substring(2); + } + else + { + yield return path; + } + } + } + + internal static bool IsDirectorySeparator(char c) + { + if (c != Path.DirectorySeparatorChar) + { + return c == Path.AltDirectorySeparatorChar; + } + return true; + } + + internal static IEnumerable RemoveProjectDirectory(IEnumerable paths, string projectDirectory) + { + bool directoryLastCharIsSeparator = IsDirectorySeparator(projectDirectory[projectDirectory.Length - 1]); + foreach (string path in paths) + { + if (path.StartsWith(projectDirectory, StringComparison.Ordinal)) + { + if (!directoryLastCharIsSeparator) + { + if (path.Length <= projectDirectory.Length || !IsDirectorySeparator(path[projectDirectory.Length])) + { + yield return path; + } + else + { + yield return path.Substring(projectDirectory.Length + 1); + } + } + else + { + yield return path.Substring(projectDirectory.Length); + } + } + else + { + yield return path; + } + } + } + + internal static bool IsMatch(string input, string pattern) + { + if (input == null) + { + throw new ArgumentNullException("input"); + } + if (pattern == null) + { + throw new ArgumentNullException("pattern"); + } + int num = pattern.Length; + int num2 = input.Length; + int num3 = -1; + int num4 = -1; + int i = 0; + int num5 = 0; + bool flag = false; + while (num5 < num2) + { + if (i < num) + { + if (pattern[i] == '*') + { + while (++i < num && pattern[i] == '*') + { + } + if (i >= num) + { + return true; + } + if (!flag) + { + int num6 = num2; + int num7 = num; + while (i < num7 && num6 > num5) + { + num7--; + num6--; + if (pattern[num7] == '*') + { + break; + } + if (!CompareIgnoreCase(input[num6], pattern[num7], num7, num6) && pattern[num7] != '?') + { + return false; + } + if (i == num7) + { + return true; + } + } + num2 = num6 + 1; + num = num7 + 1; + flag = true; + } + if (pattern[i] != '?') + { + while (!CompareIgnoreCase(input[num5], pattern[i], num5, i)) + { + if (++num5 >= num2) + { + return false; + } + } + } + num3 = i; + num4 = num5; + continue; + } + if (CompareIgnoreCase(input[num5], pattern[i], num5, i) || pattern[i] == '?') + { + i++; + num5++; + continue; + } + } + if (num3 < 0) + { + return false; + } + i = num3; + num5 = num4++; + } + for (; i < num && pattern[i] == '*'; i++) + { + } + return i >= num; + bool CompareIgnoreCase(char inputChar, char patternChar, int iIndex, int pIndex) + { + char c = (char)(inputChar | 0x20u); + if (c >= 'a' && c <= 'z') + { + return c == (patternChar | 0x20); + } + if (inputChar < '\u0080' || patternChar < '\u0080') + { + return inputChar == patternChar; + } + return string.Compare(input, iIndex, pattern, pIndex, 1, StringComparison.OrdinalIgnoreCase) == 0; + } + } + + private static string ComputeFileEnumerationCacheKey(string projectDirectoryUnescaped, string filespecUnescaped, List excludes) + { + int num = 0; + if (excludes != null) + { + foreach (string exclude in excludes) + { + num += exclude.Length; + } + } + using ReuseableStringBuilder reuseableStringBuilder = new ReuseableStringBuilder(projectDirectoryUnescaped.Length + filespecUnescaped.Length + num); + bool flag = false; + try + { + string text = Path.Combine(projectDirectoryUnescaped, filespecUnescaped); + if (text.Equals(filespecUnescaped, StringComparison.Ordinal)) + { + reuseableStringBuilder.Append(filespecUnescaped); + } + else + { + reuseableStringBuilder.Append("p"); + reuseableStringBuilder.Append(text); + } + } + catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) + { + flag = true; + } + if (flag) + { + reuseableStringBuilder.Append("e"); + reuseableStringBuilder.Append("p"); + reuseableStringBuilder.Append(projectDirectoryUnescaped); + reuseableStringBuilder.Append(filespecUnescaped); + } + if (excludes != null) + { + foreach (string exclude2 in excludes) + { + reuseableStringBuilder.Append(exclude2); + } + } + return reuseableStringBuilder.ToString(); + } + + internal string[] GetFiles(string projectDirectoryUnescaped, string filespecUnescaped, List excludeSpecsUnescaped = null) + { + if (!HasWildcards(filespecUnescaped)) + { + return CreateArrayWithSingleItemIfNotExcluded(filespecUnescaped, excludeSpecsUnescaped); + } + if (_cachedGlobExpansions == null) + { + return GetFilesImplementation(projectDirectoryUnescaped, filespecUnescaped, excludeSpecsUnescaped); + } + string key = ComputeFileEnumerationCacheKey(projectDirectoryUnescaped, filespecUnescaped, excludeSpecsUnescaped); + if (!_cachedGlobExpansions.TryGetValue(key, out var value)) + { + lock (_cachedGlobExpansionsLock.Value.GetOrAdd(key, (string _) => new object())) + { + if (!_cachedGlobExpansions.TryGetValue(key, out value)) + { + value = _cachedGlobExpansions.GetOrAdd(key, (string _) => GetFilesImplementation(projectDirectoryUnescaped, filespecUnescaped, excludeSpecsUnescaped)); + } + } + } + return value.ToArray(); + } + + internal static bool RawFileSpecIsValid(string filespec) + { + if (-1 != filespec.IndexOfAny(s_invalidPathChars)) + { + return false; + } + if (-1 != filespec.IndexOf("...", StringComparison.Ordinal)) + { + return false; + } + int num = filespec.LastIndexOf(":", StringComparison.Ordinal); + if (-1 != num && 1 != num) + { + return false; + } + return true; + } + + private static void PreprocessFileSpecForSplitting(string filespec, out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart) + { + filespec = FileUtilities.FixFilePath(filespec); + int num = filespec.LastIndexOfAny(directorySeparatorCharacters); + if (-1 == num) + { + fixedDirectoryPart = string.Empty; + wildcardDirectoryPart = string.Empty; + filenamePart = filespec; + return; + } + int num2 = filespec.IndexOfAny(s_wildcardCharacters); + if (-1 == num2 || num2 > num) + { + fixedDirectoryPart = filespec.Substring(0, num + 1); + wildcardDirectoryPart = string.Empty; + filenamePart = filespec.Substring(num + 1); + return; + } + int num3 = filespec.Substring(0, num2).LastIndexOfAny(directorySeparatorCharacters); + if (-1 == num3) + { + fixedDirectoryPart = string.Empty; + wildcardDirectoryPart = filespec.Substring(0, num + 1); + filenamePart = filespec.Substring(num + 1); + } + else + { + fixedDirectoryPart = filespec.Substring(0, num3 + 1); + wildcardDirectoryPart = filespec.Substring(num3 + 1, num - num3); + filenamePart = filespec.Substring(num + 1); + } + } + + internal string GetLongPathName(string path) + { + return GetLongPathName(path, _getFileSystemEntries); + } + + internal static string GetLongPathName(string path, GetFileSystemEntries getFileSystemEntries) + { + return path; + } + + internal void SplitFileSpec(string filespec, out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart) + { + PreprocessFileSpecForSplitting(filespec, out fixedDirectoryPart, out wildcardDirectoryPart, out filenamePart); + if ("**" == filenamePart) + { + wildcardDirectoryPart += "**"; + wildcardDirectoryPart += s_directorySeparator; + filenamePart = "*.*"; + } + fixedDirectoryPart = GetLongPathName(fixedDirectoryPart, _getFileSystemEntries); + } + + private static bool HasDotDot(string str) + { + for (int i = 0; i < str.Length - 1; i++) + { + if (str[i] == '.' && str[i + 1] == '.') + { + return true; + } + } + return false; + } + + private static bool HasMisplacedRecursiveOperator(string str) + { + for (int i = 0; i < str.Length - 1; i++) + { + bool num = str[i] == '*' && str[i + 1] == '*'; + bool flag = (i == 0 || FileUtilities.IsAnySlash(str[i - 1])) && i < str.Length - 2 && FileUtilities.IsAnySlash(str[i + 2]); + if (num && !flag) + { + return true; + } + } + return false; + } + + private static bool IsLegalFileSpec(string wildcardDirectoryPart, string filenamePart) + { + if (!HasDotDot(wildcardDirectoryPart) && !HasMisplacedRecursiveOperator(wildcardDirectoryPart)) + { + return !HasMisplacedRecursiveOperator(filenamePart); + } + return false; + } + + internal delegate (string fixedDirectoryPart, string recursiveDirectoryPart, string fileNamePart) FixupParts(string fixedDirectoryPart, string recursiveDirectoryPart, string filenamePart); + + internal void GetFileSpecInfo(string filespec, out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart, out bool needsRecursion, out bool isLegalFileSpec, FixupParts fixupParts = null) + { + needsRecursion = false; + fixedDirectoryPart = string.Empty; + wildcardDirectoryPart = string.Empty; + filenamePart = string.Empty; + if (!RawFileSpecIsValid(filespec)) + { + isLegalFileSpec = false; + return; + } + SplitFileSpec(filespec, out fixedDirectoryPart, out wildcardDirectoryPart, out filenamePart); + if (fixupParts != null) + { + (fixedDirectoryPart, wildcardDirectoryPart, filenamePart) = fixupParts(fixedDirectoryPart, wildcardDirectoryPart, filenamePart); + } + isLegalFileSpec = IsLegalFileSpec(wildcardDirectoryPart, filenamePart); + if (isLegalFileSpec) + { + needsRecursion = wildcardDirectoryPart.Length != 0; + } + } + + + private SearchAction GetFileSearchData(string projectDirectoryUnescaped, string filespecUnescaped, out bool stripProjectDirectory, out RecursionState result) + { + stripProjectDirectory = false; + result = default(RecursionState); + GetFileSpecInfo(filespecUnescaped, out var fixedDirectoryPart, out var wildcardDirectoryPart, out var filenamePart, out var needsRecursion, out var isLegalFileSpec); + if (!isLegalFileSpec) + { + return SearchAction.ReturnFileSpec; + } + string text = fixedDirectoryPart; + if (projectDirectoryUnescaped != null) + { + if (fixedDirectoryPart != null) + { + try + { + fixedDirectoryPart = Path.Combine(projectDirectoryUnescaped, fixedDirectoryPart); + } + catch (ArgumentException) + { + return SearchAction.ReturnEmptyList; + } + stripProjectDirectory = !string.Equals(fixedDirectoryPart, text, StringComparison.OrdinalIgnoreCase); + } + else + { + fixedDirectoryPart = projectDirectoryUnescaped; + stripProjectDirectory = true; + } + } + if (fixedDirectoryPart.Length > 0 && !_fileSystem.DirectoryExists(fixedDirectoryPart)) + { + return SearchAction.ReturnEmptyList; + } + string text2 = null; + if (wildcardDirectoryPart.Length > 0) + { + string text3 = wildcardDirectoryPart.TrimTrailingSlashes(); + int length = text3.Length; + if (length > 6 && text3[0] == '*' && text3[1] == '*' && FileUtilities.IsAnySlash(text3[2]) && FileUtilities.IsAnySlash(text3[length - 3]) && text3[length - 2] == '*' && text3[length - 1] == '*' && text3.IndexOfAny(FileUtilities.Slashes, 3, length - 6) == -1) + { + text2 = text3.Substring(3, length - 6); + } + } + bool flag = wildcardDirectoryPart.Length > 0 && text2 == null && !IsRecursiveDirectoryMatch(wildcardDirectoryPart); + FilesSearchData searchData = new FilesSearchData(flag ? null : filenamePart, text2, flag ? new Regex(RegularExpressionFromFileSpec(text, wildcardDirectoryPart, filenamePart), RegexOptions.IgnoreCase) : null, needsRecursion); + result.SearchData = searchData; + result.BaseDirectory = Normalize(fixedDirectoryPart); + result.RemainingWildcardDirectory = Normalize(wildcardDirectoryPart); + return SearchAction.RunSearch; + } + + internal static string RegularExpressionFromFileSpec(string fixedDirectoryPart, string wildcardDirectoryPart, string filenamePart) + { + using ReuseableStringBuilder reuseableStringBuilder = new ReuseableStringBuilder(291); + AppendRegularExpressionFromFixedDirectory(reuseableStringBuilder, fixedDirectoryPart); + AppendRegularExpressionFromWildcardDirectory(reuseableStringBuilder, wildcardDirectoryPart); + AppendRegularExpressionFromFilename(reuseableStringBuilder, filenamePart); + return reuseableStringBuilder.ToString(); + } + private static int LastIndexOfDirectorySequence(string str, int startIndex) + { + if (startIndex >= str.Length || !FileUtilities.IsAnySlash(str[startIndex])) + { + return startIndex; + } + int num = startIndex; + bool flag = false; + while (!flag && num < str.Length) + { + bool num2 = num < str.Length - 1 && FileUtilities.IsAnySlash(str[num + 1]); + bool flag2 = num < str.Length - 2 && str[num + 1] == '.' && FileUtilities.IsAnySlash(str[num + 2]); + if (num2) + { + num++; + } + else if (flag2) + { + num += 2; + } + else + { + flag = true; + } + } + return num; + } + + private static int LastIndexOfDirectoryOrRecursiveSequence(string str, int startIndex) + { + if (startIndex >= str.Length - 1 || str[startIndex] != '*' || str[startIndex + 1] != '*') + { + return LastIndexOfDirectorySequence(str, startIndex); + } + int num = startIndex + 2; + bool flag = false; + while (!flag && num < str.Length) + { + num = LastIndexOfDirectorySequence(str, num); + if (num < str.Length - 2 && str[num + 1] == '*' && str[num + 2] == '*') + { + num += 3; + } + else + { + flag = true; + } + } + return num + 1; + } + + private static void AppendRegularExpressionFromFixedDirectory(ReuseableStringBuilder regex, string fixedDir) + { + regex.Append("^"); + int num; + //if (NativeMethodsShared.IsWindows && fixedDir.Length > 1 && fixedDir[0] == '\\') + //{ + // num = ((fixedDir[1] == '\\') ? 1 : 0); + // if (num != 0) + // { + // regex.Append("\\\\\\\\"); + // } + //} + //else + { + num = 0; + } + for (int num2 = ((num != 0) ? (LastIndexOfDirectorySequence(fixedDir, 0) + 1) : LastIndexOfDirectorySequence(fixedDir, 0)); num2 < fixedDir.Length; num2 = LastIndexOfDirectorySequence(fixedDir, num2 + 1)) + { + AppendRegularExpressionFromChar(regex, fixedDir[num2]); + } + } + + private static void AppendRegularExpressionFromWildcardDirectory(ReuseableStringBuilder regex, string wildcardDir) + { + regex.Append("(?"); + if (wildcardDir.Length > 2 && wildcardDir[0] == '*' && wildcardDir[1] == '*') + { + regex.Append("((.*/)|(.*\\\\)|())"); + } + for (int num = LastIndexOfDirectoryOrRecursiveSequence(wildcardDir, 0); num < wildcardDir.Length; num = LastIndexOfDirectoryOrRecursiveSequence(wildcardDir, num + 1)) + { + char ch = wildcardDir[num]; + if (num < wildcardDir.Length - 2 && wildcardDir[num + 1] == '*' && wildcardDir[num + 2] == '*') + { + regex.Append("((/)|(\\\\)|(/.*/)|(/.*\\\\)|(\\\\.*\\\\)|(\\\\.*/))"); + } + else + { + AppendRegularExpressionFromChar(regex, ch); + } + } + regex.Append(")"); + } + + private static void AppendRegularExpressionFromFilename(ReuseableStringBuilder regex, string filename) + { + regex.Append("(?"); + bool flag = filename.Length > 0 && filename[filename.Length - 1] == '.'; + int num = (flag ? (filename.Length - 1) : filename.Length); + for (int i = 0; i < num; i++) + { + char c = filename[i]; + if (flag && c == '*') + { + regex.Append("[^\\.]*"); + } + else if (flag && c == '?') + { + regex.Append("[^\\.]."); + } + else + { + AppendRegularExpressionFromChar(regex, c); + } + if (!flag && i < num - 2 && c == '*' && filename[i + 1] == '.' && filename[i + 2] == '*') + { + i += 2; + } + } + regex.Append(")"); + regex.Append("$"); + } + + private static void AppendRegularExpressionFromChar(ReuseableStringBuilder regex, char ch) + { + switch (ch) + { + case '*': + regex.Append("[^/\\\\]*"); + return; + case '?': + regex.Append("."); + return; + } + if (FileUtilities.IsAnySlash(ch)) + { + regex.Append("[/\\\\]+"); + } + else if (IsSpecialRegexCharacter(ch)) + { + regex.Append('\\'); + regex.Append(ch); + } + else + { + regex.Append(ch); + } + } + + private static bool IsSpecialRegexCharacter(char ch) + { + if (ch != '$' && ch != '(' && ch != ')' && ch != '+' && ch != '.' && ch != '[' && ch != '^' && ch != '{') + { + return ch == '|'; + } + return true; + } + + private static bool IsValidDriveChar(char value) + { + if (value < 'A' || value > 'Z') + { + if (value >= 'a') + { + return value <= 'z'; + } + return false; + } + return true; + } + + private static int SkipSlashes(string aString, int startingIndex) + { + int i; + for (i = startingIndex; i < aString.Length && FileUtilities.IsAnySlash(aString[i]); i++) + { + } + return i; + } + internal static bool IsRecursiveDirectoryMatch(string path) + { + return path.TrimTrailingSlashes() == "**"; + } + + internal static string Normalize(string aString) + { + if (string.IsNullOrEmpty(aString)) + { + return aString; + } + StringBuilder stringBuilder = new StringBuilder(aString.Length); + int num = 0; + if (aString.Length >= 2 && aString[1] == ':' && IsValidDriveChar(aString[0])) + { + stringBuilder.Append(aString[0]); + stringBuilder.Append(aString[1]); + int num2 = SkipSlashes(aString, 2); + if (num != num2) + { + stringBuilder.Append('\\'); + } + num = num2; + } + else if (aString.StartsWith("/", StringComparison.Ordinal)) + { + stringBuilder.Append('/'); + num = SkipSlashes(aString, 1); + } + else if (aString.StartsWith("\\\\", StringComparison.Ordinal)) + { + stringBuilder.Append("\\\\"); + num = SkipSlashes(aString, 2); + } + else if (aString.StartsWith("\\", StringComparison.Ordinal)) + { + stringBuilder.Append("\\"); + num = SkipSlashes(aString, 1); + } + while (num < aString.Length) + { + int num3 = SkipSlashes(aString, num); + if (num3 >= aString.Length) + { + break; + } + if (num3 > num) + { + stringBuilder.Append(s_directorySeparator); + } + int num4 = aString.IndexOfAny(directorySeparatorCharacters, num3); + int num5 = ((num4 == -1) ? aString.Length : num4); + stringBuilder.Append(aString, num3, num5 - num3); + num = num5; + } + return stringBuilder.ToString(); + } + private string[] GetFilesImplementation(string projectDirectoryUnescaped, string filespecUnescaped, List excludeSpecsUnescaped) + { + bool stripProjectDirectory; + RecursionState result; + SearchAction fileSearchData = GetFileSearchData(projectDirectoryUnescaped, filespecUnescaped, out stripProjectDirectory, out result); + switch (fileSearchData) + { + case SearchAction.ReturnEmptyList: + return Array.Empty(); + case SearchAction.ReturnFileSpec: + return CreateArrayWithSingleItemIfNotExcluded(filespecUnescaped, excludeSpecsUnescaped); + default: + throw new NotSupportedException(fileSearchData.ToString()); + case SearchAction.RunSearch: + { + List list2 = null; + Dictionary> dictionary = null; + HashSet resultsToExclude = null; + if (excludeSpecsUnescaped != null) + { + list2 = new List(); + foreach (string item in excludeSpecsUnescaped) + { + bool stripProjectDirectory2; + RecursionState result2; + SearchAction fileSearchData2 = GetFileSearchData(projectDirectoryUnescaped, item, out stripProjectDirectory2, out result2); + switch (fileSearchData2) + { + case SearchAction.ReturnFileSpec: + if (resultsToExclude == null) + { + resultsToExclude = new HashSet(); + } + resultsToExclude.Add(item); + break; + default: + throw new NotSupportedException(fileSearchData2.ToString()); + case SearchAction.RunSearch: + { + string baseDirectory = result2.BaseDirectory; + string baseDirectory2 = result.BaseDirectory; + if (!string.Equals(baseDirectory, baseDirectory2, StringComparison.OrdinalIgnoreCase)) + { + if (baseDirectory.Length == baseDirectory2.Length) + { + break; + } + if (baseDirectory.Length > baseDirectory2.Length) + { + if (IsSubdirectoryOf(baseDirectory, baseDirectory2)) + { + if (dictionary == null) + { + dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + if (!dictionary.TryGetValue(baseDirectory, out var value)) + { + value = (dictionary[baseDirectory] = new List()); + } + value.Add(result2); + } + } + else if (IsSubdirectoryOf(result.BaseDirectory, result2.BaseDirectory) && result2.RemainingWildcardDirectory.Length != 0) + { + if (IsRecursiveDirectoryMatch(result2.RemainingWildcardDirectory)) + { + result2.BaseDirectory = result.BaseDirectory; + list2.Add(result2); + } + else + { + result2.BaseDirectory = result.BaseDirectory; + result2.RemainingWildcardDirectory = "**" + s_directorySeparator; + list2.Add(result2); + } + } + } + else + { + string text = result.SearchData.Filespec ?? string.Empty; + string text2 = result2.SearchData.Filespec ?? string.Empty; + int num = Math.Min(text.Length - text.LastIndexOfAny(s_wildcardCharacters) - 1, text2.Length - text2.LastIndexOfAny(s_wildcardCharacters) - 1); + if (string.Compare(text, text.Length - num, text2, text2.Length - num, num, StringComparison.OrdinalIgnoreCase) == 0) + { + list2.Add(result2); + } + } + break; + } + case SearchAction.ReturnEmptyList: + break; + } + } + } + if (list2 != null && list2.Count == 0) + { + list2 = null; + } + ConcurrentStack> concurrentStack = new ConcurrentStack>(); + try + { + int num2 = Math.Max(1, /*NativeMethodsShared.GetLogicalCoreCount()*/Environment.ProcessorCount / 2); + TaskOptions taskOptions = new TaskOptions(num2) + { + AvailableTasks = num2, + MaxTasksPerIteration = num2 + }; + GetFilesRecursive(concurrentStack, result, projectDirectoryUnescaped, stripProjectDirectory, list2, dictionary, taskOptions); + } + catch (AggregateException ex) + { + if (ex.Flatten().InnerExceptions.All(ExceptionHandling.IsIoRelatedException)) + { + return CreateArrayWithSingleItemIfNotExcluded(filespecUnescaped, excludeSpecsUnescaped); + } + throw; + } + catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) + { + return CreateArrayWithSingleItemIfNotExcluded(filespecUnescaped, excludeSpecsUnescaped); + } + if (resultsToExclude == null) + { + return concurrentStack.SelectMany((List list) => list).ToArray(); + } + return (from f in concurrentStack.SelectMany((List list) => list) + where !resultsToExclude.Contains(f) + select f).ToArray(); + } + } + } + + private IEnumerable GetFilesForStep(RecursiveStepResult stepResult, RecursionState recursionState, string projectDirectory, bool stripProjectDirectory) + { + if (!stepResult.ConsiderFiles) + { + return Enumerable.Empty(); + } + string pattern; + if (/*NativeMethodsShared.IsLinux*/true && recursionState.SearchData.DirectoryPattern != null) + { + pattern = "*.*"; + stepResult.NeedsToProcessEachFile = true; + } + else + { + pattern = recursionState.SearchData.Filespec; + } + IEnumerable enumerable = _getFileSystemEntries(FileSystemEntity.Files, recursionState.BaseDirectory, pattern, projectDirectory, stripProjectDirectory); + if (!stepResult.NeedsToProcessEachFile) + { + return enumerable; + } + return enumerable.Where((string o) => MatchFileRecursionStep(recursionState, o)); + } + + private static bool IsAllFilesWildcard(string pattern) + { + return pattern?.Length switch + { + 1 => pattern[0] == '*', + 3 => pattern[0] == '*' && pattern[1] == '.' && pattern[2] == '*', + _ => false, + }; + } + + private static bool MatchFileRecursionStep(RecursionState recursionState, string file) + { + if (IsAllFilesWildcard(recursionState.SearchData.Filespec)) + { + return true; + } + if (recursionState.SearchData.Filespec != null) + { + return IsMatch(Path.GetFileName(file), recursionState.SearchData.Filespec); + } + return recursionState.SearchData.RegexFileMatch.IsMatch(file); + } + + private static RecursiveStepResult GetFilesRecursiveStep(RecursionState recursionState) + { + RecursiveStepResult result = default(RecursiveStepResult); + bool flag = false; + if (recursionState.SearchData.DirectoryPattern != null) + { + flag = recursionState.IsInsideMatchingDirectory; + } + else if (recursionState.RemainingWildcardDirectory.Length == 0) + { + flag = true; + } + else if (recursionState.RemainingWildcardDirectory.IndexOf("**", StringComparison.Ordinal) == 0) + { + flag = true; + } + result.ConsiderFiles = flag; + if (flag) + { + result.NeedsToProcessEachFile = recursionState.SearchData.Filespec == null; + } + if (recursionState.SearchData.NeedsRecursion && recursionState.RemainingWildcardDirectory.Length > 0) + { + string text = null; + if (!IsRecursiveDirectoryMatch(recursionState.RemainingWildcardDirectory)) + { + int num = recursionState.RemainingWildcardDirectory.IndexOfAny(directorySeparatorCharacters); + text = ((num != -1) ? recursionState.RemainingWildcardDirectory.Substring(0, num) : recursionState.RemainingWildcardDirectory); + if (text == "**") + { + text = null; + recursionState.RemainingWildcardDirectory = "**"; + } + else + { + recursionState.RemainingWildcardDirectory = ((num != -1) ? recursionState.RemainingWildcardDirectory.Substring(num + 1) : string.Empty); + } + } + result.NeedsDirectoryRecursion = true; + result.RemainingWildcardDirectory = recursionState.RemainingWildcardDirectory; + result.DirectoryPattern = text; + } + return result; + } + + private void GetFilesRecursive(ConcurrentStack> listOfFiles, RecursionState recursionState, string projectDirectory, bool stripProjectDirectory, IList searchesToExclude, Dictionary> searchesToExcludeInSubdirs, TaskOptions taskOptions) + { + ErrorUtilities.VerifyThrow(recursionState.SearchData.Filespec == null || recursionState.SearchData.RegexFileMatch == null, "File-spec overrides the regular expression -- pass null for file-spec if you want to use the regular expression."); + ErrorUtilities.VerifyThrow(recursionState.SearchData.Filespec != null || recursionState.SearchData.RegexFileMatch != null, "Need either a file-spec or a regular expression to match files."); + ErrorUtilities.VerifyThrow(recursionState.RemainingWildcardDirectory != null, "Expected non-null remaning wildcard directory."); + RecursiveStepResult[] excludeNextSteps = null; + if (searchesToExclude != null) + { + excludeNextSteps = new RecursiveStepResult[searchesToExclude.Count]; + for (int i = 0; i < searchesToExclude.Count; i++) + { + RecursionState recursionState2 = searchesToExclude[i]; + excludeNextSteps[i] = GetFilesRecursiveStep(searchesToExclude[i]); + if (!recursionState2.IsLookingForMatchingDirectory && recursionState2.SearchData.Filespec != null && recursionState2.RemainingWildcardDirectory == recursionState.RemainingWildcardDirectory && (IsAllFilesWildcard(recursionState2.SearchData.Filespec) || recursionState2.SearchData.Filespec == recursionState.SearchData.Filespec)) + { + return; + } + } + } + RecursiveStepResult nextStep = GetFilesRecursiveStep(recursionState); + List list = null; + foreach (string item2 in GetFilesForStep(nextStep, recursionState, projectDirectory, stripProjectDirectory)) + { + if (excludeNextSteps != null) + { + bool flag = false; + for (int j = 0; j < excludeNextSteps.Length; j++) + { + if (excludeNextSteps[j].ConsiderFiles && MatchFileRecursionStep(searchesToExclude[j], item2)) + { + flag = true; + break; + } + } + if (flag) + { + continue; + } + } + if (list == null) + { + list = new List(); + } + list.Add(item2); + } + if (list != null && list.Count > 0) + { + listOfFiles.Push(list); + } + if (!nextStep.NeedsDirectoryRecursion) + { + return; + } + Action action = delegate (string subdir) + { + RecursionState recursionState3 = recursionState; + recursionState3.BaseDirectory = subdir; + recursionState3.RemainingWildcardDirectory = nextStep.RemainingWildcardDirectory; + if (recursionState3.IsLookingForMatchingDirectory && DirectoryEndsWithPattern(subdir, recursionState.SearchData.DirectoryPattern)) + { + recursionState3.IsInsideMatchingDirectory = true; + } + List list2 = null; + if (excludeNextSteps != null) + { + list2 = new List(); + for (int k = 0; k < excludeNextSteps.Length; k++) + { + if (excludeNextSteps[k].NeedsDirectoryRecursion && (excludeNextSteps[k].DirectoryPattern == null || IsMatch(Path.GetFileName(subdir), excludeNextSteps[k].DirectoryPattern))) + { + RecursionState item = searchesToExclude[k]; + item.BaseDirectory = subdir; + item.RemainingWildcardDirectory = excludeNextSteps[k].RemainingWildcardDirectory; + if (item.IsLookingForMatchingDirectory && DirectoryEndsWithPattern(subdir, item.SearchData.DirectoryPattern)) + { + item.IsInsideMatchingDirectory = true; + } + list2.Add(item); + } + } + } + if (searchesToExcludeInSubdirs != null && searchesToExcludeInSubdirs.TryGetValue(subdir, out var value)) + { + if (list2 == null) + { + list2 = new List(); + } + list2.AddRange(value); + } + GetFilesRecursive(listOfFiles, recursionState3, projectDirectory, stripProjectDirectory, list2, searchesToExcludeInSubdirs, taskOptions); + }; + int num = 0; + if (taskOptions.MaxTasks > 1 && taskOptions.MaxTasksPerIteration > 1) + { + if (taskOptions.MaxTasks == taskOptions.MaxTasksPerIteration) + { + num = taskOptions.AvailableTasks; + taskOptions.AvailableTasks = 0; + } + else + { + lock (taskOptions) + { + num = Math.Min(taskOptions.MaxTasksPerIteration, taskOptions.AvailableTasks); + taskOptions.AvailableTasks -= num; + } + } + } + if (num < 2) + { + foreach (string item3 in _getFileSystemEntries(FileSystemEntity.Directories, recursionState.BaseDirectory, nextStep.DirectoryPattern, null, stripProjectDirectory: false)) + { + action(item3); + } + } + else + { + Parallel.ForEach(_getFileSystemEntries(FileSystemEntity.Directories, recursionState.BaseDirectory, nextStep.DirectoryPattern, null, stripProjectDirectory: false), new ParallelOptions + { + MaxDegreeOfParallelism = num + }, action); + } + if (num <= 0) + { + return; + } + if (taskOptions.MaxTasks == taskOptions.MaxTasksPerIteration) + { + taskOptions.AvailableTasks = taskOptions.MaxTasks; + return; + } + lock (taskOptions) + { + taskOptions.AvailableTasks += num; + } + } + + private static bool IsSubdirectoryOf(string possibleChild, string possibleParent) + { + if (possibleParent == string.Empty) + { + return true; + } + if (!possibleChild.StartsWith(possibleParent, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (directorySeparatorCharacters.Contains(possibleParent[possibleParent.Length - 1])) + { + return true; + } + return directorySeparatorCharacters.Contains(possibleChild[possibleParent.Length]); + } + + private static bool DirectoryEndsWithPattern(string directoryPath, string pattern) + { + int num = directoryPath.LastIndexOfAny(FileUtilities.Slashes); + if (num != -1) + { + return IsMatch(directoryPath.Substring(num + 1), pattern); + } + return false; + } + + internal void GetFileSpecInfoWithRegexObject(string filespec, out Regex regexFileMatch, out bool needsRecursion, out bool isLegalFileSpec) + { + GetFileSpecInfo(filespec, out var fixedDirectoryPart, out var wildcardDirectoryPart, out var filenamePart, out needsRecursion, out isLegalFileSpec); + if (isLegalFileSpec) + { + string pattern = RegularExpressionFromFileSpec(fixedDirectoryPart, wildcardDirectoryPart, filenamePart); + regexFileMatch = new Regex(pattern, RegexOptions.IgnoreCase); + } + else + { + regexFileMatch = null; + } + } + + internal static void GetRegexMatchInfo(string fileToMatch, Regex fileSpecRegex, out bool isMatch, out string wildcardDirectoryPart, out string filenamePart) + { + Match match = fileSpecRegex.Match(fileToMatch); + isMatch = match.Success; + wildcardDirectoryPart = string.Empty; + filenamePart = string.Empty; + if (isMatch) + { + wildcardDirectoryPart = match.Groups["WILDCARDDIR"].Value; + filenamePart = match.Groups["FILENAME"].Value; + } + } + + internal Result FileMatch(string filespec, string fileToMatch) + { + Result result = new Result(); + fileToMatch = GetLongPathName(fileToMatch, _getFileSystemEntries); + GetFileSpecInfoWithRegexObject(filespec, out var regexFileMatch, out result.isFileSpecRecursive, out result.isLegalFileSpec); + if (result.isLegalFileSpec) + { + GetRegexMatchInfo(fileToMatch, regexFileMatch, out result.isMatch, out result.wildcardDirectoryPart, out var _); + } + return result; + } + + private static string[] CreateArrayWithSingleItemIfNotExcluded(string filespecUnescaped, List excludeSpecsUnescaped) + { + if (excludeSpecsUnescaped != null) + { + foreach (string item in excludeSpecsUnescaped) + { + if (FileUtilities.PathsEqual(filespecUnescaped, item)) + { + return Array.Empty(); + } + Result result = Default.FileMatch(item, filespecUnescaped); + if (result.isLegalFileSpec && result.isMatch) + { + return Array.Empty(); + } + } + } + return new string[1] { filespecUnescaped }; + } + + } +} \ No newline at end of file diff --git a/Microsoft.Build.Shared/FileSystem/FileSystems.cs b/Microsoft.Build.Shared/FileSystem/FileSystems.cs new file mode 100644 index 0000000..a1d3c34 --- /dev/null +++ b/Microsoft.Build.Shared/FileSystem/FileSystems.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Shared.FileSystem +{ + internal interface IFileSystem + { + TextReader ReadFile(string path); + + Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share); + + string ReadFileAllText(string path); + + byte[] ReadFileAllBytes(string path); + + IEnumerable EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + + IEnumerable EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + + IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + + FileAttributes GetAttributes(string path); + + DateTime GetLastWriteTimeUtc(string path); + + bool DirectoryExists(string path); + + bool FileExists(string path); + + bool FileOrDirectoryExists(string path); + } + + + internal static class FileSystems + { + public static IFileSystem Default = GetFileSystem(); + + private static IFileSystem GetFileSystem() + { +#if __ + // 不支持Windows,所以不考虑这个路径 + if (NativeMethods.IsWindows) + { + return MSBuildOnWindowsFileSystem.Singleton(); + } +#endif + return ManagedFileSystem.Singleton(); + } + } +} diff --git a/Microsoft.Build.Shared/FileSystem/ManagedFileSystem.cs b/Microsoft.Build.Shared/FileSystem/ManagedFileSystem.cs new file mode 100644 index 0000000..489633c --- /dev/null +++ b/Microsoft.Build.Shared/FileSystem/ManagedFileSystem.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Shared.FileSystem; + +namespace Microsoft.Build.Shared.FileSystem +{ + internal class ManagedFileSystem : IFileSystem + { + private static readonly ManagedFileSystem Instance = new ManagedFileSystem(); + + public static ManagedFileSystem Singleton() + { + return Instance; + } + + protected ManagedFileSystem() + { + } + + public TextReader ReadFile(string path) + { + return new StreamReader(path); + } + + public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) + { + return new FileStream(path, mode, access, share); + } + + public string ReadFileAllText(string path) + { + return File.ReadAllText(path); + } + + public byte[] ReadFileAllBytes(string path) + { + return File.ReadAllBytes(path); + } + + public virtual IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) + { + return Directory.EnumerateFiles(path, searchPattern, searchOption); + } + + public virtual IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) + { + return Directory.EnumerateDirectories(path, searchPattern, searchOption); + } + + public virtual IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) + { + return Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption); + } + + public FileAttributes GetAttributes(string path) + { + return File.GetAttributes(path); + } + + public virtual DateTime GetLastWriteTimeUtc(string path) + { + return File.GetLastWriteTimeUtc(path); + } + + public virtual bool DirectoryExists(string path) + { + return Directory.Exists(path); + } + + public virtual bool FileExists(string path) + { + return File.Exists(path); + } + + public virtual bool FileOrDirectoryExists(string path) + { + if (!FileExists(path)) + { + return DirectoryExists(path); + } + return true; + } + } +} diff --git a/Microsoft.Build.Shared/FileTracker.cs b/Microsoft.Build.Shared/FileTracker.cs new file mode 100644 index 0000000..2c80752 --- /dev/null +++ b/Microsoft.Build.Shared/FileTracker.cs @@ -0,0 +1,89 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Tasks; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Shared +{ + internal class FileTracker + { + public static bool FileIsExcludedFromDependencies(string fileName) + { +#if __ + if (!FileIsUnderPath(fileName, s_applicationDataPath) && !FileIsUnderPath(fileName, s_localApplicationDataPath) && !FileIsUnderPath(fileName, s_localLowApplicationDataPath) && !FileIsUnderPath(fileName, s_tempShortPath) && !FileIsUnderPath(fileName, s_tempLongPath)) + { + return s_commonApplicationDataPaths.Any((string p) => FileIsUnderPath(fileName, p)); + } + return true; +#else + // Linux下公共文件跟这些目录都没什么关系。 + return false; +#endif + } + + public static bool FileIsUnderPath(string fileName, string path) + { + path = FileUtilities.EnsureTrailingSlash(path); + return string.Compare(fileName, 0, path, 0, path.Length, StringComparison.OrdinalIgnoreCase) == 0; + } + + public static string FormatRootingMarker(ITaskItem source) + { + return FormatRootingMarker(new ITaskItem[1] { source }, null); + } + + public static string FormatRootingMarker(ITaskItem source, ITaskItem output) + { + return FormatRootingMarker(new ITaskItem[1] { source }, new ITaskItem[1] { output }); + } + + public static string FormatRootingMarker(ITaskItem[] sources) + { + return FormatRootingMarker(sources, null); + } + + public static string FormatRootingMarker(ITaskItem[] sources, ITaskItem[] outputs) + { + ErrorUtilities.VerifyThrowArgumentNull(sources, "sources"); + if (outputs == null) + { + outputs = Array.Empty(); + } + List list = new List(sources.Length + outputs.Length); + ITaskItem[] array = sources; + foreach (ITaskItem taskItem in array) + { + list.Add(FileUtilities.NormalizePath(taskItem.ItemSpec)/*.ToUpperInvariant()*/); + } + array = outputs; + foreach (ITaskItem taskItem2 in array) + { + list.Add(FileUtilities.NormalizePath(taskItem2.ItemSpec)/*.ToUpperInvariant()*/); + } + list.Sort(StringComparer.OrdinalIgnoreCase); + return string.Join("|", list); + } + + internal static void LogMessageFromResources(TaskLoggingHelper Log, MessageImportance importance, string messageResourceName, params object[] messageArgs) + { + if (Log != null) + { + ErrorUtilities.VerifyThrowArgumentNull(messageResourceName, "messageResourceName"); + Log.LogMessage(importance, messageResourceName, messageArgs); + } + } + + internal static void LogMessage(TaskLoggingHelper Log, MessageImportance importance, string message, params object[] messageArgs) + { + Log?.LogMessage(importance, message, messageArgs); + } + + internal static void LogWarningWithCodeFromResources(TaskLoggingHelper Log, string messageResourceName, params object[] messageArgs) + { + Log?.LogWarning(messageResourceName, messageArgs); + // Log?.LogWarningWithCodeFromResources(messageResourceName, messageArgs); + } + } +} diff --git a/Microsoft.Build.Shared/FileUtilities.cs b/Microsoft.Build.Shared/FileUtilities.cs new file mode 100644 index 0000000..6df3aa0 --- /dev/null +++ b/Microsoft.Build.Shared/FileUtilities.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Shared +{ + + internal static class FileUtilities + { + private static readonly IFileSystem DefaultFileSystem = FileSystems.Default; + + internal static readonly char[] Slashes = new char[2] { '/', '\\' }; + + // Linux大小写敏感 + private static readonly ConcurrentDictionary FileExistenceCache = new ConcurrentDictionary(StringComparer.Ordinal); + + internal static bool IsSlash(char c) + { + if (c != Path.DirectorySeparatorChar) + { + return c == Path.AltDirectorySeparatorChar; + } + return true; + } + + internal static string TrimTrailingSlashes(this string s) + { + return s.TrimEnd(Slashes); + } + + internal static string FixFilePath(string path) + { + if (!string.IsNullOrEmpty(path) && Path.DirectorySeparatorChar != '\\') + { + return path.Replace('\\', '/'); + } + return path; + } + + /// + /// If the given path doesn't have a trailing slash then add one. + /// If the path is an empty string, does not modify it. + /// + /// The path to check. + /// A path with a slash. + internal static string EnsureTrailingSlash(string fileSpec) + { + fileSpec = FixFilePath(fileSpec); + if (fileSpec.Length > 0 && !IsSlash(fileSpec[fileSpec.Length - 1])) + { + fileSpec += Path.DirectorySeparatorChar; + } + + return fileSpec; + } + + internal static string NormalizePath(string path) + { + ErrorUtilities.VerifyThrowArgumentLength(path, "path"); + return FixFilePath(GetFullPath(path)); + } + + private static string GetFullPath(string path) + { +#if __ + if (NativeMethods.IsWindows) + { + string fullPath = NativeMethods.GetFullPath(path); + if (IsPathTooLong(fullPath)) + { + throw new PathTooLongException(ResourceUtilities.FormatString(AssemblyResources.GetString("Shared.PathTooLong"), path, NativeMethods.MaxPath)); + } + Path.HasExtension(fullPath); + if (!IsUNCPath(fullPath)) + { + return fullPath; + } + return Path.GetFullPath(fullPath); + } +#endif + return Path.GetFullPath(path); + } + + internal static string EnsureNoTrailingSlash(string path) + { + path = FixFilePath(path); + if (EndsWithSlash(path)) + { + path = path.Substring(0, path.Length - 1); + } + return path; + } + + internal static bool EndsWithSlash(string fileSpec) + { + if (fileSpec.Length <= 0) + { + return false; + } + return IsSlash(fileSpec[fileSpec.Length - 1]); + } + + internal static string GetDirectoryNameOfFullPath(string fullPath) + { + if (fullPath != null) + { + int num = fullPath.Length; + while (num > 0 && fullPath[--num] != Path.DirectorySeparatorChar && fullPath[num] != Path.AltDirectorySeparatorChar) + { + } + return FixFilePath(fullPath.Substring(0, num)); + } + return null; + } + + internal static string AttemptToShortenPath(string path) + { +#if __ + if (IsPathTooLong(path) || IsPathTooLongIfRooted(path)) + { + path = GetFullPathNoThrow(path); + } +#endif + return FixFilePath(path); + } + + internal static bool PathsEqual(string path1, string path2) + { + if (path1 == null && path2 == null) + { + return true; + } + if (path1 == null || path2 == null) + { + return false; + } + int num = path1.Length - 1; + int num2 = path2.Length - 1; + for (int num3 = num; num3 >= 0; num3--) + { + char c = path1[num3]; + if (c != '/' && c != '\\') + { + break; + } + num--; + } + for (int num4 = num2; num4 >= 0; num4--) + { + char c2 = path2[num4]; + if (c2 != '/' && c2 != '\\') + { + break; + } + num2--; + } + if (num != num2) + { + return false; + } + for (int i = 0; i <= num; i++) + { + uint num5 = path1[i]; + uint num6 = path2[i]; + if ((num5 | num6) > 127) + { + return PathsEqualNonAscii(path1, path2, i, num - i + 1); + } + if (num5 - 97 <= 25) + { + num5 -= 32; + } + if (num6 - 97 <= 25) + { + num6 -= 32; + } + if (num5 == 92) + { + num5 = 47u; + } + if (num6 == 92) + { + num6 = 47u; + } + if (num5 != num6) + { + return false; + } + } + return true; + } + + private static bool PathsEqualNonAscii(string strA, string strB, int i, int length) + { + if (string.Compare(strA, i, strB, i, length, StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + string strA2 = strA.ToSlash(); + string strB2 = strB.ToSlash(); + if (string.Compare(strA2, i, strB2, i, length, StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + return false; + } + + internal static string ToSlash(this string s) + { + return s.Replace('\\', '/'); + } + + internal static bool FileExistsNoThrow(string fullPath, IFileSystem fileSystem = null) + { + fullPath = AttemptToShortenPath(fullPath); + try + { + if (fileSystem == null) + { + fileSystem = DefaultFileSystem; + } + return /*Traits.Instance.CacheFileExistence*/true ? FileExistenceCache.GetOrAdd(fullPath, (string fullPath) => fileSystem.FileExists(fullPath)) : fileSystem.FileExists(fullPath); + } + catch + { + return false; + } + } + + internal static StreamWriter OpenWrite(string path, bool append, Encoding encoding = null) + { + FileMode mode = (append ? FileMode.Append : FileMode.Create); + Stream stream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan); + if (stream != null) + FileExistenceCache[path] = true; + + if (encoding == null) + { + return new StreamWriter(stream); + } + return new StreamWriter(stream, encoding); + } + + internal static bool IsAnySlash(char c) + { + if (c != '/') + { + return c == '\\'; + } + return true; + } + + internal static void ClearFileExistenceCache() + { + FileExistenceCache.Clear(); + } + + internal static void UpdateFileExistenceCache(string path) + { + bool value; + FileExistenceCache.Remove(path, out value); + } + } +} diff --git a/Microsoft.Build.Shared/FileUtilitiesRegex.cs b/Microsoft.Build.Shared/FileUtilitiesRegex.cs new file mode 100644 index 0000000..c76ef44 --- /dev/null +++ b/Microsoft.Build.Shared/FileUtilitiesRegex.cs @@ -0,0 +1,110 @@ +using System.Runtime.CompilerServices; + +namespace Microsoft.Build.Shared +{ + internal static class FileUtilitiesRegex + { + private static readonly char _backSlash = '\\'; + + private static readonly char _forwardSlash = '/'; + + internal static bool IsDrivePattern(string pattern) + { + if (pattern.Length == 2) + { + return StartsWithDrivePattern(pattern); + } + return false; + } + + internal static bool IsDrivePatternWithSlash(string pattern) + { + if (pattern.Length == 3) + { + return StartsWithDrivePatternWithSlash(pattern); + } + return false; + } + + internal static bool StartsWithDrivePattern(string pattern) + { + if (pattern.Length >= 2 && ((pattern[0] >= 'A' && pattern[0] <= 'Z') || (pattern[0] >= 'a' && pattern[0] <= 'z'))) + { + return pattern[1] == ':'; + } + return false; + } + + internal static bool StartsWithDrivePatternWithSlash(string pattern) + { + if (pattern.Length >= 3 && StartsWithDrivePattern(pattern)) + { + if (pattern[2] != _backSlash) + { + return pattern[2] == _forwardSlash; + } + return true; + } + return false; + } + + internal static bool IsUncPattern(string pattern) + { + return StartsWithUncPatternMatchLength(pattern) == pattern.Length; + } + + internal static bool StartsWithUncPattern(string pattern) + { + return StartsWithUncPatternMatchLength(pattern) != -1; + } + + internal static int StartsWithUncPatternMatchLength(string pattern) + { + if (!MeetsUncPatternMinimumRequirements(pattern)) + { + return -1; + } + bool flag = true; + bool flag2 = false; + for (int i = 2; i < pattern.Length; i++) + { + if (pattern[i] == _backSlash || pattern[i] == _forwardSlash) + { + if (flag) + { + return -1; + } + if (flag2) + { + return i; + } + flag2 = true; + flag = true; + } + else + { + flag = false; + } + } + if (!flag2) + { + return -1; + } + return pattern.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool MeetsUncPatternMinimumRequirements(string pattern) + { + if (pattern.Length >= 5 && (pattern[0] == _backSlash || pattern[0] == _forwardSlash)) + { + if (pattern[1] != _backSlash) + { + return pattern[1] == _forwardSlash; + } + return true; + } + return false; + } + } +} diff --git a/Microsoft.Build.Shared/MSBuildConstants.cs b/Microsoft.Build.Shared/MSBuildConstants.cs new file mode 100644 index 0000000..472037c --- /dev/null +++ b/Microsoft.Build.Shared/MSBuildConstants.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Shared +{ + internal class MSBuildConstants + { + internal static readonly char[] PipeChar = new char[1] { '|' }; + } +} diff --git a/Microsoft.Build.Shared/Microsoft.Build.Shared.projitems b/Microsoft.Build.Shared/Microsoft.Build.Shared.projitems index dde3c59..7b07a72 100644 --- a/Microsoft.Build.Shared/Microsoft.Build.Shared.projitems +++ b/Microsoft.Build.Shared/Microsoft.Build.Shared.projitems @@ -10,5 +10,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Build.Shared/ReuseableStringBuilder.cs b/Microsoft.Build.Shared/ReuseableStringBuilder.cs new file mode 100644 index 0000000..38c6d2b --- /dev/null +++ b/Microsoft.Build.Shared/ReuseableStringBuilder.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Shared +{ + internal sealed class ReuseableStringBuilder : IDisposable + { + private static class ReuseableStringBuilderFactory + { + private const int MaxBuilderSize = 1024; + + private static StringBuilder s_sharedBuilder; + + internal static StringBuilder Get(int capacity) + { + StringBuilder stringBuilder = Interlocked.Exchange(ref s_sharedBuilder, null); + if (stringBuilder == null) + { + stringBuilder = new StringBuilder(capacity); + } + else if (stringBuilder.Capacity < capacity) + { + stringBuilder.Capacity = capacity; + } + return stringBuilder; + } + + internal static void Release(StringBuilder returningBuilder) + { + if (returningBuilder.Capacity < 1024) + { + returningBuilder.Clear(); + Interlocked.Exchange(ref s_sharedBuilder, returningBuilder); + } + } + } + + private StringBuilder _borrowedBuilder; + + private int _capacity; + + public int Length + { + get + { + if (_borrowedBuilder != null) + { + return _borrowedBuilder.Length; + } + return 0; + } + set + { + LazyPrepare(); + _borrowedBuilder.Length = value; + } + } + + internal ReuseableStringBuilder(int capacity = 16) + { + _capacity = capacity; + } + + public override string ToString() + { + if (_borrowedBuilder == null) + { + return string.Empty; + } + return _borrowedBuilder.ToString(); + } + + void IDisposable.Dispose() + { + if (_borrowedBuilder != null) + { + ReuseableStringBuilderFactory.Release(_borrowedBuilder); + _borrowedBuilder = null; + _capacity = -1; + } + } + + internal ReuseableStringBuilder Append(char value) + { + LazyPrepare(); + _borrowedBuilder.Append(value); + return this; + } + + internal ReuseableStringBuilder Append(string value) + { + LazyPrepare(); + _borrowedBuilder.Append(value); + return this; + } + + internal ReuseableStringBuilder Append(string value, int startIndex, int count) + { + LazyPrepare(); + _borrowedBuilder.Append(value, startIndex, count); + return this; + } + + public ReuseableStringBuilder AppendSeparated(char separator, ICollection strings) + { + LazyPrepare(); + int num = strings.Count - 1; + foreach (string @string in strings) + { + _borrowedBuilder.Append(@string); + if (num > 0) + { + _borrowedBuilder.Append(separator); + } + num--; + } + return this; + } + + public ReuseableStringBuilder Clear() + { + LazyPrepare(); + _borrowedBuilder.Clear(); + return this; + } + + internal ReuseableStringBuilder Remove(int startIndex, int length) + { + LazyPrepare(); + _borrowedBuilder.Remove(startIndex, length); + return this; + } + + private void LazyPrepare() + { + if (_borrowedBuilder == null) + { + ErrorUtilities.VerifyThrow(_capacity != -1, "Reusing after dispose"); + _borrowedBuilder = ReuseableStringBuilderFactory.Get(_capacity); + } + } + } +} diff --git a/Microsoft.Build.Shared/StringBuilderCache.cs b/Microsoft.Build.Shared/StringBuilderCache.cs new file mode 100644 index 0000000..d0c0ef6 --- /dev/null +++ b/Microsoft.Build.Shared/StringBuilderCache.cs @@ -0,0 +1,43 @@ +using System; +using System.Text; + +namespace Microsoft.Build.Shared +{ + internal static class StringBuilderCache + { + private const int MAX_BUILDER_SIZE = 360; + + [ThreadStatic] + private static StringBuilder t_cachedInstance; + + public static StringBuilder Acquire(int capacity = 16) + { + if (capacity <= 360) + { + StringBuilder stringBuilder = t_cachedInstance; + if (stringBuilder != null && capacity <= stringBuilder.Capacity) + { + t_cachedInstance = null; + stringBuilder.Length = 0; + return stringBuilder; + } + } + return new StringBuilder(capacity); + } + + public static void Release(StringBuilder sb) + { + if (sb.Capacity <= 360) + { + t_cachedInstance = sb; + } + } + + public static string GetStringAndRelease(StringBuilder sb) + { + string result = sb.ToString(); + Release(sb); + return result; + } + } +} diff --git a/Microsoft.Build.Shared/VCUtilities.cs b/Microsoft.Build.Shared/VCUtilities.cs new file mode 100644 index 0000000..4028dc0 --- /dev/null +++ b/Microsoft.Build.Shared/VCUtilities.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography; + +namespace Microsoft.Build.Shared +{ + internal static class VCUtilities + { + internal static string GetHashString(string content) + { + using SHA256 sHA = new SHA256CryptoServiceProvider(); + byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(content)).Take(16).ToArray(); + char[] array2 = new char[16]; + for (int i = 0; i < array2.Length; i++) + { + array2[i] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[(int)array[i] % "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".Length]; + } + return new string(array2); + } + } +} diff --git a/Microsoft.Build.Utilities/CanonicalTrackedFilesHelper.cs b/Microsoft.Build.Utilities/CanonicalTrackedFilesHelper.cs new file mode 100644 index 0000000..e6e68ad --- /dev/null +++ b/Microsoft.Build.Utilities/CanonicalTrackedFilesHelper.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Utilities +{ + internal static class CanonicalTrackedFilesHelper + { + internal const int MaxLogCount = 100; + + internal static bool RootContainsAllSubRootComponents(string compositeRoot, string compositeSubRoot) + { + if (string.Equals(compositeRoot, compositeSubRoot, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + string[] array = compositeSubRoot.Split(MSBuildConstants.PipeChar); + foreach (string value in array) + { + if (!compositeRoot.Contains(value)) + { + return false; + } + } + return true; + } + + internal static bool FilesExistAndRecordNewestWriteTime(ICollection files, TaskLoggingHelper log, out DateTime outputNewestTime, out string outputNewestFilename) + { + return FilesExistAndRecordRequestedWriteTime(files, log, getNewest: true, out outputNewestTime, out outputNewestFilename); + } + + internal static bool FilesExistAndRecordOldestWriteTime(ICollection files, TaskLoggingHelper log, out DateTime outputOldestTime, out string outputOldestFilename) + { + return FilesExistAndRecordRequestedWriteTime(files, log, getNewest: false, out outputOldestTime, out outputOldestFilename); + } + + private static bool FilesExistAndRecordRequestedWriteTime(ICollection files, TaskLoggingHelper log, bool getNewest, out DateTime requestedTime, out string requestedFilename) + { + bool result = true; + requestedTime = (getNewest ? DateTime.MinValue : DateTime.MaxValue); + requestedFilename = string.Empty; + if (files == null || files.Count == 0) + { + return false; + } + foreach (ITaskItem file in files) + { + DateTime lastWriteFileUtcTime = NativeMethods.GetLastWriteFileUtcTime(file.ItemSpec); + if (lastWriteFileUtcTime == DateTime.MinValue) + { + FileTracker.LogMessageFromResources(log, MessageImportance.Low, "Tracking_OutputDoesNotExist", file.ItemSpec); + return false; + } + if ((getNewest && lastWriteFileUtcTime > requestedTime) || (!getNewest && lastWriteFileUtcTime < requestedTime)) + { + requestedTime = lastWriteFileUtcTime; + requestedFilename = file.ItemSpec; + } + } + return result; + } + } +} diff --git a/Microsoft.Build.Utilities/CanonicalTrackedInputFiles.cs b/Microsoft.Build.Utilities/CanonicalTrackedInputFiles.cs new file mode 100644 index 0000000..8bb9853 --- /dev/null +++ b/Microsoft.Build.Utilities/CanonicalTrackedInputFiles.cs @@ -0,0 +1,737 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Utilities +{ + public class CanonicalTrackedInputFiles + { + private DateTime _outputNewestTime = DateTime.MinValue; + + private ITaskItem[] _tlogFiles; + + private ITaskItem[] _sourceFiles; + + private TaskLoggingHelper _log; + + private CanonicalTrackedOutputFiles _outputs; + + private ITaskItem[] _outputFileGroup; + + private ITaskItem[] _outputFiles; + + private bool _useMinimalRebuildOptimization; + + private bool _tlogAvailable; + + private bool _maintainCompositeRootingMarkers; + + private readonly HashSet _excludedInputPaths = new HashSet(StringComparer.Ordinal); + + private readonly ConcurrentDictionary _lastWriteTimeCache = new ConcurrentDictionary(StringComparer.Ordinal); + + internal ITaskItem[] SourcesNeedingCompilation { get; set; } + + public Dictionary> DependencyTable { get; private set; } + + public CanonicalTrackedInputFiles(ITaskItem[] tlogFiles, ITaskItem[] sourceFiles, CanonicalTrackedOutputFiles outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + InternalConstruct(null, tlogFiles, sourceFiles, null, null, outputs, useMinimalRebuildOptimization, maintainCompositeRootingMarkers); + } + + public CanonicalTrackedInputFiles(ITaskItem[] tlogFiles, ITaskItem[] sourceFiles, ITaskItem[] excludedInputPaths, CanonicalTrackedOutputFiles outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + InternalConstruct(null, tlogFiles, sourceFiles, null, excludedInputPaths, outputs, useMinimalRebuildOptimization, maintainCompositeRootingMarkers); + } + + public CanonicalTrackedInputFiles(ITask ownerTask, ITaskItem[] tlogFiles, ITaskItem[] sourceFiles, ITaskItem[] excludedInputPaths, CanonicalTrackedOutputFiles outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + InternalConstruct(ownerTask, tlogFiles, sourceFiles, null, excludedInputPaths, outputs, useMinimalRebuildOptimization, maintainCompositeRootingMarkers); + } + + public CanonicalTrackedInputFiles(ITask ownerTask, ITaskItem[] tlogFiles, ITaskItem[] sourceFiles, ITaskItem[] excludedInputPaths, ITaskItem[] outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + InternalConstruct(ownerTask, tlogFiles, sourceFiles, outputs, excludedInputPaths, null, useMinimalRebuildOptimization, maintainCompositeRootingMarkers); + } + + public CanonicalTrackedInputFiles(ITask ownerTask, ITaskItem[] tlogFiles, ITaskItem sourceFile, ITaskItem[] excludedInputPaths, CanonicalTrackedOutputFiles outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + InternalConstruct(ownerTask, tlogFiles, new ITaskItem[1] { sourceFile }, null, excludedInputPaths, outputs, useMinimalRebuildOptimization, maintainCompositeRootingMarkers); + } + + private void InternalConstruct(ITask ownerTask, ITaskItem[] tlogFiles, ITaskItem[] sourceFiles, ITaskItem[] outputFiles, ITaskItem[] excludedInputPaths, CanonicalTrackedOutputFiles outputs, bool useMinimalRebuildOptimization, bool maintainCompositeRootingMarkers) + { + if (ownerTask != null) + { + _log = new TaskLoggingHelper(ownerTask) + { + TaskResources = Microsoft.Build.CppTasks.Common.Properties.Microsoft_Build_CPPTasks_Strings.ResourceManager, + HelpKeywordPrefix = "MSBuild." + }; + } + _tlogFiles = TrackedDependencies.ExpandWildcards(tlogFiles); + _tlogAvailable = TrackedDependencies.ItemsExist(_tlogFiles); + _sourceFiles = sourceFiles; + _outputs = outputs; + _outputFiles = outputFiles; + _useMinimalRebuildOptimization = useMinimalRebuildOptimization; + _maintainCompositeRootingMarkers = maintainCompositeRootingMarkers; + if (excludedInputPaths != null) + { + for (int i = 0; i < excludedInputPaths.Length; i++) + { + string item = FileUtilities.EnsureNoTrailingSlash(FileUtilities.NormalizePath(excludedInputPaths[i].ItemSpec))/*.ToUpperInvariant()*/; + _excludedInputPaths.Add(item); + } + } + DependencyTable = new Dictionary>(StringComparer.OrdinalIgnoreCase); + if (_tlogFiles != null) + { + ConstructDependencyTable(); + } + } + + public ITaskItem[] ComputeSourcesNeedingCompilation() + { + return ComputeSourcesNeedingCompilation(searchForSubRootsInCompositeRootingMarkers: true); + } + + public ITaskItem[] ComputeSourcesNeedingCompilation(bool searchForSubRootsInCompositeRootingMarkers) + { + if (_outputFiles != null) + { + _outputFileGroup = _outputFiles; + } + else if (_sourceFiles != null && _outputs != null && _maintainCompositeRootingMarkers) + { + _outputFileGroup = _outputs.OutputsForSource(_sourceFiles, searchForSubRootsInCompositeRootingMarkers); + } + else if (_sourceFiles != null && _outputs != null) + { + _outputFileGroup = _outputs.OutputsForNonCompositeSource(_sourceFiles); + } + if (!_maintainCompositeRootingMarkers) + { + return ComputeSourcesNeedingCompilationFromPrimaryFiles(); + } + return ComputeSourcesNeedingCompilationFromCompositeRootingMarker(searchForSubRootsInCompositeRootingMarkers); + } + + private ITaskItem[] ComputeSourcesNeedingCompilationFromPrimaryFiles() + { + if (SourcesNeedingCompilation == null) + { + ConcurrentQueue sourcesNeedingCompilationList = new ConcurrentQueue(); + bool allOutputFilesExist = false; + if (_tlogAvailable && !_useMinimalRebuildOptimization) + { + allOutputFilesExist = FilesExistAndRecordNewestWriteTime(_outputFileGroup); + } + Parallel.For(0, _sourceFiles.Length, delegate (int index) + { + CheckIfSourceNeedsCompilation(sourcesNeedingCompilationList, allOutputFilesExist, _sourceFiles[index]); + }); + SourcesNeedingCompilation = sourcesNeedingCompilationList.ToArray(); + } + if (SourcesNeedingCompilation.Length == 0) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Normal, "Tracking_AllOutputsAreUpToDate"); + SourcesNeedingCompilation = Array.Empty(); + } + else + { + Array.Sort(SourcesNeedingCompilation, CompareTaskItems); + ITaskItem[] sourcesNeedingCompilation = SourcesNeedingCompilation; + foreach (ITaskItem taskItem in sourcesNeedingCompilation) + { + string metadata = taskItem.GetMetadata("_trackerModifiedPath"); + string metadata2 = taskItem.GetMetadata("_trackerModifiedTime"); + string metadata3 = taskItem.GetMetadata("_trackerOutputFile"); + string metadata4 = taskItem.GetMetadata("_trackerCompileReason"); + if (string.Equals(metadata4, "Tracking_SourceWillBeCompiledDependencyWasModifiedAt", StringComparison.Ordinal)) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, metadata4, taskItem.ItemSpec, metadata, metadata2); + } + else if (string.Equals(metadata4, "Tracking_SourceWillBeCompiledMissingDependency", StringComparison.Ordinal)) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, metadata4, taskItem.ItemSpec, metadata); + } + else if (string.Equals(metadata4, "Tracking_SourceWillBeCompiledOutputDoesNotExist", StringComparison.Ordinal)) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, metadata4, taskItem.ItemSpec, metadata3); + } + else + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, metadata4, taskItem.ItemSpec); + } + taskItem.RemoveMetadata("_trackerModifiedPath"); + taskItem.RemoveMetadata("_trackerModifiedTime"); + taskItem.RemoveMetadata("_trackerOutputFile"); + taskItem.RemoveMetadata("_trackerCompileReason"); + } + } + return SourcesNeedingCompilation; + } + + private void CheckIfSourceNeedsCompilation(ConcurrentQueue sourcesNeedingCompilationList, bool allOutputFilesExist, ITaskItem source) + { + if (!_tlogAvailable || _outputFileGroup == null) + { + source.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiledAsNoTrackingLog"); + sourcesNeedingCompilationList.Enqueue(source); + } + else if (!_useMinimalRebuildOptimization && !allOutputFilesExist) + { + source.SetMetadata("_trackerCompileReason", "Tracking_SourceOutputsNotAvailable"); + sourcesNeedingCompilationList.Enqueue(source); + } + else if (!IsUpToDate(source)) + { + if (string.IsNullOrEmpty(source.GetMetadata("_trackerCompileReason"))) + { + source.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiled"); + } + sourcesNeedingCompilationList.Enqueue(source); + } + else if (!_useMinimalRebuildOptimization && _outputNewestTime == DateTime.MinValue) + { + source.SetMetadata("_trackerCompileReason", "Tracking_SourceNotInTrackingLog"); + sourcesNeedingCompilationList.Enqueue(source); + } + } + + private static int CompareTaskItems(ITaskItem left, ITaskItem right) + { + return string.Compare(left.ItemSpec, right.ItemSpec, StringComparison.Ordinal); + } + + private ITaskItem[] ComputeSourcesNeedingCompilationFromCompositeRootingMarker(bool searchForSubRootsInCompositeRootingMarkers) + { + if (!_tlogAvailable) + { + return _sourceFiles; + } + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + string text = FileTracker.FormatRootingMarker(_sourceFiles); + List list = new List(); + foreach (string key in DependencyTable.Keys) + { + string text2 = key/*.ToUpperInvariant()*/; + if (searchForSubRootsInCompositeRootingMarkers) + { + if (text2.Contains(text) || CanonicalTrackedFilesHelper.RootContainsAllSubRootComponents(text, text2)) + { + SourceDependenciesForOutputRoot(dictionary, text2, _outputFileGroup); + } + } + else if (text2.Equals(text, StringComparison.Ordinal)) + { + SourceDependenciesForOutputRoot(dictionary, text2, _outputFileGroup); + } + } + if (dictionary.Count == 0) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_DependenciesForRootNotFound", text); + return _sourceFiles; + } + list.AddRange(dictionary.Values); + string outputOldestFilename = string.Empty; + if (CanonicalTrackedFilesHelper.FilesExistAndRecordNewestWriteTime(list, _log, out var outputNewestTime, out var outputNewestFilename) && CanonicalTrackedFilesHelper.FilesExistAndRecordOldestWriteTime(_outputFileGroup, _log, out var outputOldestTime, out outputOldestFilename) && outputNewestTime <= outputOldestTime) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Normal, "Tracking_AllOutputsAreUpToDate"); + return Array.Empty(); + } + if (dictionary.Count > 100) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_InputsNotShown", dictionary.Count); + } + else + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_InputsFor", text); + foreach (ITaskItem item in list) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t" + item); + } + } + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_InputNewerThanOutput", outputNewestFilename, outputOldestFilename); + return _sourceFiles; + } + + private void SourceDependenciesForOutputRoot(Dictionary sourceDependencies, string sourceKey, ITaskItem[] filesToIgnore) + { + bool flag = filesToIgnore != null && filesToIgnore.Length != 0; + if (!DependencyTable.TryGetValue(sourceKey, out var value)) + { + return; + } + foreach (string key in value.Keys) + { + bool flag2 = false; + if (flag) + { + foreach (ITaskItem taskItem in filesToIgnore) + { + if (string.Equals(key, taskItem.ItemSpec, StringComparison.OrdinalIgnoreCase)) + { + flag2 = true; + break; + } + } + } + if (!flag2 && !sourceDependencies.TryGetValue(key, out var _)) + { + sourceDependencies.Add(key, new TaskItem(key)); + } + } + } + + private bool IsUpToDate(ITaskItem sourceFile) + { + string text = FileUtilities.NormalizePath(sourceFile.ItemSpec); + Dictionary value; + bool flag = DependencyTable.TryGetValue(text, out value); + DateTime dateTime = _outputNewestTime; + if (_useMinimalRebuildOptimization && _outputs != null && flag) + { + dateTime = DateTime.MinValue; + if (!_outputs.DependencyTable.TryGetValue(text, out var value2)) + { + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceOutputsNotAvailable"); + return false; + } + DateTime lastWriteFileUtcTime = NativeMethods.GetLastWriteFileUtcTime(text); + foreach (string key in value2.Keys) + { + DateTime lastWriteFileUtcTime2 = NativeMethods.GetLastWriteFileUtcTime(key); + if (lastWriteFileUtcTime2 > DateTime.MinValue) + { + if (lastWriteFileUtcTime2 < lastWriteFileUtcTime) + { + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiledDependencyWasModifiedAt"); + sourceFile.SetMetadata("_trackerModifiedPath", text); + sourceFile.SetMetadata("_trackerModifiedTime", lastWriteFileUtcTime.ToLocalTime().ToString()); + return false; + } + if (lastWriteFileUtcTime2 > dateTime) + { + dateTime = lastWriteFileUtcTime2; + } + continue; + } + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiledOutputDoesNotExist"); + sourceFile.SetMetadata("_trackerOutputFile", key); + return false; + } + } + if (flag) + { + foreach (string key2 in value.Keys) + { + if (!FileIsExcludedFromDependencyCheck(key2)) + { + if (!_lastWriteTimeCache.TryGetValue(key2, out var value3)) + { + value3 = NativeMethods.GetLastWriteFileUtcTime(key2); + _lastWriteTimeCache[key2] = value3; + } + if (!(value3 > DateTime.MinValue)) + { + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiledMissingDependency"); + sourceFile.SetMetadata("_trackerModifiedPath", key2); + return false; + } + if (value3 > dateTime) + { + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceWillBeCompiledDependencyWasModifiedAt"); + sourceFile.SetMetadata("_trackerModifiedPath", key2); + sourceFile.SetMetadata("_trackerModifiedTime", value3.ToLocalTime().ToString()); + return false; + } + } + } + return true; + } + sourceFile.SetMetadata("_trackerCompileReason", "Tracking_SourceNotInTrackingLog"); + return false; + } + + public bool FileIsExcludedFromDependencyCheck(string fileName) + { + string directoryNameOfFullPath = FileUtilities.GetDirectoryNameOfFullPath(fileName); + return _excludedInputPaths.Contains(directoryNameOfFullPath); + } + + private bool FilesExistAndRecordNewestWriteTime(ITaskItem[] files) + { + string outputNewestFilename; + return CanonicalTrackedFilesHelper.FilesExistAndRecordNewestWriteTime(files, _log, out _outputNewestTime, out outputNewestFilename); + } + + private void ConstructDependencyTable() + { + string text; + try + { + text = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); + } + catch (ArgumentException ex) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", ex.Message); + return; + } + string path = FileUtilities.EnsureTrailingSlash(Directory.GetCurrentDirectory()); + ITaskItem[] tlogFiles; + if (!_tlogAvailable) + { + tlogFiles = _tlogFiles; + foreach (ITaskItem taskItem in tlogFiles) + { + if (!FileUtilities.FileExistsNoThrow(taskItem.ItemSpec)) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_SingleLogFileNotAvailable", taskItem.ItemSpec); + } + } + lock (DependencyTableCache.DependencyTable) + { + DependencyTableCache.DependencyTable.Remove(text); + return; + } + } + DependencyTableCacheEntry cachedEntry; + lock (DependencyTableCache.DependencyTable) + { + cachedEntry = DependencyTableCache.GetCachedEntry(text); + } + if (cachedEntry != null) + { + DependencyTable = (Dictionary>)cachedEntry.DependencyTable; + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_ReadTrackingCached"); + tlogFiles = cachedEntry.TlogFiles; + foreach (ITaskItem taskItem2 in tlogFiles) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", taskItem2.ItemSpec); + } + return; + } + bool flag = false; + bool flag2 = false; + string text2 = null; + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_ReadTrackingLogs"); + tlogFiles = _tlogFiles; + foreach (ITaskItem taskItem3 in tlogFiles) + { + try + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", taskItem3.ItemSpec); + using StreamReader streamReader = File.OpenText(taskItem3.ItemSpec); + string text3 = streamReader.ReadLine(); + while (text3 != null) + { + if (text3.Length == 0) + { + flag = true; + text2 = taskItem3.ItemSpec; + break; + } + if (text3[0] != '#') + { + bool flag3 = false; + if (text3[0] == '^') + { + text3 = text3.Substring(1); + if (text3.Length == 0) + { + flag = true; + text2 = taskItem3.ItemSpec; + break; + } + flag3 = true; + } + if (flag3) + { + Dictionary dictionary; + if (!_maintainCompositeRootingMarkers) + { + dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (text3.Contains("|")) + { + ITaskItem[] sourceFiles = _sourceFiles; + foreach (ITaskItem taskItem4 in sourceFiles) + { + if (!dictionary.ContainsKey(FileUtilities.NormalizePath(taskItem4.ItemSpec))) + { + dictionary.Add(FileUtilities.NormalizePath(taskItem4.ItemSpec), null); + } + } + } + else + { + dictionary.Add(text3, null); + } + } + else + { + dictionary = null; + } + if (!DependencyTable.TryGetValue(text3, out var value)) + { + value = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!_maintainCompositeRootingMarkers) + { + value.Add(text3, null); + } + DependencyTable.Add(text3, value); + } + text3 = streamReader.ReadLine(); + if (_maintainCompositeRootingMarkers) + { + while (text3 != null) + { + if (text3.Length == 0) + { + flag = true; + text2 = taskItem3.ItemSpec; + break; + } + if (text3[0] == '#' || text3[0] == '^') + { + break; + } + if (!value.ContainsKey(text3) && (FileTracker.FileIsUnderPath(text3, path) || !FileTracker.FileIsExcludedFromDependencies(text3))) + { + value.Add(text3, null); + } + text3 = streamReader.ReadLine(); + } + continue; + } + while (text3 != null) + { + if (text3.Length == 0) + { + flag = true; + text2 = taskItem3.ItemSpec; + break; + } + if (text3[0] == '#' || text3[0] == '^') + { + break; + } + if (dictionary.ContainsKey(text3)) + { + if (!DependencyTable.TryGetValue(text3, out value)) + { + value = new Dictionary(StringComparer.OrdinalIgnoreCase) { { text3, null } }; + DependencyTable.Add(text3, value); + } + } + else if (!value.ContainsKey(text3) && (FileTracker.FileIsUnderPath(text3, path) || !FileTracker.FileIsExcludedFromDependencies(text3))) + { + value.Add(text3, null); + } + text3 = streamReader.ReadLine(); + } + } + else + { + text3 = streamReader.ReadLine(); + } + } + else + { + text3 = streamReader.ReadLine(); + } + } + } + catch (Exception ex2) when (ExceptionHandling.IsIoRelatedException(ex2)) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", ex2.Message); + break; + } + if (flag) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLogContents", text2); + break; + } + } + lock (DependencyTableCache.DependencyTable) + { + if (flag || flag2) + { + DependencyTableCache.DependencyTable.Remove(text); + DependencyTable = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + else + { + DependencyTableCache.DependencyTable[text] = new DependencyTableCacheEntry(_tlogFiles, DependencyTable); + } + } + } + + public void SaveTlog() + { + SaveTlog(null); + } + + public void SaveTlog(DependencyFilter includeInTLog) + { + ITaskItem[] tlogFiles = _tlogFiles; + if (tlogFiles == null || tlogFiles.Length == 0) + { + return; + } + string key = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); + lock (DependencyTableCache.DependencyTable) + { + DependencyTableCache.DependencyTable.Remove(key); + } + string itemSpec = _tlogFiles[0].ItemSpec; + ITaskItem[] tlogFiles2 = _tlogFiles; + for (int i = 0; i < tlogFiles2.Length; i++) + { + File.WriteAllText(tlogFiles2[i].ItemSpec, "", Encoding.Unicode); + } + using StreamWriter streamWriter = FileUtilities.OpenWrite(itemSpec, append: false, Encoding.Unicode); + if (!_maintainCompositeRootingMarkers) + { + foreach (string key2 in DependencyTable.Keys) + { + if (!key2.Contains("|")) + { + Dictionary dictionary = DependencyTable[key2]; + streamWriter.WriteLine("^" + key2); + foreach (string key3 in dictionary.Keys) + { + if (key3 != key2 && (includeInTLog == null || includeInTLog(key3))) + { + streamWriter.WriteLine(key3); + } + } + } + } + return; + } + foreach (string key4 in DependencyTable.Keys) + { + Dictionary dictionary2 = DependencyTable[key4]; + streamWriter.WriteLine("^" + key4); + foreach (string key5 in dictionary2.Keys) + { + if (includeInTLog == null || includeInTLog(key5)) + { + streamWriter.WriteLine(key5); + } + } + } + } + + public void RemoveEntriesForSource(ITaskItem source) + { + RemoveEntriesForSource(new ITaskItem[1] { source }); + } + + public void RemoveEntriesForSource(ITaskItem[] source) + { + string key = FileTracker.FormatRootingMarker(source); + DependencyTable.Remove(key); + foreach (ITaskItem taskItem in source) + { + DependencyTable.Remove(FileUtilities.NormalizePath(taskItem.ItemSpec)); + } + } + + public void RemoveEntryForSourceRoot(string rootingMarker) + { + DependencyTable.Remove(rootingMarker); + } + + public void RemoveDependencyFromEntry(ITaskItem[] sources, ITaskItem dependencyToRemove) + { + string rootingMarker = FileTracker.FormatRootingMarker(sources); + RemoveDependencyFromEntry(rootingMarker, dependencyToRemove); + } + + public void RemoveDependencyFromEntry(ITaskItem source, ITaskItem dependencyToRemove) + { + string rootingMarker = FileTracker.FormatRootingMarker(source); + RemoveDependencyFromEntry(rootingMarker, dependencyToRemove); + } + + private void RemoveDependencyFromEntry(string rootingMarker, ITaskItem dependencyToRemove) + { + if (DependencyTable.TryGetValue(rootingMarker, out var value)) + { + value.Remove(FileUtilities.NormalizePath(dependencyToRemove.ItemSpec)); + return; + } + FileTracker.LogMessageFromResources(_log, MessageImportance.Normal, "Tracking_ReadLogEntryNotFound", rootingMarker); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem source) + { + RemoveDependenciesFromEntryIfMissing(new ITaskItem[1] { source }, null); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem source, ITaskItem correspondingOutput) + { + RemoveDependenciesFromEntryIfMissing(new ITaskItem[1] { source }, new ITaskItem[1] { correspondingOutput }); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source) + { + RemoveDependenciesFromEntryIfMissing(source, null); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] correspondingOutputs) + { + Dictionary fileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (correspondingOutputs != null) + { + ErrorUtilities.VerifyThrowArgument(source.Length == correspondingOutputs.Length, "Tracking_SourcesAndCorrespondingOutputMismatch"); + } + string rootingMarker = FileTracker.FormatRootingMarker(source, correspondingOutputs); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); + for (int i = 0; i < source.Length; i++) + { + rootingMarker = ((correspondingOutputs != null) ? FileTracker.FormatRootingMarker(source[i], correspondingOutputs[i]) : FileTracker.FormatRootingMarker(source[i])); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); + } + } + + private void RemoveDependenciesFromEntryIfMissing(string rootingMarker, Dictionary fileCache) + { + if (!DependencyTable.TryGetValue(rootingMarker, out var value)) + { + return; + } + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + int num = 0; + foreach (string key in value.Keys) + { + if (num++ > 0) + { + if (!fileCache.TryGetValue(key, out var value2)) + { + value2 = FileUtilities.FileExistsNoThrow(key); + fileCache.Add(key, value2); + } + if (value2) + { + dictionary.Add(key, value[key]); + } + } + else + { + dictionary.Add(key, key); + } + } + DependencyTable[rootingMarker] = dictionary; + } + } +} diff --git a/Microsoft.Build.Utilities/CanonicalTrackedOutputFiles.cs b/Microsoft.Build.Utilities/CanonicalTrackedOutputFiles.cs new file mode 100644 index 0000000..ae92a3a --- /dev/null +++ b/Microsoft.Build.Utilities/CanonicalTrackedOutputFiles.cs @@ -0,0 +1,516 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Utilities +{ + public class CanonicalTrackedOutputFiles + { + private ITaskItem[] _tlogFiles; + + private TaskLoggingHelper _log; + + private bool _tlogAvailable; + + public Dictionary> DependencyTable { get; private set; } + + public CanonicalTrackedOutputFiles(ITaskItem[] tlogFiles) + { + InternalConstruct(null, tlogFiles, constructOutputsFromTLogs: true); + } + + public CanonicalTrackedOutputFiles(ITask ownerTask, ITaskItem[] tlogFiles) + { + InternalConstruct(ownerTask, tlogFiles, constructOutputsFromTLogs: true); + } + + public CanonicalTrackedOutputFiles(ITask ownerTask, ITaskItem[] tlogFiles, bool constructOutputsFromTLogs) + { + InternalConstruct(ownerTask, tlogFiles, constructOutputsFromTLogs); + } + + private void InternalConstruct(ITask ownerTask, ITaskItem[] tlogFiles, bool constructOutputsFromTLogs) + { + if (ownerTask != null) + { + _log = new TaskLoggingHelper(ownerTask) + { + TaskResources = Microsoft.Build.CppTasks.Common.Properties.Microsoft_Build_CPPTasks_Strings.ResourceManager, + HelpKeywordPrefix = "MSBuild." + }; + } + _tlogFiles = TrackedDependencies.ExpandWildcards(tlogFiles); + _tlogAvailable = TrackedDependencies.ItemsExist(_tlogFiles); + DependencyTable = new Dictionary>(StringComparer.OrdinalIgnoreCase); + if (_tlogFiles != null && constructOutputsFromTLogs) + { + ConstructOutputTable(); + } + } + + private void ConstructOutputTable() + { + string text; + try + { + text = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); + } + catch (ArgumentException ex) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", ex.Message); + return; + } + string path = FileUtilities.EnsureTrailingSlash(Directory.GetCurrentDirectory()); + if (!_tlogAvailable) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_TrackingLogNotAvailable"); + lock (DependencyTableCache.DependencyTable) + { + DependencyTableCache.DependencyTable.Remove(text); + return; + } + } + DependencyTableCacheEntry dependencyTableCacheEntry = null; + lock (DependencyTableCache.DependencyTable) + { + dependencyTableCacheEntry = DependencyTableCache.GetCachedEntry(text); + } + ITaskItem[] tlogFiles; + if (dependencyTableCacheEntry != null) + { + DependencyTable = (Dictionary>)dependencyTableCacheEntry.DependencyTable; + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_WriteTrackingCached"); + tlogFiles = dependencyTableCacheEntry.TlogFiles; + foreach (ITaskItem taskItem in tlogFiles) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", taskItem.ItemSpec); + } + return; + } + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_WriteTrackingLogs"); + bool flag = false; + string text2 = null; + tlogFiles = _tlogFiles; + foreach (ITaskItem taskItem2 in tlogFiles) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t{0}", taskItem2.ItemSpec); + try + { + using StreamReader streamReader = File.OpenText(taskItem2.ItemSpec); + string text3 = streamReader.ReadLine(); + while (text3 != null) + { + if (text3.Length == 0) + { + flag = true; + text2 = taskItem2.ItemSpec; + break; + } + if (text3[0] == '^') + { + text3 = text3.Substring(1); + if (text3.Length == 0) + { + flag = true; + text2 = taskItem2.ItemSpec; + break; + } + if (!DependencyTable.TryGetValue(text3, out var value)) + { + value = new Dictionary(StringComparer.OrdinalIgnoreCase); + DependencyTable.Add(text3, value); + } + do + { + text3 = streamReader.ReadLine(); + if (text3 != null) + { + if (text3.Length == 0) + { + flag = true; + text2 = taskItem2.ItemSpec; + break; + } + if (text3[0] != '^' && text3[0] != '#' && !value.ContainsKey(text3) && (FileTracker.FileIsUnderPath(text3, path) || !FileTracker.FileIsExcludedFromDependencies(text3))) + { + DateTime lastWriteFileUtcTime = NativeMethods.GetLastWriteFileUtcTime(text3); + value.Add(text3, lastWriteFileUtcTime); + } + } + } + while (text3 != null && text3[0] != '^'); + if (flag) + { + break; + } + } + else + { + text3 = streamReader.ReadLine(); + } + } + } + catch (Exception ex2) when (ExceptionHandling.IsIoRelatedException(ex2)) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLog", ex2.Message); + break; + } + if (flag) + { + FileTracker.LogWarningWithCodeFromResources(_log, "Tracking_RebuildingDueToInvalidTLogContents", text2); + break; + } + } + lock (DependencyTableCache.DependencyTable) + { + if (flag) + { + DependencyTableCache.DependencyTable.Remove(text); + DependencyTable = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + else + { + DependencyTableCache.DependencyTable[text] = new DependencyTableCacheEntry(_tlogFiles, DependencyTable); + } + } + } + + public string[] RemoveRootsWithSharedOutputs(ITaskItem[] sources) + { + ErrorUtilities.VerifyThrowArgumentNull(sources, "sources"); + List list = new List(); + string text = FileTracker.FormatRootingMarker(sources); + if (DependencyTable.TryGetValue(text, out var value)) + { + foreach (KeyValuePair> item in DependencyTable) + { + if (text.Equals(item.Key, StringComparison.Ordinal)) + { + continue; + } + foreach (string key in value.Keys) + { + if (item.Value.ContainsKey(key)) + { + list.Add(item.Key); + break; + } + } + } + foreach (string item2 in list) + { + DependencyTable.Remove(item2); + } + } + return list.ToArray(); + } + + public bool RemoveOutputForSourceRoot(string sourceRoot, string outputPathToRemove) + { + if (DependencyTable.TryGetValue(sourceRoot, out var value)) + { + bool result = value.Remove(outputPathToRemove); + if (DependencyTable[sourceRoot].Count == 0) + { + DependencyTable.Remove(sourceRoot); + } + return result; + } + return true; + } + + public ITaskItem[] OutputsForNonCompositeSource(params ITaskItem[] sources) + { + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + List list = new List(); + string text = FileTracker.FormatRootingMarker(sources); + for (int i = 0; i < sources.Length; i++) + { + string sourceKey = FileUtilities.NormalizePath(sources[i].ItemSpec); + OutputsForSourceRoot(dictionary, sourceKey); + } + if (dictionary.Count == 0) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputForRootNotFound", text); + } + else + { + list.AddRange(dictionary.Values); + if (dictionary.Count > 100) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputsNotShown", dictionary.Count); + } + else + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputsFor", text); + foreach (ITaskItem item in list) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t" + item); + } + } + } + return list.ToArray(); + } + + public ITaskItem[] OutputsForSource(params ITaskItem[] sources) + { + return OutputsForSource(sources, searchForSubRootsInCompositeRootingMarkers: true); + } + + public ITaskItem[] OutputsForSource(ITaskItem[] sources, bool searchForSubRootsInCompositeRootingMarkers) + { + if (!_tlogAvailable) + { + return null; + } + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + string text = FileTracker.FormatRootingMarker(sources); + List list = new List(); + foreach (string key in DependencyTable.Keys) + { + string text2 = key/*.ToUpperInvariant()*/; + if (searchForSubRootsInCompositeRootingMarkers && (text.Contains(text2) || text2.Contains(text) || CanonicalTrackedFilesHelper.RootContainsAllSubRootComponents(text, text2))) + { + OutputsForSourceRoot(dictionary, text2); + } + else if (!searchForSubRootsInCompositeRootingMarkers && text2.Equals(text, StringComparison.Ordinal)) + { + OutputsForSourceRoot(dictionary, text2); + } + } + if (dictionary.Count == 0) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputForRootNotFound", text); + } + else + { + list.AddRange(dictionary.Values); + if (dictionary.Count > 100) + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputsNotShown", dictionary.Count); + } + else + { + FileTracker.LogMessageFromResources(_log, MessageImportance.Low, "Tracking_OutputsFor", text); + foreach (ITaskItem item in list) + { + FileTracker.LogMessage(_log, MessageImportance.Low, "\t" + item); + } + } + } + return list.ToArray(); + } + + private void OutputsForSourceRoot(Dictionary outputs, string sourceKey) + { + if (!DependencyTable.TryGetValue(sourceKey, out var value)) + { + return; + } + foreach (string key in value.Keys) + { + if (!outputs.ContainsKey(key)) + { + outputs.Add(key, new TaskItem(key)); + } + } + } + + public void AddComputedOutputForSourceRoot(string sourceKey, string computedOutput) + { + AddOutput(GetSourceKeyOutputs(sourceKey), computedOutput); + } + + public void AddComputedOutputsForSourceRoot(string sourceKey, string[] computedOutputs) + { + Dictionary sourceKeyOutputs = GetSourceKeyOutputs(sourceKey); + foreach (string computedOutput in computedOutputs) + { + AddOutput(sourceKeyOutputs, computedOutput); + } + } + + public void AddComputedOutputsForSourceRoot(string sourceKey, ITaskItem[] computedOutputs) + { + Dictionary sourceKeyOutputs = GetSourceKeyOutputs(sourceKey); + foreach (ITaskItem taskItem in computedOutputs) + { + AddOutput(sourceKeyOutputs, FileUtilities.NormalizePath(taskItem.ItemSpec)); + } + } + + private Dictionary GetSourceKeyOutputs(string sourceKey) + { + if (!DependencyTable.TryGetValue(sourceKey, out var value)) + { + value = new Dictionary(StringComparer.OrdinalIgnoreCase); + DependencyTable.Add(sourceKey, value); + } + return value; + } + + private static void AddOutput(Dictionary dependencies, string computedOutput) + { + var Output = FileUtilities.NormalizePath(computedOutput); + + string text = Output/*.ToUpperInvariant()*/; + if (!dependencies.ContainsKey(text)) + { + DateTime value = (FileUtilities.FileExistsNoThrow(Output) ? NativeMethods.GetLastWriteFileUtcTime(Output) : DateTime.MinValue); + dependencies.Add(text, value); + } + } + + public void SaveTlog() + { + SaveTlog(null); + } + + public void SaveTlog(DependencyFilter includeInTLog) + { + ITaskItem[] tlogFiles = _tlogFiles; + if (tlogFiles == null || tlogFiles.Length == 0) + { + return; + } + string key = DependencyTableCache.FormatNormalizedTlogRootingMarker(_tlogFiles); + lock (DependencyTableCache.DependencyTable) + { + DependencyTableCache.DependencyTable.Remove(key); + } + string itemSpec = _tlogFiles[0].ItemSpec; + ITaskItem[] tlogFiles2 = _tlogFiles; + for (int i = 0; i < tlogFiles2.Length; i++) + { + File.WriteAllText(tlogFiles2[i].ItemSpec, "", Encoding.Unicode); + } + using StreamWriter streamWriter = FileUtilities.OpenWrite(itemSpec, append: false, Encoding.Unicode); + foreach (string key2 in DependencyTable.Keys) + { + Dictionary dictionary = DependencyTable[key2]; + streamWriter.WriteLine("^" + key2); + foreach (string key3 in dictionary.Keys) + { + if (includeInTLog == null || includeInTLog(key3)) + { + streamWriter.WriteLine(key3); + } + } + } + + _tlogAvailable = true; + } + + public void RemoveEntriesForSource(ITaskItem source) + { + RemoveEntriesForSource(new ITaskItem[1] { source }, null); + } + + public void RemoveEntriesForSource(ITaskItem source, ITaskItem correspondingOutput) + { + RemoveEntriesForSource(new ITaskItem[1] { source }, new ITaskItem[1] { correspondingOutput }); + } + + public void RemoveEntriesForSource(ITaskItem[] source) + { + RemoveEntriesForSource(source, null); + } + + public void RemoveEntriesForSource(ITaskItem[] source, ITaskItem[] correspondingOutputs) + { + string key = FileTracker.FormatRootingMarker(source, correspondingOutputs); + DependencyTable.Remove(key); + foreach (ITaskItem taskItem in source) + { + DependencyTable.Remove(FileUtilities.NormalizePath(taskItem.ItemSpec)); + } + } + + public void RemoveDependencyFromEntry(ITaskItem[] sources, ITaskItem dependencyToRemove) + { + string rootingMarker = FileTracker.FormatRootingMarker(sources); + RemoveDependencyFromEntry(rootingMarker, dependencyToRemove); + } + + public void RemoveDependencyFromEntry(ITaskItem source, ITaskItem dependencyToRemove) + { + string rootingMarker = FileTracker.FormatRootingMarker(source); + RemoveDependencyFromEntry(rootingMarker, dependencyToRemove); + } + + private void RemoveDependencyFromEntry(string rootingMarker, ITaskItem dependencyToRemove) + { + if (DependencyTable.TryGetValue(rootingMarker, out var value)) + { + value.Remove(FileUtilities.NormalizePath(dependencyToRemove.ItemSpec)); + return; + } + FileTracker.LogMessageFromResources(_log, MessageImportance.Normal, "Tracking_WriteLogEntryNotFound", rootingMarker); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem source) + { + RemoveDependenciesFromEntryIfMissing(new ITaskItem[1] { source }, null); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem source, ITaskItem correspondingOutput) + { + RemoveDependenciesFromEntryIfMissing(new ITaskItem[1] { source }, new ITaskItem[1] { correspondingOutput }); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source) + { + RemoveDependenciesFromEntryIfMissing(source, null); + } + + public void RemoveDependenciesFromEntryIfMissing(ITaskItem[] source, ITaskItem[] correspondingOutputs) + { + Dictionary fileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (correspondingOutputs != null) + { + ErrorUtilities.VerifyThrowArgument(source.Length == correspondingOutputs.Length, "Tracking_SourcesAndCorrespondingOutputMismatch"); + } + string rootingMarker = FileTracker.FormatRootingMarker(source, correspondingOutputs); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); + for (int i = 0; i < source.Length; i++) + { + rootingMarker = ((correspondingOutputs != null) ? FileTracker.FormatRootingMarker(source[i], correspondingOutputs[i]) : FileTracker.FormatRootingMarker(source[i])); + RemoveDependenciesFromEntryIfMissing(rootingMarker, fileCache); + } + } + + private void RemoveDependenciesFromEntryIfMissing(string rootingMarker, Dictionary fileCache) + { + if (!DependencyTable.TryGetValue(rootingMarker, out var value)) + { + return; + } + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + int num = 0; + foreach (string key in value.Keys) + { + if (num++ > 0) + { + if (!fileCache.TryGetValue(key, out var value2)) + { + value2 = FileUtilities.FileExistsNoThrow(key); + fileCache.Add(key, value2); + } + if (value2) + { + dictionary.Add(key, value[key]); + } + } + else + { + dictionary.Add(key, DateTime.Now); + } + } + DependencyTable[rootingMarker] = dictionary; + } + } +} diff --git a/Microsoft.Build.Utilities/DependencyFilter.cs b/Microsoft.Build.Utilities/DependencyFilter.cs new file mode 100644 index 0000000..66c3e36 --- /dev/null +++ b/Microsoft.Build.Utilities/DependencyFilter.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Utilities +{ + public delegate bool DependencyFilter(string fullPath); +} diff --git a/Microsoft.Build.Utilities/DependencyTableCache.cs b/Microsoft.Build.Utilities/DependencyTableCache.cs new file mode 100644 index 0000000..e716824 --- /dev/null +++ b/Microsoft.Build.Utilities/DependencyTableCache.cs @@ -0,0 +1,125 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Utilities +{ + internal static class DependencyTableCache + { + private class TaskItemItemSpecIgnoreCaseComparer : IEqualityComparer + { + public bool Equals(ITaskItem x, ITaskItem y) + { + if (x == y) + { + return true; + } + if (x == null || y == null) + { + return false; + } + return string.Equals(x.ItemSpec, y.ItemSpec, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode(ITaskItem obj) + { + if (obj != null) + { + return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.ItemSpec); + } + return 0; + } + } + + private static readonly char[] s_numerals = new char[10] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + + private static readonly TaskItemItemSpecIgnoreCaseComparer s_taskItemComparer = new TaskItemItemSpecIgnoreCaseComparer(); + + internal static Dictionary DependencyTable { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + + private static bool DependencyTableIsUpToDate(DependencyTableCacheEntry dependencyTable) + { + DateTime tableTime = dependencyTable.TableTime; + ITaskItem[] tlogFiles = dependencyTable.TlogFiles; + for (int i = 0; i < tlogFiles.Length; i++) + { + if (NativeMethods.GetLastWriteFileUtcTime(FileUtilities.NormalizePath(tlogFiles[i].ItemSpec)) > tableTime) + { + return false; + } + } + return true; + } + + internal static DependencyTableCacheEntry GetCachedEntry(string tLogRootingMarker) + { + if (DependencyTable.TryGetValue(tLogRootingMarker, out var value)) + { + if (DependencyTableIsUpToDate(value)) + { + return value; + } + DependencyTable.Remove(tLogRootingMarker); + } + return null; + } + + internal static string FormatNormalizedTlogRootingMarker(ITaskItem[] tlogFiles) + { + HashSet hashSet = new HashSet(s_taskItemComparer); + for (int i = 0; i < tlogFiles.Length; i++) + { + ITaskItem taskItem = new TaskItem(tlogFiles[i]); + taskItem.ItemSpec = NormalizeTlogPath(tlogFiles[i].ItemSpec); + hashSet.Add(taskItem); + } + return FileTracker.FormatRootingMarker(hashSet.ToArray()); + } + + private static string NormalizeTlogPath(string tlogPath) + { + if (tlogPath.IndexOfAny(s_numerals) == -1) + { + return tlogPath; + } + StringBuilder stringBuilder = new StringBuilder(); + int num = tlogPath.Length - 1; + while (num >= 0 && tlogPath[num] != '\\') + { + if (tlogPath[num] == '.' || tlogPath[num] == '-') + { + stringBuilder.Append(tlogPath[num]); + int num2 = num - 1; + while (num2 >= 0 && tlogPath[num2] != '\\' && tlogPath[num2] >= '0' && tlogPath[num2] <= '9') + { + num2--; + } + if (num2 >= 0 && tlogPath[num2] == '.') + { + stringBuilder.Append("]DI["); + stringBuilder.Append(tlogPath[num2]); + num = num2; + } + } + else + { + stringBuilder.Append(tlogPath[num]); + } + num--; + } + StringBuilder stringBuilder2 = new StringBuilder(num + stringBuilder.Length); + if (num >= 0) + { + stringBuilder2.Append(tlogPath, 0, num + 1); + } + for (int num3 = stringBuilder.Length - 1; num3 >= 0; num3--) + { + stringBuilder2.Append(stringBuilder[num3]); + } + return stringBuilder2.ToString(); + } + } +} diff --git a/Microsoft.Build.Utilities/DependencyTableCacheEntry.cs b/Microsoft.Build.Utilities/DependencyTableCacheEntry.cs new file mode 100644 index 0000000..9c7b4f1 --- /dev/null +++ b/Microsoft.Build.Utilities/DependencyTableCacheEntry.cs @@ -0,0 +1,35 @@ +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Build.Utilities +{ + internal class DependencyTableCacheEntry + { + public ITaskItem[] TlogFiles { get; } + + public DateTime TableTime { get; } + + public IDictionary DependencyTable { get; } + + internal DependencyTableCacheEntry(ITaskItem[] tlogFiles, IDictionary dependencyTable) + { + TlogFiles = new ITaskItem[tlogFiles.Length]; + TableTime = DateTime.MinValue; + for (int i = 0; i < tlogFiles.Length; i++) + { + string text = FileUtilities.NormalizePath(tlogFiles[i].ItemSpec); + TlogFiles[i] = new TaskItem(text); + DateTime lastWriteFileUtcTime = NativeMethods.GetLastWriteFileUtcTime(text); + if (lastWriteFileUtcTime > TableTime) + { + TableTime = lastWriteFileUtcTime; + } + } + DependencyTable = dependencyTable; + } + } +} diff --git a/Microsoft.Build.Utilities/Microsoft.Build.Utilities.projitems b/Microsoft.Build.Utilities/Microsoft.Build.Utilities.projitems new file mode 100644 index 0000000..9483d1f --- /dev/null +++ b/Microsoft.Build.Utilities/Microsoft.Build.Utilities.projitems @@ -0,0 +1,20 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + b3183ec4-afd6-47b6-93c5-ffb42f82af96 + + + Microsoft.Build.Utilities + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Build.Utilities/Microsoft.Build.Utilities.shproj b/Microsoft.Build.Utilities/Microsoft.Build.Utilities.shproj new file mode 100644 index 0000000..24a4669 --- /dev/null +++ b/Microsoft.Build.Utilities/Microsoft.Build.Utilities.shproj @@ -0,0 +1,13 @@ + + + + b3183ec4-afd6-47b6-93c5-ffb42f82af96 + 14.0 + + + + + + + + diff --git a/Microsoft.Build.Utilities/TrackedDependencies.cs b/Microsoft.Build.Utilities/TrackedDependencies.cs new file mode 100644 index 0000000..db6d61a --- /dev/null +++ b/Microsoft.Build.Utilities/TrackedDependencies.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; + +namespace Microsoft.Build.Utilities +{ + public static class TrackedDependencies + { + public static ITaskItem[] ExpandWildcards(ITaskItem[] expand) + { + if (expand == null) + { + return null; + } + List list = new List(expand.Length); + foreach (ITaskItem taskItem in expand) + { + if (FileMatcher.HasWildcards(taskItem.ItemSpec)) + { + string directoryName = Path.GetDirectoryName(taskItem.ItemSpec); + string fileName = Path.GetFileName(taskItem.ItemSpec); + string[] array = ((FileMatcher.HasWildcards(directoryName) || !FileSystems.Default.DirectoryExists(directoryName)) ? FileMatcher.Default.GetFiles(null, taskItem.ItemSpec) : Directory.GetFiles(directoryName, fileName)); + string[] array2 = array; + foreach (string itemSpec in array2) + { + list.Add(new TaskItem(taskItem) + { + ItemSpec = itemSpec + }); + } + } + else + { + list.Add(taskItem); + } + } + return list.ToArray(); + } + + internal static bool ItemsExist(ITaskItem[] files) + { + bool result = true; + if (files != null && files.Length != 0) + { + for (int i = 0; i < files.Length; i++) + { + if (!FileUtilities.FileExistsNoThrow(files[i].ItemSpec)) + { + result = false; + break; + } + } + } + else + { + result = false; + } + return result; + } + } + +} diff --git a/VCTargets/v170/Microsoft.CppBuild.targets b/VCTargets/v170/Microsoft.CppBuild.targets index b984a9e..a5e1671 100644 --- a/VCTargets/v170/Microsoft.CppBuild.targets +++ b/VCTargets/v170/Microsoft.CppBuild.targets @@ -525,6 +525,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_Tmp>$(TMP) <_Temp>$(TEMP) + + <_Tmp Condition="'$(_Tmp)' == '' and Exists('/var/tmp')">/var/tmp/ + <_Temp Condition="'$(_Temp)' == '' and Exists('/var/tmp')">/var/tmp/ + <_Tmp Condition="'$(_Tmp)' != '' and !HasTrailingSlash('$(_Tmp)')">$(_Tmp)\ <_Temp Condition="'$(_Temp)' != '' and !HasTrailingSlash('$(_Temp)')">$(_Temp)\ diff --git a/YY.Build.Linux.Tasks/GCC/Ar.cs b/YY.Build.Linux.Tasks/GCC/Ar.cs index f77c0aa..7db2179 100644 --- a/YY.Build.Linux.Tasks/GCC/Ar.cs +++ b/YY.Build.Linux.Tasks/GCC/Ar.cs @@ -18,6 +18,7 @@ public class Ar : Microsoft.Build.CPPTasks.VCToolTask protected override string ToolName => "ar"; public Ar() + : base(Microsoft.Build.CppTasks.Common.Properties.Microsoft_Build_CPPTasks_Strings.ResourceManager) { switchOrderList = new ArrayList(); switchOrderList.Add("Command"); diff --git a/YY.Build.Linux.Tasks/GCC/Compile.cs b/YY.Build.Linux.Tasks/GCC/Compile.cs index 8d0e97d..f6e0f54 100644 --- a/YY.Build.Linux.Tasks/GCC/Compile.cs +++ b/YY.Build.Linux.Tasks/GCC/Compile.cs @@ -7,12 +7,16 @@ // using Microsoft.Build.Linux.Tasks; using Microsoft.Build.Utilities; using System.Resources; +using Microsoft.Build.Shared; +using System.Text; +using System; namespace YY.Build.Linux.Tasks.GCC { - public class Compile : Microsoft.Build.CPPTasks.VCToolTask + public class Compile : TrackedVCToolTask { public Compile() + : base(Microsoft.Build.CppTasks.Common.Properties.Microsoft_Build_CPPTasks_Strings.ResourceManager) { UseCommandProcessor = false; @@ -50,18 +54,55 @@ public Compile() switchOrderList.Add("ForcedIncludeFiles"); switchOrderList.Add("EnableASAN"); switchOrderList.Add("AdditionalOptions"); + switchOrderList.Add("MapFile"); base.IgnoreUnknownSwitchValues = true; } - + + private bool PreprocessToFile = false; + private bool MinimalRebuild = false; + private ArrayList switchOrderList; + private Dictionary trackedInputFilesToRemove; + + private Dictionary trackedOutputFilesToRemove; + protected override ArrayList SwitchOrderList => switchOrderList; protected override string ToolName => "g++"; protected override string AlwaysAppend => "-c"; + protected override ITaskItem[] TrackedInputFiles => Sources; + + protected override string TrackerIntermediateDirectory + { + get + { + if (TrackerLogDirectory != null) + { + return TrackerLogDirectory; + } + return string.Empty; + } + } + + private string[] objectFiles; + + [Output] + public string[] ObjectFiles + { + get + { + return objectFiles; + } + set + { + objectFiles = value; + } + } + [Required] public ITaskItem[] Sources { @@ -882,14 +923,482 @@ public bool EnableASAN } } - public override bool Execute() + protected override bool GenerateCostomCommandsAccordingToType(CommandLineBuilder builder, string switchName, bool dummyForBackwardCompatibility, CommandLineFormat format = CommandLineFormat.ForBuildLog, EscapeFormat escapeFormat = EscapeFormat.Default) + { + if (string.Equals(switchName, "MapFile", StringComparison.OrdinalIgnoreCase)) + { + ToolSwitch toolSwitch = new ToolSwitch(ToolSwitchType.File); + toolSwitch.DisplayName = "MapFile"; + toolSwitch.ArgumentRelationList = new ArrayList(); + toolSwitch.SwitchValue = "-MD -MF "; + toolSwitch.Name = "MapFile"; + + if (IsPropertySet("ObjectFileName")) + { + toolSwitch.Value = base.ActiveToolSwitches["ObjectFileName"].Value + ".map"; + } + else if(Sources.Length !=0) + { + toolSwitch.Value = Environment.ExpandEnvironmentVariables(Sources[0].ItemSpec) + ".map"; + } + else + { + return true; + } + + GenerateCommandsAccordingToType(builder, toolSwitch, format, escapeFormat); + return true; + } + return false; + } + + protected override int ExecuteTool(string pathToTool, string responseFileCommands, string commandLineCommands) { foreach (var Item in Sources) { Log.LogMessage(MessageImportance.High, Item.ItemSpec); } - return base.Execute(); + return base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands); + } + + public virtual string TrackerLogDirectory + { + get + { + if (IsPropertySet("TrackerLogDirectory")) + { + return base.ActiveToolSwitches["TrackerLogDirectory"].Value; + } + return null; + } + set + { + base.ActiveToolSwitches.Remove("TrackerLogDirectory"); + ToolSwitch toolSwitch = new ToolSwitch(ToolSwitchType.Directory); + toolSwitch.DisplayName = "Tracker Log Directory"; + toolSwitch.Description = "Tracker Log Directory."; + toolSwitch.ArgumentRelationList = new ArrayList(); + toolSwitch.Value = VCToolTask.EnsureTrailingSlash(value); + base.ActiveToolSwitches.Add("TrackerLogDirectory", toolSwitch); + AddActiveSwitchToolValue(toolSwitch); + } + } + + protected bool InputDependencyFilter(string fullInputPath) + { + if (fullInputPath.EndsWith(".PDB", StringComparison.OrdinalIgnoreCase) || fullInputPath.EndsWith(".IDB", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return !trackedInputFilesToRemove.ContainsKey(fullInputPath); + } + + protected bool OutputDependencyFilter(string fullOutputPath) + { + if (fullOutputPath.EndsWith(".TLH", StringComparison.OrdinalIgnoreCase) || fullOutputPath.EndsWith(".TLI", StringComparison.OrdinalIgnoreCase) || fullOutputPath.EndsWith(".DLL", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return !trackedOutputFilesToRemove.ContainsKey(fullOutputPath); + } + + protected override int PostExecuteTool(int exitCode) + { + if (base.MinimalRebuildFromTracking || base.TrackFileAccess) + { + base.SourceOutputs = new CanonicalTrackedOutputFiles(base.TLogWriteFiles); + base.SourceDependencies = new CanonicalTrackedInputFiles(base.TLogReadFiles, Sources, base.ExcludedInputPaths, base.SourceOutputs, UseMinimalRebuildOptimization, MaintainCompositeRootingMarkers); + DependencyFilter includeInTLog = OutputDependencyFilter; + DependencyFilter dependencyFilter = InputDependencyFilter; + trackedInputFilesToRemove = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (base.TrackedInputFilesToIgnore != null) + { + ITaskItem[] array = base.TrackedInputFilesToIgnore; + foreach (ITaskItem taskItem in array) + { + trackedInputFilesToRemove.Add(taskItem.GetMetadata("FullPath"), taskItem); + } + } + trackedOutputFilesToRemove = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (base.TrackedOutputFilesToIgnore != null) + { + ITaskItem[] array2 = base.TrackedOutputFilesToIgnore; + foreach (ITaskItem taskItem2 in array2) + { + trackedOutputFilesToRemove.Add(taskItem2.GetMetadata("FullPath"), taskItem2); + } + } + //if (PreprocessToFile) + //{ + // base.SourceOutputs.RemoveDependenciesFromEntryIfMissing(base.SourcesCompiled, preprocessOutput); + // base.SourceDependencies.RemoveDependenciesFromEntryIfMissing(base.SourcesCompiled, preprocessOutput); + //} + //else + { + base.SourceOutputs.RemoveDependenciesFromEntryIfMissing(base.SourcesCompiled); + base.SourceDependencies.RemoveDependenciesFromEntryIfMissing(base.SourcesCompiled); + } + if (exitCode != 0 && !MinimalRebuild) + { + ITaskItem[] array5; + ITaskItem[] upToDateSources; + if (!PreprocessToFile && base.SourcesCompiled.Length > 1) + { + KeyValuePair[] array3 = new KeyValuePair[] + { + new KeyValuePair("ObjectFile", value: true) + }; + ITaskItem[] sources = Sources; + foreach (ITaskItem taskItem3 in sources) + { + string sourceKey = FileTracker.FormatRootingMarker(taskItem3); + KeyValuePair[] array4 = array3; + for (int l = 0; l < array4.Length; l++) + { + KeyValuePair keyValuePair = array4[l]; + string metadata = taskItem3.GetMetadata(keyValuePair.Key); + if (keyValuePair.Value && !string.IsNullOrEmpty(metadata)) + { + base.SourceOutputs.AddComputedOutputForSourceRoot(sourceKey, metadata); + } + } + } + array5 = base.SourceDependencies.ComputeSourcesNeedingCompilation(); + List list = new List(); + int num = 0; + ITaskItem[] array6 = base.SourcesCompiled; + foreach (ITaskItem taskItem4 in array6) + { + if (num >= array5.Length) + { + list.Add(taskItem4); + } + else if (!array5[num].Equals(taskItem4)) + { + list.Add(taskItem4); + } + else + { + num++; + } + } + upToDateSources = list.ToArray(); + ITaskItem[] sources2 = Sources; + foreach (ITaskItem taskItem5 in sources2) + { + string sourceRoot = FileTracker.FormatRootingMarker(taskItem5); + KeyValuePair[] array7 = array3; + for (int num2 = 0; num2 < array7.Length; num2++) + { + KeyValuePair keyValuePair2 = array7[num2]; + string metadata2 = taskItem5.GetMetadata(keyValuePair2.Key); + if (keyValuePair2.Value && !string.IsNullOrEmpty(metadata2)) + { + base.SourceOutputs.RemoveOutputForSourceRoot(sourceRoot, metadata2); + } + } + } + } + else + { + array5 = base.SourcesCompiled; + upToDateSources = new ITaskItem[0]; + } + // base.SourceOutputs.RemoveEntriesForSource(array5, preprocessOutput); + base.SourceOutputs.SaveTlog(includeInTLog); + base.SourceDependencies.RemoveEntriesForSource(array5); + base.SourceDependencies.SaveTlog(dependencyFilter); + ConstructCommandTLog(upToDateSources, dependencyFilter); + } + //else if (PreprocessToFile) + //{ + // bool flag = true; + // if (string.IsNullOrEmpty(PreprocessOutputPath)) + // { + // ITaskItem[] array8 = base.SourcesCompiled; + // foreach (ITaskItem source in array8) + // { + // flag = flag && MovePreprocessedOutput(source, base.SourceDependencies, base.SourceOutputs); + // } + // } + // if (flag) + // { + // AddPdbToCompactOutputs(base.SourcesCompiled, base.SourceOutputs); + // base.SourceOutputs.SaveTlog(includeInTLog); + // base.SourceDependencies.SaveTlog(dependencyFilter); + // ConstructCommandTLog(base.SourcesCompiled, dependencyFilter); + // } + //} + else + { + AddPdbToCompactOutputs(base.SourcesCompiled, base.SourceOutputs); + RemoveTaskSpecificInputs(base.SourceDependencies); + base.SourceOutputs.SaveTlog(includeInTLog); + base.SourceDependencies.SaveTlog(dependencyFilter); + ConstructCommandTLog(base.SourcesCompiled, dependencyFilter); + } + TrackedVCToolTask.DeleteEmptyFile(base.TLogWriteFiles); + TrackedVCToolTask.DeleteEmptyFile(base.TLogReadFiles); + TrackedVCToolTask.DeleteFiles(base.TLogDeleteFiles); + } + //else if (PreprocessToFile) + //{ + // bool flag2 = true; + // if (string.IsNullOrEmpty(PreprocessOutputPath)) + // { + // ITaskItem[] array9 = base.SourcesCompiled; + // foreach (ITaskItem source2 in array9) + // { + // flag2 = flag2 && MovePreprocessedOutput(source2, null, null); + // } + // } + // if (!flag2) + // { + // exitCode = -1; + // } + //} + return exitCode; + } + + protected void ConstructCommandTLog(ITaskItem[] upToDateSources, DependencyFilter inputFilter) + { + IDictionary dictionary = MapSourcesToCommandLines(); + string text = GenerateCommandLineExceptSwitches(new string[1] { "Sources" }, CommandLineFormat.ForTracking); + if (upToDateSources != null) + { + foreach (ITaskItem taskItem in upToDateSources) + { + string metadata = taskItem.GetMetadata("FullPath"); + if (inputFilter == null || inputFilter(metadata)) + { + dictionary[FileTracker.FormatRootingMarker(taskItem)] = text + " " + metadata/*.ToUpperInvariant()*/; + } + else + { + dictionary.Remove(FileTracker.FormatRootingMarker(taskItem)); + } + } + } + WriteSourcesToCommandLinesTable(dictionary); + } + + + protected void AddPdbToCompactOutputs(ITaskItem[] sources, CanonicalTrackedOutputFiles compactOutputs) + { + // todo: gcc的符号?? + } + + protected void ComputeObjectFiles() + { + ObjectFiles = new string[Sources.Length]; + int num = 0; + ITaskItem[] sources = Sources; + foreach (ITaskItem taskItem in sources) + { + ObjectFiles[num] = Helpers.GetOutputFileName(taskItem.ItemSpec, ObjectFileName, "o"); + taskItem.SetMetadata("ObjectFile", ObjectFiles[num]); + num++; + } + } + + protected override void SaveTracking() + { + if(ObjectFiles == null) + ComputeObjectFiles(); + + // 保存Write文件 + { + string WriteFilePath = TLogWriteFiles[0].GetMetadata("FullPath"); + Directory.CreateDirectory(Path.GetDirectoryName(WriteFilePath)); + using StreamWriter WriteFileWriter = FileUtilities.OpenWrite(WriteFilePath, append: true, Encoding.Unicode); + + KeyValuePair[] array = new KeyValuePair[] + { + new KeyValuePair("ObjectFile", value: true) + }; + + foreach (ITaskItem taskItem in Sources) + { + string sourceKey = FileTracker.FormatRootingMarker(taskItem); + WriteFileWriter.WriteLine("^" + sourceKey); + KeyValuePair[] array2 = array; + for (int j = 0; j < array2.Length; j++) + { + KeyValuePair keyValuePair = array2[j]; + string metadata = taskItem.GetMetadata(keyValuePair.Key); + if (keyValuePair.Value && !string.IsNullOrEmpty(metadata)) + { + FileUtilities.UpdateFileExistenceCache(metadata); + WriteFileWriter.WriteLine(metadata); + } + } + } + } + + // 保存Read文件 + { + string ReadFilePath = TLogReadFiles[0].GetMetadata("FullPath"); + Directory.CreateDirectory(Path.GetDirectoryName(ReadFilePath)); + using StreamWriter WriteFileWriter = FileUtilities.OpenWrite(ReadFilePath, append: true, Encoding.Unicode); + + GCCMapReader MapReader = new GCCMapReader(); + + foreach (ITaskItem taskItem in Sources) + { + if (!MapReader.Init(ObjectFileName + ".map")) + continue; + + string sourceKey = FileTracker.FormatRootingMarker(taskItem); + WriteFileWriter.WriteLine("^" + sourceKey); + + for(; ; ) + { + var Tmp = MapReader.ReadLine(); + if (Tmp == null) + break; + + if (Tmp.Length == 0) + continue; + + WriteFileWriter.WriteLine(FileUtilities.NormalizePath(Tmp)); + } + } + } + } + + protected internal override bool ComputeOutOfDateSources() + { + if (base.MinimalRebuildFromTracking || base.TrackFileAccess) + { + AssignDefaultTLogPaths(); + } + +#if __REMOVE + if (PreprocessToFile) + { + ComputePreprocessedOutputFiles(); + } +#endif + if (base.MinimalRebuildFromTracking && !ForcedRebuildRequired()) + { + base.SourceOutputs = new CanonicalTrackedOutputFiles(this, base.TLogWriteFiles, constructOutputsFromTLogs: false); + ComputeObjectFiles(); +#if __REMOVE + ComputeBrowseInformationFiles(); + ComputeXmlDocumentationFiles(); +#endif + KeyValuePair[] array = new KeyValuePair[] + { + new KeyValuePair("ObjectFile", value: true) + // new KeyValuePair("BrowseInformationFile", BrowseInformation), + // new KeyValuePair("XMLDocumentationFileName", GenerateXMLDocumentationFiles) + }; +#if __REMOVE + if (PreprocessToFile) + { + if (string.IsNullOrEmpty(PreprocessOutputPath)) + { + base.RootSource = FileTracker.FormatRootingMarker(Sources, preprocessOutput); + base.SourceOutputs.AddComputedOutputsForSourceRoot(base.RootSource, preprocessOutput); + } + array[0] = new KeyValuePair("PreprocessOutputFile", value: true); + } +#endif + ITaskItem[] sources = Sources; + foreach (ITaskItem taskItem in sources) + { + string sourceKey = FileTracker.FormatRootingMarker(taskItem); + KeyValuePair[] array2 = array; + for (int j = 0; j < array2.Length; j++) + { + KeyValuePair keyValuePair = array2[j]; + string metadata = taskItem.GetMetadata(keyValuePair.Key); + if (keyValuePair.Value && !string.IsNullOrEmpty(metadata)) + { + base.SourceOutputs.AddComputedOutputForSourceRoot(sourceKey, metadata); + if (File.Exists(taskItem.GetMetadata("ObjectFile")) && !File.Exists(metadata)) + { + File.Delete(taskItem.GetMetadata("ObjectFile")); + } + } + } + } + +#if __REMOVE + if (IsPropertySet("PrecompiledHeader") && PrecompiledHeader == "Create" && IsPropertySet("PrecompiledHeaderOutputFile")) + { + ITaskItem[] sources2 = Sources; + foreach (ITaskItem source in sources2) + { + string sourceKey2 = FileTracker.FormatRootingMarker(source); + base.SourceOutputs.AddComputedOutputForSourceRoot(sourceKey2, PrecompiledHeaderOutputFile); + } + } +#endif + base.SourceDependencies = new CanonicalTrackedInputFiles(this, base.TLogReadFiles, Sources, base.ExcludedInputPaths, base.SourceOutputs, useMinimalRebuildOptimization: true, MaintainCompositeRootingMarkers); + ITaskItem[] sourcesOutOfDateThroughTracking = base.SourceDependencies.ComputeSourcesNeedingCompilation(); + List sourcesWithChangedCommandLines = GenerateSourcesOutOfDateDueToCommandLine(); + base.SourcesCompiled = MergeOutOfDateSourceLists(sourcesOutOfDateThroughTracking, sourcesWithChangedCommandLines); + if (base.SourcesCompiled.Length == 0) + { + base.SkippedExecution = true; + return base.SkippedExecution; + } + if (!MinimalRebuild || PreprocessToFile) + { + base.SourceDependencies.RemoveEntriesForSource(base.SourcesCompiled); + base.SourceDependencies.SaveTlog(); + if (base.DeleteOutputOnExecute) + { + TrackedVCToolTask.DeleteFiles(base.SourceOutputs.OutputsForSource(base.SourcesCompiled, searchForSubRootsInCompositeRootingMarkers: false)); + } + base.SourceOutputs = new CanonicalTrackedOutputFiles(this, base.TLogWriteFiles); + base.SourceOutputs.RemoveEntriesForSource(base.SourcesCompiled /*, preprocessOutput*/); + base.SourceOutputs.SaveTlog(); + IDictionary dictionary = MapSourcesToCommandLines(); + ITaskItem[] array3 = base.SourcesCompiled; + foreach (ITaskItem source2 in array3) + { + dictionary.Remove(FileTracker.FormatRootingMarker(source2)); + } + WriteSourcesToCommandLinesTable(dictionary); + } + AssignOutOfDateSources(base.SourcesCompiled); + } + else + { + base.SourcesCompiled = Sources; + } + if (string.IsNullOrEmpty(base.RootSource)) + { +#if __REMOVE + if (PreprocessToFile && string.IsNullOrEmpty(PreprocessOutputPath)) + { + if (!base.MinimalRebuildFromTracking) + { + base.RootSource = FileTracker.FormatRootingMarker(Sources, preprocessOutput); + } + }else +#endif + if (base.TrackFileAccess) + { + base.RootSource = FileTracker.FormatRootingMarker(base.SourcesCompiled); + } + } + +#if __REMOVE + if (base.UseMsbuildResourceManager && !EnableCLServerMode && IsSetToTrue("MultiProcessorCompilation")) + { + int num = base.BuildEngine9.RequestCores(base.SourcesCompiled.Length); + if (ProcessorNumber == 0 || ProcessorNumber > num) + { + ProcessorNumber = num; + } + } +#endif + base.SkippedExecution = false; + return base.SkippedExecution; } } } \ No newline at end of file diff --git a/YY.Build.Linux.Tasks/GCC/GCCMapReader.cs b/YY.Build.Linux.Tasks/GCC/GCCMapReader.cs new file mode 100644 index 0000000..a50eee8 --- /dev/null +++ b/YY.Build.Linux.Tasks/GCC/GCCMapReader.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace YY.Build.Linux.Tasks.GCC +{ + // 用于读取GCC -MD 生成的Map文件。 + internal class GCCMapReader + { + private StreamReader StreamReader; + private string TextBuffer; + private int CurrentTextBufferIndex = 0; + private bool FileEnd = true; + + public bool Init(string MapFile) + { + TextBuffer = null; + CurrentTextBufferIndex = 0; + FileEnd = false; + + try + { + StreamReader = File.OpenText(MapFile); + + var ObjectName = ReadLine(); + // 文件内容不对。 + if (ObjectName == null || ObjectName.Length == 0 || ObjectName[ObjectName.Length - 1] != ':') + { + return false; + } + + ObjectName = ReadLine(); + // 开头指向自己,所以跳过即可。 + if(ObjectName == null || ObjectName.Length == 0) + { + return false; + } + + return true; + } + catch (Exception ex) + { + return false; + } + } + + private char? GetChar() + { + if (FileEnd) + return null; + Read: + if (TextBuffer == null || TextBuffer.Length == CurrentTextBufferIndex) + { + CurrentTextBufferIndex = 0; + TextBuffer = StreamReader.ReadLine(); + if (TextBuffer == null) + { + FileEnd = true; + return null; + } + } + + var ch = TextBuffer[CurrentTextBufferIndex]; + ++CurrentTextBufferIndex; + + if (ch == ' ') + return '\0'; + + // 转义 ? + if (ch == '\\') + { + if (TextBuffer.Length == CurrentTextBufferIndex) + { + // 这是一个连接符,重新再读一行 + goto Read; + } + + ch = TextBuffer[CurrentTextBufferIndex]; + ++CurrentTextBufferIndex; + } + + return ch; + } + + public string? ReadLine() + { + if (FileEnd) + return null; + + string Tmp = ""; + + char? ch; + + // 删除开头多余的0终止 + for(; ;) + { + ch = GetChar(); + if (ch == null) + return null; + + if (ch != '\0') + break; + } + + do + { + Tmp += ch; + + ch = GetChar(); + } while (ch != null && ch != '\0'); + + return Tmp; + } + } +} diff --git a/YY.Build.Linux.Tasks/GCC/Ld.cs b/YY.Build.Linux.Tasks/GCC/Ld.cs index e1a1203..4069061 100644 --- a/YY.Build.Linux.Tasks/GCC/Ld.cs +++ b/YY.Build.Linux.Tasks/GCC/Ld.cs @@ -17,6 +17,7 @@ namespace YY.Build.Linux.Tasks.GCC public class Ld : Microsoft.Build.CPPTasks.VCToolTask { public Ld() + : base(Microsoft.Build.CppTasks.Common.Properties.Microsoft_Build_CPPTasks_Strings.ResourceManager) { switchOrderList = new ArrayList(); switchOrderList.Add("OutputFile"); diff --git a/YY.Build.Linux.Tasks/Targets/YY.Linux.Cross.targets b/YY.Build.Linux.Tasks/Targets/YY.Linux.Cross.targets index 1f6cd81..736b331 100644 --- a/YY.Build.Linux.Tasks/Targets/YY.Linux.Cross.targets +++ b/YY.Build.Linux.Tasks/Targets/YY.Linux.Cross.targets @@ -14,9 +14,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> - - - + + + - diff --git a/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.csproj b/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.csproj deleted file mode 100644 index 0083cd2..0000000 --- a/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net6.0 - enable - enable - - - - - - - - - - - - - - - diff --git a/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.projitems b/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.projitems new file mode 100644 index 0000000..da61f85 --- /dev/null +++ b/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.projitems @@ -0,0 +1,25 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 8d4550a9-defb-4705-9dca-873b81497c9d + + + YY.Build.Linux.Tasks2 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.shproj b/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.shproj new file mode 100644 index 0000000..b3bf640 --- /dev/null +++ b/YY.Build.Linux.Tasks/YY.Build.Linux.Tasks.shproj @@ -0,0 +1,13 @@ + + + + 8d4550a9-defb-4705-9dca-873b81497c9d + 14.0 + + + + + + + +