diff --git a/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs index cc0562f9752..678577ef4a0 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs @@ -8,6 +8,7 @@ using System.Text; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Xunit; diff --git a/src/Build.OM.UnitTests/Construction/ProjectImportElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectImportElement_Tests.cs index a7dd4f9d1d8..2a5cdef3fb3 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectImportElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectImportElement_Tests.cs @@ -7,6 +7,7 @@ using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; @@ -159,12 +160,12 @@ public void SettingProjectDirties() try { - file1 = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file1 = FileUtilities.GetTemporaryFileName(); ProjectRootElement importProject1 = ProjectRootElement.Create(); importProject1.AddProperty("p", "v1"); importProject1.Save(file1); - file2 = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file2 = FileUtilities.GetTemporaryFileName(); ProjectRootElement importProject2 = ProjectRootElement.Create(); importProject2.AddProperty("p", "v2"); importProject2.Save(file2); @@ -203,7 +204,7 @@ public void SettingConditionDirties() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file = FileUtilities.GetTemporaryFileName(); ProjectRootElement importProject = ProjectRootElement.Create(); importProject.AddProperty("p", "v1"); importProject.Save(file); diff --git a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs index 95256f674d9..50d577d8ba9 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs @@ -19,6 +19,7 @@ using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using ProjectCollection = Microsoft.Build.Evaluation.ProjectCollection; using Xunit; +using Microsoft.Build.Framework; #nullable disable @@ -253,7 +254,7 @@ public void ConstructOverSameFileReturnsSameEvenWithOneBeingRelativePath4() public void SetFullPathProjectXmlAlreadyLoaded() { ProjectRootElement projectXml1 = ProjectRootElement.Create(); - projectXml1.FullPath = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + projectXml1.FullPath = FileUtilities.GetTemporaryFile(); ProjectRootElement projectXml2 = ProjectRootElement.Create(); projectXml2.FullPath = projectXml1.FullPath; @@ -425,7 +426,7 @@ public void ValidXmlInvalidSyntaxOpenFromDiskTwice() { try { - path = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + path = FileUtilities.GetTemporaryFileName(); File.WriteAllText(path, content); ProjectRootElement.Open(path); @@ -909,7 +910,7 @@ public void SolutionCanNotBeOpened() try { - tempFileSentinel = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + tempFileSentinel = FileUtilities.GetTemporaryFile(); solutionFile = Path.ChangeExtension(tempFileSentinel, ".sln"); File.Copy(tempFileSentinel, solutionFile); @@ -954,7 +955,7 @@ public void ProjectCanNotBeOpened() try { // Does not have .sln or .vcproj extension so loads as project - projectFile = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + projectFile = FileUtilities.GetTemporaryFile(); security = new FileSecurity(projectFile, System.Security.AccessControl.AccessControlSections.All); security.AddAccessRule(rule); @@ -990,7 +991,7 @@ public void SolutionCorrupt() try { - solutionFile = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + solutionFile = FileUtilities.GetTemporaryFileName(); // Arbitrary corrupt content string content = @"Microsoft Visual Studio Solution File, Format Version 10.00 diff --git a/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs b/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs index 836d884c5f1..af2ff258097 100644 --- a/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs @@ -6,6 +6,7 @@ using System.Threading; using Microsoft.Build.Construction; using Microsoft.Build.Exceptions; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/src/Build.OM.UnitTests/Construction/WhiteSpacePreservation_Tests.cs b/src/Build.OM.UnitTests/Construction/WhiteSpacePreservation_Tests.cs index 87017af556a..9020696e810 100644 --- a/src/Build.OM.UnitTests/Construction/WhiteSpacePreservation_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/WhiteSpacePreservation_Tests.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Build.OM.UnitTests/Definition/ProjectCollection_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectCollection_Tests.cs index 168cb67c58e..360edefcac2 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectCollection_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectCollection_Tests.cs @@ -9,6 +9,7 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using Shouldly; diff --git a/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs index dcce4d61853..2c53cc130a6 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs @@ -121,7 +121,7 @@ public void UpdateMetadataImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + file = FileUtilities.GetTemporaryFile(); ProjectRootElement import = ProjectRootElement.Create(file); import.AddItemDefinitionGroup().AddItemDefinition("i").AddMetadata("m", "m0"); import.Save(); @@ -151,7 +151,7 @@ public void SetMetadataImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFile(); + file = FileUtilities.GetTemporaryFile(); ProjectRootElement import = ProjectRootElement.Create(file); import.AddItemDefinitionGroup().AddItemDefinition("i").AddMetadata("m", "m0"); import.Save(); diff --git a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs index 600f24ad21e..079d5592786 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs @@ -319,7 +319,7 @@ public void BuiltInMetadataTimes() try { - path = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + path = FileUtilities.GetTemporaryFileName(); File.WriteAllText(path, String.Empty); FileInfo info = new FileInfo(path); @@ -2053,7 +2053,7 @@ public void RenameImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file = FileUtilities.GetTemporaryFileName(); Project import = new Project(); import.AddItem("i", "i1"); import.Save(file); @@ -2084,7 +2084,7 @@ public void SetMetadataImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file = FileUtilities.GetTemporaryFileName(); Project import = new Project(); import.AddItem("i", "i1"); import.Save(file); @@ -2115,7 +2115,7 @@ public void RemoveMetadataImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file = FileUtilities.GetTemporaryFileName(); Project import = new Project(); ProjectItem item = import.AddItem("i", "i1")[0]; item.SetMetadataValue("m", "m0"); @@ -2478,7 +2478,7 @@ public void RemoveWithItemReferenceOnFilePathMatchingMetadata() var project = ObjectModelHelpers.CreateInMemoryProject(content); var items = project.ItemsIgnoringCondition.Where(i => i.ItemType.Equals("I2")); - if (FileUtilities.GetIsFileSystemCaseSensitive()) + if (FileUtilities.IsFileSystemCaseSensitive) { items.Select(i => i.EvaluatedInclude).ShouldBe(new[] { "a2", "b2", "c2", "g2" }); diff --git a/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs index c028fd294cd..bac78f09a21 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs @@ -7,6 +7,7 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Xunit; #nullable disable @@ -260,7 +261,7 @@ public void SetPropertyImported() try { - file = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + file = FileUtilities.GetTemporaryFileName(); Project import = new Project(); import.SetProperty("p", "v0"); import.Save(file); diff --git a/src/Build.OM.UnitTests/Definition/Project_Tests.cs b/src/Build.OM.UnitTests/Definition/Project_Tests.cs index d1e43441816..021fdd8780c 100644 --- a/src/Build.OM.UnitTests/Definition/Project_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/Project_Tests.cs @@ -2089,7 +2089,7 @@ public void BuildEvaluationUsesCustomLoggers() ObjectModelHelpers.CleanupFileContents(@" "); - string importFileName = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName() + ".proj"; + string importFileName = FileUtilities.GetTemporaryFileName() + ".proj"; File.WriteAllText(importFileName, importProjectContent); string projectContent = @@ -3350,7 +3350,7 @@ public void GetItemProvenancePathMatchingShouldBeCaseInsensitive() ("A", Operation.Include, Provenance.StringLiteral, 1) }; - AssertProvenanceResult(expected, project, FileUtilities.GetIsFileSystemCaseSensitive() ? "a" : "A"); + AssertProvenanceResult(expected, project, FileUtilities.IsFileSystemCaseSensitive ? "a" : "A"); } diff --git a/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs b/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs index 6a337e3bbc9..786ecef6691 100644 --- a/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Xunit; #nullable disable @@ -103,7 +104,7 @@ public ProtectImports_Tests() "; importContents = Expand(importContents); - _importFilename = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName() + ".targets"; + _importFilename = FileUtilities.GetTemporaryFileName() + ".targets"; File.WriteAllText(_importFilename, importContents); } diff --git a/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs index fb41c9da8a1..8f7dacd23fe 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs @@ -6,6 +6,7 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Xunit; #nullable disable @@ -111,7 +112,7 @@ public void FileLocationAvailableEvenAfterEdits() try { - path = Microsoft.Build.Shared.FileUtilities.GetTemporaryFileName(); + path = FileUtilities.GetTemporaryFileName(); ProjectRootElement projectXml = ProjectRootElement.Create(path); projectXml.Save(); diff --git a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj index fc675e1852f..9acc79843a2 100644 --- a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj +++ b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj @@ -1,6 +1,5 @@ - + - @@ -46,11 +45,6 @@ BuildEnvironmentHelper.cs - - - - - App.config Designer diff --git a/src/Build.OM.UnitTests/TransientIO.cs b/src/Build.OM.UnitTests/TransientIO.cs index 2bf038c227c..efacda698f1 100644 --- a/src/Build.OM.UnitTests/TransientIO.cs +++ b/src/Build.OM.UnitTests/TransientIO.cs @@ -8,6 +8,7 @@ namespace Microsoft.Build.UnitTests using System; using System.Collections.Generic; using System.IO; + using Microsoft.Build.Framework; using Microsoft.Build.Shared; public class TransientIO : IDisposable diff --git a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs index 82e190d1e1d..f26704bbc6e 100644 --- a/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/AssemblyTaskFactory_Tests.cs @@ -250,9 +250,7 @@ public void VerifyGoodTaskInstantiation() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -290,9 +288,7 @@ public void VerifyMatchingTaskParametersDontLaunchTaskHost1() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -330,9 +326,7 @@ public void VerifyMatchingTaskParametersDontLaunchTaskHost2() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -372,9 +366,7 @@ public void VerifyMatchingUsingTaskParametersDontLaunchTaskHost1() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -414,9 +406,7 @@ public void VerifyMatchingUsingTaskParametersDontLaunchTaskHost2() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -458,9 +448,7 @@ public void VerifyMatchingParametersDontLaunchTaskHost() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -500,9 +488,7 @@ public void VerifyNonmatchingUsingTaskParametersLaunchTaskHost() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -540,9 +526,7 @@ public void VerifyNonmatchingTaskParametersLaunchTaskHost() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -584,9 +568,7 @@ public void VerifyNonmatchingParametersLaunchTaskHost() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -624,9 +606,7 @@ public void VerifyExplicitlyLaunchTaskHost() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -666,9 +646,7 @@ public void VerifyExplicitlyLaunchTaskHostEvenIfParametersMatch1() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -708,9 +686,7 @@ public void VerifyExplicitlyLaunchTaskHostEvenIfParametersMatch2() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -751,9 +727,7 @@ public void VerifySameFactoryCanGenerateDifferentTaskInstances() new MockHost(), TaskHostParameters.Empty, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif @@ -783,9 +757,7 @@ public void VerifySameFactoryCanGenerateDifferentTaskInstances() new MockHost(), taskParameters, projectFile: "proj.proj", -#if !NET35 hostServices: null, -#endif #if FEATURE_APPDOMAIN new AppDomainSetup(), #endif diff --git a/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs index c10a0600e87..cd8e07ac4fd 100644 --- a/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs +++ b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs @@ -18,15 +18,15 @@ public class DebugUtils_Tests [Fact] public void DumpExceptionToFileShouldWriteInDebugDumpPath() { - ExceptionHandling.ResetDebugDumpPathInRunningTests = true; - var exceptionFilesBefore = Directory.GetFiles(ExceptionHandling.DebugDumpPath, "MSBuild_*failure.txt"); + DebugUtils.ResetDebugDumpPathInRunningTests = true; + var exceptionFilesBefore = Directory.GetFiles(DebugUtils.DebugDumpPath, "MSBuild_*failure.txt"); string[] exceptionFiles = null; try { - ExceptionHandling.DumpExceptionToFile(new Exception("hello world")); - exceptionFiles = Directory.GetFiles(ExceptionHandling.DebugDumpPath, "MSBuild_*failure.txt"); + DebugUtils.DumpExceptionToFile(new Exception("hello world")); + exceptionFiles = Directory.GetFiles(DebugUtils.DebugDumpPath, "MSBuild_*failure.txt"); } finally { diff --git a/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs b/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs index 0ae6ed74774..8feb2a31d9c 100644 --- a/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs +++ b/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs @@ -1851,7 +1851,7 @@ public void RemoveWithItemReferenceOnFilePathMatchingMetadata() var items = lookup.GetItems("I2"); - if (FileUtilities.GetIsFileSystemCaseSensitive()) + if (FileUtilities.IsFileSystemCaseSensitive) { items.Select(i => i.EvaluatedInclude).ShouldBe(new[] { "a2", "b2", "c2", "g2" }); diff --git a/src/Build.UnitTests/BackEnd/SdkResolverLoader_Tests.cs b/src/Build.UnitTests/BackEnd/SdkResolverLoader_Tests.cs index acd78200020..18c88d259dc 100644 --- a/src/Build.UnitTests/BackEnd/SdkResolverLoader_Tests.cs +++ b/src/Build.UnitTests/BackEnd/SdkResolverLoader_Tests.cs @@ -426,7 +426,7 @@ public void LoadResolverAssembly_MSBuildSdkResolver_WithAndWithoutFallback(bool if (string.IsNullOrEmpty(msBuildExePath)) { // Use the executing assembly path as fallback - msBuildExePath = FileUtilities.ExecutingAssemblyPath; + msBuildExePath = BuildEnvironmentHelper.ExecutingAssemblyPath; // If that's also null/empty, use test assembly location if (string.IsNullOrEmpty(msBuildExePath)) { diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs index 8be60f5b825..cedfcf48166 100644 --- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs @@ -996,9 +996,7 @@ public void TestTaskResolutionFailureWithUsingTask() #if FEATURE_APPDOMAIN null, #endif -#if !NET35 null, -#endif false, CancellationToken.None, TaskEnvironmentHelper.CreateForTest()); @@ -1029,9 +1027,7 @@ public void TestTaskResolutionFailureWithNoUsingTask() #if FEATURE_APPDOMAIN null, #endif -#if !NET35 null, -#endif false, CancellationToken.None, TaskEnvironmentHelper.CreateForTest()); @@ -1097,11 +1093,11 @@ public void TaskExceptionHandlingTest(Type exceptionType, bool isCritical) ml.AssertLogDoesntContain(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("UnhandledMSBuildError", string.Empty)); ml.AssertLogContains(testExceptionMessage); - File.Exists(ExceptionHandling.DumpFilePath).ShouldBe(isCritical, - $"{ExceptionHandling.DumpFilePath} expected to exist: {isCritical}"); + File.Exists(DebugUtils.DumpFilePath).ShouldBe(isCritical, $"{DebugUtils.DumpFilePath} expected to exist: {isCritical}"); + if (isCritical) { - FileUtilities.DeleteNoThrow(ExceptionHandling.DumpFilePath); + FileUtilities.DeleteNoThrow(DebugUtils.DumpFilePath); } // Reset DebugPath to not affect other tests @@ -1277,9 +1273,7 @@ private void InitializeHost(bool throwOnExecute) #if FEATURE_APPDOMAIN null, #endif -#if !NET35 null, -#endif false, CancellationToken.None, TaskEnvironmentHelper.CreateForTest()); diff --git a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs index 8b77b14045c..d67845b31c1 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs @@ -43,17 +43,11 @@ public void ConstructorWithNullName() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "", @@ -83,17 +77,11 @@ public void ConstructorWithEmptyName() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "", @@ -123,17 +111,11 @@ public void ConstructorWithNullLocation() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "", @@ -165,17 +147,11 @@ public void ConstructorWithEmptyLocation() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "", @@ -205,17 +181,11 @@ public void TestValidConstructors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -236,17 +206,11 @@ public void TestValidConstructors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -268,17 +232,11 @@ public void TestValidConstructors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -305,17 +263,11 @@ public void TestValidConstructors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -342,17 +294,11 @@ public void TestValidConstructors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -386,17 +332,11 @@ public void TestTranslationWithNullDictionary() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -442,9 +382,7 @@ public void TestTranslationWithAppDomainSetup(byte[] configBytes) buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, appDomainSetup: setup, lineNumberOfTask: 1, columnNumberOfTask: 1, @@ -499,17 +437,11 @@ public void TestTranslationWithEmptyDictionary() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -555,17 +487,11 @@ public void TestTranslationWithValueTypesInDictionary() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif -#if FEATURE_APPDOMAIN - appDomainSetup: + hostServices: null, #if FEATURE_APPDOMAIN - null, + appDomainSetup: null, #endif - lineNumberOfTask: -#endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -609,17 +535,11 @@ public void TestTranslationWithITaskItemInDictionary() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -662,17 +582,11 @@ public void TestTranslationWithITaskItemArrayInDictionary() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -722,17 +636,11 @@ public void TestTranslationWithWarningsAsErrors() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", @@ -777,17 +685,11 @@ public void TestTranslationWithWarningsAsMessages() buildProcessEnvironment: null, culture: Thread.CurrentThread.CurrentCulture, uiCulture: Thread.CurrentThread.CurrentUICulture, -#if !NET35 - null, -#endif + hostServices: null, #if FEATURE_APPDOMAIN - appDomainSetup: -#if FEATURE_APPDOMAIN - null, -#endif - lineNumberOfTask: + appDomainSetup: null, #endif - 1, + lineNumberOfTask: 1, columnNumberOfTask: 1, projectFileOfTask: @"c:\my project\myproj.proj", targetName: "TargetName", diff --git a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs index 95572078dff..d52d5f72651 100644 --- a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs +++ b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs @@ -18,7 +18,7 @@ public class BuildEnvironmentHelper_Tests [Fact] public void GetExecutablePath() { - var msbuildPath = Path.GetDirectoryName(FileUtilities.ExecutingAssemblyPath); + var msbuildPath = Path.GetDirectoryName(BuildEnvironmentHelper.ExecutingAssemblyPath); string expectedMSBuildPath = Path.Combine(msbuildPath, Constants.MSBuildExecutableName).ToLowerInvariant(); string configFilePath = BuildEnvironmentHelper.Instance.CurrentMSBuildConfigurationFile.ToLowerInvariant(); diff --git a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs index 6d0da53ee93..12d33ccc514 100644 --- a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs +++ b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs @@ -1038,17 +1038,16 @@ public void ForwardCompatibleRead_HandleAppendOnlyChanges() // Some future data that are not known in current version binaryWriter.Write(new byte[] { 1, 2, 3, 4 }); - int positionAfterFirstEvent = (int)memoryStream.Position; memoryStream.Position = 0; // event type - Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + binaryReader.Read7BitEncodedInt(); int eventSizePos = (int)memoryStream.Position; - int eventSize = Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + int eventSize = binaryReader.Read7BitEncodedInt(); int positionAfterFirstEventSize = (int)memoryStream.Position; memoryStream.Position = eventSizePos; // the extra 4 bytes - Microsoft.Build.Shared.BinaryWriterExtensions.Write7BitEncodedInt(binaryWriter, eventSize + 4); + binaryWriter.Write7BitEncodedInt(eventSize + 4); memoryStream.Position.ShouldBe(positionAfterFirstEventSize, "The event size need to be overwritten in place - without overwriting any bytes after the size info"); memoryStream.Position = positionAfterFirstEvent; @@ -1102,13 +1101,13 @@ public void ForwardCompatibleRead_HandleUnknownEvent() int positionAfterFirstEvent = (int)memoryStream.Position; memoryStream.Position = 0; // event type - Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + binaryReader.Read7BitEncodedInt(); int eventSizePos = (int)memoryStream.Position; memoryStream.Position = 0; // some future type that is not known in current version BinaryLogRecordKind unknownType = (BinaryLogRecordKind)Enum.GetValues(typeof(BinaryLogRecordKind)).Cast().Select(e => (int)e).Max() + 2; - Microsoft.Build.Shared.BinaryWriterExtensions.Write7BitEncodedInt(binaryWriter, (int)unknownType); + binaryWriter.Write7BitEncodedInt((int)unknownType); memoryStream.Position.ShouldBe(eventSizePos, "The event type need to be overwritten in place - without overwriting any bytes after the type info"); memoryStream.Position = positionAfterFirstEvent; @@ -1156,8 +1155,8 @@ public void ForwardCompatibleRead_HandleMismatchedFormatOfEvent() int positionAfterFirstEvent = (int)memoryStream.Position; memoryStream.Position = 0; // event type - Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); - int eventSize = Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + binaryReader.Read7BitEncodedInt(); + int eventSize = binaryReader.Read7BitEncodedInt(); // overwrite the entire event with garbage binaryWriter.Write(Enumerable.Repeat(byte.MaxValue, eventSize).ToArray()); @@ -1208,13 +1207,13 @@ public void ForwardCompatibleRead_HandleRemovalOfDataFromEventDefinition() int positionAfterFirstEvent = (int)memoryStream.Position; memoryStream.Position = 0; // event type - Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + binaryReader.Read7BitEncodedInt(); int eventSizePos = (int)memoryStream.Position; - int eventSize = Microsoft.Build.Shared.BinaryReaderExtensions.Read7BitEncodedInt(binaryReader); + int eventSize = binaryReader.Read7BitEncodedInt(); int positionAfterFirstEventSize = (int)memoryStream.Position; memoryStream.Position = eventSizePos; // simulate there are 4 bytes less in the future version of the event - while our reader expects those - Microsoft.Build.Shared.BinaryWriterExtensions.Write7BitEncodedInt(binaryWriter, eventSize - 4); + binaryWriter.Write7BitEncodedInt(eventSize - 4); memoryStream.Position.ShouldBe(positionAfterFirstEventSize, "The event size need to be overwritten in place - without overwriting any bytes after the size info"); // remove the 4 bytes - so that actual size of event is inline with it's written size. memoryStream.Position = positionAfterFirstEvent - 4; diff --git a/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs b/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs index a15fffd391d..873cd2d1b5c 100644 --- a/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs +++ b/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading; using Microsoft.Build.Construction; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/src/Build.UnitTests/Construction/SolutionFile_OldParser_Tests.cs b/src/Build.UnitTests/Construction/SolutionFile_OldParser_Tests.cs index 0d197044b63..e471c0373ce 100644 --- a/src/Build.UnitTests/Construction/SolutionFile_OldParser_Tests.cs +++ b/src/Build.UnitTests/Construction/SolutionFile_OldParser_Tests.cs @@ -8,6 +8,7 @@ using System.Text; using Microsoft.Build.Construction; using Microsoft.Build.Exceptions; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/Definition/ToolsetConfigurationReaderTestHelper.cs b/src/Build.UnitTests/Definition/ToolsetConfigurationReaderTestHelper.cs index 66d03679314..84a7a8131b1 100644 --- a/src/Build.UnitTests/Definition/ToolsetConfigurationReaderTestHelper.cs +++ b/src/Build.UnitTests/Definition/ToolsetConfigurationReaderTestHelper.cs @@ -4,6 +4,7 @@ using System; using System.Configuration; using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #pragma warning disable 436 diff --git a/src/Build.UnitTests/EscapingInProjects_Tests.cs b/src/Build.UnitTests/EscapingInProjects_Tests.cs index d517bc50c07..90095b266e0 100644 --- a/src/Build.UnitTests/EscapingInProjects_Tests.cs +++ b/src/Build.UnitTests/EscapingInProjects_Tests.cs @@ -18,7 +18,6 @@ #if FEATURE_COMPILE_IN_TESTS using EscapingUtilities = Microsoft.Build.Shared.EscapingUtilities; #endif -using FileUtilities = Microsoft.Build.Shared.FileUtilities; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using ResourceUtilities = Microsoft.Build.Shared.ResourceUtilities; using Shouldly; diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index a7eaef54016..d3261771808 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -3455,7 +3455,7 @@ public void PropertyFunctionStaticMethodDirectoryNameOfFileAbove() string result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetDirectoryNameOfFileAbove($(StartingDirectory), $(FileToFind)))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); - Assert.Equal(FrameworkFileUtilities.EnsureTrailingSlash(tempPath), FrameworkFileUtilities.EnsureTrailingSlash(result)); + Assert.Equal(FileUtilities.EnsureTrailingSlash(tempPath), FileUtilities.EnsureTrailingSlash(result)); result = expander.ExpandIntoStringAndUnescape(@"$([MSBuild]::GetDirectoryNameOfFileAbove($(StartingDirectory), Hobbits))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); diff --git a/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs b/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs index 53ca81fcccb..421611fef76 100644 --- a/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs @@ -8,6 +8,7 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs b/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs index 64bd31cb639..7e7ee0638a9 100644 --- a/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; diff --git a/src/Build.UnitTests/ExpressionTree_Tests.cs b/src/Build.UnitTests/ExpressionTree_Tests.cs index 1c730e8d38d..36aaaf341e6 100644 --- a/src/Build.UnitTests/ExpressionTree_Tests.cs +++ b/src/Build.UnitTests/ExpressionTree_Tests.cs @@ -9,12 +9,11 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Xunit; - - #nullable disable namespace Microsoft.Build.UnitTests diff --git a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs index d6295254e32..9a18627d008 100644 --- a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs +++ b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Build.Framework; using Microsoft.Build.Globbing; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Build.UnitTests/Instance/TaskItem_Tests.cs b/src/Build.UnitTests/Instance/TaskItem_Tests.cs index 51914da66d5..7e6fd0f64f4 100644 --- a/src/Build.UnitTests/Instance/TaskItem_Tests.cs +++ b/src/Build.UnitTests/Instance/TaskItem_Tests.cs @@ -60,7 +60,7 @@ public void Serialization() Assert.Equal(item.ItemSpec, deserializedItem.ItemSpec); Assert.Equal(item.MetadataCount, deserializedItem.MetadataCount); Assert.Equal(item.GetMetadata("a"), deserializedItem.GetMetadata("a")); - Assert.Equal(item.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath), deserializedItem.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath)); + Assert.Equal(item.GetMetadata(ItemSpecModifiers.DefiningProjectFullPath), deserializedItem.GetMetadata(ItemSpecModifiers.DefiningProjectFullPath)); } /// diff --git a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj index 5149275192f..3ff0799db7e 100644 --- a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj +++ b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj @@ -65,10 +65,8 @@ - - diff --git a/src/Build.UnitTests/TestComparers/TaskItemComparer.cs b/src/Build.UnitTests/TestComparers/TaskItemComparer.cs index b5e5d766b5b..191807d291d 100644 --- a/src/Build.UnitTests/TestComparers/TaskItemComparer.cs +++ b/src/Build.UnitTests/TestComparers/TaskItemComparer.cs @@ -50,8 +50,8 @@ public int Compare(ITaskItem x, ITaskItem y) foreach (string metadataName in x.MetadataNames) { - if (!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(metadataName) || - FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) + if (!ItemSpecModifiers.IsItemSpecModifier(metadataName) || + ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) { if (x.GetMetadata(metadataName) != y.GetMetadata(metadataName)) { @@ -62,8 +62,8 @@ public int Compare(ITaskItem x, ITaskItem y) foreach (string metadataName in y.MetadataNames) { - if (!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(metadataName) || - FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) + if (!ItemSpecModifiers.IsItemSpecModifier(metadataName) || + ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) { if (x.GetMetadata(metadataName) != y.GetMetadata(metadataName)) { diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 9656669047a..c83533a6a5f 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -38,7 +38,7 @@ using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.TelemetryInfra; using Microsoft.NET.StringTools; -using ExceptionHandling = Microsoft.Build.Shared.ExceptionHandling; +using ExceptionHandling = Microsoft.Build.Framework.ExceptionHandling; using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord; using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; @@ -1231,7 +1231,7 @@ private void EndBuildTelemetry() /// private void RecordCrashTelemetry(Exception exception, bool isUnhandled) { - string? host = _buildTelemetry?.BuildEngineHost ?? BuildEnvironmentState.GetHostName(); + string? host = _buildTelemetry?.BuildEngineHost ?? BuildEnvironmentState.GetHostName(); int? activeNodeCount; int? submissionCount; @@ -1766,7 +1766,7 @@ private void ProcessWorkQueue(Action action) { // On the off chance we get an exception from our exception handler (oh, the irony!), we want to know about it (and still not kill this block // which could lead to a somewhat mysterious hang.) - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); } } @@ -2669,8 +2669,8 @@ private void HandleNodeShutdown(int node, NodeShutdown shutdownPacket) foreach (BuildSubmissionBase submission in _buildSubmissions.Values) { BuildEventContext buildEventContext = new BuildEventContext(submission.SubmissionId, BuildEventContext.InvalidNodeId, BuildEventContext.InvalidProjectInstanceId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId); - string exception = ExceptionHandling.ReadAnyExceptionFromFile(_instantiationTimeUtc); - loggingService?.LogError(buildEventContext, new BuildEventFileInfo(string.Empty) /* no project file */, "ChildExitedPrematurely", node, ExceptionHandling.DebugDumpPath, exception); + string exception = DebugUtils.ReadAnyExceptionFromFile(_instantiationTimeUtc); + loggingService?.LogError(buildEventContext, new BuildEventFileInfo(string.Empty) /* no project file */, "ChildExitedPrematurely", node, DebugUtils.DebugDumpPath, exception); } } else if (shutdownPacket.Reason == NodeShutdownReason.Error && _buildSubmissions.Values.Count == 0) @@ -2679,7 +2679,7 @@ private void HandleNodeShutdown(int node, NodeShutdown shutdownPacket) if (shutdownPacket.Exception != null) { ILoggingService loggingService = ((IBuildComponentHost)this).GetComponent(BuildComponentType.LoggingService); - loggingService?.LogError(BuildEventContext.Invalid, new BuildEventFileInfo(string.Empty) /* no project file */, "ChildExitedPrematurely", node, ExceptionHandling.DebugDumpPath, shutdownPacket.Exception.ToString()); + loggingService?.LogError(BuildEventContext.Invalid, new BuildEventFileInfo(string.Empty) /* no project file */, "ChildExitedPrematurely", node, DebugUtils.DebugDumpPath, shutdownPacket.Exception.ToString()); OnThreadException(shutdownPacket.Exception); } } @@ -2933,9 +2933,7 @@ private void CheckAllSubmissionsComplete(BuildRequestDataFlags? flags) _buildParameters?.ProjectRootElementCache?.Clear(); FileMatcher.ClearCaches(); -#if !CLR2COMPATIBILITY FileUtilities.ClearFileExistenceCache(); -#endif } _noActiveSubmissionsEvent?.Set(); diff --git a/src/Build/BackEnd/BuildManager/BuildRequestData.cs b/src/Build/BackEnd/BuildManager/BuildRequestData.cs index ab64b422770..4a7d5b3d38d 100644 --- a/src/Build/BackEnd/BuildManager/BuildRequestData.cs +++ b/src/Build/BackEnd/BuildManager/BuildRequestData.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.Build.Collections; using Microsoft.Build.Experimental.BuildCheck; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; namespace Microsoft.Build.Execution diff --git a/src/Build/BackEnd/BuildManager/CacheSerialization.cs b/src/Build/BackEnd/BuildManager/CacheSerialization.cs index 1b6b2635c71..81a59ee77c9 100644 --- a/src/Build/BackEnd/BuildManager/CacheSerialization.cs +++ b/src/Build/BackEnd/BuildManager/CacheSerialization.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index 8c66af36507..616075f775a 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -1439,7 +1439,7 @@ private void QueueAction(Action action, bool isLastTask) // Dump all engine exceptions to a temp file // so that we have something to go on in the // event of a failure - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); // Raise the exception to the host, so that it can signal termination of the build. RaiseEngineException(e); diff --git a/src/Build/BackEnd/Components/Communications/CurrentHost.cs b/src/Build/BackEnd/Components/Communications/CurrentHost.cs index aa54232dc75..463203d6dfe 100644 --- a/src/Build/BackEnd/Components/Communications/CurrentHost.cs +++ b/src/Build/BackEnd/Components/Communications/CurrentHost.cs @@ -3,6 +3,7 @@ #if RUNTIME_TYPE_NETCORE using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Constants = Microsoft.Build.Framework.Constants; diff --git a/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs b/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs index 36805407d21..a41eb93edd2 100644 --- a/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs @@ -8,7 +8,7 @@ #endif using System.Threading; using Microsoft.Build.Shared; - +using Microsoft.Build.Shared.Debugging; using BuildParameters = Microsoft.Build.Execution.BuildParameters; #nullable disable @@ -462,7 +462,7 @@ private void PacketPumpProc() // Dump all engine exceptions to a temp file // so that we have something to go on in the // event of a failure - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); throw; } } diff --git a/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs b/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs index d45e5bd870f..6a293ba21cf 100644 --- a/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs +++ b/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs @@ -45,7 +45,7 @@ public void InitializeComponent(IBuildComponentHost host) { _scheduler = host.GetComponent(BuildComponentType.Scheduler) as IScheduler; _configCache = host.GetComponent(BuildComponentType.ConfigCache) as IConfigCache; - _tempDirectory = FrameworkFileUtilities.EnsureNoTrailingSlash(FileUtilities.TempFileDirectory); + _tempDirectory = FileUtilities.EnsureNoTrailingSlash(FileUtilities.TempFileDirectory); } public void ShutdownComponent() diff --git a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs index cfa0640c7c0..989db6b6fa0 100644 --- a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs +++ b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs @@ -8,7 +8,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; - +using Microsoft.Build.Shared.Debugging; using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; namespace Microsoft.Build.BackEnd.Logging @@ -421,7 +421,7 @@ private void RaiseAnyEvent(BuildEventArgs buildEvent) // We ought to dump this further up the stack, but if for example a task is logging an event within a // catch(Exception) block and not rethrowing it, there's the possibility that this exception could // just get silently eaten. So better to have duplicates than to not log the problem at all. :) - ExceptionHandling.DumpExceptionToFile(exception); + DebugUtils.DumpExceptionToFile(exception); throw; } @@ -431,7 +431,7 @@ private void RaiseAnyEvent(BuildEventArgs buildEvent) // We ought to dump this further up the stack, but if for example a task is logging an event within a // catch(Exception) block and not rethrowing it, there's the possibility that this exception could // just get silently eaten. So better to have duplicates than to not log the problem at all. :) - ExceptionHandling.DumpExceptionToFile(exception); + DebugUtils.DumpExceptionToFile(exception); if (ExceptionHandling.IsCriticalException(exception)) { diff --git a/src/Build/BackEnd/Components/Logging/LoggingService.cs b/src/Build/BackEnd/Components/Logging/LoggingService.cs index 24d8dcb0944..1fbf8e00cdb 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingService.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingService.cs @@ -14,6 +14,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Logging; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; @@ -1530,7 +1531,7 @@ private void LoggingEventProcessor(object loggingEvent) // Dump all engine exceptions to a temp file // so that we have something to go on in the // event of a failure - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); // Catch all exceptions in order to pass them over to the engine thread. Due to // hosts expecting to get logger exceptions on the same thread the engine was called from. diff --git a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs index 0d35062f96b..c58a4d0798e 100644 --- a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs +++ b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs @@ -24,7 +24,7 @@ using Microsoft.Build.Graph; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using ExceptionHandling = Microsoft.Build.Shared.ExceptionHandling; +using ExceptionHandling = Microsoft.Build.Framework.ExceptionHandling; #pragma warning disable CS0618 // Type or member is obsolete, this class is adapting to both Experimental and new plugin APIs namespace Microsoft.Build.ProjectCache diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs index 59193af2a5e..db7e94103fe 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs @@ -711,7 +711,7 @@ internal static async Task ExecuteTargets( // Set a metadata on the output items called "MSBuildProjectFile" which tells you which project file produced this item. if (String.IsNullOrEmpty(outputItemFromTarget.GetMetadata(ItemMetadataNames.msbuildSourceProjectFile))) { - outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceProjectFile, projects[i].GetMetadata(FileUtilities.ItemSpecModifiers.FullPath)); + outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceProjectFile, projects[i].GetMetadata(ItemSpecModifiers.FullPath)); } } diff --git a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs index 2709e004fcb..409ee34f360 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs @@ -21,6 +21,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; using Microsoft.Build.TelemetryInfra; using NodeLoggingContext = Microsoft.Build.BackEnd.Logging.NodeLoggingContext; using ProjectLoggingContext = Microsoft.Build.BackEnd.Logging.ProjectLoggingContext; @@ -844,7 +845,7 @@ private async Task RequestThreadProc(bool setThreadParameters) // Dump all engine exceptions to a temp file // so that we have something to go on in the // event of a failure - ExceptionHandling.DumpExceptionToFile(ex); + DebugUtils.DumpExceptionToFile(ex); // This includes InternalErrorException, which we definitely want a callstack for. // Fortunately the default console UnhandledExceptionHandler will log the callstack even diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs index 56094d8f078..16afcef0c2e 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs @@ -978,7 +978,7 @@ internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependenc // possibly the outputs list isn't actually the shortest list. However it always is the shortest // in the cases I've seen, and adding this optimization would make the code hard to read. - string oldestOutput = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(outputs[0].ToString())); + string oldestOutput = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(outputs[0].ToString())); ErrorUtilities.ThrowIfTypeDoesNotImplementToString(outputs[0]); DateTime oldestOutputFileTime = DateTime.MinValue; @@ -996,7 +996,7 @@ internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependenc if (oldestOutputFileTime == DateTime.MinValue) { // First output is missing: we must build the target - string arbitraryInput = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(inputs[0].ToString())); + string arbitraryInput = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(inputs[0].ToString())); ErrorUtilities.ThrowIfTypeDoesNotImplementToString(inputs[0]); dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(arbitraryInput, oldestOutput, null, null, OutofdateReason.MissingOutput); return true; @@ -1004,7 +1004,7 @@ internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependenc for (int i = 1; i < outputs.Count; i++) { - string candidateOutput = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(outputs[i].ToString())); + string candidateOutput = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(outputs[i].ToString())); ErrorUtilities.ThrowIfTypeDoesNotImplementToString(outputs[i]); DateTime candidateOutputFileTime = DateTime.MinValue; try @@ -1022,7 +1022,7 @@ internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependenc { // An output is missing: we must build the target string arbitraryInput = - EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(inputs[0].ToString())); + EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(inputs[0].ToString())); ErrorUtilities.ThrowIfTypeDoesNotImplementToString(inputs[0]); dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(arbitraryInput, candidateOutput, null, null, OutofdateReason.MissingOutput); return true; @@ -1039,7 +1039,7 @@ internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependenc // Now compare the oldest output with each input and break out if we find one newer. foreach (T input in inputs) { - string unescapedInput = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(input.ToString())); + string unescapedInput = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(input.ToString())); ErrorUtilities.ThrowIfTypeDoesNotImplementToString(input); DateTime inputFileTime = DateTime.MaxValue; try @@ -1127,8 +1127,8 @@ private void RecordUniqueInputsAndOutputs(IList inputs, IList outputs) /// true, if "input" is newer than "output" private bool IsOutOfDate(string input, string output, string inputItemName, string outputItemName) { - input = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(input)); - output = EscapingUtilities.UnescapeAll(FrameworkFileUtilities.FixFilePath(output)); + input = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(input)); + output = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(output)); ProjectErrorUtilities.VerifyThrowInvalidProject(input.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", input, inputItemName); ProjectErrorUtilities.VerifyThrowInvalidProject(output.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", output, outputItemName); bool outOfDate = (CompareLastWriteTimes(input, output, out bool inputDoesNotExist, out bool outputDoesNotExist) == 1) || inputDoesNotExist; diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs index 51857855ddd..a6c6592bca0 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs @@ -332,9 +332,7 @@ private async ValueTask ExecuteTask(TaskExecutionMode mode, Look #if FEATURE_APPDOMAIN taskHost.AppDomainSetup, #endif -#if !NET35 _buildRequestEntry.Request.HostServices, -#endif taskHost.IsOutOfProc, _cancellationToken, _buildRequestEntry.TaskEnvironment); diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverManifest.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverManifest.cs index fc01468bac7..fbd98e38777 100644 --- a/src/Build/BackEnd/Components/SdkResolution/SdkResolverManifest.cs +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverManifest.cs @@ -80,7 +80,7 @@ internal static SdkResolverManifest Load(string filePath, string manifestFolder) { SdkResolverManifest manifest = ParseSdkResolverElement(reader, filePath); - manifest.Path = FrameworkFileUtilities.FixFilePath(manifest.Path); + manifest.Path = FileUtilities.FixFilePath(manifest.Path); if (!System.IO.Path.IsPathRooted(manifest.Path)) { manifest.Path = System.IO.Path.Combine(manifestFolder, manifest.Path); diff --git a/src/Build/BackEnd/Node/InProcNode.cs b/src/Build/BackEnd/Node/InProcNode.cs index b86e29247f3..3aea5d39fa4 100644 --- a/src/Build/BackEnd/Node/InProcNode.cs +++ b/src/Build/BackEnd/Node/InProcNode.cs @@ -11,6 +11,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService; using NodeLoggingContext = Microsoft.Build.BackEnd.Logging.NodeLoggingContext; @@ -187,7 +188,7 @@ public NodeEngineShutdownReason Run(out Exception shutdownException) // Dump all engine exceptions to a temp file // so that we have something to go on in the // event of a failure - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); // This is fatal: process will terminate: make sure the // debugger launches diff --git a/src/Build/BackEnd/Shared/ConfigurationMetadata.cs b/src/Build/BackEnd/Shared/ConfigurationMetadata.cs index a08d818d4a6..1e54edafa33 100644 --- a/src/Build/BackEnd/Shared/ConfigurationMetadata.cs +++ b/src/Build/BackEnd/Shared/ConfigurationMetadata.cs @@ -7,6 +7,7 @@ using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/BackEnd/TaskExecutionHost/MultiThreadedTaskEnvironmentDriver.cs b/src/Build/BackEnd/TaskExecutionHost/MultiThreadedTaskEnvironmentDriver.cs index db7bc2413e5..e46edd1aa1d 100644 --- a/src/Build/BackEnd/TaskExecutionHost/MultiThreadedTaskEnvironmentDriver.cs +++ b/src/Build/BackEnd/TaskExecutionHost/MultiThreadedTaskEnvironmentDriver.cs @@ -63,7 +63,7 @@ public AbsolutePath ProjectDirectory _currentDirectory = value.GetCanonicalForm(); // Keep the thread-static in sync for use by Expander and Modifiers during property/item expansion. // This allows Path.GetFullPath and %(FullPath) functions used in project files to resolve relative paths correctly in multithreaded mode. - FrameworkFileUtilities.CurrentThreadWorkingDirectory = _currentDirectory.Value; + FileUtilities.CurrentThreadWorkingDirectory = _currentDirectory.Value; } } @@ -130,7 +130,7 @@ public ProcessStartInfo GetProcessStartInfo() public void Dispose() { // Clear the thread-static to prevent pollution between builds on the same thread. - FrameworkFileUtilities.CurrentThreadWorkingDirectory = null; + FileUtilities.CurrentThreadWorkingDirectory = null; } } } diff --git a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 670a3866842..46273a65ec1 100644 --- a/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -231,9 +231,7 @@ internal TaskFactoryWrapper _UNITTESTONLY_TaskFactoryWrapper set => _taskFactoryWrapper = value; } -#if !NET35 private HostServices _hostServices; -#endif #if FEATURE_APPDOMAIN /// @@ -273,9 +271,7 @@ public void InitializeForTask( #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif -#if !NET35 HostServices hostServices, -#endif bool isOutOfProc, CancellationToken cancellationToken, TaskEnvironment taskEnvironment) @@ -292,9 +288,7 @@ public void InitializeForTask( #if FEATURE_APPDOMAIN AppDomainSetup = appDomainSetup; #endif -#if !NET35 _hostServices = hostServices; -#endif IsOutOfProc = isOutOfProc; TaskEnvironment = taskEnvironment; } @@ -1002,9 +996,7 @@ private ITask InstantiateTask(int scheduledNodeId, in TaskHostParameters taskIde _buildComponentHost, taskIdentityParameters, _projectFile, -#if !NET35 _hostServices, -#endif #if FEATURE_APPDOMAIN AppDomainSetup, #endif @@ -1852,9 +1844,7 @@ private ITask CreateTaskHostTaskForOutOfProcFactory( #if FEATURE_APPDOMAIN AppDomainSetup, #endif -#if !NET35 _hostServices, -#endif scheduledNodeId, TaskEnvironment); } diff --git a/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs b/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs index 47c8ee510dc..851b26ece37 100644 --- a/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs +++ b/src/Build/BuildCheck/Checks/UntrustedLocationCheck.cs @@ -6,6 +6,7 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.Build.Construction; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; @@ -40,7 +41,7 @@ public override void RegisterActions(IBuildCheckRegistrationContext registration private void EvaluatedPropertiesAction(BuildCheckDataContext context) { if (checkedProjects.Add(context.Data.ProjectFilePath) && - context.Data.ProjectFileDirectory.StartsWith(PathsHelper.Downloads, Shared.FileUtilities.PathComparison)) + context.Data.ProjectFileDirectory.StartsWith(PathsHelper.Downloads, FileUtilities.PathComparison)) { context.ReportResult(BuildCheckResult.Create( SupportedRule, diff --git a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs index 944e03113a8..1fa7752f9a7 100644 --- a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs +++ b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs @@ -5,7 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; using static Microsoft.Build.Experimental.BuildCheck.Infrastructure.EditorConfig.EditorConfigGlobsMatcher; @@ -45,7 +45,7 @@ internal List DiscoverEditorConfigFiles(string filePath) { var editorConfigDataFromFilesList = new List(); - var directoryOfTheProject = Path.GetDirectoryName(filePath); + var directoryOfTheProject = Path.GetDirectoryName(filePath)!; // The method will look for the file in parent directory if not found in current until found or the directory is root. var editorConfigFilePath = FileUtilities.GetPathOfFileAbove(EditorconfigFile, directoryOfTheProject); while (editorConfigFilePath != string.Empty) @@ -64,7 +64,7 @@ internal List DiscoverEditorConfigFiles(string filePath) else { // search in upper directory - editorConfigFilePath = FileUtilities.GetPathOfFileAbove(EditorconfigFile, Path.GetDirectoryName(Path.GetDirectoryName(editorConfigFilePath))); + editorConfigFilePath = FileUtilities.GetPathOfFileAbove(EditorconfigFile, Path.GetDirectoryName(Path.GetDirectoryName(editorConfigFilePath))!); } } diff --git a/src/Build/BuildCheck/Utilities/BuildCheckUtilities.cs b/src/Build/BuildCheck/Utilities/BuildCheckUtilities.cs index 01461b7c9ef..1b7b377d84e 100644 --- a/src/Build/BuildCheck/Utilities/BuildCheckUtilities.cs +++ b/src/Build/BuildCheck/Utilities/BuildCheckUtilities.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Build/Construction/ProjectImportElement.cs b/src/Build/Construction/ProjectImportElement.cs index 852ab8e7d05..6bbc04a9e7d 100644 --- a/src/Build/Construction/ProjectImportElement.cs +++ b/src/Build/Construction/ProjectImportElement.cs @@ -52,7 +52,7 @@ internal ProjectImportElement(XmlElementWithLocation xmlElement, ProjectRootElem /// public string Project { - get => FrameworkFileUtilities.FixFilePath(GetAttributeValue(XMakeAttributes.project)); + get => FileUtilities.FixFilePath(GetAttributeValue(XMakeAttributes.project)); set { ErrorUtilities.VerifyThrowArgumentLength(value, XMakeAttributes.project); @@ -71,7 +71,7 @@ public string Project /// public string Sdk { - get => FrameworkFileUtilities.FixFilePath(GetAttributeValue(XMakeAttributes.sdk)); + get => FileUtilities.FixFilePath(GetAttributeValue(XMakeAttributes.sdk)); set { ErrorUtilities.VerifyThrowArgumentLength(value, XMakeAttributes.sdk); diff --git a/src/Build/Construction/ProjectMetadataElement.cs b/src/Build/Construction/ProjectMetadataElement.cs index 156755286d1..60592634cfe 100644 --- a/src/Build/Construction/ProjectMetadataElement.cs +++ b/src/Build/Construction/ProjectMetadataElement.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using Microsoft.Build.Framework; using Microsoft.Build.ObjectModelRemoting; using Microsoft.Build.Shared; @@ -103,7 +104,7 @@ public string Value internal static ProjectMetadataElement CreateDisconnected(string name, ProjectRootElement containingProject, ElementLocation location = null) { XmlUtilities.VerifyThrowArgumentValidElementName(name); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name); XmlElementWithLocation element = containingProject.CreateElement(name, location); diff --git a/src/Build/Construction/ProjectRootElement.cs b/src/Build/Construction/ProjectRootElement.cs index 83a084eb685..57366d1349b 100644 --- a/src/Build/Construction/ProjectRootElement.cs +++ b/src/Build/Construction/ProjectRootElement.cs @@ -1256,7 +1256,7 @@ public ProjectTargetElement AddTarget(string name) /// public ProjectUsingTaskElement AddUsingTask(string name, string assemblyFile, string assemblyName) { - ProjectUsingTaskElement usingTask = CreateUsingTaskElement(name, FrameworkFileUtilities.FixFilePath(assemblyFile), assemblyName); + ProjectUsingTaskElement usingTask = CreateUsingTaskElement(name, FileUtilities.FixFilePath(assemblyFile), assemblyName); AppendChild(usingTask); return usingTask; diff --git a/src/Build/Construction/ProjectUsingTaskElement.cs b/src/Build/Construction/ProjectUsingTaskElement.cs index cecae44497d..9eb1534256f 100644 --- a/src/Build/Construction/ProjectUsingTaskElement.cs +++ b/src/Build/Construction/ProjectUsingTaskElement.cs @@ -48,14 +48,14 @@ private ProjectUsingTaskElement(XmlElementWithLocation xmlElement, ProjectRootEl /// public string AssemblyFile { - get => FrameworkFileUtilities.FixFilePath( + get => FileUtilities.FixFilePath( GetAttributeValue(XMakeAttributes.assemblyFile)); set { ErrorUtilities.VerifyThrowArgumentLength(value, XMakeAttributes.assemblyName); ErrorUtilities.VerifyThrowInvalidOperation(String.IsNullOrEmpty(AssemblyName), "OM_EitherAttributeButNotBoth", ElementName, XMakeAttributes.assemblyFile, XMakeAttributes.assemblyName); - value = FrameworkFileUtilities.FixFilePath(value); + value = FileUtilities.FixFilePath(value); SetOrRemoveAttribute(XMakeAttributes.assemblyFile, value, "Set usingtask AssemblyFile {0}", value); } } @@ -249,7 +249,7 @@ internal static ProjectUsingTaskElement CreateDisconnected(string taskName, stri if (!String.IsNullOrEmpty(assemblyFile)) { - usingTask.AssemblyFile = FrameworkFileUtilities.FixFilePath(assemblyFile); + usingTask.AssemblyFile = FileUtilities.FixFilePath(assemblyFile); } else { diff --git a/src/Build/Construction/Solution/ProjectInSolution.cs b/src/Build/Construction/Solution/ProjectInSolution.cs index 106b21221ac..fc8ecf2c35b 100644 --- a/src/Build/Construction/Solution/ProjectInSolution.cs +++ b/src/Build/Construction/Solution/ProjectInSolution.cs @@ -14,7 +14,7 @@ using System.Text; #else using System.Buffers; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; #endif using XMakeAttributes = Microsoft.Build.Shared.XMakeAttributes; diff --git a/src/Build/Construction/Solution/SolutionFile.cs b/src/Build/Construction/Solution/SolutionFile.cs index 42888852c35..ff5c243a0fa 100644 --- a/src/Build/Construction/Solution/SolutionFile.cs +++ b/src/Build/Construction/Solution/SolutionFile.cs @@ -21,7 +21,6 @@ using Microsoft.VisualStudio.SolutionPersistence.Serializer; using BuildEventFileInfo = Microsoft.Build.Shared.BuildEventFileInfo; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; -using ExceptionUtilities = Microsoft.Build.Shared.ExceptionHandling; using ProjectFileErrorUtilities = Microsoft.Build.Shared.ProjectFileErrorUtilities; using ResourceUtilities = Microsoft.Build.Shared.ResourceUtilities; using VisualStudioConstants = Microsoft.Build.Shared.VisualStudioConstants; @@ -746,7 +745,7 @@ internal void ParseSolutionFile() SolutionReader = new StreamReader(fileStream, Encoding.GetEncoding(0)); // HIGHCHAR: If solution files have no byte-order marks, then assume ANSI rather than ASCII. ParseSolution(); } - catch (Exception e) when (ExceptionUtilities.IsIoRelatedException(e)) + catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) { ProjectFileErrorUtilities.ThrowInvalidProjectFile(new BuildEventFileInfo(_solutionFile), "InvalidProjectFile", e.Message); } diff --git a/src/Build/Construction/Solution/SolutionProjectGenerator.cs b/src/Build/Construction/Solution/SolutionProjectGenerator.cs index 8f3735ba170..833ee5b2867 100644 --- a/src/Build/Construction/Solution/SolutionProjectGenerator.cs +++ b/src/Build/Construction/Solution/SolutionProjectGenerator.cs @@ -1308,7 +1308,7 @@ private string GetMetaprojectName(ProjectInSolution project) baseName = project.ProjectName; } - baseName = FrameworkFileUtilities.EnsureNoTrailingSlash(baseName); + baseName = FileUtilities.EnsureNoTrailingSlash(baseName); return GetMetaprojectName(baseName); } diff --git a/src/Build/Definition/BuiltInMetadata.cs b/src/Build/Definition/BuiltInMetadata.cs index 1e28ec1c379..b93845b4b3b 100644 --- a/src/Build/Definition/BuiltInMetadata.cs +++ b/src/Build/Definition/BuiltInMetadata.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; namespace Microsoft.Build.Evaluation @@ -22,7 +23,7 @@ internal static int MetadataCount { [DebuggerStepThrough] get - { return FileUtilities.ItemSpecModifiers.All.Length; } + { return ItemSpecModifiers.All.Length; } } /// @@ -32,7 +33,7 @@ internal static ICollection MetadataNames { [DebuggerStepThrough] get - { return FileUtilities.ItemSpecModifiers.All; } + { return ItemSpecModifiers.All; } } /// @@ -71,16 +72,16 @@ internal static string GetMetadataValue(string currentDirectory, string evaluate internal static string GetMetadataValueEscaped(string currentDirectory, string evaluatedIncludeBeforeWildcardExpansionEscaped, string evaluatedIncludeEscaped, string definingProjectEscaped, string name, ref string fullPath) { // This is an assert, not a VerifyThrow, because the caller should already have done this check, and it's slow/hot. - Debug.Assert(FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name)); + Debug.Assert(ItemSpecModifiers.IsItemSpecModifier(name)); string value; - if (String.Equals(name, FileUtilities.ItemSpecModifiers.RecursiveDir, StringComparison.OrdinalIgnoreCase)) + if (String.Equals(name, ItemSpecModifiers.RecursiveDir, StringComparison.OrdinalIgnoreCase)) { value = GetRecursiveDirValue(evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped); } else { - value = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, evaluatedIncludeEscaped, definingProjectEscaped, name, ref fullPath); + value = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, evaluatedIncludeEscaped, definingProjectEscaped, name, ref fullPath); } return value; diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index e68f2e3316e..e64a57a7627 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -492,7 +492,7 @@ public static Version Version // Use .CodeBase instead of .Location, because .Location doesn't // work when Microsoft.Build.dll has been shadow-copied, for example // in scenarios where NUnit is loading Microsoft.Build. - var versionInfo = FileVersionInfo.GetVersionInfo(FileUtilities.ExecutingAssemblyPath); + var versionInfo = FileVersionInfo.GetVersionInfo(BuildEnvironmentHelper.ExecutingAssemblyPath); s_engineVersion = new Version(versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart); } diff --git a/src/Build/Definition/ProjectItem.cs b/src/Build/Definition/ProjectItem.cs index 96aae94ac4f..19c954401c2 100644 --- a/src/Build/Definition/ProjectItem.cs +++ b/src/Build/Definition/ProjectItem.cs @@ -303,7 +303,7 @@ public int MetadataCount { [DebuggerStepThrough] get - { return Metadata.Count + FileUtilities.ItemSpecModifiers.All.Length; } + { return Metadata.Count + ItemSpecModifiers.All.Length; } } /// @@ -458,7 +458,7 @@ public bool HasMetadata(string name) return true; } - if (FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name)) + if (ItemSpecModifiers.IsItemSpecModifier(name)) { return true; } @@ -583,7 +583,7 @@ private ProjectMetadata SetMetadataOperation(string name, string unevaluatedValu Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); XmlUtilities.VerifyThrowArgumentValidElementName(name); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent?.Parent != null, "OM_ObjectIsNoLongerActive"); @@ -641,7 +641,7 @@ public bool RemoveMetadata(string name) } ErrorUtilities.VerifyThrowArgumentLength(name); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); Project.VerifyThrowInvalidOperationNotImported(_xml.ContainingProject); ErrorUtilities.VerifyThrowInvalidOperation(_xml.Parent?.Parent != null, "OM_ObjectIsNoLongerActive"); @@ -857,7 +857,7 @@ private string GetBuiltInMetadataEscaped(string name) { string value = null; - if (FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name)) + if (ItemSpecModifiers.IsItemSpecModifier(name)) { value = BuiltInMetadata.GetMetadataValueEscaped(_project.DirectoryPath, _evaluatedIncludeBeforeWildcardExpansionEscaped, _evaluatedIncludeEscaped, this.Xml.ContainingProject.FullPath, name, ref _fullPath); } diff --git a/src/Build/Definition/ProjectItemDefinition.cs b/src/Build/Definition/ProjectItemDefinition.cs index 8fa69153f7e..9524297487c 100644 --- a/src/Build/Definition/ProjectItemDefinition.cs +++ b/src/Build/Definition/ProjectItemDefinition.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Collections; using Microsoft.Build.Construction; +using Microsoft.Build.Framework; using Microsoft.Build.ObjectModelRemoting; using Microsoft.Build.Shared; @@ -150,7 +151,7 @@ public ProjectMetadata SetMetadataValue(string name, string unevaluatedValue) } XmlUtilities.VerifyThrowArgumentValidElementName(name); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name); ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name); ProjectMetadata metadatum; diff --git a/src/Build/Definition/Toolset.cs b/src/Build/Definition/Toolset.cs index 45f5863e894..cbba8dbbd56 100644 --- a/src/Build/Definition/Toolset.cs +++ b/src/Build/Definition/Toolset.cs @@ -367,7 +367,7 @@ private set // technically hurt anything, but it doesn't look nice.) string toolsPathToUse = value; - if (FrameworkFileUtilities.EndsWithSlash(toolsPathToUse)) + if (FileUtilities.EndsWithSlash(toolsPathToUse)) { string rootPath = Path.GetPathRoot(Path.GetFullPath(toolsPathToUse)); diff --git a/src/Build/Errors/InvalidProjectFileException.cs b/src/Build/Errors/InvalidProjectFileException.cs index 8d063ba4a38..8878b742d9c 100644 --- a/src/Build/Errors/InvalidProjectFileException.cs +++ b/src/Build/Errors/InvalidProjectFileException.cs @@ -9,6 +9,7 @@ #endif using Microsoft.Build.Framework.BuildException; using Microsoft.Build.Shared; +using Microsoft.Build.Framework; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs b/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs index a28b263a3e6..e819fef644d 100644 --- a/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs @@ -120,7 +120,7 @@ private static string ExpandArgumentForScalarParameter(string function, GenericE // Fix path before expansion if (isFilePath) { - argument = FrameworkFileUtilities.FixFilePath(argument); + argument = FileUtilities.FixFilePath(argument); } IList items = state.ExpandIntoTaskItems(argument); @@ -154,7 +154,7 @@ private List ExpandArgumentAsFileList(GenericExpressionNode argumentNode // Fix path before expansion if (isFilePath) { - argument = FrameworkFileUtilities.FixFilePath(argument); + argument = FileUtilities.FixFilePath(argument); } IList expanded = state.ExpandIntoTaskItems(argument); diff --git a/src/Build/Evaluation/Conditionals/Scanner.cs b/src/Build/Evaluation/Conditionals/Scanner.cs index 718ae73d23d..f231c5a4676 100644 --- a/src/Build/Evaluation/Conditionals/Scanner.cs +++ b/src/Build/Evaluation/Conditionals/Scanner.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Globalization; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable @@ -474,7 +475,7 @@ private bool CheckForUnexpectedMetadata(string expression) expression = expression.Substring(period + 1); } - bool isItemSpecModifier = FileUtilities.ItemSpecModifiers.IsItemSpecModifier(expression); + bool isItemSpecModifier = ItemSpecModifiers.IsItemSpecModifier(expression); if (((_options & ParserOptions.AllowBuiltInMetadata) == 0) && isItemSpecModifier) diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 98a2a8aeed5..3f6c0228f55 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -28,7 +28,7 @@ using Microsoft.NET.StringTools; using Microsoft.Win32; using AvailableStaticMethods = Microsoft.Build.Internal.AvailableStaticMethods; -using ItemSpecModifiers = Microsoft.Build.Shared.FileUtilities.ItemSpecModifiers; +using ItemSpecModifiers = Microsoft.Build.Framework.ItemSpecModifiers; using ParseArgs = Microsoft.Build.Evaluation.Expander.ArgumentParser; using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; @@ -1159,7 +1159,7 @@ internal static string ExpandSingleMetadata(Match itemMetadataMatch, MetadataMat string metadataValue = null; - bool isBuiltInMetadata = FileUtilities.ItemSpecModifiers.IsItemSpecModifier(metadataName); + bool isBuiltInMetadata = ItemSpecModifiers.IsItemSpecModifier(metadataName); if ( (isBuiltInMetadata && ((evaluator._options & ExpanderOptions.ExpandBuiltInMetadata) != 0)) || @@ -1713,7 +1713,7 @@ private static object ExpandMSBuildThisFileProperty(string propertyName, IElemen } else if (String.Equals(propertyName, ReservedPropertyNames.thisFileDirectory, StringComparison.OrdinalIgnoreCase)) { - value = FrameworkFileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(elementLocation.File)); + value = FileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(elementLocation.File)); } else if (String.Equals(propertyName, ReservedPropertyNames.thisFileDirectoryNoRoot, StringComparison.OrdinalIgnoreCase)) { @@ -1954,7 +1954,7 @@ internal static List> Transform( ItemTransformFunctions functionType; - if (FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(functionName)) + if (ItemSpecModifiers.IsDerivableItemSpecModifier(functionName)) { functionType = ItemTransformFunctions.ItemSpecModifierFunction; } @@ -2552,10 +2552,10 @@ internal static void ItemSpecModifierFunction(IElementLocation elementLocation, // 1. in multiprocess mode we're safe to get the current directory as we'll be running on TaskItems which // only exist within a target where we can trust the current directory // 2. in single process mode we get the project directory set for the thread - string directoryToUse = item.Value.ProjectDirectory ?? FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory(); - string definingProjectEscaped = item.Value.GetMetadataValueEscaped(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath); + string directoryToUse = item.Value.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory(); + string definingProjectEscaped = item.Value.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath); - result = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(directoryToUse, item.Key, definingProjectEscaped, functionName); + result = ItemSpecModifiers.GetItemSpecModifier(directoryToUse, item.Key, definingProjectEscaped, functionName); } // InvalidOperationException is how GetItemSpecModifier communicates invalid conditions upwards, so // we do not want to rethrow in that case. @@ -2610,7 +2610,7 @@ internal static void Exists(IElementLocation elementLocation, string functionNam // 1. in multiprocess mode we're safe to get the current directory as we'll be running on TaskItems which // only exist within a target where we can trust the current directory // 2. in single process mode we get the project directory set for the thread - string baseDirectoryToUse = item.Value.ProjectDirectory ?? FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; + string baseDirectoryToUse = item.Value.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; rootedPath = Path.Combine(baseDirectoryToUse, unescapedPath); } } @@ -2690,7 +2690,7 @@ internal static void GetPathsOfAllDirectoriesAbove(IElementLocation elementLocat // 1. in multiprocess mode we're safe to get the current directory as we'll be running on TaskItems which // only exist within a target where we can trust the current directory // 2. in single process mode we get the project directory set for the thread - string baseDirectoryToUse = item.Value.ProjectDirectory ?? FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; + string baseDirectoryToUse = item.Value.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; rootedPath = Path.Combine(baseDirectoryToUse, unescapedPath); } @@ -2769,7 +2769,7 @@ internal static void DirectoryName(IElementLocation elementLocation, bool includ // 1. in multiprocess mode we're safe to get the current directory as we'll be running on TaskItems which // only exist within a target where we can trust the current directory // 2. in single process mode we get the project directory set for the thread - string baseDirectoryToUse = item.Value.ProjectDirectory ?? FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; + string baseDirectoryToUse = item.Value.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? String.Empty; rootedPath = Path.Combine(baseDirectoryToUse, unescapedPath); } @@ -3313,17 +3313,17 @@ private static string GetMetadataValueFromMatch( string value = null; try { - if (FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(match.Name)) + if (ItemSpecModifiers.IsDerivableItemSpecModifier(match.Name)) { // If we're not a ProjectItem or ProjectItemInstance, then ProjectDirectory will be null. // In that case, // 1. in multiprocess mode we're safe to get the current directory as we'll be running on TaskItems which // only exist within a target where we can trust the current directory // 2. in single process mode we get the project directory set for the thread - string directoryToUse = sourceOfMetadata.ProjectDirectory ?? FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory(); - string definingProjectEscaped = sourceOfMetadata.GetMetadataValueEscaped(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath); + string directoryToUse = sourceOfMetadata.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory(); + string definingProjectEscaped = sourceOfMetadata.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath); - value = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(directoryToUse, itemSpec, definingProjectEscaped, match.Name); + value = ItemSpecModifiers.GetItemSpecModifier(directoryToUse, itemSpec, definingProjectEscaped, match.Name); } else { @@ -4008,7 +4008,7 @@ internal object Execute(object objectInstance, IPropertyProvider properties, if (_receiverType == typeof(File) || _receiverType == typeof(Directory) || _receiverType == typeof(Path)) { - argumentValue = FrameworkFileUtilities.FixFilePath(argumentValue); + argumentValue = FileUtilities.FixFilePath(argumentValue); } args[n] = EscapingUtilities.UnescapeAll(argumentValue); diff --git a/src/Build/Evaluation/Expander/WellKnownFunctions.cs b/src/Build/Evaluation/Expander/WellKnownFunctions.cs index 497b2eef1a9..7fb9c62b886 100644 --- a/src/Build/Evaluation/Expander/WellKnownFunctions.cs +++ b/src/Build/Evaluation/Expander/WellKnownFunctions.cs @@ -105,8 +105,8 @@ internal static bool TryExecutePathFunction(string methodName, out object? retur { if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null) { - returnVal = !string.IsNullOrEmpty(FrameworkFileUtilities.CurrentThreadWorkingDirectory) - ? Path.GetFullPath(Path.Combine(FrameworkFileUtilities.CurrentThreadWorkingDirectory, arg0)) + returnVal = !string.IsNullOrEmpty(FileUtilities.CurrentThreadWorkingDirectory) + ? Path.GetFullPath(Path.Combine(FileUtilities.CurrentThreadWorkingDirectory, arg0)) : Path.GetFullPath(arg0); return true; } diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index df3783f5ba9..5393497e564 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -525,7 +525,7 @@ internal static bool DoesTaskHostExist(string runtime, string architecture) /// The specified path with a trailing slash. internal static string EnsureTrailingSlash(string path) { - return FrameworkFileUtilities.EnsureTrailingSlash(path); + return FileUtilities.EnsureTrailingSlash(path); } /// diff --git a/src/Build/Evaluation/ItemSpec.cs b/src/Build/Evaluation/ItemSpec.cs index ea5bbfd0330..c894a8b5f87 100644 --- a/src/Build/Evaluation/ItemSpec.cs +++ b/src/Build/Evaluation/ItemSpec.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Build.Framework; using Microsoft.Build.Globbing; using Microsoft.Build.Internal; using Microsoft.Build.Shared; diff --git a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs index 78909e1f660..7ef2740b5b3 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.Build.Construction; using Microsoft.Build.Eventing; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/ProjectParser.cs b/src/Build/Evaluation/ProjectParser.cs index a71a572677d..a186fba1726 100644 --- a/src/Build/Evaluation/ProjectParser.cs +++ b/src/Build/Evaluation/ProjectParser.cs @@ -376,7 +376,7 @@ internal static void CheckMetadataAsAttributeName(string name, out bool isReserv return; } - if (FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name) || XMakeElements.ReservedItemNames.Contains(name)) + if (ItemSpecModifiers.IsItemSpecModifier(name) || XMakeElements.ReservedItemNames.Contains(name)) { isReservedAttributeName = false; isValidMetadataNameInAttribute = false; @@ -397,7 +397,7 @@ private ProjectMetadataElement ParseProjectMetadataElement(XmlElementWithLocatio XmlUtilities.VerifyThrowProjectValidElementName(element); ProjectErrorUtilities.VerifyThrowInvalidProject(!(parent is ProjectItemElement) || ((ProjectItemElement)parent).Remove.Length == 0, element.Location, "ChildElementsBelowRemoveNotAllowed", element.Name); - ProjectErrorUtilities.VerifyThrowInvalidProject(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(element.Name), element.Location, "ItemSpecModifierCannotBeCustomMetadata", element.Name); + ProjectErrorUtilities.VerifyThrowInvalidProject(!ItemSpecModifiers.IsItemSpecModifier(element.Name), element.Location, "ItemSpecModifierCannotBeCustomMetadata", element.Name); ProjectErrorUtilities.VerifyThrowInvalidProject(!XMakeElements.ReservedItemNames.Contains(element.Name), element.Location, "CannotModifyReservedItemMetadata", element.Name); ProjectMetadataElement metadatum = new ProjectMetadataElement(element, parent, _project); diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index c97ebad4bff..92c36003026 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -12,7 +12,6 @@ using Microsoft.Build.Construction; using Microsoft.Build.Framework; using Microsoft.Build.Internal; -using Microsoft.Build.Shared; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; using OutOfProcNode = Microsoft.Build.Execution.OutOfProcNode; diff --git a/src/Build/Globbing/MSBuildGlob.cs b/src/Build/Globbing/MSBuildGlob.cs index 38915e44cd3..930a16f9eaf 100644 --- a/src/Build/Globbing/MSBuildGlob.cs +++ b/src/Build/Globbing/MSBuildGlob.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text.RegularExpressions; using Microsoft.Build.Collections; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.NET.StringTools; diff --git a/src/Build/Graph/GraphBuilder.cs b/src/Build/Graph/GraphBuilder.cs index 3a30af61030..35ecbb5122a 100644 --- a/src/Build/Graph/GraphBuilder.cs +++ b/src/Build/Graph/GraphBuilder.cs @@ -15,6 +15,7 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Graph/ProjectGraphEntryPoint.cs b/src/Build/Graph/ProjectGraphEntryPoint.cs index cb5ee4d3c30..fc392d02987 100644 --- a/src/Build/Graph/ProjectGraphEntryPoint.cs +++ b/src/Build/Graph/ProjectGraphEntryPoint.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Graph/ProjectGraphNode.cs b/src/Build/Graph/ProjectGraphNode.cs index 022b63737c1..6028f9ab8a7 100644 --- a/src/Build/Graph/ProjectGraphNode.cs +++ b/src/Build/Graph/ProjectGraphNode.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; namespace Microsoft.Build.Graph diff --git a/src/Build/Instance/ProjectItemInstance.cs b/src/Build/Instance/ProjectItemInstance.cs index 352e148e892..b8a70ceac25 100644 --- a/src/Build/Instance/ProjectItemInstance.cs +++ b/src/Build/Instance/ProjectItemInstance.cs @@ -866,8 +866,8 @@ internal TaskItem( ErrorUtilities.VerifyThrowArgumentLength(includeEscaped); ErrorUtilities.VerifyThrowArgumentLength(includeBeforeWildcardExpansionEscaped); - _includeEscaped = FrameworkFileUtilities.FixFilePath(includeEscaped); - _includeBeforeWildcardExpansionEscaped = FrameworkFileUtilities.FixFilePath(includeBeforeWildcardExpansionEscaped); + _includeEscaped = FileUtilities.FixFilePath(includeEscaped); + _includeBeforeWildcardExpansionEscaped = FileUtilities.FixFilePath(includeBeforeWildcardExpansionEscaped); _directMetadata = (directMetadata == null || directMetadata.Count == 0) ? null : directMetadata; // If the metadata was all removed, toss the dictionary _itemDefinitions = itemDefinitions; _projectDirectory = projectDirectory; @@ -974,14 +974,14 @@ public ICollection MetadataNames { ImmutableDictionary metadataCollection = MetadataCollection; - List names = new List(capacity: metadataCollection.Count + FileUtilities.ItemSpecModifiers.All.Length); + List names = new List(capacity: metadataCollection.Count + ItemSpecModifiers.All.Length); foreach (KeyValuePair metadatum in metadataCollection) { names.Add(metadatum.Key); } - names.AddRange(FileUtilities.ItemSpecModifiers.All); + names.AddRange(ItemSpecModifiers.All); return names; } @@ -1817,7 +1817,7 @@ public bool Equals(TaskItem other) public bool HasMetadata(string name) { if ((_directMetadata?.ContainsKey(name) == true) || - FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name) || + ItemSpecModifiers.IsItemSpecModifier(name) || GetItemDefinitionMetadataEscaped(name) != null) { return true; @@ -2012,7 +2012,7 @@ internal void SetMetadataOnTaskOutput(string name, string evaluatedValueEscaped) { ProjectInstance.VerifyThrowNotImmutable(_isImmutable); - if (!FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(name)) + if (!ItemSpecModifiers.IsDerivableItemSpecModifier(name)) { ProjectMetadataInstance.VerifyThrowReservedNameAllowItemSpecModifiers(name); _directMetadata = DirectMetadata.SetItem(name, evaluatedValueEscaped ?? string.Empty); @@ -2025,7 +2025,7 @@ internal void SetMetadataOnTaskOutput(IEnumerable> _directMetadata ??= ImmutableDictionaryExtensions.EmptyMetadata; var metadata = items - .Where(item => !FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(item.Key)); + .Where(item => !ItemSpecModifiers.IsDerivableItemSpecModifier(item.Key)); _directMetadata = DirectMetadata.SetItems(metadata, ProjectMetadataInstance.VerifyThrowReservedNameAllowItemSpecModifiers); } @@ -2084,7 +2084,7 @@ private string GetBuiltInMetadataEscaped(string name) { string value = String.Empty; - if (FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name)) + if (ItemSpecModifiers.IsItemSpecModifier(name)) { value = BuiltInMetadata.GetMetadataValueEscaped(_projectDirectory, _includeBeforeWildcardExpansionEscaped, _includeEscaped, _definingFileEscaped, name, ref _fullPath); } @@ -2185,9 +2185,9 @@ public bool MoveNext() } } - if (_itemSpecModifiersIndex < FileUtilities.ItemSpecModifiers.All.Length) + if (_itemSpecModifiersIndex < ItemSpecModifiers.All.Length) { - Current = FileUtilities.ItemSpecModifiers.All[_itemSpecModifiersIndex]; + Current = ItemSpecModifiers.All[_itemSpecModifiersIndex]; ++_itemSpecModifiersIndex; return true; diff --git a/src/Build/Instance/ProjectMetadataInstance.cs b/src/Build/Instance/ProjectMetadataInstance.cs index da068713afb..548f35ff5fd 100644 --- a/src/Build/Instance/ProjectMetadataInstance.cs +++ b/src/Build/Instance/ProjectMetadataInstance.cs @@ -6,6 +6,7 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable @@ -240,7 +241,7 @@ internal static void VerifyThrowReservedName(string name) // PERF: This sequence of checks is faster than a full HashSet lookup since finding a match is an error case. // Otherwise, many keys would still match to a bucket and begin a string comparison. VerifyThrowReservedNameAllowItemSpecModifiers(name); - foreach (string itemSpecModifier in FileUtilities.ItemSpecModifiers.All) + foreach (string itemSpecModifier in ItemSpecModifiers.All) { if (itemSpecModifier.Length == name.Length && itemSpecModifier[0] == char.ToUpperInvariant(name[0])) { diff --git a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs index 7b080d2b3b5..a33d9f0226d 100644 --- a/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs +++ b/src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs @@ -318,9 +318,7 @@ internal ITask CreateTaskInstance( IBuildComponentHost buildComponentHost, in TaskHostParameters taskIdentityParameters, string projectFile, -#if !NET35 HostServices hostServices, -#endif #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif @@ -385,9 +383,7 @@ internal ITask CreateTaskInstance( #if FEATURE_APPDOMAIN appDomainSetup, #endif -#if !NET35 hostServices, -#endif scheduledNodeId, taskEnvironment: taskEnvironment); return task; diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 62729224986..269362b1c7a 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -146,9 +146,7 @@ internal class TaskHostTask : IGeneratedTask, ICancelableTask, INodePacketFactor /// private bool _useSidecarTaskHost = false; -#if !NET35 private readonly HostServices _hostServices; -#endif /// /// The project file path that requests task execution. @@ -174,9 +172,7 @@ public TaskHostTask( #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif -#if !NET35 HostServices hostServices, -#endif int scheduledNodeId, TaskEnvironment taskEnvironment) { @@ -192,9 +188,7 @@ public TaskHostTask( #if FEATURE_APPDOMAIN _appDomainSetup = appDomainSetup; #endif -#if !NET35 _hostServices = hostServices; -#endif _projectFile = projectFile; _taskHostParameters = taskHostParameters; _useSidecarTaskHost = useSidecarTaskHost; @@ -336,9 +330,7 @@ public bool Execute() (IDictionary)_taskEnvironment.GetEnvironmentVariables(), _buildComponentHost.BuildParameters.Culture, _buildComponentHost.BuildParameters.UICulture, -#if !NET35 _hostServices, -#endif #if FEATURE_APPDOMAIN _appDomainSetup, #endif diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index ccdc34b700f..24d15831948 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -353,7 +353,7 @@ private static void RegisterTasksFromUsingTaskElement // don't want paths from imported projects being interpreted relative to the main project file. try { - assemblyFile = FrameworkFileUtilities.FixFilePath(assemblyFile); + assemblyFile = FileUtilities.FixFilePath(assemblyFile); if (assemblyFile != null && !Path.IsPathRooted(assemblyFile)) { diff --git a/src/Build/Logging/FileLogger.cs b/src/Build/Logging/FileLogger.cs index 60fca6f6868..a2d306a1341 100644 --- a/src/Build/Logging/FileLogger.cs +++ b/src/Build/Logging/FileLogger.cs @@ -198,7 +198,7 @@ private void ApplyFileLoggerParameter(string parameterName, string parameterValu switch (parameterName.ToUpperInvariant()) { case "LOGFILE": - _logFileName = FrameworkFileUtilities.FixFilePath(parameterValue); + _logFileName = FileUtilities.FixFilePath(parameterValue); break; case "APPEND": _append = true; diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 5343eabdc97..3bf8513c8f7 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -1,6 +1,5 @@ - + - @@ -30,7 +29,6 @@ - @@ -657,29 +655,15 @@ Errors\ErrorUtilities.cs - - SharedUtilities\EscapingUtilities.cs - SharedUtilities\VersionUtilities.cs SharedUtilities\EventArgsFormatting.cs - - SharedUtilities\ExceptionHandling.cs - SharedUtilities\FileMatcher.cs - - SharedUtilities\FileUtilities.cs - - - - - SharedUtilities\FileUtilitiesRegex.cs - SharedUtilities\FrameworkLocationHelper.cs diff --git a/src/Build/Utilities/FileSpecMatchTester.cs b/src/Build/Utilities/FileSpecMatchTester.cs index 76b19b3d1a4..8c5afe9c08d 100644 --- a/src/Build/Utilities/FileSpecMatchTester.cs +++ b/src/Build/Utilities/FileSpecMatchTester.cs @@ -132,7 +132,7 @@ private static void CreateRegexOrFilenamePattern(string unescapedFileSpec, strin ? Directory.GetCurrentDirectory() : FileUtilities.GetFullPathNoThrow(absoluteFixedDirPart); - normalizedFixedDirPart = FrameworkFileUtilities.EnsureTrailingSlash(normalizedFixedDirPart); + normalizedFixedDirPart = FileUtilities.EnsureTrailingSlash(normalizedFixedDirPart); var recombinedFileSpec = string.Concat(normalizedFixedDirPart, wildcardDirectoryPart, filenamePart); diff --git a/src/Build/Utilities/RegistryKeyWrapper.cs b/src/Build/Utilities/RegistryKeyWrapper.cs index ac4ecb68421..a14b9b5d94a 100644 --- a/src/Build/Utilities/RegistryKeyWrapper.cs +++ b/src/Build/Utilities/RegistryKeyWrapper.cs @@ -4,7 +4,7 @@ #if FEATURE_WIN32_REGISTRY using System; - +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Win32; using RegistryException = Microsoft.Build.Exceptions.RegistryException; diff --git a/src/Framework.UnitTests/EscapingUtilities_Tests.cs b/src/Framework.UnitTests/EscapingUtilities_Tests.cs new file mode 100644 index 00000000000..3a6ddc09c8a --- /dev/null +++ b/src/Framework.UnitTests/EscapingUtilities_Tests.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Shared; +using Xunit; + +#nullable disable + +namespace Microsoft.Build.Framework.UnitTests; + +public sealed class EscapingUtilities_Tests +{ + /// + /// + [Fact] + public void Unescape() + { + Assert.Equal("", EscapingUtilities.UnescapeAll("")); + Assert.Equal("foo", EscapingUtilities.UnescapeAll("foo")); + Assert.Equal("foo space", EscapingUtilities.UnescapeAll("foo%20space")); + Assert.Equal("foo2;", EscapingUtilities.UnescapeAll("foo2%3B")); + Assert.Equal(";foo3", EscapingUtilities.UnescapeAll("%3bfoo3")); + Assert.Equal(";", EscapingUtilities.UnescapeAll("%3b")); + Assert.Equal(";;;;;", EscapingUtilities.UnescapeAll("%3b%3B;%3b%3B")); + Assert.Equal("%3B", EscapingUtilities.UnescapeAll("%253B")); + Assert.Equal("===%ZZ %%%===", EscapingUtilities.UnescapeAll("===%ZZ%20%%%===")); + Assert.Equal("hello; escaping% how( are) you?", EscapingUtilities.UnescapeAll("hello%3B escaping%25 how%28 are%29 you%3f")); + + Assert.Equal("%*?*%*", EscapingUtilities.UnescapeAll("%25*?*%25*")); + Assert.Equal("%*?*%*", EscapingUtilities.UnescapeAll("%25%2a%3f%2a%25%2a")); + + Assert.Equal("*Star*craft or *War*cr@ft??", EscapingUtilities.UnescapeAll("%2aStar%2Acraft%20or %2aWar%2Acr%40ft%3f%3F")); + } + + /// + /// + [Fact] + public void Escape() + { + Assert.Equal("%2a", EscapingUtilities.Escape("*")); + Assert.Equal("%3f", EscapingUtilities.Escape("?")); + Assert.Equal("#%2a%3f%2a#%2a", EscapingUtilities.Escape("#*?*#*")); + Assert.Equal("%25%2a%3f%2a%25%2a", EscapingUtilities.Escape("%*?*%*")); + } + + /// + /// + [Fact] + public void UnescapeEscape() + { + string text = "*"; + Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); + + text = "?"; + Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); + + text = "#*?*#*"; + Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); + } + + /// + /// + [Fact] + public void EscapeUnescape() + { + string text = "%2a"; + Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); + + text = "%3f"; + Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); + + text = "#%2a%3f%2a#%2a"; + Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); + } + + [Fact] + public void ContainsEscapedWildcards() + { + Assert.False(EscapingUtilities.ContainsEscapedWildcards("NoStarOrQMark")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%%")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%2")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%4")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%3A")); + Assert.False(EscapingUtilities.ContainsEscapedWildcards("%2B")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2a")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2A")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3F")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3f")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%%3f")); + Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3%3f")); + } +} diff --git a/src/Framework.UnitTests/FileUtilities_Tests.cs b/src/Framework.UnitTests/FileUtilities_Tests.cs new file mode 100644 index 00000000000..6e8965ade92 --- /dev/null +++ b/src/Framework.UnitTests/FileUtilities_Tests.cs @@ -0,0 +1,1058 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.UnitTests; +using Shouldly; +using Xunit; + +#nullable disable + +namespace Microsoft.Build.Framework.UnitTests; + +public class FileUtilities_Tests +{ + /// + /// Exercises ItemSpecModifiers.GetItemSpecModifier + /// + [Fact] + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + public void GetItemSpecModifier() + { + TestGetItemSpecModifier(Directory.GetCurrentDirectory()); + TestGetItemSpecModifier(null); + } + + private static void TestGetItemSpecModifier(string currentDirectory) + { + string cache = null; + string modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, ItemSpecModifiers.RecursiveDir, ref cache); + Assert.Equal(String.Empty, modifier); + + cache = null; + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, ItemSpecModifiers.ModifiedTime, ref cache); + Assert.Equal(String.Empty, modifier); + + cache = null; + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, ItemSpecModifiers.RelativeDir, ref cache); + Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier); + + // confirm we get the same thing back the second time + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, ItemSpecModifiers.RelativeDir, ref cache); + Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier); + + cache = null; + string itemSpec = NativeMethodsShared.IsWindows ? @"c:\foo.txt" : "/foo.txt"; + string itemSpecDir = NativeMethodsShared.IsWindows ? @"c:\" : "/"; + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.FullPath, ref cache); + Assert.Equal(itemSpec, modifier); + Assert.Equal(itemSpec, cache); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.RootDir, ref cache); + Assert.Equal(itemSpecDir, modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Filename, ref cache); + Assert.Equal(@"foo", modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Extension, ref cache); + Assert.Equal(@".txt", modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Directory, ref cache); + Assert.Equal(String.Empty, modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, ItemSpecModifiers.Identity, ref cache); + Assert.Equal(itemSpec, modifier); + + string projectPath = NativeMethodsShared.IsWindows ? @"c:\abc\goo.proj" : @"/abc/goo.proj"; + string projectPathDir = NativeMethodsShared.IsWindows ? @"c:\abc\" : @"/abc/"; + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectDirectory, ref cache); + Assert.Equal(projectPathDir, modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectExtension, ref cache); + Assert.Equal(@".proj", modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectFullPath, ref cache); + Assert.Equal(projectPath, modifier); + + modifier = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, ItemSpecModifiers.DefiningProjectName, ref cache); + Assert.Equal(@"goo", modifier); + } + + [Fact] + public void MakeRelativeTests() + { + if (NativeMethodsShared.IsWindows) + { + Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\foo.cpp")); + Assert.Equal(@"def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\foo.cpp")); + Assert.Equal(@"..\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\foo.cpp")); + Assert.Equal(@"..\ttt\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\foo.cpp")); + Assert.Equal(@"e:\abc\def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"e:\abc\def\foo.cpp")); + Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"\\aaa\abc\def", @"\\aaa\abc\def\foo.cpp")); + Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"foo.cpp")); + Assert.Equal(@"\\host\path\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\path\file")); + Assert.Equal(@"\\host\d$\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\d$\file")); + Assert.Equal(@"..\fff\ggg.hh", FileUtilities.MakeRelative(@"c:\foo\bar\..\abc\cde", @"c:\foo\bar\..\abc\fff\ggg.hh")); + + /* Directories */ + Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\")); + Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\")); + Assert.Equal(@"..\ttt\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\def\")); + + /* Directory + File */ + Assert.Equal(@"def", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def")); + Assert.Equal(@"..\..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\ghi")); + Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ghi")); + Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\ghi")); + + /* File + Directory */ + Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc", @"c:\abc\def\")); + Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\")); + Assert.Equal(@"..\ghi\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\ghi\")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\")); + } + else + { + Assert.Equal(@"bar.cpp", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/bar.cpp")); + Assert.Equal(@"def/foo.cpp", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/foo.cpp")); + Assert.Equal(@"../foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/foo.cpp")); + Assert.Equal(@"../ttt/foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/foo.cpp")); + Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"/abc/def", @"foo.cpp")); + Assert.Equal(@"../fff/ggg.hh", FileUtilities.MakeRelative(@"/foo/bar/../abc/cde", @"/foo/bar/../abc/fff/ggg.hh")); + + /* Directories */ + Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/")); + Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/")); + Assert.Equal(@"../ttt/", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/def/")); + + /* Directory + File */ + Assert.Equal(@"def", FileUtilities.MakeRelative(@"/abc/", @"/abc/def")); + Assert.Equal(@"../../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/ghi")); + Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ghi")); + Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/ghi")); + + /* File + Directory */ + Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc", @"/abc/def/")); + Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/")); + Assert.Equal(@"../ghi/", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/ghi/")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/")); + } + } + + /// + /// Exercises ItemSpecModifiers.GetItemSpecModifier on a bad path. + /// + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void GetItemSpecModifierOnBadPath() + { + Assert.Throws(() => + { + TestGetItemSpecModifierOnBadPath(Directory.GetCurrentDirectory()); + }); + } + /// + /// Exercises ItemSpecModifiers.GetItemSpecModifier on a bad path. + /// + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void GetItemSpecModifierOnBadPath2() + { + Assert.Throws(() => + { + TestGetItemSpecModifierOnBadPath(null); + }); + } + + private static void TestGetItemSpecModifierOnBadPath(string currentDirectory) + { + try + { + string cache = null; + ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"http://www.microsoft.com", String.Empty, ItemSpecModifiers.RootDir, ref cache); + } + catch (Exception e) + { + // so I can see the exception message in NUnit's "Standard Out" window + Console.WriteLine(e.Message); + throw; + } + } + + [Fact] + public void GetFileInfoNoThrowBasic() + { + string file = null; + try + { + file = FileUtilities.GetTemporaryFile(); + FileInfo info = FileUtilities.GetFileInfoNoThrow(file); + Assert.Equal(info.LastWriteTime, new FileInfo(file).LastWriteTime); + } + finally + { + if (file != null) + { + File.Delete(file); + } + } + } + + [Fact] + public void GetFileInfoNoThrowNonexistent() + { + FileInfo info = FileUtilities.GetFileInfoNoThrow("this_file_is_nonexistent"); + Assert.Null(info); + } + + /// + /// Exercises FrameworkFileUtilities.EndsWithSlash + /// + [Fact] + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + public void EndsWithSlash() + { + Assert.True(FileUtilities.EndsWithSlash(@"C:\foo\")); + Assert.True(FileUtilities.EndsWithSlash(@"C:\")); + Assert.True(FileUtilities.EndsWithSlash(@"\")); + + Assert.True(FileUtilities.EndsWithSlash(@"http://www.microsoft.com/")); + Assert.True(FileUtilities.EndsWithSlash(@"//server/share/")); + Assert.True(FileUtilities.EndsWithSlash(@"/")); + + Assert.False(FileUtilities.EndsWithSlash(@"C:\foo")); + Assert.False(FileUtilities.EndsWithSlash(@"C:")); + Assert.False(FileUtilities.EndsWithSlash(@"foo")); + + // confirm that empty string doesn't barf + Assert.False(FileUtilities.EndsWithSlash(String.Empty)); + } + + /// + /// Exercises FileUtilities.GetDirectory + /// + [Fact] + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + public void GetDirectoryWithTrailingSlash() + { + Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\" : "/")); + Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\foo" : "/foo")); + Assert.Equal(NativeMethodsShared.IsWindows ? @"c:" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:" : "/")); + Assert.Equal(FileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\")); + Assert.Equal(FileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\foo")); + Assert.Equal(FileUtilities.FixFilePath(@"..\"), FileUtilities.GetDirectory(@"..\foo")); + Assert.Equal(FileUtilities.FixFilePath(@"\foo\"), FileUtilities.GetDirectory(@"\foo\")); + Assert.Equal(FileUtilities.FixFilePath(@"\\server\share"), FileUtilities.GetDirectory(@"\\server\share")); + Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\")); + Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\file")); + Assert.Equal(FileUtilities.FixFilePath(@"\\server\share\directory\"), FileUtilities.GetDirectory(@"\\server\share\directory\")); + Assert.Equal(FileUtilities.FixFilePath(@"foo\"), FileUtilities.GetDirectory(@"foo\bar")); + Assert.Equal(FileUtilities.FixFilePath(@"\foo\bar\"), FileUtilities.GetDirectory(@"\foo\bar\")); + Assert.Equal(String.Empty, FileUtilities.GetDirectory("foo")); + } + + [Theory] + [InlineData("foo.txt", new[] { ".txt" })] + [InlineData("foo.txt", new[] { ".TXT" })] + [InlineData("foo.txt", new[] { ".EXE", ".TXT" })] + public void HasExtension_WhenFileNameHasExtension_ReturnsTrue(string fileName, string[] allowedExtensions) + { + var result = FileUtilities.HasExtension(fileName, allowedExtensions); + + if (!FileUtilities.IsFileSystemCaseSensitive || allowedExtensions.Any(extension => fileName.Contains(extension))) + { + result.ShouldBeTrue(); + } + } + + [Theory] + [InlineData("foo.txt", new[] { ".DLL" })] + [InlineData("foo.txt", new[] { ".EXE", ".DLL" })] + [InlineData("foo.exec", new[] { ".exe", })] + [InlineData("foo.exe", new[] { ".exec", })] + [InlineData("foo", new[] { ".exe", })] + [InlineData("", new[] { ".exe" })] + [InlineData(null, new[] { ".exe" })] + public void HasExtension_WhenFileNameDoesNotHaveExtension_ReturnsFalse(string fileName, string[] allowedExtensions) + { + var result = FileUtilities.HasExtension(fileName, allowedExtensions); + + Assert.False(result); + } + + [WindowsFullFrameworkOnlyFact] + public void HasExtension_WhenInvalidFileName_ThrowsArgumentException() + { + Assert.Throws(() => + { + FileUtilities.HasExtension("|/", new[] { ".exe" }); + }); + } + + [Fact] + public void HasExtension_UsesOrdinalIgnoreCase() + { + var currentCulture = Thread.CurrentThread.CurrentCulture; + try + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR"); // Turkish + + var result = FileUtilities.HasExtension("foo.ini", new string[] { ".INI" }); + + result.ShouldBe(!FileUtilities.IsFileSystemCaseSensitive); + } + finally + { + Thread.CurrentThread.CurrentCulture = currentCulture; + } + } + + /// + /// Exercises FrameworkFileUtilities.EnsureTrailingSlash + /// + [Fact] + public void EnsureTrailingSlash() + { + // Doesn't have a trailing slash to start with. + Assert.Equal(FileUtilities.FixFilePath(@"foo\bar\"), FileUtilities.EnsureTrailingSlash(@"foo\bar")); // "test 1" + Assert.Equal(FileUtilities.FixFilePath(@"foo/bar\"), FileUtilities.EnsureTrailingSlash(@"foo/bar")); // "test 2" + + // Already has a trailing slash to start with. + Assert.Equal(FileUtilities.FixFilePath(@"foo/bar/"), FileUtilities.EnsureTrailingSlash(@"foo/bar/")); // test 3" + Assert.Equal(FileUtilities.FixFilePath(@"foo\bar\"), FileUtilities.EnsureTrailingSlash(@"foo\bar\")); // test 4" + Assert.Equal(FileUtilities.FixFilePath(@"foo/bar\"), FileUtilities.EnsureTrailingSlash(@"foo/bar\")); // test 5" + Assert.Equal(FileUtilities.FixFilePath(@"foo\bar/"), FileUtilities.EnsureTrailingSlash(@"foo\bar/")); // "test 5" + } + + /// + /// Exercises ItemSpecModifiers.IsItemSpecModifier + /// + [Fact] + public void IsItemSpecModifier() + { + // Positive matches using exact case. + Assert.True(ItemSpecModifiers.IsItemSpecModifier("FullPath")); // "test 1" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("RootDir")); // "test 2" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("Filename")); // "test 3" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("Extension")); // "test 4" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("RelativeDir")); // "test 5" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("Directory")); // "test 6" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("RecursiveDir")); // "test 7" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("Identity")); // "test 8" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("ModifiedTime")); // "test 9" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("CreatedTime")); // "test 10" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("AccessedTime")); // "test 11" + + // Positive matches using different case. + Assert.True(ItemSpecModifiers.IsItemSpecModifier("fullPath")); // "test 21" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("rootDir")); // "test 22" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("filename")); // "test 23" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("extension")); // "test 24" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("relativeDir")); // "test 25" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("directory")); // "test 26" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("recursiveDir")); // "test 27" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("identity")); // "test 28" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("modifiedTime")); // "test 29" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("createdTime")); // "test 30" + Assert.True(ItemSpecModifiers.IsItemSpecModifier("accessedTime")); // "test 31" + + // Negative tests to get maximum code coverage inside the many different branches + // of ItemSpecModifiers.IsItemSpecModifier. + Assert.False(ItemSpecModifiers.IsItemSpecModifier("rootxxx")); // "test 41" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Rootxxx")); // "test 42" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxx")); // "test 43" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier("filexxxx")); // "test 44" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Filexxxx")); // "test 45" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("idenxxxx")); // "test 46" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Idenxxxx")); // "test 47" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxx")); // "test 48" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier("extenxxxx")); // "test 49" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Extenxxxx")); // "test 50" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("direcxxxx")); // "test 51" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Direcxxxx")); // "test 52" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxx")); // "test 53" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxx")); // "test 54" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier("relativexxx")); // "test 55" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Relativexxx")); // "test 56" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("createdxxxx")); // "test 57" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Createdxxxx")); // "test 58" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxx")); // "test 59" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier("recursivexxx")); // "test 60" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Recursivexxx")); // "test 61" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("accessedxxxx")); // "test 62" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Accessedxxxx")); // "test 63" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("modifiedxxxx")); // "test 64" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("Modifiedxxxx")); // "test 65" + Assert.False(ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxxx")); // "test 66" + + Assert.False(ItemSpecModifiers.IsItemSpecModifier(null)); // "test 67" + } + + [Fact] + public void CheckDerivableItemSpecModifiers() + { + Assert.True(ItemSpecModifiers.IsDerivableItemSpecModifier("Filename")); + Assert.False(ItemSpecModifiers.IsDerivableItemSpecModifier("RecursiveDir")); + Assert.False(ItemSpecModifiers.IsDerivableItemSpecModifier("recursivedir")); + } + + [WindowsOnlyFact] + public void NormalizePathThatFitsIntoMaxPath() + { + string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; + string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; + string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; + + Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath))); + } + + [LongPathSupportDisabledFact(fullFrameworkOnly: true, additionalMessage: "https://github.com/dotnet/msbuild/issues/4363")] + public void NormalizePathThatDoesntFitIntoMaxPath() + { + Assert.Throws(() => + { + string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; + string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; + + // This path ends up over 420 characters long + string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; + + Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath))); + }); + } + + [WindowsOnlyFact] + public void GetItemSpecModifierRootDirThatFitsIntoMaxPath() + { + string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; + string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; + string cache = fullPath; + + Assert.Equal(@"c:\", ItemSpecModifiers.GetItemSpecModifier(currentDirectory, fullPath, String.Empty, ItemSpecModifiers.RootDir, ref cache)); + } + + [Fact] + public void NormalizePathNull() + { + Assert.Throws(() => + { + Assert.Null(FileUtilities.NormalizePath(null, null)); + }); + } + + [Fact] + public void NormalizePathEmpty() + { + Assert.Throws(() => + { + Assert.Null(FileUtilities.NormalizePath(String.Empty)); + }); + } + + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void NormalizePathBadUNC1() + { + Assert.Throws(() => + { + Assert.Null(FileUtilities.NormalizePath(@"\\")); + }); + } + + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void NormalizePathBadUNC2() + { + Assert.Throws(() => + { + Assert.Null(FileUtilities.NormalizePath(@"\\XXX\")); + }); + } + + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void NormalizePathBadUNC3() + { + Assert.Throws(() => + { + Assert.Equal(@"\\localhost", FileUtilities.NormalizePath(@"\\localhost")); + }); + } + + [WindowsOnlyFact] + public void NormalizePathGoodUNC() + { + Assert.Equal(@"\\localhost\share", FileUtilities.NormalizePath(@"\\localhost\share")); + } + + [WindowsOnlyFact] + public void NormalizePathTooLongWithDots() + { + string longPart = new string('x', 300); + Assert.Equal(@"c:\abc\def", FileUtilities.NormalizePath(@"c:\abc\" + longPart + @"\..\def")); + } + + [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] + public void NormalizePathInvalid() + { + string filePath = @"c:\aardvark\|||"; + + Assert.Throws(() => + { + FileUtilities.NormalizePath(filePath); + }); + } + + [WindowsOnlyFact] + public void CannotNormalizePathWithNewLineAndSpace() + { + string filePath = "\r\n C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll\r\n "; + +#if FEATURE_LEGACY_GETFULLPATH + Assert.Throws(() => FileUtilities.NormalizePath(filePath)); +#else + Assert.NotEqual("C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll", FileUtilities.NormalizePath(filePath)); +#endif + } + + [Fact] + public void FileOrDirectoryExistsNoThrow() + { + var isWindows = NativeMethodsShared.IsWindows; + + Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow("||")); + Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\doesnot_exist" : "/doesnot_exist")); + Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\" : "/")); + Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(Path.GetTempPath())); + + string path = null; + + try + { + path = FileUtilities.GetTemporaryFile(); + Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(path)); + } + finally + { + File.Delete(path); + } + } + +#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY + // These tests will need to be redesigned for Linux + + [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] + public void FileOrDirectoryExistsNoThrowTooLongWithDots() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath)); + Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X'))); + } + + [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] + public void FileOrDirectoryExistsNoThrowTooLongWithDotsRelative() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + + string currentDirectory = Directory.GetCurrentDirectory(); + + try + { + Directory.SetCurrentDirectory(Environment.SystemDirectory); + + Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath)); + Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X'))); + } + finally + { + Directory.SetCurrentDirectory(currentDirectory); + } + } + + [Fact] + public void DirectoryExistsNoThrowTooLongWithDots() + { + string path = Path.Combine(Environment.SystemDirectory, "..", "..", "..") + Path.DirectorySeparatorChar; + if (NativeMethodsShared.IsWindows) + { + path += Environment.SystemDirectory.Substring(3); + } + + int length = path.Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = Path.Combine(new[] { Environment.SystemDirectory, longPart, "..", "..", ".." }) + + Path.DirectorySeparatorChar; + if (NativeMethodsShared.IsWindows) + { + path += Environment.SystemDirectory.Substring(3); + } + + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + Assert.True(FileUtilities.DirectoryExistsNoThrow(inputPath)); + } + + [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] + public void DirectoryExistsNoThrowTooLongWithDotsRelative() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\..\windows\system32" exists + + string currentDirectory = Directory.GetCurrentDirectory(); + + try + { + Directory.SetCurrentDirectory(Environment.SystemDirectory); + + FileUtilities.DirectoryExistsNoThrow(inputPath).ShouldBeTrue(); + FileUtilities.DirectoryExistsNoThrow(inputPath.Replace('\\', 'X')).ShouldBeFalse(); + } + finally + { + Directory.SetCurrentDirectory(currentDirectory); + } + } + + public static bool RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241() + { + // Run these tests only when we're not on Windows + return !NativeMethodsShared.IsWindows || + // OR we're on Windows and long paths aren't enabled + // https://github.com/dotnet/msbuild/issues/4241 + NativeMethodsShared.IsMaxPathLegacyWindows(); + } + + [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] + public void FileExistsNoThrowTooLongWithDots() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; + Console.WriteLine(inputPath.Length); + Console.WriteLine(inputPath); + + // "c:\windows\system32\\..\..\windows\system32" exists + Assert.True(FileUtilities.FileExistsNoThrow(inputPath)); + } + + [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] + public void FileExistsNoThrowTooLongWithDotsRelative() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + + string currentDirectory = Directory.GetCurrentDirectory(); + + try + { + Directory.SetCurrentDirectory(Environment.SystemDirectory); + + Assert.True(FileUtilities.FileExistsNoThrow(inputPath)); + Assert.False(FileUtilities.FileExistsNoThrow(inputPath.Replace('\\', 'X'))); + } + finally + { + Directory.SetCurrentDirectory(currentDirectory); + } + } + + [Fact] + public void GetFileInfoNoThrowTooLongWithDots() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null); + Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null); + } + + [Fact] + public void GetFileInfoNoThrowTooLongWithDotsRelative() + { + int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; + + string longPart = new string('x', 260 - length); // We want the shortest that is > max path. + + string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; + Console.WriteLine(inputPath.Length); + + // "c:\windows\system32\\..\..\windows\system32" exists + + string currentDirectory = Directory.GetCurrentDirectory(); + + try + { + Directory.SetCurrentDirectory(Environment.SystemDirectory); + + Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null); + Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null); + } + finally + { + Directory.SetCurrentDirectory(currentDirectory); + } + } +#endif + + /// + /// Simple test, neither the base file nor retry files exist + /// + [Fact] + public void GenerateTempFileNameSimple() + { + string path = null; + + try + { + path = FileUtilities.GetTemporaryFile(); + + Assert.EndsWith(".tmp", path); + Assert.True(File.Exists(path)); + Assert.StartsWith(Path.GetTempPath(), path); + } + finally + { + File.Delete(path); + } + } + + /// + /// Choose an extension + /// + [Fact] + public void GenerateTempFileNameWithExtension() + { + string path = null; + + try + { + path = FileUtilities.GetTemporaryFile(".bat"); + + Assert.EndsWith(".bat", path); + Assert.True(File.Exists(path)); + Assert.StartsWith(Path.GetTempPath(), path); + } + finally + { + File.Delete(path); + } + } + + /// + /// Choose a (missing) directory and extension + /// + [Fact] + public void GenerateTempFileNameWithDirectoryAndExtension() + { + string path = null; + string directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "subfolder"); + + try + { + path = FileUtilities.GetTemporaryFile(directory, null, ".bat"); + + Assert.EndsWith(".bat", path); + Assert.True(File.Exists(path)); + Assert.StartsWith(directory, path); + } + finally + { + File.Delete(path); + FileUtilities.DeleteWithoutTrailingBackslash(directory); + } + } + + /// + /// Extension without a period + /// + [Fact] + public void GenerateTempFileNameWithExtensionNoPeriod() + { + string path = null; + + try + { + path = FileUtilities.GetTemporaryFile("bat"); + + Assert.EndsWith(".bat", path); + Assert.True(File.Exists(path)); + Assert.StartsWith(Path.GetTempPath(), path); + } + finally + { + File.Delete(path); + } + } + + /// + /// Extension is invalid + /// + [Fact] + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + public void GenerateTempBatchFileWithBadExtension() + { + Assert.Throws(() => + { + FileUtilities.GetTemporaryFile("|"); + }); + } + + /// + /// Directory is invalid + /// + [Fact] + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + public void GenerateTempBatchFileWithBadDirectory() + { + Assert.Throws(() => + { + FileUtilities.GetTemporaryFile("|", null, ".tmp"); + }); + } + + [UnixOnlyFact] + public void AbsolutePathLooksLikeUnixPathOnUnix() + { + var secondSlash = SystemSpecificAbsolutePath.Substring(1).IndexOf(Path.DirectorySeparatorChar) + 1; + var rootLevelPath = SystemSpecificAbsolutePath.Substring(0, secondSlash); + + Assert.True(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath)); + Assert.True(FileUtilities.LooksLikeUnixFilePath(rootLevelPath)); + } + + [WindowsOnlyFact] + public void PathDoesNotLookLikeUnixPathOnWindows() + { + Assert.False(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath)); + Assert.False(FileUtilities.LooksLikeUnixFilePath("/path/that/looks/unixy")); + Assert.False(FileUtilities.LooksLikeUnixFilePath("/root")); + } + + [UnixOnlyFact] + public void RelativePathLooksLikeUnixPathOnUnixWithBaseDirectory() + { + string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty); + string oldCWD = Directory.GetCurrentDirectory(); + + try + { + // /first + string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath)); + string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath))); + + Directory.SetCurrentDirectory(tmpDirectory); + + // We are in and second is not under that, so this will be false + Assert.False(FileUtilities.LooksLikeUnixFilePath("second/file.txt")); + + // .. but if we have baseDirectory:firstDirectory, then it will be true + Assert.True(FileUtilities.LooksLikeUnixFilePath("second/file.txt", firstDirectory)); + } + finally + { + if (filePath != null) + { + File.Delete(filePath); + } + Directory.SetCurrentDirectory(oldCWD); + } + } + + [UnixOnlyFact] + public void RelativePathMaybeAdjustFilePathWithBaseDirectory() + { + // /first/second/file.txt + string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty); + string oldCWD = Directory.GetCurrentDirectory(); + + try + { + // /first + string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath)); + string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath))); + + Directory.SetCurrentDirectory(tmpDirectory); + + // We are in and second is not under that, so this won't convert + Assert.Equal("second\\file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt")); + + // .. but if we have baseDirectory:firstDirectory, then it will + Assert.Equal("second/file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt", firstDirectory)); + } + finally + { + if (filePath != null) + { + File.Delete(filePath); + } + Directory.SetCurrentDirectory(oldCWD); + } + } + + private static string SystemSpecificAbsolutePath => BuildEnvironmentHelper.ExecutingAssemblyPath; + + + [Fact] + public void GetFolderAboveTest() + { + string root = NativeMethodsShared.IsWindows ? @"c:\" : "/"; + string path = Path.Combine(root, "1", "2", "3", "4", "5"); + + Assert.Equal(Path.Combine(root, "1", "2", "3", "4", "5"), FileUtilities.GetFolderAbove(path, 0)); + Assert.Equal(Path.Combine(root, "1", "2", "3", "4"), FileUtilities.GetFolderAbove(path)); + Assert.Equal(Path.Combine(root, "1", "2", "3"), FileUtilities.GetFolderAbove(path, 2)); + Assert.Equal(Path.Combine(root, "1", "2"), FileUtilities.GetFolderAbove(path, 3)); + Assert.Equal(Path.Combine(root, "1"), FileUtilities.GetFolderAbove(path, 4)); + Assert.Equal(root, FileUtilities.GetFolderAbove(path, 5)); + Assert.Equal(root, FileUtilities.GetFolderAbove(path, 99)); + + Assert.Equal(root, FileUtilities.GetFolderAbove(root, 99)); + } + + [Fact] + public void CombinePathsTest() + { + // These tests run in .NET 4+, so we can cheat + var root = @"c:\"; + + Assert.Equal( + Path.Combine(root, "path1"), + FileUtilities.CombinePaths(root, "path1")); + + Assert.Equal( + Path.Combine(root, "path1", "path2", "file.txt"), + FileUtilities.CombinePaths(root, "path1", "path2", "file.txt")); + } + + [Theory] + [InlineData(@"c:\a\.\b", true)] + [InlineData(@"c:\a\..\b", true)] + [InlineData(@"c:\a\..", true)] + [InlineData(@"c:\a\.", true)] + [InlineData(@".\a", true)] + [InlineData(@"..\b", true)] + [InlineData(@"..", true)] + [InlineData(@".", true)] + [InlineData(@"..\", true)] + [InlineData(@".\", true)] + [InlineData(@"\..", true)] + [InlineData(@"\.", true)] + [InlineData(@"..\..\a", true)] + [InlineData(@"..\..\..\a", true)] + [InlineData(@"b..\", false)] + [InlineData(@"b.\", false)] + [InlineData(@"\b..", false)] + [InlineData(@"\b.", false)] + [InlineData(@"\b..\", false)] + [InlineData(@"\b.\", false)] + [InlineData(@"...", false)] + [InlineData(@"....", false)] + public void ContainsRelativeSegmentsTest(string path, bool expectedResult) + { + FileUtilities.ContainsRelativePathSegments(path).ShouldBe(expectedResult); + } + + [Theory] + [InlineData("a/b/c/d", 0, "")] + [InlineData("a/b/c/d", 1, "d")] + [InlineData("a/b/c/d", 2, "c/d")] + [InlineData("a/b/c/d", 3, "b/c/d")] + [InlineData("a/b/c/d", 4, "a/b/c/d")] + [InlineData("a/b/c/d", 5, "a/b/c/d")] + [InlineData(@"a\/\/\//b/\/\/\//c//\/\/\/d/\//\/\/", 2, "c/d")] + public static void TestTruncatePathToTrailingSegments(string path, int trailingSegments, string expectedTruncatedPath) + { + expectedTruncatedPath = expectedTruncatedPath.Replace('/', Path.DirectorySeparatorChar); + + FileUtilities.TruncatePathToTrailingSegments(path, trailingSegments).ShouldBe(expectedTruncatedPath); + } + + /// + /// Exercises FileUtilities.EnsureSingleQuotes + /// + [Theory] + [InlineData(null, null)] // Null test + [InlineData("", "")] // Empty string test + [InlineData(@" ", @"' '")] // One character which is a space + [InlineData(@"'", @"'''")] // One character which is a single quote + [InlineData(@"""", @"'""'")] // One character which is a double quote + [InlineData(@"example", @"'example'")] // Unquoted string + [InlineData(@"'example'", @"'example'")] // Single quoted string + [InlineData(@"""example""", @"'example'")] // Double quoted string + [InlineData(@"'example""", @"''example""'")] // Mixed Quotes - Leading Single + [InlineData(@"""example'", @"'""example''")] // Mixed Quotes - Leading Double + [InlineData(@"ex""am'ple", @"'ex""am'ple'")] // Interior Quotes + public void EnsureSingleQuotesTest(string path, string expectedResult) + { + FileUtilities.EnsureSingleQuotes(path).ShouldBe(expectedResult); + } + + /// + /// Exercises FileUtilities.EnsureDoubleQuotes + /// + [Theory] + [InlineData(null, null)] // Null test + [InlineData("", "")] // Empty string test + [InlineData(@" ", @""" """)] // One character which is a space + [InlineData(@"'", @"""'""")] // One character which is a single quote + [InlineData(@"""", @"""""""")] // One character which is a double quote + [InlineData(@"example", @"""example""")] // Unquoted string + [InlineData(@"'example'", @"""example""")] // Single quoted string + [InlineData(@"""example""", @"""example""")] // Double quoted string + [InlineData(@"'example""", @"""'example""""")] // Mixed Quotes - Leading Single + [InlineData(@"""example'", @"""""example'""")] // Mixed Quotes - Leading Double + [InlineData(@"ex""am'ple", @"""ex""am'ple""")] // Interior Quotes + public void EnsureDoubleQuotesTest(string path, string expectedResult) + { + FileUtilities.EnsureDoubleQuotes(path).ShouldBe(expectedResult); + } +} diff --git a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj index d31ef573b24..878a0e7d8c7 100644 --- a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj +++ b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj @@ -1,4 +1,4 @@ - + $(RuntimeOutputTargetFrameworks) @@ -19,7 +19,6 @@ - @@ -28,9 +27,7 @@ - - diff --git a/src/Framework/AssemblyLoadBuildEventArgs.cs b/src/Framework/AssemblyLoadBuildEventArgs.cs index 767772f5fc8..7ce71b7d960 100644 --- a/src/Framework/AssemblyLoadBuildEventArgs.cs +++ b/src/Framework/AssemblyLoadBuildEventArgs.cs @@ -5,7 +5,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/AssemblyUtilities.cs b/src/Framework/AssemblyUtilities.cs index 6b21b7d22eb..95246ed5b49 100644 --- a/src/Framework/AssemblyUtilities.cs +++ b/src/Framework/AssemblyUtilities.cs @@ -30,12 +30,8 @@ internal static class AssemblyUtilities private static Lazy s_validCultures = new Lazy(() => GetValidCultures(), true); #endif -#if !CLR2COMPATIBILITY private static Lazy s_entryAssembly = new Lazy(() => GetEntryAssembly()); public static Assembly EntryAssembly => s_entryAssembly.Value; -#else - public static Assembly EntryAssembly = GetEntryAssembly(); -#endif public static string GetAssemblyLocation(Assembly assembly) { @@ -55,22 +51,8 @@ public static string GetAssemblyLocation(Assembly assembly) #endif } -#if CLR2COMPATIBILITY - /// - /// Shim for the lack of in .NET 3.5. - /// - public static Type GetTypeInfo(this Type t) - { - return t; - } -#endif - public static AssemblyName CloneIfPossible(this AssemblyName assemblyNameToClone) { -#if CLR2COMPATIBILITY - return (AssemblyName)assemblyNameToClone.Clone(); -#else - // NOTE: In large projects, this is called a lot. Avoid calling AssemblyName.Clone // because it clones the Version property (which is immutable) and the PublicKey property // and the PublicKeyToken property. @@ -98,8 +80,6 @@ public static AssemblyName CloneIfPossible(this AssemblyName assemblyNameToClone #endif return name; -#endif - } #if !FEATURE_CULTUREINFO_GETCULTURES diff --git a/src/Framework/BinaryReaderExtensions.cs b/src/Framework/BinaryReaderExtensions.cs new file mode 100644 index 00000000000..8cc81b03988 --- /dev/null +++ b/src/Framework/BinaryReaderExtensions.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.Build.Framework; + +internal static class BinaryReaderExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? ReadOptionalString(this BinaryReader reader) + { + return reader.ReadByte() == 0 ? null : reader.ReadString(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int? ReadOptionalInt32(this BinaryReader reader) + { + return reader.ReadByte() == 0 ? null : reader.ReadInt32(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Read7BitEncodedInt(this BinaryReader reader) + { + // Read out an Int32 7 bits at a time. The high bit + // of the byte when on means to continue reading more bytes. + int count = 0; + int shift = 0; + byte b; + do + { + // Check for a corrupted stream. Read a max of 5 bytes. + // In a future version, add a DataFormatException. + if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 + { + throw new FormatException(); + } + + // ReadByte handles end of stream cases for us. + b = reader.ReadByte(); + count |= (b & 0x7F) << shift; + shift += 7; + } while ((b & 0x80) != 0); + return count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime ReadTimestamp(this BinaryReader reader) + { + long timestampTicks = reader.ReadInt64(); + DateTimeKind kind = (DateTimeKind)reader.ReadInt32(); + var timestamp = new DateTime(timestampTicks, kind); + return timestamp; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BuildEventContext? ReadOptionalBuildEventContext(this BinaryReader reader) + { + if (reader.ReadByte() == 0) + { + return null; + } + + return reader.ReadBuildEventContext(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BuildEventContext ReadBuildEventContext(this BinaryReader reader) + { + int nodeId = reader.ReadInt32(); + int projectContextId = reader.ReadInt32(); + int targetId = reader.ReadInt32(); + int taskId = reader.ReadInt32(); + int submissionId = reader.ReadInt32(); + int projectInstanceId = reader.ReadInt32(); + int evaluationId = reader.ReadInt32(); + + var buildEventContext = new BuildEventContext(submissionId, nodeId, evaluationId, projectInstanceId, projectContextId, targetId, taskId); + return buildEventContext; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Guid ReadGuid(this BinaryReader reader) + { + return new Guid(reader.ReadBytes(sizeof(Guid))); + } + + public static void ReadExtendedBuildEventData(this BinaryReader reader, IExtendedBuildEventArgs data) + { + data.ExtendedType = reader.ReadString(); + data.ExtendedData = reader.ReadOptionalString(); + + bool haveMetadata = reader.ReadBoolean(); + if (haveMetadata) + { + data.ExtendedMetadata = new Dictionary(); + + int count = reader.Read7BitEncodedInt(); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + string? value = reader.ReadOptionalString(); + + data.ExtendedMetadata.Add(key, value); + } + } + else + { + data.ExtendedMetadata = null; + } + } + + public static Dictionary ReadDurationDictionary(this BinaryReader reader) + { + int count = reader.Read7BitEncodedInt(); + var durations = new Dictionary(count); + for (int i = 0; i < count; i++) + { + string key = reader.ReadString(); + TimeSpan value = TimeSpan.FromTicks(reader.ReadInt64()); + + durations.Add(key, value); + } + + return durations; + } +} diff --git a/src/Framework/BinaryTranslator.cs b/src/Framework/BinaryTranslator.cs index b42cbddd95e..518b31ab30b 100644 --- a/src/Framework/BinaryTranslator.cs +++ b/src/Framework/BinaryTranslator.cs @@ -475,11 +475,6 @@ public void Translate(ref TimeSpan value) value = new System.TimeSpan(ticks); } - // MSBuildTaskHost is based on CLR 3.5, which does not have the 6-parameter constructor for BuildEventContext. - // However, it also does not ever need to translate BuildEventContexts, so it should be perfectly safe to - // compile this method out of that assembly. -#if !CLR2COMPATIBILITY - /// /// Translates a BuildEventContext /// @@ -499,7 +494,6 @@ public void Translate(ref BuildEventContext value) _reader.ReadInt32(), _reader.ReadInt32()); } -#endif /// /// Translates a CultureInfo @@ -509,39 +503,9 @@ public void TranslateCulture(ref CultureInfo value) { string cultureName = _reader.ReadString(); -#if CLR2COMPATIBILITY - // It may be that some culture codes are accepted on later .net framework versions - // but not on the older 3.5 or 2.0. Fallbacks are required in this case to prevent - // exceptions - value = LoadCultureWithFallback(cultureName); -#else value = new CultureInfo(cultureName); -#endif - } - -#if CLR2COMPATIBILITY - private static CultureInfo LoadCultureWithFallback(string cultureName) - { - CultureInfo cultureInfo; - - return TryLoadCulture(cultureName, out cultureInfo) ? cultureInfo : CultureInfo.CurrentCulture; } - private static bool TryLoadCulture(string cultureName, out CultureInfo cultureInfo) - { - try - { - cultureInfo = new CultureInfo(cultureName); - return true; - } - catch - { - cultureInfo = null; - return false; - } - } -#endif - /// /// Translates an enumeration. /// @@ -1337,11 +1301,6 @@ public void Translate(ref TimeSpan value) _writer.Write(value.Ticks); } - // MSBuildTaskHost is based on CLR 3.5, which does not have the 6-parameter constructor for BuildEventContext. - // However, it also does not ever need to translate BuildEventContexts, so it should be perfectly safe to - // compile this method out of that assembly. -#if !CLR2COMPATIBILITY - /// /// Translates a BuildEventContext /// @@ -1360,7 +1319,6 @@ public void Translate(ref BuildEventContext value) _writer.Write(value.TargetId); _writer.Write(value.TaskId); } -#endif /// /// Translates a CultureInfo diff --git a/src/Framework/BinaryWriterExtensions.cs b/src/Framework/BinaryWriterExtensions.cs new file mode 100644 index 00000000000..fe7ac5f7b59 --- /dev/null +++ b/src/Framework/BinaryWriterExtensions.cs @@ -0,0 +1,129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; + +namespace Microsoft.Build.Framework; + +internal static class BinaryWriterExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteOptionalString(this BinaryWriter writer, string? value) + { + if (value == null) + { + writer.Write((byte)0); + } + else + { + writer.Write((byte)1); + writer.Write(value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteOptionalInt32(this BinaryWriter writer, int? value) + { + if (value == null) + { + writer.Write((byte)0); + } + else + { + writer.Write((byte)1); + writer.Write(value.Value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteTimestamp(this BinaryWriter writer, DateTime timestamp) + { + writer.Write(timestamp.Ticks); + writer.Write((Int32)timestamp.Kind); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Write7BitEncodedInt(this BinaryWriter writer, int value) + { + // Write out an int 7 bits at a time. The high bit of the byte, + // when on, tells reader to continue reading more bytes. + uint v = (uint)value; // support negative numbers + while (v >= 0x80) + { + writer.Write((byte)(v | 0x80)); + v >>= 7; + } + + writer.Write((byte)v); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteOptionalBuildEventContext(this BinaryWriter writer, BuildEventContext? context) + { + if (context == null) + { + writer.Write((byte)0); + } + else + { + writer.Write((byte)1); + writer.WriteBuildEventContext(context); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteBuildEventContext(this BinaryWriter writer, BuildEventContext context) + { + writer.Write(context.NodeId); + writer.Write(context.ProjectContextId); + writer.Write(context.TargetId); + writer.Write(context.TaskId); + writer.Write(context.SubmissionId); + writer.Write(context.ProjectInstanceId); + writer.Write(context.EvaluationId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteGuid(this BinaryWriter writer, Guid value) + { + Guid val = value; + unsafe + { + byte* ptr = (byte*)&val; + for (int i = 0; i < sizeof(Guid); i++, ptr++) + { + writer.Write(*ptr); + } + } + } + + public static void WriteExtendedBuildEventData(this BinaryWriter writer, IExtendedBuildEventArgs data) + { + writer.Write(data.ExtendedType); + writer.WriteOptionalString(data.ExtendedData); + + writer.Write(data.ExtendedMetadata != null); + if (data.ExtendedMetadata != null) + { + writer.Write7BitEncodedInt(data.ExtendedMetadata.Count); + foreach (KeyValuePair kvp in data.ExtendedMetadata) + { + writer.Write(kvp.Key); + writer.WriteOptionalString(kvp.Value); + } + } + } + + public static void WriteDurationsDictionary(this BinaryWriter writer, Dictionary durations) + { + writer.Write7BitEncodedInt(durations.Count); + foreach (KeyValuePair kvp in durations) + { + writer.Write(kvp.Key); + writer.Write(kvp.Value.Ticks); + } + } +} diff --git a/src/Framework/BuildCheck/BuildCheckEventArgs.cs b/src/Framework/BuildCheck/BuildCheckEventArgs.cs index d1d3c682a3f..e8491975a71 100644 --- a/src/Framework/BuildCheck/BuildCheckEventArgs.cs +++ b/src/Framework/BuildCheck/BuildCheckEventArgs.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Framework/BuildErrorEventArgs.cs b/src/Framework/BuildErrorEventArgs.cs index 97cb5b1f1df..6f119b373fb 100644 --- a/src/Framework/BuildErrorEventArgs.cs +++ b/src/Framework/BuildErrorEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/BuildEventArgs.cs b/src/Framework/BuildEventArgs.cs index a26e7b26948..3ea2c615208 100644 --- a/src/Framework/BuildEventArgs.cs +++ b/src/Framework/BuildEventArgs.cs @@ -5,7 +5,6 @@ using System.IO; using System.Runtime.Serialization; using System.Text; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/BuildException/BuildExceptionBase.cs b/src/Framework/BuildException/BuildExceptionBase.cs index 869b74070b6..d09c0a6fd14 100644 --- a/src/Framework/BuildException/BuildExceptionBase.cs +++ b/src/Framework/BuildException/BuildExceptionBase.cs @@ -7,7 +7,6 @@ using System.IO; using System.Runtime.Serialization; using Microsoft.Build.BackEnd; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework.BuildException; diff --git a/src/Framework/BuildMessageEventArgs.cs b/src/Framework/BuildMessageEventArgs.cs index 20a1898bea2..785f7b923f8 100644 --- a/src/Framework/BuildMessageEventArgs.cs +++ b/src/Framework/BuildMessageEventArgs.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.Serialization; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/BuildSubmissionStartedEventArgs.cs b/src/Framework/BuildSubmissionStartedEventArgs.cs index 6a1712abf83..b750087b10d 100644 --- a/src/Framework/BuildSubmissionStartedEventArgs.cs +++ b/src/Framework/BuildSubmissionStartedEventArgs.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using Microsoft.Build.Execution; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/BuildWarningEventArgs.cs b/src/Framework/BuildWarningEventArgs.cs index 543281e8c26..3d257346d46 100644 --- a/src/Framework/BuildWarningEventArgs.cs +++ b/src/Framework/BuildWarningEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/EnvironmentUtilities.cs b/src/Framework/EnvironmentUtilities.cs new file mode 100644 index 00000000000..9517f781cdf --- /dev/null +++ b/src/Framework/EnvironmentUtilities.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.Build.Framework; + +internal static partial class EnvironmentUtilities +{ +#if !NETCOREAPP + private static volatile int s_processId; + private static volatile string? s_processPath; +#endif + private static volatile string? s_processName; + + /// Gets the unique identifier for the current process. + public static int CurrentProcessId + { + get + { +#if NETCOREAPP + return Environment.ProcessId; +#else + // copied from Environment.ProcessPath + int processId = s_processId; + if (processId == 0) + { + using Process currentProcess = Process.GetCurrentProcess(); + s_processId = processId = currentProcess.Id; + + // Assume that process Id zero is invalid for user processes. It holds for all mainstream operating systems. + Debug.Assert(processId != 0); + } + + return processId; +#endif + } + } + + /// + /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. + /// + /// Path of the executable that started the currently executing process + /// + /// If the executable is renamed or deleted before this property is first accessed, the return value is undefined and depends on the operating system. + /// + public static string? ProcessPath + { + get + { +#if NETCOREAPP + return Environment.ProcessPath; +#else + // copied from Environment.ProcessPath + string? processPath = s_processPath; + if (processPath == null) + { + // The value is cached both as a performance optimization and to ensure that the API always returns + // the same path in a given process. + using Process currentProcess = Process.GetCurrentProcess(); + Interlocked.CompareExchange(ref s_processPath, currentProcess?.MainModule?.FileName ?? "", null); + processPath = s_processPath; + Debug.Assert(processPath != null); + } + + return (processPath?.Length != 0) ? processPath : null; +#endif + } + } + + public static string ProcessName + { + get + { + string? processName = s_processName; + if (processName == null) + { + using Process currentProcess = Process.GetCurrentProcess(); + Interlocked.CompareExchange(ref s_processName, currentProcess.ProcessName, null); + processName = s_processName; + } + + return processName; + } + } + + public static bool IsWellKnownEnvironmentDerivedProperty(string propertyName) + { + return propertyName.StartsWith("MSBUILD", StringComparison.OrdinalIgnoreCase) || + propertyName.StartsWith("COMPLUS_", StringComparison.OrdinalIgnoreCase) || + propertyName.StartsWith("DOTNET_", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/Framework/EnvironmentVariableReadEventArgs.cs b/src/Framework/EnvironmentVariableReadEventArgs.cs index 496fe31afe9..de755828413 100644 --- a/src/Framework/EnvironmentVariableReadEventArgs.cs +++ b/src/Framework/EnvironmentVariableReadEventArgs.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/ErrorUtilities.cs b/src/Framework/ErrorUtilities.cs index e9b9275d7c8..c0534973ccf 100644 --- a/src/Framework/ErrorUtilities.cs +++ b/src/Framework/ErrorUtilities.cs @@ -45,6 +45,23 @@ internal static void VerifyThrowInternalNull([NotNull] object? parameter, [Calle } } + /// + /// Helper to throw an InternalErrorException when the specified parameter is null or zero length. + /// This should be used ONLY if this would indicate a bug in MSBuild rather than + /// anything caused by user action. + /// + /// The value of the argument. + /// Parameter that should not be null or zero length + internal static void VerifyThrowInternalLength([NotNull] string? parameterValue, [CallerArgumentExpression(nameof(parameterValue))] string? parameterName = null) + { + VerifyThrowInternalNull(parameterValue, parameterName); + + if (parameterValue.Length == 0) + { + ThrowInternalError("{0} unexpectedly empty", innerException: null, args: parameterName); + } + } + /// /// Throws InternalErrorException. /// This is only for situations that would mean that there is a bug in MSBuild itself. diff --git a/src/Shared/EscapingUtilities.cs b/src/Framework/EscapingUtilities.cs similarity index 100% rename from src/Shared/EscapingUtilities.cs rename to src/Framework/EscapingUtilities.cs diff --git a/src/Framework/ExceptionHandling.cs b/src/Framework/ExceptionHandling.cs new file mode 100644 index 00000000000..29c290bd63f --- /dev/null +++ b/src/Framework/ExceptionHandling.cs @@ -0,0 +1,206 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security; +using System.Threading; +using System.Xml; +using System.Xml.Schema; + +namespace Microsoft.Build.Framework; + +internal static class ExceptionHandling +{ + /// + /// If the given exception is "ignorable under some circumstances" return false. + /// Otherwise it's "really bad", and return true. + /// This makes it possible to catch(Exception ex) without catching disasters. + /// + /// The exception to check. + /// True if exception is critical. + internal static bool IsCriticalException(Exception e) + { + if (e is OutOfMemoryException + || e is StackOverflowException + || e is ThreadAbortException + || e is ThreadInterruptedException + || e is AccessViolationException + || e is CriticalTaskException + || e is InternalErrorException) + { + // Ideally we would include NullReferenceException, because it should only ever be thrown by CLR (use ArgumentNullException for arguments) + // but we should handle it if tasks and loggers throw it. + + // ExecutionEngineException has been deprecated by the CLR + return true; + } + + // Check if any critical exceptions + var aggregateException = e as AggregateException; + + if (aggregateException != null) + { + // If the aggregate exception contains a critical exception it is considered a critical exception + if (aggregateException.InnerExceptions.Any(innerException => IsCriticalException(innerException))) + { + return true; + } + } + + return false; + } + + /// + /// If the given exception is file IO related or expected return false. + /// Otherwise, return true. + /// + /// The exception to check. + /// True if exception is not IO related or expected otherwise false. + internal static bool NotExpectedException(Exception e) + { + return !IsIoRelatedException(e); + } + + /// + /// Determine whether the exception is file-IO related. + /// + /// The exception to check. + /// True if exception is IO related. + internal static bool IsIoRelatedException(Exception e) + { + // These all derive from IOException + // DirectoryNotFoundException + // DriveNotFoundException + // EndOfStreamException + // FileLoadException + // FileNotFoundException + // PathTooLongException + // PipeException + return e is UnauthorizedAccessException + || e is NotSupportedException + || (e is ArgumentException && !(e is ArgumentNullException)) + || e is SecurityException + || e is IOException; + } + + /// Checks if the exception is an XML one. + /// Exception to check. + /// True if exception is related to XML parsing. + internal static bool IsXmlException(Exception e) + { + return e is XmlException +#if FEATURE_SECURITY_PERMISSIONS + || e is XmlSyntaxException +#endif + || e is XmlSchemaException + || e is UriFormatException; // XmlTextReader for example uses this under the covers + } + + /// + /// If the given exception is file IO related or Xml related return false. + /// Otherwise, return true. + /// + /// The exception to check. + internal static bool NotExpectedIoOrXmlException(Exception e) + { + if + ( + IsXmlException(e) + || !NotExpectedException(e)) + { + return false; + } + + return true; + } + + /// + /// If the given exception is reflection-related return false. + /// Otherwise, return true. + /// + /// The exception to check. + internal static bool NotExpectedReflectionException(Exception e) + { + // We are explicitly not handling TargetInvocationException. Those are just wrappers around + // exceptions thrown by the called code (such as a task or logger) which callers will typically + // want to treat differently. + if + ( + e is TypeLoadException // thrown when the common language runtime cannot find the assembly, the type within the assembly, or cannot load the type + || e is MethodAccessException // thrown when a class member is not found or access to the member is not permitted + || e is MissingMethodException // thrown when code in a dependent assembly attempts to access a missing method in an assembly that was modified + || e is MemberAccessException // thrown when a class member is not found or access to the member is not permitted + || e is BadImageFormatException // thrown when the file image of a DLL or an executable program is invalid + || e is ReflectionTypeLoadException // thrown by the Module.GetTypes method if any of the classes in a module cannot be loaded + || e is TargetParameterCountException // thrown when the number of parameters for an invocation does not match the number expected + || e is InvalidCastException + || e is AmbiguousMatchException // thrown when binding to a member results in more than one member matching the binding criteria + || e is CustomAttributeFormatException // thrown if a custom attribute on a data type is formatted incorrectly + || e is InvalidFilterCriteriaException // thrown in FindMembers when the filter criteria is not valid for the type of filter you are using + || e is TargetException // thrown when an attempt is made to invoke a non-static method on a null object. This may occur because the caller does not + // have access to the member, or because the target does not define the member, and so on. + || e is MissingFieldException // thrown when code in a dependent assembly attempts to access a missing field in an assembly that was modified. + || !NotExpectedException(e)) // Reflection can throw IO exceptions if the assembly cannot be opened + { + return false; + } + + return true; + } + + /// + /// Serialization has been observed to throw TypeLoadException as + /// well as SerializationException and IO exceptions. (Obviously + /// it has to do reflection but it ought to be wrapping the exceptions.) + /// + internal static bool NotExpectedSerializationException(Exception e) + { + if + ( + e is SerializationException || + !NotExpectedReflectionException(e)) + { + return false; + } + + return true; + } + + /// + /// Returns false if this is a known exception thrown by the registry API. + /// + internal static bool NotExpectedRegistryException(Exception e) + { + if (e is SecurityException + || e is UnauthorizedAccessException + || e is IOException + || e is ObjectDisposedException + || e is ArgumentException) + { + return false; + } + + return true; + } + + /// + /// Returns false if this is a known exception thrown by function evaluation + /// + internal static bool NotExpectedFunctionException(Exception e) + { + if (e is InvalidCastException + || e is ArgumentNullException + || e is FormatException + || e is InvalidOperationException + || !NotExpectedReflectionException(e)) + { + return false; + } + + return true; + } +} diff --git a/src/Framework/ExtendedBuildErrorEventArgs.cs b/src/Framework/ExtendedBuildErrorEventArgs.cs index 54f558432b1..e28be6ebde3 100644 --- a/src/Framework/ExtendedBuildErrorEventArgs.cs +++ b/src/Framework/ExtendedBuildErrorEventArgs.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; diff --git a/src/Framework/ExtendedBuildMessageEventArgs.cs b/src/Framework/ExtendedBuildMessageEventArgs.cs index 53ec510c8da..bd2dbc8749b 100644 --- a/src/Framework/ExtendedBuildMessageEventArgs.cs +++ b/src/Framework/ExtendedBuildMessageEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; diff --git a/src/Framework/ExtendedBuildWarningEventArgs.cs b/src/Framework/ExtendedBuildWarningEventArgs.cs index 2d9a163eb15..dcd018b65a3 100644 --- a/src/Framework/ExtendedBuildWarningEventArgs.cs +++ b/src/Framework/ExtendedBuildWarningEventArgs.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; diff --git a/src/Framework/ExtendedCriticalBuildMessageEventArgs.cs b/src/Framework/ExtendedCriticalBuildMessageEventArgs.cs index 3897a1de6e6..c59dd4ea422 100644 --- a/src/Framework/ExtendedCriticalBuildMessageEventArgs.cs +++ b/src/Framework/ExtendedCriticalBuildMessageEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; diff --git a/src/Framework/ExtendedCustomBuildEventArgs.cs b/src/Framework/ExtendedCustomBuildEventArgs.cs index c29352897a1..a83de3498ba 100644 --- a/src/Framework/ExtendedCustomBuildEventArgs.cs +++ b/src/Framework/ExtendedCustomBuildEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework; diff --git a/src/Framework/ExternalProjectFinishedEventArgs.cs b/src/Framework/ExternalProjectFinishedEventArgs.cs index 4417569e8fc..51f661fe499 100644 --- a/src/Framework/ExternalProjectFinishedEventArgs.cs +++ b/src/Framework/ExternalProjectFinishedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/ExternalProjectStartedEventArgs.cs b/src/Framework/ExternalProjectStartedEventArgs.cs index 0d25191f08e..09a4d4c1ce9 100644 --- a/src/Framework/ExternalProjectStartedEventArgs.cs +++ b/src/Framework/ExternalProjectStartedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Shared/FileSystem/CachingFileSystemWrapper.cs b/src/Framework/FileSystem/CachingFileSystemWrapper.cs similarity index 100% rename from src/Shared/FileSystem/CachingFileSystemWrapper.cs rename to src/Framework/FileSystem/CachingFileSystemWrapper.cs diff --git a/src/Shared/FileSystem/FileSystems.cs b/src/Framework/FileSystem/FileSystems.cs similarity index 82% rename from src/Shared/FileSystem/FileSystems.cs rename to src/Framework/FileSystem/FileSystems.cs index a0338849c0e..4ceaaa2085d 100644 --- a/src/Shared/FileSystem/FileSystems.cs +++ b/src/Framework/FileSystem/FileSystems.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Build.Framework; namespace Microsoft.Build.Shared.FileSystem { @@ -13,10 +14,7 @@ internal static class FileSystems private static IFileSystem GetFileSystem() { -#if CLR2COMPATIBILITY - return MSBuildTaskHostFileSystem.Singleton(); -#else - if (NativeMethodsShared.IsWindows) + if (NativeMethods.IsWindows) { return MSBuildOnWindowsFileSystem.Singleton(); } @@ -24,7 +22,6 @@ private static IFileSystem GetFileSystem() { return ManagedFileSystem.Singleton(); } -#endif } } } diff --git a/src/Shared/FileSystem/IFileSystem.cs b/src/Framework/FileSystem/IFileSystem.cs similarity index 100% rename from src/Shared/FileSystem/IFileSystem.cs rename to src/Framework/FileSystem/IFileSystem.cs diff --git a/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs b/src/Framework/FileSystem/MSBuildOnWindowsFileSystem.cs similarity index 99% rename from src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs rename to src/Framework/FileSystem/MSBuildOnWindowsFileSystem.cs index d385c059783..d7386b41871 100644 --- a/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs +++ b/src/Framework/FileSystem/MSBuildOnWindowsFileSystem.cs @@ -6,7 +6,6 @@ using System.IO; using System.Runtime.Versioning; - namespace Microsoft.Build.Shared.FileSystem { /// diff --git a/src/Shared/FileSystem/ManagedFileSystem.cs b/src/Framework/FileSystem/ManagedFileSystem.cs similarity index 94% rename from src/Shared/FileSystem/ManagedFileSystem.cs rename to src/Framework/FileSystem/ManagedFileSystem.cs index 6e49213da9a..4f81a669d94 100644 --- a/src/Shared/FileSystem/ManagedFileSystem.cs +++ b/src/Framework/FileSystem/ManagedFileSystem.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.IO; +#if FEATURE_MSIOREDIST +using Microsoft.Build.Framework; +#endif namespace Microsoft.Build.Shared.FileSystem { @@ -22,13 +25,7 @@ private static bool ShouldUseMicrosoftIO { get { -#if !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS - return NativeMethodsShared.IsWindows; -#else - // We need to mock usage of ChangeWaves class, - // because Microsoft.Build.Engine.OM.UnitTests should not have access to internals of Microsoft.Build.Framework. - return true; -#endif + return NativeMethods.IsWindows; } } #endif diff --git a/src/Shared/FileSystem/NativeWin32Exception.cs b/src/Framework/FileSystem/NativeWin32Exception.cs similarity index 100% rename from src/Shared/FileSystem/NativeWin32Exception.cs rename to src/Framework/FileSystem/NativeWin32Exception.cs diff --git a/src/Shared/FileSystem/SafeFileHandle.cs b/src/Framework/FileSystem/SafeFileHandle.cs similarity index 100% rename from src/Shared/FileSystem/SafeFileHandle.cs rename to src/Framework/FileSystem/SafeFileHandle.cs diff --git a/src/Shared/FileSystem/WindowsFileSystem.cs b/src/Framework/FileSystem/WindowsFileSystem.cs similarity index 94% rename from src/Shared/FileSystem/WindowsFileSystem.cs rename to src/Framework/FileSystem/WindowsFileSystem.cs index 0a6ee86805f..c215adc4733 100644 --- a/src/Shared/FileSystem/WindowsFileSystem.cs +++ b/src/Framework/FileSystem/WindowsFileSystem.cs @@ -7,6 +7,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using Microsoft.Build.Framework; namespace Microsoft.Build.Shared.FileSystem @@ -57,10 +58,10 @@ public override bool DirectoryExists(string path) if (!string.IsNullOrEmpty(path) && FileUtilities.IsPathTooLong(path)) { // If the path is too long, we can't check if it exists on windows - string message = ResourceUtilities.FormatString(AssemblyResources.GetString("Shared.PathTooLong"), path, NativeMethodsShared.MaxPath); - throw new PathTooLongException(message); + throw new PathTooLongException(SR.FormatPathTooLong(path, NativeMethods.MaxPath)); } - return NativeMethodsShared.DirectoryExistsWindows(path); + + return NativeMethods.DirectoryExistsWindows(path); } public override bool FileExists(string path) @@ -74,12 +75,12 @@ public override bool FileExists(string path) public override bool FileOrDirectoryExists(string path) { - return NativeMethodsShared.FileOrDirectoryExistsWindows(path); + return NativeMethods.FileOrDirectoryExistsWindows(path); } public override DateTime GetLastWriteTimeUtc(string path) { - var fileLastWriteTime = NativeMethodsShared.GetLastWriteFileUtcTime(path); + var fileLastWriteTime = NativeMethods.GetLastWriteFileUtcTime(path); if (fileLastWriteTime != DateTime.MinValue) { @@ -87,7 +88,7 @@ public override DateTime GetLastWriteTimeUtc(string path) } else { - NativeMethodsShared.GetLastWriteDirectoryUtcTime(path, out var directoryLastWriteTime); + NativeMethods.GetLastWriteDirectoryUtcTime(path, out var directoryLastWriteTime); return directoryLastWriteTime; } } diff --git a/src/Shared/FileSystem/WindowsNative.cs b/src/Framework/FileSystem/WindowsNative.cs similarity index 100% rename from src/Shared/FileSystem/WindowsNative.cs rename to src/Framework/FileSystem/WindowsNative.cs diff --git a/src/Framework/FileUtilities.cs b/src/Framework/FileUtilities.cs index d861b6821ee..6877bd85318 100644 --- a/src/Framework/FileUtilities.cs +++ b/src/Framework/FileUtilities.cs @@ -1,33 +1,44 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if !TASKHOST -using System.Threading; +using System; +#if NET +using System.Buffers; #endif +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; -#if NETFRAMEWORK && !TASKHOST -using Path = Microsoft.IO.Path; +#if NETFRAMEWORK +using NewPath = Microsoft.IO.Path; +using Path = System.IO.Path; #else -using System.IO; +using NewPath = System.IO.Path; +using Path = System.IO.Path; #endif namespace Microsoft.Build.Framework { - // TODO: this should be unified with Shared\FileUtilities, but it is hard to untangle everything in one go. - // Moved some of the methods here for now. - /// /// This class contains utility methods for file IO. /// Functions from FileUtilities are transferred here as part of the effort to remove Shared files. /// - internal static class FrameworkFileUtilities + internal static partial class FileUtilities { private const char UnixDirectorySeparator = '/'; private const char WindowsDirectorySeparator = '\\'; internal static readonly char[] Slashes = [UnixDirectorySeparator, WindowsDirectorySeparator]; -#if !TASKHOST /// /// AsyncLocal working directory for use during property/item expansion in multithreaded mode. /// Set by MultiThreadedTaskEnvironmentDriver when building projects. null in multi-process mode. @@ -40,8 +51,123 @@ internal static string? CurrentThreadWorkingDirectory get => s_currentThreadWorkingDirectory.Value; set => s_currentThreadWorkingDirectory.Value = value; } + + /// + /// The directory where MSBuild stores cache information used during the build. + /// + internal static string? cacheDirectory = null; + + /// + /// FOR UNIT TESTS ONLY + /// Clear out the static variable used for the cache directory so that tests that + /// modify it can validate their modifications. + /// + internal static void ClearCacheDirectoryPath() + { + cacheDirectory = null; + } + + public static readonly StringComparison PathComparison = IsFileSystemCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + + public static readonly StringComparer PathComparer = IsFileSystemCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; + + private static bool? s_isFileSystemCaseSensitive; + + public static bool IsFileSystemCaseSensitive + => s_isFileSystemCaseSensitive ??= ComputeIsFileSystemCaseSensitive(); + + /// + /// Determines whether the file system is case sensitive. + /// Copied from https://github.com/dotnet/runtime/blob/73ba11f3015216b39cb866d9fb7d3d25e93489f2/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs#L41-L59. + /// + private static bool ComputeIsFileSystemCaseSensitive() + { + try + { + string pathWithUpperCase = Path.Combine(Path.GetTempPath(), $"CASESENSITIVETEST{Guid.NewGuid():N}"); + using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) + { + string lowerCased = pathWithUpperCase.ToLowerInvariant(); + return !FileSystems.Default.FileExists(lowerCased); + } + } + catch (Exception ex) + { + // In case something goes terribly wrong, we don't want to fail just because + // of a casing test, so we assume case-insensitive-but-preserving. + Debug.Fail($"Casing test failed: {ex}"); + return false; + } + } + + /// + /// Copied from https://github.com/dotnet/corefx/blob/056715ff70e14712419d82d51c8c50c54b9ea795/src/Common/src/System/IO/PathInternal.Windows.cs#L61 + /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 + /// +#if NET + internal static readonly SearchValues InvalidPathChars = SearchValues.Create( +#else + internal static readonly char[] InvalidPathChars = ( +#endif + [ + '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31 + ]); + + /// + /// Copied from https://github.com/dotnet/corefx/blob/387cf98c410bdca8fd195b28cbe53af578698f94/src/System.Runtime.Extensions/src/System/IO/Path.Windows.cs#L18 + /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 + /// + internal static readonly char[] InvalidFileNameCharsArray = + [ + '\"', '<', '>', '|', '\0', + (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, + (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, + (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, + (char)31, ':', '*', '?', '\\', '/' + ]; + +#if NET + internal static readonly SearchValues InvalidFileNameChars = SearchValues.Create(InvalidFileNameCharsArray); +#else + internal static char[] InvalidFileNameChars => InvalidFileNameCharsArray; #endif + internal static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString(); + + private static readonly ConcurrentDictionary FileExistenceCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + private static readonly IFileSystem DefaultFileSystem = FileSystems.Default; + + /// + /// Retrieves the MSBuild runtime cache directory + /// + internal static string GetCacheDirectory() + { + if (cacheDirectory == null) + { + cacheDirectory = Path.Combine(TempFileDirectory, string.Format(CultureInfo.CurrentUICulture, "MSBuild{0}-{1}", EnvironmentUtilities.CurrentProcessId, AppDomain.CurrentDomain.Id)); + } + + return cacheDirectory; + } + + /// + /// Clears the MSBuild runtime cache + /// + internal static void ClearCacheDirectory() + { + string cacheDirectory = GetCacheDirectory(); + + if (DefaultFileSystem.DirectoryExists(cacheDirectory)) + { + DeleteDirectoryNoThrow(cacheDirectory, true); + } + } + /// /// Indicates if the given character is a slash in current OS. /// @@ -104,7 +230,9 @@ internal static string EnsureNoTrailingSlash(string path) return path; } -#if !TASKHOST + public static bool IsPathTooLong(string path) + => path.Length >= NativeMethods.MaxPath; // >= not > because MAX_PATH assumes a trailing null + /// /// Checks if the path contains backslashes on Unix. /// @@ -209,7 +337,7 @@ internal static AbsolutePath NormalizePath(AbsolutePath path) return path; } - return new AbsolutePath(FixFilePath(Path.GetFullPath(path.Value)), + return new AbsolutePath(FixFilePath(NewPath.GetFullPath(path.Value)), original: path.OriginalValue, ignoreRootedCheck: true); } @@ -228,6 +356,1365 @@ internal static AbsolutePath FixFilePath(AbsolutePath path) original: path.OriginalValue, ignoreRootedCheck: true); } + + /// + /// Get the hex hash string for the string + /// + internal static string GetHexHash(string stringToHash) + { + return stringToHash.GetHashCode().ToString("X", CultureInfo.InvariantCulture); + } + + /// + /// Get the hash for the assemblyPaths + /// + internal static int GetPathsHash(IEnumerable assemblyPaths) + { + StringBuilder builder = new StringBuilder(); + + foreach (string path in assemblyPaths) + { + if (path != null) + { + string directoryPath = path.Trim(); + if (directoryPath.Length > 0) + { + DateTime lastModifiedTime; + if (NativeMethods.GetLastWriteDirectoryUtcTime(directoryPath, out lastModifiedTime)) + { + builder.Append(lastModifiedTime.Ticks); + builder.Append('|'); + builder.Append(directoryPath.ToUpperInvariant()); + builder.Append('|'); + } + } + } + } + + return builder.ToString().GetHashCode(); + } + + /// + /// Returns whether MSBuild can write to the given directory. Throws for PathTooLongExceptions + /// but not other exceptions. + /// + internal static bool CanWriteToDirectory(string directory) + { + try + { + string testFilePath = Path.Combine(directory, $"MSBuild_{Guid.NewGuid():N}_testFile.txt"); + FileInfo file = new(testFilePath); + file.Directory!.Create(); // If the directory already exists, this method does nothing. + File.WriteAllText(testFilePath, $"MSBuild process {EnvironmentUtilities.CurrentProcessId} successfully wrote to file."); + File.Delete(testFilePath); + return true; + } + catch (PathTooLongException) + { + throw new ArgumentException(SR.FormatDebugPathTooLong(directory)); + } + catch (Exception) + { + return false; + } + } + + /// + /// Ensures the path does not have a leading or trailing slash after removing the first 'start' characters. + /// + internal static string EnsureNoLeadingOrTrailingSlash(string path, int start) + { + int stop = path.Length; + while (start < stop && IsSlash(path[start])) + { + start++; + } + while (start < stop && IsSlash(path[stop - 1])) + { + stop--; + } + + return FixFilePath(path.Substring(start, stop - start)); + } + + /// + /// Ensures the path does not have a leading slash after removing the first 'start' characters but does end in a slash. + /// + internal static string EnsureTrailingNoLeadingSlash(string path, int start) + { + int stop = path.Length; + while (start < stop && IsSlash(path[start])) + { + start++; + } + + return FixFilePath(start < stop && IsSlash(path[stop - 1]) ? + path.Substring(start) : +#if NET + string.Concat(path.AsSpan(start), new(in Path.DirectorySeparatorChar))); +#else + path.Substring(start) + Path.DirectorySeparatorChar); +#endif + } + + /// + /// Ensures the path is enclosed within single quotes. + /// + /// The path to check. + /// The path enclosed by quotes. + internal static string EnsureSingleQuotes(string path) + { + return EnsureQuotes(path); + } + + /// + /// Ensures the path is enclosed within double quotes. + /// + /// The path to check. + /// The path enclosed by quotes. + internal static string EnsureDoubleQuotes(string path) + { + return EnsureQuotes(path, isSingleQuote: false); + } + + /// + /// Ensures the path is enclosed within quotes. + /// + /// The path to check. + /// Indicates if single or double quotes should be used + /// The path enclosed by quotes. + internal static string EnsureQuotes(string path, bool isSingleQuote = true) + { + path = FixFilePath(path); + + const char singleQuote = '\''; + const char doubleQuote = '\"'; + var targetQuote = isSingleQuote ? singleQuote : doubleQuote; + var convertQuote = isSingleQuote ? doubleQuote : singleQuote; + + if (!string.IsNullOrEmpty(path)) + { + // Special case: convert the quotes. + if (path.Length > 1 && path[0] == convertQuote && path[path.Length - 1] == convertQuote) + { +#if NET + path = $"{targetQuote}{path.AsSpan(1, path.Length - 2)}{targetQuote}"; +#else + path = $"{targetQuote}{path.Substring(1, path.Length - 2)}{targetQuote}"; +#endif + } + // Enclose the path in a set of the 'target' quote unless the string is already quoted with the 'target' quotes. + else if (path.Length == 1 || path[0] != targetQuote || path[path.Length - 1] != targetQuote) + { + path = $"{targetQuote}{path}{targetQuote}"; + } + } + + return path; + } + + /// + /// Trims the string and removes any double quotes around it. + /// + [return: NotNullIfNotNull(nameof(path))] + internal static string? TrimAndStripAnyQuotes(string? path) + { + if (path is null) + { + return path; + } + + // Trim returns the same string if trimming isn't needed + path = path.Trim(); + path = path.Trim(['"']); + + return path; + } + + /// + /// Get the directory name of a rooted full path + /// + /// + /// + internal static string? GetDirectoryNameOfFullPath(string fullPath) + { + if (fullPath != null) + { + int i = fullPath.Length; + while (i > 0 && fullPath[--i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) + { + ; + } + + return FixFilePath(fullPath.Substring(0, i)); + } + return null; + } + + internal static string TruncatePathToTrailingSegments(string path, int trailingSegmentsToKeep) + { + FrameworkErrorUtilities.VerifyThrowInternalLength(path, nameof(path)); + FrameworkErrorUtilities.VerifyThrow(trailingSegmentsToKeep >= 0, "trailing segments must be positive"); + + var segments = path.Split(Slashes, StringSplitOptions.RemoveEmptyEntries); + + var headingSegmentsToRemove = Math.Max(0, segments.Length - trailingSegmentsToKeep); + + return string.Join(DirectorySeparatorString, segments.Skip(headingSegmentsToRemove)); + } + + internal static bool ContainsRelativePathSegments(string path) + { + for (int i = 0; i < path.Length; i++) + { + if (i + 1 < path.Length && path[i] == '.' && path[i + 1] == '.') + { + if (RelativePathBoundsAreValid(path, i, i + 1)) + { + return true; + } + else + { + i += 2; + continue; + } + } + + if (path[i] == '.' && RelativePathBoundsAreValid(path, i, i)) + { + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool RelativePathBoundsAreValid(string path, int leftIndex, int rightIndex) + { + var leftBound = leftIndex - 1 >= 0 + ? path[leftIndex - 1] + : (char?)null; + + var rightBound = rightIndex + 1 < path.Length + ? path[rightIndex + 1] + : (char?)null; + + return IsValidRelativePathBound(leftBound) && IsValidRelativePathBound(rightBound); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsValidRelativePathBound(char? c) + { + return c == null || IsAnySlash(c.Value); + } + + /// + /// Gets the canonicalized full path of the provided path. + /// Guidance for use: call this on all paths accepted through public entry + /// points that need normalization. After that point, only verify the path + /// is rooted, using ErrorUtilities.VerifyThrowPathRooted. + /// ASSUMES INPUT IS ALREADY UNESCAPED. + /// + internal static string NormalizePath(string path) + { + ArgumentException.ThrowIfNullOrEmpty(path); + string fullPath = GetFullPath(path); + return FixFilePath(fullPath); + } + + internal static string NormalizePath(string directory, string file) + { + return NormalizePath(Path.Combine(directory, file)); + } + + internal static string NormalizePath(params string[] paths) + { + return NormalizePath(Path.Combine(paths)); + } + + private static string GetFullPath(string path) + { +#if FEATURE_LEGACY_GETFULLPATH + if (NativeMethods.IsWindows) + { + string uncheckedFullPath = NativeMethods.GetFullPath(path); + + if (IsPathTooLong(uncheckedFullPath)) + { + throw new PathTooLongException(SR.FormatPathTooLong(path, NativeMethods.MaxPath)); + } + + // We really don't care about extensions here, but Path.HasExtension provides a great way to + // invoke the CLR's invalid path checks (these are independent of path length) + Path.HasExtension(uncheckedFullPath); + + // If we detect we are a UNC path then we need to use the regular get full path in order to do the correct checks for UNC formatting + // and security checks for strings like \\?\GlobalRoot + return IsUNCPath(uncheckedFullPath) ? Path.GetFullPath(uncheckedFullPath) : uncheckedFullPath; + } #endif + + return Path.GetFullPath(path); + } + +#if FEATURE_LEGACY_GETFULLPATH + private static bool IsUNCPath(string path) + { + if (!NativeMethods.IsWindows || !path.StartsWith(@"\\", StringComparison.Ordinal)) + { + return false; + } + bool isUNC = true; + for (int i = 2; i < path.Length - 1; i++) + { + if (path[i] == '\\') + { + isUNC = false; + break; + } + } + + /* + From Path.cs in the CLR + + Throw an ArgumentException for paths like \\, \\server, \\server\ + This check can only be properly done after normalizing, so + \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\ + (an internal kernel path) because it provides aliases for drives. + + throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); + + // Check for \\?\Globalroot, an internal mechanism to the kernel + // that provides aliases for drives and other undocumented stuff. + // The kernel team won't even describe the full set of what + // is available here - we don't want managed apps mucking + // with this for security reasons. + */ + return isUNC || path.IndexOf(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase) != -1; + } +#endif // FEATURE_LEGACY_GETFULLPATH + + /// + /// Normalizes all path separators (both forward and back slashes) to forward slashes. + /// This is platform-independent, unlike FrameworkFileUtilities.FixFilePath which only normalizes on non-Windows platforms. + /// Use this when you need consistent path comparison regardless of which separator style is used. + /// + /// The path to normalize + /// The path with all backslashes replaced by forward slashes, or the original path if null/empty + internal static string NormalizePathSeparatorsToForwardSlash(string path) + { + return string.IsNullOrEmpty(path) ? path : path.Replace('\\', '/'); + } + + /// + /// If on Unix, convert backslashes to slashes for strings that resemble paths. + /// The heuristic is if something resembles paths (contains slashes) check if the + /// first segment exists and is a directory. + /// Use a native shared method to massage file path. If the file is adjusted, + /// that qualifies is as a path. + /// + /// @baseDirectory is just passed to LooksLikeUnixFilePath, to help with the check + /// + internal static string MaybeAdjustFilePath(string value, string baseDirectory = "") + { + var comparisonType = StringComparison.Ordinal; + + // Don't bother with arrays or properties or network paths, or those that + // have no slashes. + if (NativeMethods.IsWindows || string.IsNullOrEmpty(value) + || value.StartsWith("$(", comparisonType) || value.StartsWith("@(", comparisonType) + || value.StartsWith("\\\\", comparisonType)) + { + return value; + } + + // For Unix-like systems, we may want to convert backslashes to slashes + Span newValue = ConvertToUnixSlashes(value.ToCharArray()); + + // Find the part of the name we want to check, that is remove quotes, if present + bool shouldAdjust = newValue.IndexOf('/') != -1 && LooksLikeUnixFilePath(RemoveQuotes(newValue), baseDirectory); + return shouldAdjust ? newValue.ToString() : value; + } + + /// + /// If on Unix, convert backslashes to slashes for strings that resemble paths. + /// This overload takes and returns ReadOnlyMemory of characters. + /// + internal static ReadOnlyMemory MaybeAdjustFilePath(ReadOnlyMemory value, string baseDirectory = "") + { + if (NativeMethods.IsWindows || value.IsEmpty) + { + return value; + } + + // Don't bother with arrays or properties or network paths. + if (value.Length >= 2) + { + var span = value.Span; + + // The condition is equivalent to span.StartsWith("$(") || span.StartsWith("@(") || span.StartsWith("\\\\") + if ((span[1] == '(' && (span[0] == '$' || span[0] == '@')) || + (span[1] == '\\' && span[0] == '\\')) + { + return value; + } + } + + // For Unix-like systems, we may want to convert backslashes to slashes + Span newValue = ConvertToUnixSlashes(value.ToArray()); + + // Find the part of the name we want to check, that is remove quotes, if present + bool shouldAdjust = newValue.IndexOf('/') != -1 && LooksLikeUnixFilePath(RemoveQuotes(newValue), baseDirectory); + return shouldAdjust ? newValue.ToString().AsMemory() : value; + } + + private static Span ConvertToUnixSlashes(Span path) + { + return path.IndexOf('\\') == -1 ? path : CollapseSlashes(path); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Span CollapseSlashes(Span str) + { + int sliceLength = 0; + + // Performs Regex.Replace(str, @"[\\/]+", "/") + for (int i = 0; i < str.Length; i++) + { + bool isCurSlash = IsAnySlash(str[i]); + bool isPrevSlash = i > 0 && IsAnySlash(str[i - 1]); + + if (!isCurSlash || !isPrevSlash) + { + str[sliceLength] = str[i] == '\\' ? '/' : str[i]; + sliceLength++; + } + } + + return str.Slice(0, sliceLength); + } + + private static Span RemoveQuotes(Span path) + { + int endId = path.Length - 1; + char singleQuote = '\''; + char doubleQuote = '\"'; + + bool hasQuotes = path.Length > 2 + && ((path[0] == singleQuote && path[endId] == singleQuote) + || (path[0] == doubleQuote && path[endId] == doubleQuote)); + + return hasQuotes ? path.Slice(1, endId - 1) : path; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsAnySlash(char c) => c == '/' || c == '\\'; + + /// + /// If on Unix, check if the string looks like a file path. + /// The heuristic is if something resembles paths (contains slashes) check if the + /// first segment exists and is a directory. + /// + /// If @baseDirectory is not null, then look for the first segment exists under + /// that + /// + internal static bool LooksLikeUnixFilePath(string value, string baseDirectory = "") + => LooksLikeUnixFilePath(value.AsSpan(), baseDirectory); + + internal static bool LooksLikeUnixFilePath(ReadOnlySpan value, string baseDirectory = "") + { + if (NativeMethods.IsWindows) + { + return false; + } + + // The first slash will either be at the beginning of the string or after the first directory name + int directoryLength = value.Slice(1).IndexOf('/') + 1; + bool shouldCheckDirectory = directoryLength != 0; + + // Check for actual files or directories under / that get missed by the above logic + bool shouldCheckFileOrDirectory = !shouldCheckDirectory && value.Length > 0 && value[0] == '/'; + ReadOnlySpan directory = value.Slice(0, directoryLength); + + return (shouldCheckDirectory && DefaultFileSystem.DirectoryExists(Path.Combine(baseDirectory, directory.ToString()))) + || (shouldCheckFileOrDirectory && DefaultFileSystem.FileOrDirectoryExists(value.ToString())); + } + + /// + /// Extracts the directory from the given file-spec. + /// + /// The filespec. + /// directory path + internal static string GetDirectory(string fileSpec) + { + string? directory = Path.GetDirectoryName(FixFilePath(fileSpec)); + + // if file-spec is a root directory e.g. c:, c:\, \, \\server\share + // NOTE: Path.GetDirectoryName also treats invalid UNC file-specs as root directories e.g. \\, \\server + if (directory == null) + { + // just use the file-spec as-is + directory = fileSpec; + } + else if ((directory.Length > 0) && !EndsWithSlash(directory)) + { + // restore trailing slash if Path.GetDirectoryName has removed it (this happens with non-root directories) + directory += Path.DirectorySeparatorChar; + } + + return directory; + } + + /// + /// Deletes all subdirectories within the specified directory without throwing exceptions. + /// This method enumerates all subdirectories in the given directory and attempts to delete + /// each one recursively. If any IO-related exceptions occur during enumeration or deletion, + /// they are silently ignored. + /// + /// The directory whose subdirectories should be deleted. + /// + /// This method is useful for cleanup operations where partial failure is acceptable. + /// It will not delete the root directory itself, only its subdirectories. + /// IO exceptions during directory enumeration or deletion are caught and ignored. + /// + internal static void DeleteSubdirectoriesNoThrow(string directory) + { + try + { + foreach (string dir in FileSystems.Default.EnumerateDirectories(directory)) + { + DeleteDirectoryNoThrow(dir, recursive: true, retryCount: 1); + } + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + // If we can't enumerate the directories, ignore. Other cases should be handled by DeleteDirectoryNoThrow. + } + } + + /// + /// Determines whether the given assembly file name has one of the listed extensions. + /// + /// The name of the file + /// Array of extensions to consider. + /// + internal static bool HasExtension(string fileName, string[] allowedExtensions) + { + Debug.Assert(allowedExtensions?.Length > 0); + + // Easiest way to invoke invalid path chars + // check, which callers are relying on. + if (allowedExtensions != null && Path.HasExtension(fileName)) + { + foreach (string extension in allowedExtensions) + { + Debug.Assert(!String.IsNullOrEmpty(extension) && extension[0] == '.'); + + if (fileName.EndsWith(extension, PathComparison)) + { + return true; + } + } + } + + return false; + } + + // ISO 8601 Universal time with sortable format + internal const string FileTimeFormat = "yyyy'-'MM'-'dd HH':'mm':'ss'.'fffffff"; + + /// + /// Determines the full path for the given file-spec. + /// ASSUMES INPUT IS STILL ESCAPED + /// + /// The file spec to get the full path of. + /// + /// Whether to escape the path after getting the full path. + /// Full path to the file, escaped if not specified otherwise. + internal static string GetFullPath(string fileSpec, string currentDirectory, bool escape = true) + { + // Sending data out of the engine into the filesystem, so time to unescape. + fileSpec = FixFilePath(EscapingUtilities.UnescapeAll(fileSpec)); + + string fullPath = NormalizePath(Path.Combine(currentDirectory, fileSpec)); + // In some cases we might want to NOT escape in order to preserve symbols like @, %, $ etc. + if (escape) + { + // Data coming back from the filesystem into the engine, so time to escape it back. + fullPath = EscapingUtilities.Escape(fullPath); + } + + if (NativeMethods.IsWindows && !EndsWithSlash(fullPath)) + { + if (FileUtilitiesRegex.IsDrivePattern(fileSpec) || + FileUtilitiesRegex.IsUncPattern(fullPath)) + { + // append trailing slash if Path.GetFullPath failed to (this happens with drive-specs and UNC shares) + fullPath += Path.DirectorySeparatorChar; + } + } + + return fullPath; + } + + /// + /// A variation of Path.GetFullPath that will return the input value + /// instead of throwing any IO exception. + /// Useful to get a better path for an error message, without the risk of throwing + /// if the error message was itself caused by the path being invalid! + /// + internal static string GetFullPathNoThrow(string path) + { + try + { + path = NormalizePath(path); + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + } + + return path; + } + + /// + /// Compare if two paths, relative to the given currentDirectory are equal. + /// Does not throw IO exceptions. See + /// + /// + /// + /// + /// + /// + internal static bool ComparePathsNoThrow(string first, string second, string currentDirectory, bool alwaysIgnoreCase = false) + { + StringComparison pathComparison = alwaysIgnoreCase ? StringComparison.OrdinalIgnoreCase : PathComparison; + // perf: try comparing the bare strings first + if (string.Equals(first, second, pathComparison)) + { + return true; + } + + var firstFullPath = NormalizePathForComparisonNoThrow(first, currentDirectory); + var secondFullPath = NormalizePathForComparisonNoThrow(second, currentDirectory); + + return string.Equals(firstFullPath, secondFullPath, pathComparison); + } + + /// + /// Normalizes a path for path comparison + /// Does not throw IO exceptions. See + /// + /// + internal static string NormalizePathForComparisonNoThrow(string path, string currentDirectory) + { + // file is invalid, return early to avoid triggering an exception + if (PathIsInvalid(path)) + { + return path; + } + + var normalizedPath = NormalizeForPathComparison(path); + var fullPath = GetFullPathNoThrow(Path.Combine(currentDirectory, normalizedPath)); + + return fullPath; + } + + internal static bool PathIsInvalid(string path) + { + // Path.GetFileName does not react well to malformed filenames. + // For example, Path.GetFileName("a/b/foo:bar") returns bar instead of foo:bar + // It also throws exceptions on illegal path characters +#if NET + if (!path.AsSpan().ContainsAny(InvalidPathChars)) + { + int lastDirectorySeparator = path.LastIndexOfAny(Slashes); + return path.AsSpan(lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0).ContainsAny(InvalidFileNameChars); + } +#else + if (path.IndexOfAny(InvalidPathChars) < 0) + { + int lastDirectorySeparator = path.LastIndexOfAny(Slashes); + return path.IndexOfAny(InvalidFileNameChars, lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0) >= 0; + } +#endif + return true; + } + + /// + /// A variation on File.Delete that will throw ExceptionHandling.NotExpectedException exceptions + /// + internal static void DeleteNoThrow(string path) + { + try + { + File.Delete(FixFilePath(path)); + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + } + } + + /// + /// A variation on Directory.Delete that will throw ExceptionHandling.NotExpectedException exceptions + /// + [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Int32.TryParse(System.String,System.Int32@)", Justification = "We expect the out value to be 0 if the parse fails and compensate accordingly")] + internal static void DeleteDirectoryNoThrow(string path, bool recursive, int retryCount = 0, int retryTimeOut = 0) + { + // Try parse will set the out parameter to 0 if the string passed in is null, or is outside the range of an int. + if (!int.TryParse(Environment.GetEnvironmentVariable("MSBUILDDIRECTORYDELETERETRYCOUNT"), out retryCount)) + { + retryCount = 0; + } + + if (!int.TryParse(Environment.GetEnvironmentVariable("MSBUILDDIRECTORYDELETRETRYTIMEOUT"), out retryTimeOut)) + { + retryTimeOut = 0; + } + + retryCount = retryCount < 1 ? 2 : retryCount; + retryTimeOut = retryTimeOut < 1 ? 500 : retryTimeOut; + + path = FixFilePath(path); + + for (int i = 0; i < retryCount; i++) + { + try + { + if (DefaultFileSystem.DirectoryExists(path)) + { + Directory.Delete(path, recursive); + break; + } + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + } + + if (i + 1 < retryCount) // should not wait for the final iteration since we not gonna check anyway + { + Thread.Sleep(retryTimeOut); + } + } + } + + /// + /// Deletes a directory, ensuring that Directory.Delete does not get a path ending in a slash. + /// + /// + /// This is a workaround for https://github.com/dotnet/corefx/issues/3780, which clashed with a common + /// pattern in our tests. + /// + internal static void DeleteWithoutTrailingBackslash(string path, bool recursive = false) + { + // Some tests (such as FileMatcher and Evaluation tests) were failing with an UnauthorizedAccessException or directory not empty. + // This retry logic works around that issue. + const int NUM_TRIES = 3; + for (int i = 0; i < NUM_TRIES; i++) + { + try + { + Directory.Delete(EnsureNoTrailingSlash(path), recursive); + + // If we got here, the directory was successfully deleted + return; + } + catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) + { + if (i == NUM_TRIES - 1) + { + // var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories); + // string fileString = string.Join(Environment.NewLine, files); + // string message = $"Unable to delete directory '{path}'. Contents:" + Environment.NewLine + fileString; + // throw new IOException(message, ex); + throw; + } + } + + Thread.Sleep(10); + } + } + + /// + /// Gets a file info object for the specified file path. If the file path + /// is invalid, or is a directory, or cannot be accessed, or does not exist, + /// it returns null rather than throwing or returning a FileInfo around a non-existent file. + /// This allows it to be called where File.Exists() (which never throws, and returns false + /// for directories) was called - but with the advantage that a FileInfo object is returned + /// that can be queried (e.g., for LastWriteTime) without hitting the disk again. + /// + /// + /// FileInfo around path if it is an existing /file/, else null + internal static FileInfo? GetFileInfoNoThrow(string filePath) + { + filePath = AttemptToShortenPath(filePath); + + FileInfo fileInfo; + + try + { + fileInfo = new FileInfo(filePath); + } + catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) + { + // Invalid or inaccessible path: treat as if nonexistent file, just as File.Exists does + return null; + } + + if (fileInfo.Exists) + { + // It's an existing file + return fileInfo; + } + else + { + // Nonexistent, or existing but a directory, just as File.Exists behaves + return null; + } + } + + /// + /// Returns if the directory exists + /// + /// Full path to the directory in the filesystem + /// The file system + /// + internal static bool DirectoryExistsNoThrow(string fullPath, IFileSystem? fileSystem = null) + { + fullPath = AttemptToShortenPath(fullPath); + + try + { + fileSystem ??= DefaultFileSystem; + + return Traits.Instance.CacheFileExistence + ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.DirectoryExists(fullPath)) + : fileSystem.DirectoryExists(fullPath); + } + catch + { + return false; + } + } + + /// + /// Returns if the directory exists + /// + /// Full path to the file in the filesystem + /// The file system + /// + internal static bool FileExistsNoThrow(string fullPath, IFileSystem? fileSystem = null) + { + fullPath = AttemptToShortenPath(fullPath); + + try + { + fileSystem ??= DefaultFileSystem; + + return Traits.Instance.CacheFileExistence + ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.FileExists(fullPath)) + : fileSystem.FileExists(fullPath); + } + catch + { + return false; + } + } + + /// + /// If there is a directory or file at the specified path, returns true. + /// Otherwise, returns false. + /// Does not throw IO exceptions, to match Directory.Exists and File.Exists. + /// Unlike calling each of those in turn it only accesses the disk once, which is faster. + /// + internal static bool FileOrDirectoryExistsNoThrow(string fullPath, IFileSystem? fileSystem = null) + { + fullPath = AttemptToShortenPath(fullPath); + + try + { + fileSystem ??= DefaultFileSystem; + + return Traits.Instance.CacheFileExistence + ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.FileOrDirectoryExists(fullPath)) + : fileSystem.FileOrDirectoryExists(fullPath); + } + catch + { + return false; + } + } + + /// + /// This method returns true if the specified filename is a solution file (.sln) or + /// solution filter file (.slnf); otherwise, it returns false. + /// + /// + /// Solution filters are included because they are a thin veneer over solutions, just + /// with a more limited set of projects to build, and should be treated the same way. + /// + internal static bool IsSolutionFilename(string filename) + { + return HasExtension(filename, ".sln") || + HasExtension(filename, ".slnf") || + HasExtension(filename, ".slnx"); + } + + internal static bool IsSolutionFilterFilename(string filename) + { + return HasExtension(filename, ".slnf"); + } + + internal static bool IsSolutionXFilename(string filename) + { + return HasExtension(filename, ".slnx"); + } + + /// + /// Returns true if the specified filename is a VC++ project file, otherwise returns false + /// + internal static bool IsVCProjFilename(string filename) + { + return HasExtension(filename, ".vcproj"); + } + + internal static bool IsDspFilename(string filename) + { + return HasExtension(filename, ".dsp"); + } + + /// + /// Returns true if the specified filename is a metaproject file (.metaproj), otherwise false. + /// + internal static bool IsMetaprojectFilename(string filename) + { + return HasExtension(filename, ".metaproj"); + } + + internal static bool IsBinaryLogFilename(string filename) + { + return HasExtension(filename, ".binlog"); + } + + private static bool HasExtension(string filename, string extension) + { + if (String.IsNullOrEmpty(filename)) + { + return false; + } + + return filename.EndsWith(extension, PathComparison); + } + + /// + /// Given the absolute location of a file, and a disc location, returns relative file path to that disk location. + /// Throws UriFormatException. + /// + /// + /// The base path we want to be relative to. Must be absolute. + /// Should not include a filename as the last segment will be interpreted as a directory. + /// + /// + /// The path we need to make relative to basePath. The path can be either absolute path or a relative path in which case it is relative to the base path. + /// If the path cannot be made relative to the base path (for example, it is on another drive), it is returned verbatim. + /// If the basePath is an empty string, returns the path. + /// + /// relative path (can be the full path) + internal static string MakeRelative(string basePath, string path) + { + ArgumentNullException.ThrowIfNull(basePath); + ArgumentException.ThrowIfNullOrEmpty(path); + + string fullBase = GetFullPath(basePath); + string fullPath = GetFullPath(path); + + string[] splitBase = fullBase.Split(MSBuildConstants.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries); + string[] splitPath = fullPath.Split(MSBuildConstants.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries); + + FrameworkErrorUtilities.VerifyThrow(splitPath.Length > 0, "Cannot call MakeRelative on a path of only slashes."); + + // On a mac, the path could start with any number of slashes and still be valid. We have to check them all. + int indexOfFirstNonSlashChar = 0; + while (path[indexOfFirstNonSlashChar] == Path.DirectorySeparatorChar) + { + indexOfFirstNonSlashChar++; + } + if (path.IndexOf(splitPath[0]) != indexOfFirstNonSlashChar) + { + // path was already relative so just return it + return FixFilePath(path); + } + + int index = 0; + while (index < splitBase.Length && index < splitPath.Length && splitBase[index].Equals(splitPath[index], PathComparison)) + { + index++; + } + + if (index == splitBase.Length && index == splitPath.Length) + { + return "."; + } + + // If the paths have no component in common, the only valid relative path is the full path. + if (index == 0) + { + return fullPath; + } + + StringBuilder sb = StringBuilderCache.Acquire(); + + for (int i = index; i < splitBase.Length; i++) + { + sb.Append("..").Append(Path.DirectorySeparatorChar); + } + for (int i = index; i < splitPath.Length; i++) + { + sb.Append(splitPath[i]).Append(Path.DirectorySeparatorChar); + } + + if (fullPath[fullPath.Length - 1] != Path.DirectorySeparatorChar) + { + sb.Length--; + } + + return StringBuilderCache.GetStringAndRelease(sb); + } + + /// + /// Normalizes the path if and only if it is longer than max path, + /// or would be if rooted by the current directory. + /// This may make it shorter by removing ".."'s. + /// + internal static string AttemptToShortenPath(string path) + { + if (IsPathTooLong(path) || IsPathTooLongIfRooted(path)) + { + // Attempt to make it shorter -- perhaps there are some \..\ elements + path = GetFullPathNoThrow(path); + } + return FixFilePath(path); + } + private static bool IsPathTooLongIfRooted(string path) + { + bool hasMaxPath = NativeMethods.HasMaxPath; + int maxPath = NativeMethods.MaxPath; + // >= not > because MAX_PATH assumes a trailing null + return hasMaxPath && !IsRootedNoThrow(path) && NativeMethods.GetCurrentDirectory().Length + path.Length + 1 /* slash */ >= maxPath; + } + + /// + /// A variation of Path.IsRooted that not throw any IO exception. + /// + private static bool IsRootedNoThrow(string path) + { + try + { + return Path.IsPathRooted(FixFilePath(path)); + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + return false; + } + } + + /// + /// Get the folder N levels above the given. Will stop and return current path when rooted. + /// + /// Path to get the folder above. + /// Number of levels up to walk. + /// Full path to the folder N levels above the path. + internal static string GetFolderAbove(string path, int count = 1) + { + if (count < 1) + { + return path; + } + + var parent = Directory.GetParent(path); + + while (count > 1 && parent?.Parent != null) + { + parent = parent.Parent; + count--; + } + + return parent?.FullName ?? path; + } + + /// + /// Combine multiple paths. Should only be used when compiling against .NET 2.0. + /// + /// Only use in .NET 2.0. Otherwise, use System.IO.Path.Combine(...) + /// + /// + /// Root path. + /// Paths to concatenate. + /// Combined path. + internal static string CombinePaths(string root, params string[] paths) + { + ArgumentNullException.ThrowIfNull(root); + ArgumentNullException.ThrowIfNull(paths); + + return paths.Aggregate(root, Path.Combine); + } + + internal static string TrimTrailingSlashes(this string s) + { + return s.TrimEnd(Slashes); + } + + /// + /// Replace all backward slashes to forward slashes + /// + internal static string ToSlash(this string s) + { + return s.Replace('\\', '/'); + } + + internal static string ToBackslash(this string s) + { + return s.Replace('/', '\\'); + } + + /// + /// Ensure all slashes are the current platform's slash + /// + /// + /// + internal static string ToPlatformSlash(this string s) + { + var separator = Path.DirectorySeparatorChar; + + return s.Replace(separator == '/' ? '\\' : '/', separator); + } + + internal static string WithTrailingSlash(this string s) + { + return EnsureTrailingSlash(s); + } + + internal static string NormalizeForPathComparison(this string s) + => s.ToPlatformSlash().TrimTrailingSlashes(); + + // TODO: assumption on file system case sensitivity: https://github.com/dotnet/msbuild/issues/781 + internal static bool PathsEqual(string path1, string path2) + { + if (path1 == null && path2 == null) + { + return true; + } + if (path1 == null || path2 == null) + { + return false; + } + + var endA = path1.Length - 1; + var endB = path2.Length - 1; + + // Trim trailing slashes + for (var i = endA; i >= 0; i--) + { + var c = path1[i]; + if (c == '/' || c == '\\') + { + endA--; + } + else + { + break; + } + } + + for (var i = endB; i >= 0; i--) + { + var c = path2[i]; + if (c == '/' || c == '\\') + { + endB--; + } + else + { + break; + } + } + + if (endA != endB) + { + // Lengths not the same + return false; + } + + for (var i = 0; i <= endA; i++) + { + var charA = (uint)path1[i]; + var charB = (uint)path2[i]; + + if ((charA | charB) > 0x7F) + { + // Non-ascii chars move to non fast path + return PathsEqualNonAscii(path1, path2, i, endA - i + 1); + } + + // uppercase both chars - notice that we need just one compare per char + if ((uint)(charA - 'a') <= (uint)('z' - 'a')) + { + charA -= 0x20; + } + + if ((uint)(charB - 'a') <= (uint)('z' - 'a')) + { + charB -= 0x20; + } + + // Set path delimiters the same + if (charA == '\\') + { + charA = '/'; + } + if (charB == '\\') + { + charB = '/'; + } + + if (charA != charB) + { + return false; + } + } + + return true; + } + + internal static StreamWriter OpenWrite(string path, bool append, Encoding? encoding = null) + { + const int DefaultFileStreamBufferSize = 4096; + FileMode mode = append ? FileMode.Append : FileMode.Create; + Stream fileStream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); + if (encoding == null) + { + return new StreamWriter(fileStream); + } + else + { + return new StreamWriter(fileStream, encoding); + } + } + + internal static StreamReader OpenRead(string path, Encoding? encoding = null, bool detectEncodingFromByteOrderMarks = true) + { + const int DefaultFileStreamBufferSize = 4096; + Stream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); + if (encoding == null) + { + return new StreamReader(fileStream); + } + else + { + return new StreamReader(fileStream, encoding, detectEncodingFromByteOrderMarks); + } + } + + /// + /// Locate a file in either the directory specified or a location in the + /// directory structure above that directory. + /// + internal static string GetDirectoryNameOfFileAbove(string startingDirectory, string fileName, IFileSystem? fileSystem = null) + { + fileSystem ??= DefaultFileSystem; + + // Canonicalize our starting location + string? lookInDirectory = GetFullPath(startingDirectory); + + do + { + // Construct the path that we will use to test against + string possibleFileDirectory = Path.Combine(lookInDirectory, fileName); + + // If we successfully locate the file in the directory that we're + // looking in, simply return that location. Otherwise we'll + // keep moving up the tree. + if (fileSystem.FileExists(possibleFileDirectory)) + { + // We've found the file, return the directory we found it in + return lookInDirectory; + } + else + { + // GetDirectoryName will return null when we reach the root + // terminating our search + lookInDirectory = Path.GetDirectoryName(lookInDirectory); + } + } + while (lookInDirectory != null); + + // When we didn't find the location, then return an empty string + return string.Empty; + } + + /// + /// Searches for a file based on the specified starting directory. + /// + /// The file to search for. + /// An optional directory to start the search in. The default location is the directory + /// of the file containing the property function. + /// The filesystem + /// The full path of the file if it is found, otherwise an empty string. + internal static string GetPathOfFileAbove(string file, string startingDirectory, IFileSystem? fileSystem = null) + { + // This method does not accept a path, only a file name + if (file.Any(i => i.Equals(Path.DirectorySeparatorChar) || i.Equals(Path.AltDirectorySeparatorChar))) + { + throw new ArgumentException(SR.FormatInvalidGetPathOfFileAboveParameter(file)); + } + + // Search for a directory that contains that file + string directoryName = GetDirectoryNameOfFileAbove(startingDirectory, file, fileSystem); + + return String.IsNullOrEmpty(directoryName) ? String.Empty : NormalizePath(directoryName, file); + } + + internal static void EnsureDirectoryExists(string directoryPath) + { + if (!string.IsNullOrEmpty(directoryPath) && !DefaultFileSystem.DirectoryExists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + } + + // Method is simple set of function calls and may inline; + // we don't want it inlining into the tight loop that calls it as an exit case, + // so mark as non-inlining + [MethodImpl(MethodImplOptions.NoInlining)] + 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; + } + + var slash1 = ToSlash(strA); + var slash2 = ToSlash(strB); + + if (string.Compare(slash1, i, slash2, i, length, StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + + return false; + } + + /// + /// Clears the file existence cache. + /// + internal static void ClearFileExistenceCache() + { + FileExistenceCache.Clear(); + } + + internal static void ReadFromStream(this Stream stream, byte[] content, int startIndex, int length) + { + stream.ReadExactly(content, startIndex, length); + } } } diff --git a/src/Shared/FileUtilitiesRegex.cs b/src/Framework/FileUtilitiesRegex.cs similarity index 99% rename from src/Shared/FileUtilitiesRegex.cs rename to src/Framework/FileUtilitiesRegex.cs index 6a27e415aec..03a29979c53 100644 --- a/src/Shared/FileUtilitiesRegex.cs +++ b/src/Framework/FileUtilitiesRegex.cs @@ -155,9 +155,7 @@ internal static int StartsWithUncPatternMatchLength(string pattern) /// /// Input to check for UNC pattern minimum requirements. /// true if the UNC pattern is a minimum length of 5 and the first two characters are be a slash, false otherwise. -#if !NET35 [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif internal static bool MeetsUncPatternMinimumRequirements(string pattern) { return pattern.Length >= 5 && diff --git a/src/Framework/FileUtilities_TempFiles.cs b/src/Framework/FileUtilities_TempFiles.cs new file mode 100644 index 00000000000..956bb8ad882 --- /dev/null +++ b/src/Framework/FileUtilities_TempFiles.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.Build.Shared.FileSystem; + +#nullable disable + +namespace Microsoft.Build.Framework; + +internal static partial class FileUtilities +{ + private static Lazy tempFileDirectory = CreateTempFileDirectoryLazy(); + + private const string msbuildTempFolderPrefix = "MSBuildTemp"; + + internal static string TempFileDirectory => tempFileDirectory.Value; + + private static Lazy CreateTempFileDirectoryLazy() + { + return new Lazy( + () => + { + string path = CreateFolderUnderTemp(); + RegisterCleanupOnExit(path); + return path; + }, + LazyThreadSafetyMode.ExecutionAndPublication); + } + + private static void RegisterCleanupOnExit(string pathToCleanup) + { + AppDomain.CurrentDomain.ProcessExit += (_, _) => + { + try + { + if (Directory.Exists(pathToCleanup)) + { + Directory.Delete(pathToCleanup, recursive: true); + } + } + catch + { + // Best effort - ignore failures during cleanup + } + }; + } + + internal static void ClearTempFileDirectory() + { + tempFileDirectory = CreateTempFileDirectoryLazy(); + } + + private static string CreateFolderUnderTemp() + { + string path; + +#if NET + path = Directory.CreateTempSubdirectory(msbuildTempFolderPrefix).FullName; +#else + // CreateTempSubdirectory API is not available in .NET Framework + path = Path.Combine(Path.GetTempPath(), $"{msbuildTempFolderPrefix}{Guid.NewGuid():N}"); + Directory.CreateDirectory(path); +#endif + + return EnsureTrailingSlash(path); + } + + /// + /// Generates a unique directory name in the temporary folder. + /// Caller must delete when finished. + /// + /// + /// + internal static string GetTemporaryDirectory(bool createDirectory = true, string subfolder = null) + { + string temporaryDirectory = Path.Combine(TempFileDirectory, $"Temporary{Guid.NewGuid():N}", subfolder ?? string.Empty); + + if (createDirectory) + { + Directory.CreateDirectory(temporaryDirectory); + } + + return temporaryDirectory; + } + + /// + /// Generates a unique temporary file name with a given extension in the temporary folder. + /// File is guaranteed to be unique. + /// Extension may have an initial period. + /// File will NOT be created. + /// May throw IOException. + /// + internal static string GetTemporaryFileName() + { + return GetTemporaryFileName(".tmp"); + } + + /// + /// Generates a unique temporary file name with a given extension in the temporary folder. + /// File is guaranteed to be unique. + /// Extension may have an initial period. + /// File will NOT be created. + /// May throw IOException. + /// + internal static string GetTemporaryFileName(string extension) + { + return GetTemporaryFile(null, null, extension, false); + } + + /// + /// Generates a unique temporary file name with a given extension in the temporary folder. + /// If no extension is provided, uses ".tmp". + /// File is guaranteed to be unique. + /// Caller must delete it when finished. + /// + internal static string GetTemporaryFile() + { + return GetTemporaryFile(".tmp"); + } + + /// + /// Generates a unique temporary file name with a given extension in the temporary folder. + /// File is guaranteed to be unique. + /// Caller must delete it when finished. + /// + internal static string GetTemporaryFile(string fileName, string extension, bool createFile) + { + return GetTemporaryFile(null, fileName, extension, createFile); + } + + /// + /// Generates a unique temporary file name with a given extension in the temporary folder. + /// File is guaranteed to be unique. + /// Extension may have an initial period. + /// Caller must delete it when finished. + /// May throw IOException. + /// + internal static string GetTemporaryFile(string extension) + { + return GetTemporaryFile(null, null, extension); + } + + /// + /// Creates a file with unique temporary file name with a given extension in the specified folder. + /// File is guaranteed to be unique. + /// Extension may have an initial period. + /// If folder is null, the temporary folder will be used. + /// Caller must delete it when finished. + /// May throw IOException. + /// + internal static string GetTemporaryFile(string directory, string fileName, string extension, bool createFile = true) + { + if (directory is not null) + { + ArgumentException.ThrowIfNullOrEmpty(directory); + } + + try + { + directory ??= TempFileDirectory; + + // If the extension needs a dot prepended, do so. + if (extension is null) + { + extension = string.Empty; + } + else if (extension.Length > 0 && extension[0] != '.') + { + extension = '.' + extension; + } + + // If the fileName is null, use tmp{Guid}; otherwise use fileName. + if (string.IsNullOrEmpty(fileName)) + { + fileName = $"tmp{Guid.NewGuid():N}"; + } + + Directory.CreateDirectory(directory); + + string file = Path.Combine(directory, $"{fileName}{extension}"); + + FrameworkErrorUtilities.VerifyThrow(!FileSystems.Default.FileExists(file), "Guid should be unique"); + + if (createFile) + { + File.WriteAllText(file, string.Empty); + } + + return file; + } + catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) + { + throw new IOException(SR.FormatFailedCreatingTempFile(ex.Message), ex); + } + } + + internal static void CopyDirectory(string source, string dest) + { + Directory.CreateDirectory(dest); + + DirectoryInfo sourceInfo = new DirectoryInfo(source); + foreach (var fileInfo in sourceInfo.GetFiles()) + { + string destFile = Path.Combine(dest, fileInfo.Name); + fileInfo.CopyTo(destFile); + } + foreach (var subdirInfo in sourceInfo.GetDirectories()) + { + string destDir = Path.Combine(dest, subdirInfo.Name); + CopyDirectory(subdirInfo.FullName, destDir); + } + } + + public sealed class TempWorkingDirectory : IDisposable + { + public string Path { get; } + + public TempWorkingDirectory(string sourcePath, [CallerMemberName] string name = null) + { + Path = name == null + ? GetTemporaryDirectory() + : System.IO.Path.Combine(TempFileDirectory, name); + + if (FileSystems.Default.DirectoryExists(Path)) + { + Directory.Delete(Path, true); + } + + CopyDirectory(sourcePath, Path); + } + + public void Dispose() + { + Directory.Delete(Path, true); + } + } +} diff --git a/src/Framework/IConstrainedEqualityComparer.cs b/src/Framework/IConstrainedEqualityComparer.cs index 3ecc49524e9..2132ce59c88 100644 --- a/src/Framework/IConstrainedEqualityComparer.cs +++ b/src/Framework/IConstrainedEqualityComparer.cs @@ -11,11 +11,7 @@ namespace Microsoft.Build.Collections /// Defines methods to support the comparison of objects for /// equality over constrained inputs. /// -#if TASKHOST - internal interface IConstrainedEqualityComparer : IEqualityComparer -#else internal interface IConstrainedEqualityComparer : IEqualityComparer -#endif { /// /// Determines whether the specified objects are equal, factoring in the specified bounds when comparing . diff --git a/src/Framework/IMSBuildElementLocation.cs b/src/Framework/IMSBuildElementLocation.cs new file mode 100644 index 00000000000..aa994b712e6 --- /dev/null +++ b/src/Framework/IMSBuildElementLocation.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Build.Shared; + +/// +/// Represents the location information for error reporting purposes. This is normally used to +/// associate a run-time error with the original XML. +/// This is not used for arbitrary errors from tasks, which store location in a BuildXXXXEventArgs. +/// All implementations should be IMMUTABLE. +/// Any editing of the project XML through the MSBuild API's will invalidate locations in that XML until the XML is reloaded. +/// +public interface IMSBuildElementLocation +{ + /// + /// The file from which this particular element originated. It may + /// differ from the ProjectFile if, for instance, it was part of + /// an import or originated in a targets file. + /// Should always have a value. + /// If not known, returns empty string. + /// + string File + { + get; + } + + /// + /// The line number where this element exists in its file. + /// The first line is numbered 1. + /// Zero indicates "unknown location". + /// + int Line + { + get; + } + + /// + /// The column number where this element exists in its file. + /// The first column is numbered 1. + /// Zero indicates "unknown location". + /// + int Column + { + get; + } + + /// + /// The location in a form suitable for replacement + /// into a message. + /// + string LocationString + { + get; + } +} diff --git a/src/Framework/ITranslator.cs b/src/Framework/ITranslator.cs index 3b191f6503c..4196a66293b 100644 --- a/src/Framework/ITranslator.cs +++ b/src/Framework/ITranslator.cs @@ -267,14 +267,6 @@ BinaryWriter Writer /// The value to be translated. void Translate(ref TimeSpan value); - // MSBuildTaskHost is based on CLR 3.5, which does not have the 6-parameter constructor for BuildEventContext, - // which is what current implementations of this method use. However, it also does not ever need to translate - // BuildEventContexts, so it should be perfectly safe to compile this method out of that assembly. I am compiling - // the method out of the interface as well, instead of just making the method empty, so that if we ever do need - // to translate BuildEventContexts from the CLR 3.5 task host, it will become immediately obvious, rather than - // failing or misbehaving silently. -#if !CLR2COMPATIBILITY - /// /// Translates a BuildEventContext /// @@ -284,7 +276,6 @@ BinaryWriter Writer /// /// The context to be translated. void Translate(ref BuildEventContext value); -#endif /// /// Translates an enumeration. diff --git a/src/Framework/ImmutableDictionaryExtensions.cs b/src/Framework/ImmutableDictionaryExtensions.cs index ba7b04d91ae..47972976cca 100644 --- a/src/Framework/ImmutableDictionaryExtensions.cs +++ b/src/Framework/ImmutableDictionaryExtensions.cs @@ -16,7 +16,6 @@ internal static class ImmutableDictionaryExtensions public static readonly ImmutableDictionary EmptyMetadata = ImmutableDictionary.Empty.WithComparers(MSBuildNameIgnoreCaseComparer.Default); -#if !TASKHOST /// /// Sets the given items while running a validation function on each key. /// @@ -42,6 +41,5 @@ public static ImmutableDictionary SetItems( return builder.ToImmutable(); } -#endif } } diff --git a/src/Framework/ItemSpecModifiers.cs b/src/Framework/ItemSpecModifiers.cs new file mode 100644 index 00000000000..7e87652adb7 --- /dev/null +++ b/src/Framework/ItemSpecModifiers.cs @@ -0,0 +1,446 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; + +#nullable disable + +namespace Microsoft.Build.Framework; + +/// +/// Encapsulates the definitions of the item-spec modifiers a.k.a. reserved item metadata. +/// +internal static class ItemSpecModifiers +{ + internal const string FullPath = "FullPath"; + internal const string RootDir = "RootDir"; + internal const string Filename = "Filename"; + internal const string Extension = "Extension"; + internal const string RelativeDir = "RelativeDir"; + internal const string Directory = "Directory"; + internal const string RecursiveDir = "RecursiveDir"; + internal const string Identity = "Identity"; + internal const string ModifiedTime = "ModifiedTime"; + internal const string CreatedTime = "CreatedTime"; + internal const string AccessedTime = "AccessedTime"; + internal const string DefiningProjectFullPath = "DefiningProjectFullPath"; + internal const string DefiningProjectDirectory = "DefiningProjectDirectory"; + internal const string DefiningProjectName = "DefiningProjectName"; + internal const string DefiningProjectExtension = "DefiningProjectExtension"; + + // These are all the well-known attributes. + internal static readonly string[] All = + { + FullPath, + RootDir, + Filename, + Extension, + RelativeDir, + Directory, + RecursiveDir, // <-- Not derivable. + Identity, + ModifiedTime, + CreatedTime, + AccessedTime, + DefiningProjectFullPath, + DefiningProjectDirectory, + DefiningProjectName, + DefiningProjectExtension + }; + + private static readonly FrozenSet s_tableOfItemSpecModifiers = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, All); + private static readonly FrozenSet s_tableOfDefiningProjectModifiers = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, + [ + DefiningProjectFullPath, + DefiningProjectDirectory, + DefiningProjectName, + DefiningProjectExtension, + ]); + + /// + /// Indicates if the given name is reserved for an item-spec modifier. + /// + internal static bool IsItemSpecModifier(string name) + { + if (name == null) + { + return false; + } + + // Could still be a case-insensitive match. + bool result = s_tableOfItemSpecModifiers.Contains(name); + + return result; + } + + /// + /// Indicates if the given name is reserved for one of the specific subset of itemspec + /// modifiers to do with the defining project of the item. + /// + internal static bool IsDefiningProjectModifier(string name) => s_tableOfDefiningProjectModifiers.Contains(name); + + /// + /// Indicates if the given name is reserved for a derivable item-spec modifier. + /// Derivable means it can be computed given a file name. + /// + /// Name to check. + /// true, if name of a derivable modifier + internal static bool IsDerivableItemSpecModifier(string name) + { + bool isItemSpecModifier = IsItemSpecModifier(name); + + if (isItemSpecModifier) + { + if (name.Length == 12) + { + if (name[0] == 'R' || name[0] == 'r') + { + // The only 12 letter ItemSpecModifier that starts with 'R' is 'RecursiveDir' + return false; + } + } + } + + return isItemSpecModifier; + } + + /// + /// Performs path manipulations on the given item-spec as directed. + /// Does not cache the result. + /// + internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string definingProjectEscaped, string modifier) + { + string dummy = null; + return GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, modifier, ref dummy); + } + + /// + /// Performs path manipulations on the given item-spec as directed. + /// + /// Supported modifiers: + /// %(FullPath) = full path of item + /// %(RootDir) = root directory of item + /// %(Filename) = item filename without extension + /// %(Extension) = item filename extension + /// %(RelativeDir) = item directory as given in item-spec + /// %(Directory) = full path of item directory relative to root + /// %(RecursiveDir) = portion of item path that matched a recursive wildcard + /// %(Identity) = item-spec as given + /// %(ModifiedTime) = last write time of item + /// %(CreatedTime) = creation time of item + /// %(AccessedTime) = last access time of item + /// + /// NOTES: + /// 1) This method always returns an empty string for the %(RecursiveDir) modifier because it does not have enough + /// information to compute it -- only the BuildItem class can compute this modifier. + /// 2) All but the file time modifiers could be cached, but it's not worth the space. Only full path is cached, as the others are just string manipulations. + /// + /// + /// Methods of the Path class "normalize" slashes and periods. For example: + /// 1) successive slashes are combined into 1 slash + /// 2) trailing periods are discarded + /// 3) forward slashes are changed to back-slashes + /// + /// As a result, we cannot rely on any file-spec that has passed through a Path method to remain the same. We will + /// therefore not bother preserving slashes and periods when file-specs are transformed. + /// + /// Never returns null. + /// + /// The root directory for relative item-specs. When called on the Engine thread, this is the project directory. When called as part of building a task, it is null, indicating that the current directory should be used. + /// The item-spec to modify. + /// The path to the project that defined this item (may be null). + /// The modifier to apply to the item-spec. + /// Full path if any was previously computed, to cache. + /// The modified item-spec (can be empty string, but will never be null). + /// Thrown when the item-spec is not a path. + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Pre-existing")] + internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string definingProjectEscaped, string modifier, ref string fullPath) + { + FrameworkErrorUtilities.VerifyThrow(itemSpec != null, "Need item-spec to modify."); + FrameworkErrorUtilities.VerifyThrow(modifier != null, "Need modifier to apply to item-spec."); + + string modifiedItemSpec = null; + + try + { + if (string.Equals(modifier, FullPath, StringComparison.OrdinalIgnoreCase)) + { + if (fullPath != null) + { + return fullPath; + } + + if (currentDirectory == null) + { + currentDirectory = FileUtilities.CurrentThreadWorkingDirectory ?? string.Empty; + } + + modifiedItemSpec = FileUtilities.GetFullPath(itemSpec, currentDirectory); + fullPath = modifiedItemSpec; + + ThrowForUrl(modifiedItemSpec, itemSpec, currentDirectory); + } + else if (string.Equals(modifier, RootDir, StringComparison.OrdinalIgnoreCase)) + { + GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, FullPath, ref fullPath); + + modifiedItemSpec = Path.GetPathRoot(fullPath); + + if (!FileUtilities.EndsWithSlash(modifiedItemSpec)) + { + FrameworkErrorUtilities.VerifyThrow( + FileUtilitiesRegex.StartsWithUncPattern(modifiedItemSpec), + "Only UNC shares should be missing trailing slashes."); + + // restore/append trailing slash if Path.GetPathRoot() has either removed it, or failed to add it + // (this happens with UNC shares) + modifiedItemSpec += Path.DirectorySeparatorChar; + } + } + else if (string.Equals(modifier, Filename, StringComparison.OrdinalIgnoreCase)) + { + // if the item-spec is a root directory, it can have no filename + if (IsRootDirectory(itemSpec)) + { + // NOTE: this is to prevent Path.GetFileNameWithoutExtension() from treating server and share elements + // in a UNC file-spec as filenames e.g. \\server, \\server\share + modifiedItemSpec = string.Empty; + } + else + { + // Fix path to avoid problem with Path.GetFileNameWithoutExtension when backslashes in itemSpec on Unix + modifiedItemSpec = Path.GetFileNameWithoutExtension(FileUtilities.FixFilePath(itemSpec)); + } + } + else if (string.Equals(modifier, Extension, StringComparison.OrdinalIgnoreCase)) + { + // if the item-spec is a root directory, it can have no extension + if (IsRootDirectory(itemSpec)) + { + // NOTE: this is to prevent Path.GetExtension() from treating server and share elements in a UNC + // file-spec as filenames e.g. \\server.ext, \\server\share.ext + modifiedItemSpec = string.Empty; + } + else + { + modifiedItemSpec = Path.GetExtension(itemSpec); + } + } + else if (string.Equals(modifier, RelativeDir, StringComparison.OrdinalIgnoreCase)) + { + modifiedItemSpec = FileUtilities.GetDirectory(itemSpec); + } + else if (string.Equals(modifier, Directory, StringComparison.OrdinalIgnoreCase)) + { + GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, FullPath, ref fullPath); + + modifiedItemSpec = FileUtilities.GetDirectory(fullPath); + + if (NativeMethods.IsWindows) + { + int length = -1; + if (FileUtilitiesRegex.StartsWithDrivePattern(modifiedItemSpec)) + { + length = 2; + } + else + { + length = FileUtilitiesRegex.StartsWithUncPatternMatchLength(modifiedItemSpec); + } + + if (length != -1) + { + FrameworkErrorUtilities.VerifyThrow((modifiedItemSpec.Length > length) && FileUtilities.IsSlash(modifiedItemSpec[length]), + "Root directory must have a trailing slash."); + + modifiedItemSpec = modifiedItemSpec.Substring(length + 1); + } + } + else + { + FrameworkErrorUtilities.VerifyThrow(!string.IsNullOrEmpty(modifiedItemSpec) && FileUtilities.IsSlash(modifiedItemSpec[0]), + "Expected a full non-windows path rooted at '/'."); + + // A full unix path is always rooted at + // `/`, and a root-relative path is the + // rest of the string. + modifiedItemSpec = modifiedItemSpec.Substring(1); + } + } + else if (string.Equals(modifier, RecursiveDir, StringComparison.OrdinalIgnoreCase)) + { + // only the BuildItem class can compute this modifier -- so leave empty + modifiedItemSpec = String.Empty; + } + else if (string.Equals(modifier, Identity, StringComparison.OrdinalIgnoreCase)) + { + modifiedItemSpec = itemSpec; + } + else if (string.Equals(modifier, ModifiedTime, StringComparison.OrdinalIgnoreCase)) + { + // About to go out to the filesystem. This means data is leaving the engine, so need + // to unescape first. + string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); + + FileInfo info = FileUtilities.GetFileInfoNoThrow(unescapedItemSpec); + + if (info != null) + { + modifiedItemSpec = info.LastWriteTime.ToString(FileUtilities.FileTimeFormat, null); + } + else + { + // File does not exist, or path is a directory + modifiedItemSpec = String.Empty; + } + } + else if (string.Equals(modifier, CreatedTime, StringComparison.OrdinalIgnoreCase)) + { + // About to go out to the filesystem. This means data is leaving the engine, so need + // to unescape first. + string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); + + if (FileSystems.Default.FileExists(unescapedItemSpec)) + { + modifiedItemSpec = File.GetCreationTime(unescapedItemSpec).ToString(FileUtilities.FileTimeFormat, null); + } + else + { + // File does not exist, or path is a directory + modifiedItemSpec = String.Empty; + } + } + else if (string.Equals(modifier, AccessedTime, StringComparison.OrdinalIgnoreCase)) + { + // About to go out to the filesystem. This means data is leaving the engine, so need + // to unescape first. + string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); + + if (FileSystems.Default.FileExists(unescapedItemSpec)) + { + modifiedItemSpec = File.GetLastAccessTime(unescapedItemSpec).ToString(FileUtilities.FileTimeFormat, null); + } + else + { + // File does not exist, or path is a directory + modifiedItemSpec = String.Empty; + } + } + else if (IsDefiningProjectModifier(modifier)) + { + if (String.IsNullOrEmpty(definingProjectEscaped)) + { + // We have nothing to work with, but that's sometimes OK -- so just return String.Empty + modifiedItemSpec = String.Empty; + } + else + { + if (string.Equals(modifier, DefiningProjectDirectory, StringComparison.OrdinalIgnoreCase)) + { + // ItemSpecModifiers.Directory does not contain the root directory + modifiedItemSpec = Path.Combine( + GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, RootDir), + GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, Directory)); + } + else + { + string additionalModifier = null; + + if (string.Equals(modifier, DefiningProjectFullPath, StringComparison.OrdinalIgnoreCase)) + { + additionalModifier = FullPath; + } + else if (string.Equals(modifier, DefiningProjectName, StringComparison.OrdinalIgnoreCase)) + { + additionalModifier = Filename; + } + else if (string.Equals(modifier, DefiningProjectExtension, StringComparison.OrdinalIgnoreCase)) + { + additionalModifier = Extension; + } + else + { + throw new InternalErrorException($"\"{modifier}\" is not a valid item-spec modifier."); + } + + modifiedItemSpec = GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, additionalModifier); + } + } + } + else + { + throw new InternalErrorException($"\"{modifier}\" is not a valid item-spec modifier."); + } + } + catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) + { + throw new InvalidOperationException(SR.FormatInvalidFilespecForTransform(modifier, itemSpec, e.Message)); + } + + return modifiedItemSpec; + } + + /// + /// Indicates whether the given path is a UNC or drive pattern root directory. + /// Note: This function mimics the behavior of checking if Path.GetDirectoryName(path) == null. + /// + /// + /// + private static bool IsRootDirectory(string path) + { + // Eliminate all non-rooted paths + if (!Path.IsPathRooted(path)) + { + return false; + } + + int uncMatchLength = FileUtilitiesRegex.StartsWithUncPatternMatchLength(path); + + // Determine if the given path is a standard drive/unc pattern root + if (FileUtilitiesRegex.IsDrivePattern(path) || + FileUtilitiesRegex.IsDrivePatternWithSlash(path) || + uncMatchLength == path.Length) + { + return true; + } + + // Eliminate all non-root unc paths. + if (uncMatchLength != -1) + { + return false; + } + + // Eliminate any drive patterns that don't have a slash after the colon or where the 4th character is a non-slash + // A non-slash at [3] is specifically checked here because Path.GetDirectoryName + // considers "C:///" a valid root. + if (FileUtilitiesRegex.StartsWithDrivePattern(path) && + ((path.Length >= 3 && path[2] != '\\' && path[2] != '/') || + (path.Length >= 4 && path[3] != '\\' && path[3] != '/'))) + { + return false; + } + + // There are some edge cases that can get to this point. + // After eliminating valid / invalid roots, fall back on original behavior. + return Path.GetDirectoryName(path) == null; + } + + /// + /// Temporary check for something like http://foo which will end up like c:\foo\bar\http://foo + /// We should either have no colon, or exactly one colon. + /// UNDONE: This is a minimal safe change for Dev10. The correct fix should be to make GetFullPath/NormalizePath throw for this. + /// + private static void ThrowForUrl(string fullPath, string itemSpec, string currentDirectory) + { + if (fullPath.IndexOf(':') != fullPath.LastIndexOf(':')) + { + // Cause a better error to appear + fullPath = Path.GetFullPath(Path.Combine(currentDirectory, itemSpec)); + } + } +} diff --git a/src/Shared/Constants.cs b/src/Framework/MSBuildConstants.cs similarity index 99% rename from src/Shared/Constants.cs rename to src/Framework/MSBuildConstants.cs index 28a6bfe8183..4e8e8fd29d8 100644 --- a/src/Shared/Constants.cs +++ b/src/Framework/MSBuildConstants.cs @@ -7,7 +7,6 @@ #endif using System.IO; - namespace Microsoft.Build.Shared { /// diff --git a/src/Framework/MetaProjectGeneratedEventArgs.cs b/src/Framework/MetaProjectGeneratedEventArgs.cs index 79e0a68ad09..f7714d7c484 100644 --- a/src/Framework/MetaProjectGeneratedEventArgs.cs +++ b/src/Framework/MetaProjectGeneratedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 8397124ae9f..e8606d7302a 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -50,20 +50,7 @@ - - Shared\Constants.cs - - - Shared\BinaryReaderExtensions.cs - - - Shared\BinaryWriterExtensions.cs - - - - Shared\IMSBuildElementLocation.cs - @@ -72,6 +59,10 @@ + + + + @@ -81,6 +72,6 @@ true - + diff --git a/src/Framework/NativeMethods.cs b/src/Framework/NativeMethods.cs index 4ae854ba1b4..7195d8ee36f 100644 --- a/src/Framework/NativeMethods.cs +++ b/src/Framework/NativeMethods.cs @@ -10,14 +10,10 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using Microsoft.Build.Framework.Logging; using Microsoft.Build.Shared; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; - -#if !CLR2COMPATIBILITY -using Microsoft.Build.Framework.Logging; -#endif - using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; #nullable disable @@ -291,11 +287,7 @@ internal class MemoryStatus /// public MemoryStatus() { -#if CLR2COMPATIBILITY - _length = (uint)Marshal.SizeOf(typeof(MemoryStatus)); -#else _length = (uint)Marshal.SizeOf(); -#endif } /// @@ -396,11 +388,7 @@ internal class SecurityAttributes { public SecurityAttributes() { -#if (CLR2COMPATIBILITY) - _nLength = (uint)Marshal.SizeOf(typeof(SecurityAttributes)); -#else _nLength = (uint)Marshal.SizeOf(); -#endif } private uint _nLength; @@ -762,11 +750,7 @@ internal static bool IsUnixLike [SupportedOSPlatformGuard("linux")] internal static bool IsLinux { -#if CLR2COMPATIBILITY - get { return false; } -#else get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); } -#endif } /// @@ -775,41 +759,30 @@ internal static bool IsLinux [SupportedOSPlatformGuard("freebsd")] internal static bool IsBSD { -#if CLR2COMPATIBILITY - get { return false; } -#else get { return RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")) || RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD")) || RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD")); } -#endif } -#if !CLR2COMPATIBILITY private static bool? _isWindows; -#endif + /// /// Gets a flag indicating if we are running under some version of Windows /// [SupportedOSPlatformGuard("windows")] internal static bool IsWindows { -#if CLR2COMPATIBILITY - get { return true; } -#else get { _isWindows ??= RuntimeInformation.IsOSPlatform(OSPlatform.Windows); return _isWindows.Value; } -#endif } -#if !CLR2COMPATIBILITY private static bool? _isOSX; -#endif /// /// Gets a flag indicating if we are running under Mac OSX @@ -817,15 +790,11 @@ internal static bool IsWindows [SupportedOSPlatformGuard("macos")] internal static bool IsOSX { -#if CLR2COMPATIBILITY - get { return false; } -#else get { _isOSX ??= RuntimeInformation.IsOSPlatform(OSPlatform.OSX); return _isOSX.Value; } -#endif } /// @@ -867,7 +836,6 @@ internal static bool OSUsesCaseSensitivePaths get { return IsLinux; } } -#if !CLR2COMPATIBILITY /// /// Determines whether the file system is case sensitive by creating a test file. /// Copied from FileUtilities.GetIsFileSystemCaseSensitive() in Shared. @@ -890,7 +858,6 @@ internal static bool OSUsesCaseSensitivePaths }); internal static bool IsFileSystemCaseSensitive => s_isFileSystemCaseSensitive.Value; -#endif /// /// The base directory for all framework paths in Mono @@ -1179,7 +1146,7 @@ internal static bool MakeSymbolicLink(string newFileName, string existingFileNam /// internal static DateTime GetLastWriteFileUtcTime(string fullPath) { -#if !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS +#if !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS if (Traits.Instance.EscapeHatches.AlwaysDoImmutableFilesUpToDateCheck) { return LastWriteFileUtcTime(fullPath); @@ -1407,7 +1374,6 @@ internal static void KillTree(int processIdToKill) internal static int GetParentProcessId(int processId) { int ParentID = 0; -#if !CLR2COMPATIBILITY if (IsUnixLike) { string line = null; @@ -1442,7 +1408,6 @@ internal static int GetParentProcessId(int processId) } } else -#endif { using SafeProcessHandle hProcess = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, processId); { @@ -1581,24 +1546,7 @@ private static unsafe int GetFullPathWin32(string target, int bufferLength, char /// True only if the contents of and the first characters in are identical. private static unsafe bool AreStringsEqual(char* buffer, int len, string s) { -#if CLR2COMPATIBILITY - if (len != s.Length) - { - return false; - } - - foreach (char ch in s) - { - if (ch != *buffer++) - { - return false; - } - } - - return true; -#else return s.AsSpan().SequenceEqual(new ReadOnlySpan(buffer, len)); -#endif } internal static void VerifyThrowWin32Result(int result) @@ -1611,7 +1559,6 @@ internal static void VerifyThrowWin32Result(int result) } } -#if !CLR2COMPATIBILITY internal static (bool acceptAnsiColorCodes, bool outputIsScreen, uint? originalConsoleMode) QueryIsScreenAndTryEnableAnsiColorCodes(StreamHandleType handleType = StreamHandleType.StdOut) { if (Console.IsOutputRedirected) @@ -1683,7 +1630,6 @@ internal static void RestoreConsoleMode(uint? originalConsoleMode, StreamHandleT _ = SetConsoleMode(stdOut, originalConsoleMode.Value); } } -#endif // !CLR2COMPATIBILITY #endregion @@ -1911,5 +1857,4 @@ internal static bool FileOrDirectoryExistsWindows(string path) } #endregion - } diff --git a/src/Framework/Polyfills/StreamExtensions.cs b/src/Framework/Polyfills/StreamExtensions.cs new file mode 100644 index 00000000000..098c882fc78 --- /dev/null +++ b/src/Framework/Polyfills/StreamExtensions.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if !NET +using Microsoft.Build; + +namespace System.IO; + +internal static class StreamExtensions +{ + internal static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) + { + ArgumentNullException.ThrowIfNull(buffer); + ArgumentOutOfRangeException.ThrowIfNegative(offset); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)(buffer.Length - offset)); + + while (count > 0) + { + int read = stream.Read(buffer, offset, count); + if (read <= 0) + { + throw new EndOfStreamException(); + } + offset += read; + count -= read; + } + } +} +#endif diff --git a/src/Framework/Profiler/EvaluationIdProvider.cs b/src/Framework/Profiler/EvaluationIdProvider.cs index 887db13af9a..bc0c1a41636 100644 --- a/src/Framework/Profiler/EvaluationIdProvider.cs +++ b/src/Framework/Profiler/EvaluationIdProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework.Profiler { diff --git a/src/Framework/ProjectFinishedEventArgs.cs b/src/Framework/ProjectFinishedEventArgs.cs index 9308f830c2f..b425ece314f 100644 --- a/src/Framework/ProjectFinishedEventArgs.cs +++ b/src/Framework/ProjectFinishedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/ProjectImportedEventArgs.cs b/src/Framework/ProjectImportedEventArgs.cs index 4884d1bcf23..2ec6c5193ce 100644 --- a/src/Framework/ProjectImportedEventArgs.cs +++ b/src/Framework/ProjectImportedEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/ProjectStartedEventArgs.cs b/src/Framework/ProjectStartedEventArgs.cs index 6033d11cb53..57aefbe97c4 100644 --- a/src/Framework/ProjectStartedEventArgs.cs +++ b/src/Framework/ProjectStartedEventArgs.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Runtime.Serialization; using Microsoft.Build.Experimental.BuildCheck; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/PropertyInitialValueSetEventArgs.cs b/src/Framework/PropertyInitialValueSetEventArgs.cs index fe1e52023b6..409ba29693b 100644 --- a/src/Framework/PropertyInitialValueSetEventArgs.cs +++ b/src/Framework/PropertyInitialValueSetEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/PropertyReassignmentEventArgs.cs b/src/Framework/PropertyReassignmentEventArgs.cs index d7477ee8caa..f3c49e4a18b 100644 --- a/src/Framework/PropertyReassignmentEventArgs.cs +++ b/src/Framework/PropertyReassignmentEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/Resources/SR.resx b/src/Framework/Resources/SR.resx index 56e83f04183..a21aa59f83b 100644 --- a/src/Framework/Resources/SR.resx +++ b/src/Framework/Resources/SR.resx @@ -153,4 +153,20 @@ Path must be rooted. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + + + The parameter '{0}' can only be a file name and cannot include a directory. + + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + \ No newline at end of file diff --git a/src/Framework/Resources/xlf/SR.cs.xlf b/src/Framework/Resources/xlf/SR.cs.xlf index f4f8e2b05ac..f95b773869e 100644 --- a/src/Framework/Resources/xlf/SR.cs.xlf +++ b/src/Framework/Resources/xlf/SR.cs.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + Cesta „{0}“ použitá pro protokoly ladění je příliš dlouhá. Nastavte ji na kratší hodnotu pomocí proměnné prostředí MSBUILDDEBUGPATH nebo změňte konfigurace vašeho systému, aby povolovala dlouhé cesty. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Vytvoření dočasného souboru se nepovedlo. Složka dočasných souborů je plná nebo cesta k této složce není správná. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Metadata položky %({0}) nelze použít na cestu {1}. {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Parametr {0} může být jenom název souboru a nemůže obsahovat adresář. + + Path must be rooted. Cesta musí začínat kořenem. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Cesta: {0} překračuje maximální limit pro cestu k OS. Plně kvalifikovaný název souboru musí být kratší než {1} znaků. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.de.xlf b/src/Framework/Resources/xlf/SR.de.xlf index eb572f92b9e..374b5365d0d 100644 --- a/src/Framework/Resources/xlf/SR.de.xlf +++ b/src/Framework/Resources/xlf/SR.de.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + Der für Debugprotokolle verwendete Pfad "{0}" ist zu lang. Legen Sie den Wert mithilfe der Umgebungsvariablen MSBUILDDEBUGPATH auf einen kürzeren Wert fest, oder ändern Sie die Systemkonfiguration so, dass lange Pfade zulässig sind. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Fehler beim Erstellen einer temporären Datei. Der Ordner für temporäre Dateien ist voll, oder der Pfad ist falsch. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Die %({0})-Elementmetadaten können nicht auf den Pfad "{1}" angewendet werden. {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Der Parameter '{0}' kann nur ein Dateiname sein und darf kein Verzeichnis enthalten. + + Path must be rooted. Der Pfad muss einen Stamm besitzen. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Der Pfad "{0}" überschreitet das maximale Pfadlimit des Betriebssystems. Der vollqualifizierte Dateiname muss weniger als {1} Zeichen umfassen. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.es.xlf b/src/Framework/Resources/xlf/SR.es.xlf index 19122de0467..233a3d7b9f7 100644 --- a/src/Framework/Resources/xlf/SR.es.xlf +++ b/src/Framework/Resources/xlf/SR.es.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + La ruta de acceso "{0}" usada para los registros de depuración es demasiado larga. Establézcalo en un valor más corto con la variable de entorno MSBUILDDEBUGPATH o cambie la configuración del sistema para permitir rutas de acceso largas. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Error al crear un archivo temporal. La carpeta de archivos temporales está llena o la ruta de acceso no es correcta. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Los metadatos "%({0})" del elemento no pueden aplicarse a la ruta de acceso "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + El parámetro "{0}" solo puede ser el nombre de un archivo y no puede incluir un directorio. + + Path must be rooted. Debe ser una ruta de acceso raíz. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + La ruta de acceso {0} supera el límite máximo para la ruta de acceso del sistema operativo. El nombre de archivo completo debe ser inferior a {1} caracteres. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.fr.xlf b/src/Framework/Resources/xlf/SR.fr.xlf index a2f744dca0e..0186d55e61b 100644 --- a/src/Framework/Resources/xlf/SR.fr.xlf +++ b/src/Framework/Resources/xlf/SR.fr.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + Le chemin d’accès "{0}" utilisé pour les journaux de débogage est trop long. Définissez-la sur une valeur plus courte à l’aide de la variable d’environnement MSBUILDDEBUGPATH ou modifiez votre configuration système pour autoriser les chemins longs. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Échec de la création d'un fichier temporaire. Le dossier de fichiers temporaires est plein ou son chemin est incorrect. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Impossible d'appliquer la métadonnée d'élément "%({0})" au chemin d'accès "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Le paramètre "{0}" peut uniquement être un nom de fichier et ne peut pas inclure de répertoire. + + Path must be rooted. Le chemin doit être associé à une racine. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Le chemin {0} dépasse la limite maximale de chemin du système d'exploitation. Le nom du fichier qualifié complet doit contenir moins de {1} caractères. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.it.xlf b/src/Framework/Resources/xlf/SR.it.xlf index 4a071c970d8..53ab9950b6b 100644 --- a/src/Framework/Resources/xlf/SR.it.xlf +++ b/src/Framework/Resources/xlf/SR.it.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + il percorso "{0}" usato per i log di debug è troppo lungo. Impostarlo su un valore più breve usando la variabile dell'ambiente MSBUILDDEBUGPATH o modificare la configurazione del sistema per consentire percorsi lunghi. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + non è stato possibile creare un file temporaneo. La cartella dei file temporanei è piena oppure il percorso è errato. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Non è possibile applicare i metadati dell'elemento "%({0})" al percorso "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Il parametro '{0}' può solo essere un nome file e non può includere una directory. + + Path must be rooted. Il percorso deve contenere una radice. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Il percorso {0} supera il limite massimo dei percorsi del sistema operativo. Il nome completo del file deve essere composto da meno di {1}. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.ja.xlf b/src/Framework/Resources/xlf/SR.ja.xlf index 689291c930e..f2e3a0ed1e8 100644 --- a/src/Framework/Resources/xlf/SR.ja.xlf +++ b/src/Framework/Resources/xlf/SR.ja.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + デバッグ ログに使用されるパス "{0}" が長すぎます。MSBUILDDEBUGPATH 環境変数を使用して短い値に設定するか、長いパスを許可するようにシステム構成を変更してください。 + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + 一時ファイルを作成できませんでした。一時ファイル フォルダーがいっぱいであるか、またはそのパスが正しくありません。{0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + 項目メタデータ "%({0})" をパス "{1}" に適用できません。{2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + パラメーター '{0}' に使用できるのはファイル名のみで、ディレクトリを含めることはできません。 + + Path must be rooted. パスはルート指定パスである必要があります。 + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + パス: {0} は OS のパスの上限を越えています。完全修飾のファイル名は {1} 文字以下にする必要があります。 + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.ko.xlf b/src/Framework/Resources/xlf/SR.ko.xlf index 3cee3f89bbc..9e415e4b9a6 100644 --- a/src/Framework/Resources/xlf/SR.ko.xlf +++ b/src/Framework/Resources/xlf/SR.ko.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + 디버그 로그에 사용된 경로 "{0}"이(가) 너무 깁니다. MSBUILDDEBUGPATH 환경 변수를 사용하여 값을 더 짧게 설정하거나 긴 경로를 허용하도록 시스템 구성을 변경합니다. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + 임시 파일을 만들지 못했습니다. 임시 파일 폴더가 꽉 찼거나 경로가 올바르지 않습니다. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + 항목 메타데이터 "%({0})"을(를) "{1}" 경로에 적용할 수 없습니다. {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + '{0}' 매개 변수는 파일 이름일 수만 있으며 디렉터리를 포함할 수 없습니다. + + Path must be rooted. 루트 경로로 지정해야 합니다. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + 경로: {0}은(는) OS 최대 경로 제한을 초과합니다. 정규화된 파일 이름은 {1}자 이하여야 합니다. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.pl.xlf b/src/Framework/Resources/xlf/SR.pl.xlf index 5dba01b5471..2be8a5e7500 100644 --- a/src/Framework/Resources/xlf/SR.pl.xlf +++ b/src/Framework/Resources/xlf/SR.pl.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + Ścieżka „{0}” używana w dziennikach debugowania jest za długa. Ustaw ją na krótszą wartość przy użyciu zmiennej środowiskowej MSBUILDEBUGPATH lub zmień konfigurację systemu, aby zezwolić na długie ścieżki. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Nie można utworzyć pliku tymczasowego. Folder plików tymczasowych jest zapełniony lub jego ścieżka jest niepoprawna. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Elementu metadanych „%({0})” nie można zastosować do ścieżki „{1}”. {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Parametr „{0}” może zawierać tylko nazwę pliku i nie może zawierać katalogu. + + Path must be rooted. Ścieżka musi zaczynać się od katalogu głównego. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Ścieżka: {0} przekracza limit maksymalnej długości ścieżki w systemie operacyjnym. W pełni kwalifikowana nazwa pliku musi się składać z mniej niż {1} znaków. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.pt-BR.xlf b/src/Framework/Resources/xlf/SR.pt-BR.xlf index 8dc6f5e3e4d..a7bb6bbb606 100644 --- a/src/Framework/Resources/xlf/SR.pt-BR.xlf +++ b/src/Framework/Resources/xlf/SR.pt-BR.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + O caminho "{0}" usado para logs de depuração é muito longo. Defina-o para um valor mais curto usando a variável de ambiente MSBUILDDEBUGPATH ou altere a configuração do sistema para permitir caminhos longos. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Falha ao criar arquivo temporário. A pasta de arquivos temporários está cheia ou o caminho está incorreto. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Os metadados do item "%({0})" não podem ser aplicados ao caminho "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + O parâmetro '{0}' pode ser somente um nome de arquivo e não pode incluir um diretório. + + Path must be rooted. Caminho deve ter raiz. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Caminho: {0} excede o limite máximo do caminho do SO. O nome do arquivo totalmente qualificado deve ter menos de {1} caracteres. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.ru.xlf b/src/Framework/Resources/xlf/SR.ru.xlf index b4848dfe4fd..85c60ccb1c1 100644 --- a/src/Framework/Resources/xlf/SR.ru.xlf +++ b/src/Framework/Resources/xlf/SR.ru.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + слишком длинный путь "{0}" для журналов отладки. Установите более короткое значение, используя переменную среду MSBUILDDEBUGPATH, или измените конфигурацию системы, чтобы разрешить длинные пути. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + не удалось создать временный файл. Папка временных файлов переполнена, или указан неверный путь. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + Не удается применить метаданные элемента "%({0})" к пути "{1}". {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + Параметр "{0}" может быть только именем файла и не может включать в себя каталог. + + Path must be rooted. Путь должен иметь корень. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Длина пути {0} превышает максимально допустимую в ОС. Символов в полном имени файла должно быть не больше {1}. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.tr.xlf b/src/Framework/Resources/xlf/SR.tr.xlf index 021ba510b13..4f9d8c4d5e5 100644 --- a/src/Framework/Resources/xlf/SR.tr.xlf +++ b/src/Framework/Resources/xlf/SR.tr.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + Hata ayıklama günlükleri için kullanılan "{0}" yolu çok uzun. MSBUILDDEBUGPATH ortam değişkenini kullanarak yolu daha kısa bir değere ayarlayın veya sistem yapılandırmanızı uzun yollara izin verecek şekilde değiştirin. + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + Geçici bir dosya oluşturulamadı. Geçici dosyalar klasörü dolu veya yolu hatalı. {0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + "%({0})" öğe meta verisi "{1}" yoluna uygulanamıyor. {2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + '{0}' yalnızca bir dosya adı olabilir ve dizin içeremez. + + Path must be rooted. Yol kökü belirtilmelidir. + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + Yol: {0}, işletim sisteminin en yüksek yol sınırını aşıyor. Tam dosya adı en fazla {1} karakter olmalıdır. + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.zh-Hans.xlf b/src/Framework/Resources/xlf/SR.zh-Hans.xlf index 47ed63f6cc3..95d321cba9c 100644 --- a/src/Framework/Resources/xlf/SR.zh-Hans.xlf +++ b/src/Framework/Resources/xlf/SR.zh-Hans.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + 用于调试日志的路径"{0}"太长。使用 MSBUILDDEBUGPATH 环境变量将其设置为较短值,或更改系统配置以允许长路径。 + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + 未能创建临时文件。临时文件文件夹已满或其路径不正确。{0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + 无法将项元数据“%({0})”应用于路径“{1}”。{2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + 参数“{0}”只能是文件名,不能包含目录。 + + Path must be rooted. 路径必须是根路径。 + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + 路径: {0} 超过 OS 最大路径限制。完全限定的文件名必须少于 {1} 个字符。 + + - \ No newline at end of file + diff --git a/src/Framework/Resources/xlf/SR.zh-Hant.xlf b/src/Framework/Resources/xlf/SR.zh-Hant.xlf index 308821aef1a..1c5eae44bec 100644 --- a/src/Framework/Resources/xlf/SR.zh-Hant.xlf +++ b/src/Framework/Resources/xlf/SR.zh-Hant.xlf @@ -57,11 +57,36 @@ The value cannot be an empty string. + + The path "{0}" used for debug logs is too long. Set it to a shorter value using the MSBUILDDEBUGPATH environment variable or change your system configuration to allow long paths. + 用於偵錯記錄檔 "{0}" 的路徑太長。使用 MSBUILDDEBUGPATH 環境變數將它設定為較短的值,或變更您的系統設定以允許長路徑。 + + + + Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} + 無法建立暫存檔案。暫存檔案資料夾已滿或其路徑錯誤。{0} + + + + The item metadata "%({0})" cannot be applied to the path "{1}". {2} + 無法將項目中繼資料 "%({0})" 套用至路徑 "{1}"。{2} + UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. + + + The parameter '{0}' can only be a file name and cannot include a directory. + 參數 '{0}' 只可以是檔案名稱,不得包含目錄。 + + Path must be rooted. 路徑必須為根路徑。 + + Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. + 路徑: {0} 超過 OS 路徑上限。完整檔案名稱必須少於 {1} 個字元。 + + - \ No newline at end of file + diff --git a/src/Framework/StringBuilderCache.cs b/src/Framework/StringBuilderCache.cs index 5fd67790e9d..929411cdddc 100644 --- a/src/Framework/StringBuilderCache.cs +++ b/src/Framework/StringBuilderCache.cs @@ -4,7 +4,7 @@ using System; using System.Diagnostics; using System.Text; -#if DEBUG && !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS +#if DEBUG && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS using Microsoft.Build.Eventing; #endif @@ -50,7 +50,7 @@ public static StringBuilder Acquire(int capacity = 16 /*StringBuilder.DefaultCap if (capacity <= sb.Capacity) { sb.Length = 0; // Equivalent of sb.Clear() that works on .Net 3.5 -#if DEBUG && !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS +#if DEBUG && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS MSBuildEventSource.Log.ReusableStringBuilderFactoryStart(hash: sb.GetHashCode(), newCapacity: capacity, oldCapacity: sb.Capacity, type: "sbc-hit"); #endif return sb; @@ -59,7 +59,7 @@ public static StringBuilder Acquire(int capacity = 16 /*StringBuilder.DefaultCap } StringBuilder stringBuilder = new StringBuilder(capacity); -#if DEBUG && !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS +#if DEBUG && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS MSBuildEventSource.Log.ReusableStringBuilderFactoryStart(hash: stringBuilder.GetHashCode(), newCapacity: capacity, oldCapacity: stringBuilder.Capacity, type: "sbc-miss"); #endif return stringBuilder; @@ -92,7 +92,7 @@ public static void Release(StringBuilder sb) Debug.Assert(StringBuilderCache.t_cachedInstance == null, "Unexpected replacing of other StringBuilder."); StringBuilderCache.t_cachedInstance = sb; } -#if DEBUG && !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS +#if DEBUG && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS MSBuildEventSource.Log.ReusableStringBuilderFactoryStop(hash: sb.GetHashCode(), returningCapacity: sb.Capacity, returningLength: sb.Length, type: sb.Capacity <= MAX_BUILDER_SIZE ? "sbc-return" : "sbc-discard"); #endif } diff --git a/src/Framework/TargetFinishedEventArgs.cs b/src/Framework/TargetFinishedEventArgs.cs index 454d2ce24cc..6762cbd10a4 100644 --- a/src/Framework/TargetFinishedEventArgs.cs +++ b/src/Framework/TargetFinishedEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Collections; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/TargetSkippedEventArgs.cs b/src/Framework/TargetSkippedEventArgs.cs index 8536d2bec8a..ab2773c12d2 100644 --- a/src/Framework/TargetSkippedEventArgs.cs +++ b/src/Framework/TargetSkippedEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/TargetStartedEventArgs.cs b/src/Framework/TargetStartedEventArgs.cs index 22f162a6392..7a2fb476abd 100644 --- a/src/Framework/TargetStartedEventArgs.cs +++ b/src/Framework/TargetStartedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/TaskFinishedEventArgs.cs b/src/Framework/TaskFinishedEventArgs.cs index 4713316748d..47a3e09ddcc 100644 --- a/src/Framework/TaskFinishedEventArgs.cs +++ b/src/Framework/TaskFinishedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/TaskParameterEventArgs.cs b/src/Framework/TaskParameterEventArgs.cs index 8dcf97730c7..52f623017af 100644 --- a/src/Framework/TaskParameterEventArgs.cs +++ b/src/Framework/TaskParameterEventArgs.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/TaskStartedEventArgs.cs b/src/Framework/TaskStartedEventArgs.cs index c3b1881616b..ab6da376c33 100644 --- a/src/Framework/TaskStartedEventArgs.cs +++ b/src/Framework/TaskStartedEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs index 38c2bb43910..38b51ee6668 100644 --- a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs +++ b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs @@ -4,9 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -#if NETFRAMEWORK -using Microsoft.Build.Shared; -#endif namespace Microsoft.Build.Framework.Telemetry; diff --git a/src/Framework/TelemetryEventArgs.cs b/src/Framework/TelemetryEventArgs.cs index 645a72526d3..0601688632f 100644 --- a/src/Framework/TelemetryEventArgs.cs +++ b/src/Framework/TelemetryEventArgs.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Shared; namespace Microsoft.Build.Framework { diff --git a/src/Framework/UninitializedPropertyReadEventArgs.cs b/src/Framework/UninitializedPropertyReadEventArgs.cs index 7980bdb5485..fc67e238f63 100644 --- a/src/Framework/UninitializedPropertyReadEventArgs.cs +++ b/src/Framework/UninitializedPropertyReadEventArgs.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/MSBuild/CommandLine/CommandLineParser.cs b/src/MSBuild/CommandLine/CommandLineParser.cs index fc94bcb0b34..69ba921f643 100644 --- a/src/MSBuild/CommandLine/CommandLineParser.cs +++ b/src/MSBuild/CommandLine/CommandLineParser.cs @@ -117,7 +117,7 @@ internal void GatherAllSwitches( switchesFromAutoResponseFile = new CommandLineSwitches(); if (!switchesNotFromAutoResponseFile[ParameterlessSwitch.NoAutoResponse]) { - string exePath = Path.GetDirectoryName(FileUtilities.ExecutingAssemblyPath); // Copied from XMake + string exePath = Path.GetDirectoryName(BuildEnvironmentHelper.ExecutingAssemblyPath); // Copied from XMake GatherAutoResponseFileSwitches(exePath, switchesFromAutoResponseFile, fullCommandLine); } @@ -328,7 +328,7 @@ private void GatherResponseFileSwitch(string unquotedCommandLineArg, CommandLine { try { - string responseFile = FrameworkFileUtilities.FixFilePath(unquotedCommandLineArg.Substring(1)); + string responseFile = FileUtilities.FixFilePath(unquotedCommandLineArg.Substring(1)); if (responseFile.Length == 0) { @@ -358,7 +358,7 @@ private void GatherResponseFileSwitch(string unquotedCommandLineArg, CommandLine if (!isRepeatedResponseFile) { - var responseFileDirectory = FrameworkFileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(responseFile)); + var responseFileDirectory = FileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(responseFile)); includedResponseFiles.Add(responseFile); List argsFromResponseFile; @@ -532,7 +532,7 @@ internal bool CheckAndGatherProjectAutoResponseFile(CommandLineSwitches switches found = !string.IsNullOrWhiteSpace(directoryResponseFile) && GatherAutoResponseFileSwitchesFromFullPath(directoryResponseFile, switchesFromAutoResponseFile, commandLine); // Don't look for more response files if it's only in the same place we already looked (next to the exe) - string exePath = Path.GetDirectoryName(FileUtilities.ExecutingAssemblyPath); // Copied from XMake + string exePath = Path.GetDirectoryName(BuildEnvironmentHelper.ExecutingAssemblyPath); // Copied from XMake if (!string.Equals(projectDirectory, exePath, StringComparison.OrdinalIgnoreCase)) { // this combines any found, with higher precedence, with the switches from the original auto response file switches @@ -550,7 +550,7 @@ private static string GetProjectDirectory(string[] projectSwitchParameters) if (projectSwitchParameters.Length == 1) { - var projectFile = FrameworkFileUtilities.FixFilePath(projectSwitchParameters[0]); + var projectFile = FileUtilities.FixFilePath(projectSwitchParameters[0]); if (FileSystems.Default.DirectoryExists(projectFile)) { diff --git a/src/MSBuild/JsonOutputFormatter.cs b/src/MSBuild/JsonOutputFormatter.cs index a3c2f0afc9d..2aabd7b7bc4 100644 --- a/src/MSBuild/JsonOutputFormatter.cs +++ b/src/MSBuild/JsonOutputFormatter.cs @@ -101,7 +101,7 @@ internal void AddItemsInJsonFormat(string[] itemNames, Project project) jsonItem[metadatum.Name] = metadatum.EvaluatedValue; } - foreach (string metadatumName in FileUtilities.ItemSpecModifiers.All) + foreach (string metadatumName in ItemSpecModifiers.All) { if (metadatumName.Equals("Identity")) { diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index cf205b2366d..6047716de27 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -1,6 +1,5 @@ - + - @@ -69,21 +68,13 @@ FileDelegates.cs - - FileUtilities.cs - - - FileUtilitiesRegex.cs - RegisteredTaskObjectCacheBase.cs - - @@ -91,11 +82,7 @@ - - - TempFileUtilities.cs - diff --git a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs index 0ad4fc8fe6b..7fb11f41a27 100644 --- a/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs +++ b/src/MSBuild/OutOfProcTaskAppDomainWrapperBase.cs @@ -9,11 +9,9 @@ using System.Reflection; using Microsoft.Build.BackEnd; +using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Shared; -#if !NET35 -using Microsoft.Build.Execution; -#endif #nullable disable @@ -58,9 +56,7 @@ internal class OutOfProcTaskAppDomainWrapperBase /// private string taskName; -#if !NET35 private HostServices _hostServices; -#endif /// /// This is the actual user task whose instance we will create and invoke Execute @@ -110,9 +106,7 @@ internal OutOfProcTaskHostTaskResult ExecuteTask( #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif -#if !NET35 HostServices hostServices, -#endif IDictionary taskParams) { buildEngine = oopTaskHostNode; @@ -121,9 +115,7 @@ internal OutOfProcTaskHostTaskResult ExecuteTask( #if FEATURE_APPDOMAIN _taskAppDomain = null; #endif -#if !NET35 _hostServices = hostServices; -#endif wrappedTask = null; LoadedType taskType = null; @@ -285,11 +277,7 @@ private OutOfProcTaskHostTaskResult InstantiateAndExecuteTaskInSTAThread( } finally { -#if CLR2COMPATIBILITY - taskRunnerFinished.Close(); -#else taskRunnerFinished.Dispose(); -#endif taskRunnerFinished = null; } @@ -348,12 +336,10 @@ private OutOfProcTaskHostTaskResult InstantiateAndExecuteTask( ); #pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter -#if !NET35 if (projectFile != null && _hostServices != null) { wrappedTask.HostObject = _hostServices.GetHostObject(projectFile, targetName, taskName); } -#endif wrappedTask.BuildEngine = oopTaskHostNode; } diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index cc3e18e6525..731da1973b4 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -3,26 +3,17 @@ using System; using System.Collections; -#if !CLR2COMPATIBILITY using System.Collections.Concurrent; -#endif using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Threading; -#if !CLR2COMPATIBILITY using System.Threading.Tasks; -#endif using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; -#if !CLR2COMPATIBILITY -using Microsoft.Build.Exceptions; -#endif using Microsoft.Build.Framework; -#if !CLR2COMPATIBILITY using Microsoft.Build.Experimental.FileAccess; -#endif using Microsoft.Build.Internal; using Microsoft.Build.Shared; #if FEATURE_APPDOMAIN @@ -40,12 +31,7 @@ internal class OutOfProcTaskHostNode : #if FEATURE_APPDOMAIN MarshalByRefObject, #endif - INodePacketFactory, INodePacketHandler, -#if CLR2COMPATIBILITY - IBuildEngine3 -#else - IBuildEngine10 -#endif + INodePacketFactory, INodePacketHandler, IBuildEngine10 { /// /// Keeps a record of all environment variables that, on startup of the task host, have a different @@ -173,12 +159,10 @@ internal class OutOfProcTaskHostNode : /// private bool _nodeReuse; -#if !CLR2COMPATIBILITY /// /// The task object cache. /// private RegisteredTaskObjectCacheBase _registeredTaskObjectCache; -#endif #if FEATURE_REPORTFILEACCESSES /// @@ -187,7 +171,6 @@ internal class OutOfProcTaskHostNode : private List _fileAccessData = new List(); #endif -#if !CLR2COMPATIBILITY /// /// Counter for generating unique request IDs for callback correlation. /// @@ -216,7 +199,6 @@ internal class OutOfProcTaskHostNode : /// True if the worker node's packet version is high enough, or if the feature is force-enabled via env var. /// private bool CallbacksSupported => _parentPacketVersion >= CallbacksMinPacketVersion || Traits.Instance.EnableTaskHostCallbacks; -#endif /// /// Constructor. @@ -243,15 +225,10 @@ public OutOfProcTaskHostNode() thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostConfiguration, TaskHostConfiguration.FactoryForDeserialization, this); thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostTaskCancelled, TaskHostTaskCancelled.FactoryForDeserialization, this); thisINodePacketFactory.RegisterPacketHandler(NodePacketType.NodeBuildComplete, NodeBuildComplete.FactoryForDeserialization, this); - -#if !CLR2COMPATIBILITY thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostIsRunningMultipleNodesResponse, TaskHostIsRunningMultipleNodesResponse.FactoryForDeserialization, this); thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostCoresResponse, TaskHostCoresResponse.FactoryForDeserialization, this); -#endif -#if !CLR2COMPATIBILITY EngineServices = new EngineServicesImpl(this); -#endif } #region IBuildEngine Implementation (Properties) @@ -317,10 +294,6 @@ public bool IsRunningMultipleNodes { get { -#if CLR2COMPATIBILITY - LogErrorFromResource("BuildEngineCallbacksInTaskHostUnsupported"); - return false; -#else if (!CallbacksSupported) { LogErrorFromResource("BuildEngineCallbacksInTaskHostUnsupported"); @@ -330,7 +303,6 @@ public bool IsRunningMultipleNodes var request = new TaskHostIsRunningMultipleNodesRequest(); var response = SendCallbackRequestAndWaitForResponse(request); return response.IsRunningMultipleNodes; -#endif } } @@ -482,7 +454,6 @@ public void Reacquire() #endregion // IBuildEngine3 Implementation -#if !CLR2COMPATIBILITY #region IBuildEngine4 Implementation /// @@ -564,11 +535,6 @@ public IReadOnlyDictionary GetGlobalProperties() public int RequestCores(int requestedCores) { -#if CLR2COMPATIBILITY - // CLR2 task host doesn't support resource management. - // If they somehow get here, throw. - throw new NotImplementedException(); -#else ErrorUtilities.VerifyThrowArgumentOutOfRange(requestedCores > 0, nameof(requestedCores)); if (!CallbacksSupported) @@ -581,15 +547,10 @@ public int RequestCores(int requestedCores) var request = new TaskHostCoresRequest(requestedCores, isRelease: false); var response = SendCallbackRequestAndWaitForResponse(request); return response.GrantedCores; -#endif } public void ReleaseCores(int coresToRelease) { -#if CLR2COMPATIBILITY - // CLR2 task host doesn't support resource management. - throw new NotImplementedException(); -#else ErrorUtilities.VerifyThrowArgumentOutOfRange(coresToRelease > 0, nameof(coresToRelease)); if (!CallbacksSupported) @@ -599,7 +560,6 @@ public void ReleaseCores(int coresToRelease) var request = new TaskHostCoresRequest(coresToRelease, isRelease: true); SendCallbackRequestAndWaitForResponse(request); -#endif } #endregion @@ -647,8 +607,6 @@ public void ReportFileAccess(FileAccessData fileAccessData) #endregion -#endif - #region INodePacketFactory Members /// @@ -732,10 +690,9 @@ public void PacketReceived(int node, INodePacket packet) /// The reason for shutting down. public NodeEngineShutdownReason Run(out Exception shutdownException, bool nodeReuse = false, byte parentPacketVersion = 1) { -#if !CLR2COMPATIBILITY _registeredTaskObjectCache = new RegisteredTaskObjectCacheBase(); _parentPacketVersion = parentPacketVersion; -#endif + shutdownException = null; // Snapshot the current environment @@ -813,17 +770,14 @@ private void HandlePacket(INodePacket packet) HandleNodeBuildComplete(packet as NodeBuildComplete); break; -#if !CLR2COMPATIBILITY // Callback response packets - route to pending request case NodePacketType.TaskHostIsRunningMultipleNodesResponse: case NodePacketType.TaskHostCoresResponse: HandleCallbackResponse(packet); break; -#endif } } -#if !CLR2COMPATIBILITY /// /// Handles a callback response packet by completing the pending request's TaskCompletionSource. /// This is called on the main thread and unblocks the task thread waiting for the response. @@ -902,7 +856,6 @@ private TResponse SendCallbackRequestAndWaitForResponse(ITaskHostCall _pendingCallbackRequests.TryRemove(requestId, out _); } } -#endif /// /// Configure the task host according to the information received in the @@ -1015,10 +968,8 @@ private NodeEngineShutdownReason HandleShutdown() debugWriter?.WriteLine("Node shutting down with reason {0}.", _shutdownReason); -#if !CLR2COMPATIBILITY _registeredTaskObjectCache.DisposeCacheObjects(RegisteredTaskObjectLifetime.Build); _registeredTaskObjectCache = null; -#endif // On Windows, a process holds a handle to the current directory, // so reset it away from a user-requested folder that may get deleted. @@ -1046,17 +997,10 @@ private NodeEngineShutdownReason HandleShutdown() _nodeEndpoint.Disconnect(); // Dispose these WaitHandles -#if CLR2COMPATIBILITY - _packetReceivedEvent.Close(); - _shutdownEvent.Close(); - _taskCompleteEvent.Close(); - _taskCancelledEvent.Close(); -#else _packetReceivedEvent.Dispose(); _shutdownEvent.Dispose(); _taskCompleteEvent.Dispose(); _taskCancelledEvent.Dispose(); -#endif return _shutdownReason; } @@ -1072,7 +1016,6 @@ private void OnLinkStatusChanged(INodeEndpoint endpoint, LinkStatus status) case LinkStatus.Failed: _shutdownReason = NodeEngineShutdownReason.ConnectionFailed; -#if !CLR2COMPATIBILITY // Fail all pending callback requests so task threads unblock immediately // instead of waiting indefinitely for responses that will never arrive. foreach (var kvp in _pendingCallbackRequests) @@ -1083,7 +1026,6 @@ private void OnLinkStatusChanged(INodeEndpoint endpoint, LinkStatus status) "TaskHost lost connection to owning worker node during callback.")); } } -#endif _shutdownEvent.Set(); break; @@ -1127,9 +1069,7 @@ private void RunTask(object state) // Now set the new environment SetTaskHostEnvironment(taskConfiguration.BuildProcessEnvironment); -#if !CLR2COMPATIBILITY DotnetHostEnvironmentHelper.ClearBootstrapDotnetRootEnvironment(taskConfiguration.BuildProcessEnvironment); -#endif // Set culture Thread.CurrentThread.CurrentCulture = taskConfiguration.Culture; @@ -1137,9 +1077,9 @@ private void RunTask(object state) string taskName = taskConfiguration.TaskName; string taskLocation = taskConfiguration.TaskLocation; -#if !CLR2COMPATIBILITY + TaskFactoryUtilities.RegisterAssemblyResolveHandlersFromManifest(taskLocation); -#endif + // We will not create an appdomain now because of a bug // As a fix, we will create the class directly without wrapping it in a domain _taskWrapper = new OutOfProcTaskAppDomainWrapper(); @@ -1156,9 +1096,7 @@ private void RunTask(object state) #if FEATURE_APPDOMAIN taskConfiguration.AppDomainSetup, #endif -#if !NET35 taskConfiguration.HostServices, -#endif taskParams); } catch (ThreadAbortException) diff --git a/src/MSBuild/PerformanceLogEventListener.cs b/src/MSBuild/PerformanceLogEventListener.cs index a42f05194bc..bda499f6112 100644 --- a/src/MSBuild/PerformanceLogEventListener.cs +++ b/src/MSBuild/PerformanceLogEventListener.cs @@ -6,7 +6,7 @@ using System.IO; using System.Text; using Microsoft.Build.Eventing; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; #nullable disable diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index 089e93d533a..4fab975382b 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -160,7 +160,7 @@ static MSBuildApp() // This forces the type to initialize in this static constructor and thus // // any configuration file exceptions can be caught here. // //////////////////////////////////////////////////////////////////////////////// - s_exePath = Path.GetDirectoryName(FileUtilities.ExecutingAssemblyPath); + s_exePath = Path.GetDirectoryName(BuildEnvironmentHelper.ExecutingAssemblyPath); commandLineParser = new CommandLineParser(); s_initialized = true; @@ -660,7 +660,7 @@ public static ExitType Execute(string[] commandLine) ErrorUtilities.VerifyThrowArgumentLength(commandLine); - AppDomain.CurrentDomain.UnhandledException += ExceptionHandling.UnhandledExceptionHandler; + AppDomain.CurrentDomain.UnhandledException += DebugUtils.UnhandledExceptionHandler; ExitType exitType = ExitType.Success; @@ -3103,7 +3103,7 @@ internal static string ProcessProjectSwitch( if (parameters.Length == 1) { - projectFile = FrameworkFileUtilities.FixFilePath(parameters[0]); + projectFile = FileUtilities.FixFilePath(parameters[0]); if (FileSystems.Default.DirectoryExists(projectFile)) { @@ -3689,7 +3689,7 @@ internal static void ProcessDistributedFileLogger( // Check to see if the logfile parameter has been set, if not set it to the current directory string logFileParameter = ExtractAnyLoggerParameter(fileParameters, "logfile"); - string logFileName = FrameworkFileUtilities.FixFilePath(ExtractAnyParameterValue(logFileParameter)); + string logFileName = FileUtilities.FixFilePath(ExtractAnyParameterValue(logFileParameter)); try { @@ -3953,7 +3953,7 @@ private static LoggerDescription ParseLoggingParameter(string parameter, string } // figure out whether the assembly's identity (strong/weak name), or its filename/path is provided - string testFile = FrameworkFileUtilities.FixFilePath(loggerAssemblySpec); + string testFile = FileUtilities.FixFilePath(loggerAssemblySpec); if (FileSystems.Default.FileExists(testFile)) { loggerAssemblyFile = testFile; @@ -4111,7 +4111,7 @@ private static string ProcessValidateSwitch(string[] parameters) foreach (string parameter in parameters) { InitializationException.VerifyThrow(schemaFile == null, "MultipleSchemasError", parameter); - string fileName = FrameworkFileUtilities.FixFilePath(parameter); + string fileName = FileUtilities.FixFilePath(parameter); InitializationException.VerifyThrow(FileSystems.Default.FileExists(fileName), "SchemaNotFoundError", fileName); schemaFile = Path.Combine(Directory.GetCurrentDirectory(), fileName); diff --git a/src/Shared/AssemblyLoadInfo.cs b/src/Shared/AssemblyLoadInfo.cs index 4ced73e1360..931d96a5480 100644 --- a/src/Shared/AssemblyLoadInfo.cs +++ b/src/Shared/AssemblyLoadInfo.cs @@ -228,11 +228,7 @@ internal override string AssemblyLocation /// internal override bool IsInlineTask { -#if !NET35 get { return _assemblyFile?.EndsWith(TaskFactoryUtilities.InlineTaskSuffix, StringComparison.OrdinalIgnoreCase) == true; } -#else - get { return false; } -#endif } } } diff --git a/src/Shared/BinaryReaderExtensions.cs b/src/Shared/BinaryReaderExtensions.cs deleted file mode 100644 index 9078401ba2f..00000000000 --- a/src/Shared/BinaryReaderExtensions.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using Microsoft.Build.Framework; - -namespace Microsoft.Build.Shared -{ - internal static class BinaryReaderExtensions - { -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static string? ReadOptionalString(this BinaryReader reader) - { - return reader.ReadByte() == 0 ? null : reader.ReadString(); - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static int? ReadOptionalInt32(this BinaryReader reader) - { - return reader.ReadByte() == 0 ? null : reader.ReadInt32(); - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static int Read7BitEncodedInt(this BinaryReader reader) - { - // Read out an Int32 7 bits at a time. The high bit - // of the byte when on means to continue reading more bytes. - int count = 0; - int shift = 0; - byte b; - do - { - // Check for a corrupted stream. Read a max of 5 bytes. - // In a future version, add a DataFormatException. - if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 - { - throw new FormatException(); - } - - // ReadByte handles end of stream cases for us. - b = reader.ReadByte(); - count |= (b & 0x7F) << shift; - shift += 7; - } while ((b & 0x80) != 0); - return count; - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static DateTime ReadTimestamp(this BinaryReader reader) - { - long timestampTicks = reader.ReadInt64(); - DateTimeKind kind = (DateTimeKind)reader.ReadInt32(); - var timestamp = new DateTime(timestampTicks, kind); - return timestamp; - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BuildEventContext? ReadOptionalBuildEventContext(this BinaryReader reader) - { - if (reader.ReadByte() == 0) - { - return null; - } - - return reader.ReadBuildEventContext(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BuildEventContext ReadBuildEventContext(this BinaryReader reader) - { - int nodeId = reader.ReadInt32(); - int projectContextId = reader.ReadInt32(); - int targetId = reader.ReadInt32(); - int taskId = reader.ReadInt32(); - int submissionId = reader.ReadInt32(); - int projectInstanceId = reader.ReadInt32(); - int evaluationId = reader.ReadInt32(); - - var buildEventContext = new BuildEventContext(submissionId, nodeId, evaluationId, projectInstanceId, projectContextId, targetId, taskId); - return buildEventContext; - } -#endif - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static unsafe Guid ReadGuid(this BinaryReader reader) - { - return new Guid(reader.ReadBytes(sizeof(Guid))); - } - - public static void ReadExtendedBuildEventData(this BinaryReader reader, IExtendedBuildEventArgs data) - { - data.ExtendedType = reader.ReadString(); - data.ExtendedData = reader.ReadOptionalString(); - - bool haveMetadata = reader.ReadBoolean(); - if (haveMetadata) - { - data.ExtendedMetadata = new Dictionary(); - - int count = reader.Read7BitEncodedInt(); - for (int i = 0; i < count; i++) - { - string key = reader.ReadString(); - string? value = reader.ReadOptionalString(); - - data.ExtendedMetadata.Add(key, value); - } - } - else - { - data.ExtendedMetadata = null; - } - } - - public static Dictionary ReadDurationDictionary(this BinaryReader reader) - { - int count = reader.Read7BitEncodedInt(); - var durations = new Dictionary(count); - for (int i = 0; i < count; i++) - { - string key = reader.ReadString(); - TimeSpan value = TimeSpan.FromTicks(reader.ReadInt64()); - - durations.Add(key, value); - } - - return durations; - } - } -} diff --git a/src/Shared/BinaryWriterExtensions.cs b/src/Shared/BinaryWriterExtensions.cs deleted file mode 100644 index 9cb458f4ec7..00000000000 --- a/src/Shared/BinaryWriterExtensions.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.CompilerServices; -using Microsoft.Build.Framework; - -namespace Microsoft.Build.Shared -{ - internal static class BinaryWriterExtensions - { -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void WriteOptionalString(this BinaryWriter writer, string? value) - { - if (value == null) - { - writer.Write((byte)0); - } - else - { - writer.Write((byte)1); - writer.Write(value); - } - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void WriteOptionalInt32(this BinaryWriter writer, int? value) - { - if (value == null) - { - writer.Write((byte)0); - } - else - { - writer.Write((byte)1); - writer.Write(value.Value); - } - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void WriteTimestamp(this BinaryWriter writer, DateTime timestamp) - { - writer.Write(timestamp.Ticks); - writer.Write((Int32)timestamp.Kind); - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void Write7BitEncodedInt(this BinaryWriter writer, int value) - { - // Write out an int 7 bits at a time. The high bit of the byte, - // when on, tells reader to continue reading more bytes. - uint v = (uint)value; // support negative numbers - while (v >= 0x80) - { - writer.Write((byte)(v | 0x80)); - v >>= 7; - } - - writer.Write((byte)v); - } - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteOptionalBuildEventContext(this BinaryWriter writer, BuildEventContext? context) - { - if (context == null) - { - writer.Write((byte)0); - } - else - { - writer.Write((byte)1); - writer.WriteBuildEventContext(context); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteBuildEventContext(this BinaryWriter writer, BuildEventContext context) - { - writer.Write(context.NodeId); - writer.Write(context.ProjectContextId); - writer.Write(context.TargetId); - writer.Write(context.TaskId); - writer.Write(context.SubmissionId); - writer.Write(context.ProjectInstanceId); - writer.Write(context.EvaluationId); - } -#endif - -#if !TASKHOST - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void WriteGuid(this BinaryWriter writer, Guid value) - { - Guid val = value; - unsafe - { - byte* ptr = (byte*)&val; - for (int i = 0; i < sizeof(Guid); i++, ptr++) - { - writer.Write(*ptr); - } - } - } - - public static void WriteExtendedBuildEventData(this BinaryWriter writer, IExtendedBuildEventArgs data) - { - writer.Write(data.ExtendedType); - writer.WriteOptionalString(data.ExtendedData); - - writer.Write(data.ExtendedMetadata != null); - if (data.ExtendedMetadata != null) - { - writer.Write7BitEncodedInt(data.ExtendedMetadata.Count); - foreach (KeyValuePair kvp in data.ExtendedMetadata) - { - writer.Write(kvp.Key); - writer.WriteOptionalString(kvp.Value); - } - } - } - - public static void WriteDurationsDictionary(this BinaryWriter writer, Dictionary durations) - { - writer.Write7BitEncodedInt(durations.Count); - foreach (KeyValuePair kvp in durations) - { - writer.Write(kvp.Key); - writer.Write(kvp.Value.Ticks); - } - } - } -} diff --git a/src/Shared/BuildEnvironmentHelper.cs b/src/Shared/BuildEnvironmentHelper.cs index 92c2416a835..d6535d808a4 100644 --- a/src/Shared/BuildEnvironmentHelper.cs +++ b/src/Shared/BuildEnvironmentHelper.cs @@ -33,6 +33,16 @@ internal sealed class BuildEnvironmentHelper /// private static readonly string[] s_msBuildProcess = { "MSBUILD", "MSBUILDTASKHOST" }; + /// + /// Get the currently executing assembly path. + /// + /// + /// This property depends on BuildEnvironmentHelper being compiled into separate assemblies. + /// If BuildEnvironmentHelper is moved to a shared assembly, this property will need to be re-evaluated. + /// + internal static string ExecutingAssemblyPath + => Path.GetFullPath(AssemblyUtilities.GetAssemblyLocation(typeof(BuildEnvironmentHelper).Assembly)); + /// /// Gets the cached Build Environment instance. /// @@ -443,16 +453,12 @@ private static string GetProcessFromRunningProcess() private static string GetExecutingAssemblyPath() { - return FileUtilities.ExecutingAssemblyPath; + return ExecutingAssemblyPath; } private static string GetAppContextBaseDirectory() { -#if !CLR2COMPATIBILITY // Assemblies compiled against anything older than .NET 4.0 won't have a System.AppContext return AppContext.BaseDirectory; -#else - return null; -#endif } private static string GetEnvironmentVariable(string variable) diff --git a/src/Shared/CollectionHelpers.cs b/src/Shared/CollectionHelpers.cs index 17265682d2a..624502e3196 100644 --- a/src/Shared/CollectionHelpers.cs +++ b/src/Shared/CollectionHelpers.cs @@ -49,7 +49,6 @@ internal static bool ContainsValueAndIsEqual(this Dictionary dic return false; } -#if !CLR2COMPATIBILITY internal static bool SetEquivalent(IEnumerable a, IEnumerable b) { return a.ToHashSet().SetEquals(b); @@ -77,6 +76,5 @@ internal static bool DictionaryEquals(IReadOnlyDictionary a, IReadOn return true; } -#endif } } diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 708e7151a99..3261eb11b45 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -20,13 +20,11 @@ using Microsoft.Build.Shared; using Microsoft.Build.BackEnd; -#if !CLR2COMPATIBILITY using Microsoft.Build.Shared.Debugging; using System.Collections; using System.Collections.Frozen; using Microsoft.NET.StringTools; -#endif #if !FEATURE_APM using System.Threading.Tasks; #endif @@ -435,13 +433,11 @@ internal static class CommunicationsUtilities /// private static long s_lastLoggedTicks = DateTime.UtcNow.Ticks; -#if !CLR2COMPATIBILITY /// /// A set of environment variables cached from the last time we called GetEnvironmentVariables. /// Used to avoid allocations if the environment has not changed. /// private static EnvironmentState s_environmentState; -#endif /// /// Delegate to debug the communication utilities. @@ -501,14 +497,12 @@ internal static void SetEnvironmentVariable(string name, string value) } #endif -#if !CLR2COMPATIBILITY /// /// A container to atomically swap a cached set of environment variables and the block string used to create it. /// The environment block property will only be set on Windows, since on Unix we need to directly call /// Environment.GetEnvironmentVariables(). /// private sealed record class EnvironmentState(FrozenDictionary EnvironmentVariables, ReadOnlyMemory EnvironmentBlock = default); -#endif /// /// Returns key value pairs of environment variables in a new dictionary @@ -517,10 +511,6 @@ private sealed record class EnvironmentState(FrozenDictionary En /// /// Copied from the BCL implementation to eliminate some expensive security asserts on .NET Framework. /// -#if CLR2COMPATIBILITY - internal static Dictionary GetEnvironmentVariables() - { -#else [System.Runtime.Versioning.SupportedOSPlatform("windows")] private static FrozenDictionary GetEnvironmentVariablesWindows() { @@ -528,7 +518,6 @@ private static FrozenDictionary GetEnvironmentVariablesWindows() // Need to ensure that constructor is called before this method returns in order to capture its env var write. // Otherwise the env var is not captured and thus gets deleted when RequiestBuilder resets the environment based on the cached results of this method. ErrorUtilities.VerifyThrowInternalNull(DebugUtils.ProcessInfoString, nameof(DebugUtils.DebugPath)); -#endif unsafe { @@ -550,7 +539,6 @@ private static FrozenDictionary GetEnvironmentVariablesWindows() } long stringBlockLength = pEnvironmentBlockEnd - pEnvironmentBlock; -#if !CLR2COMPATIBILITY // Avoid allocating any objects if the environment still matches the last state. // We speed this up by comparing the full block instead of individual key-value pairs. ReadOnlySpan stringBlock = new(pEnvironmentBlock, (int)stringBlockLength); @@ -559,7 +547,6 @@ private static FrozenDictionary GetEnvironmentVariablesWindows() { return lastState.EnvironmentVariables; } -#endif Dictionary table = new(200, StringComparer.OrdinalIgnoreCase); // Razzle has 150 environment variables @@ -604,11 +591,7 @@ private static FrozenDictionary GetEnvironmentVariablesWindows() continue; } -#if !CLR2COMPATIBILITY string key = Strings.WeakIntern(new ReadOnlySpan(pEnvironmentBlock + startKey, i - startKey)); -#else - string key = new string(pEnvironmentBlock, startKey, i - startKey); -#endif i++; @@ -621,25 +604,17 @@ private static FrozenDictionary GetEnvironmentVariablesWindows() i++; } -#if !CLR2COMPATIBILITY string value = Strings.WeakIntern(new ReadOnlySpan(pEnvironmentBlock + startValue, i - startValue)); -#else - string value = new string(pEnvironmentBlock, startValue, i - startValue); -#endif // skip over 0 handled by for loop's i++ table[key] = value; } -#if !CLR2COMPATIBILITY // Update with the current state. EnvironmentState currentState = new(table.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase), stringBlock.ToArray()); s_environmentState = currentState; return currentState.EnvironmentVariables; -#else - return table; -#endif } finally { @@ -659,7 +634,6 @@ internal static void SetEnvironmentVariable(string name, string value) => Environment.SetEnvironmentVariable(name, value); #endif -#if !CLR2COMPATIBILITY /// /// Returns key value pairs of environment variables in a read-only dictionary /// with a case-insensitive key comparer. @@ -720,7 +694,6 @@ internal static FrozenDictionary GetEnvironmentVariables() return newState.EnvironmentVariables; } -#endif /// /// Updates the environment to match the provided dictionary. @@ -1184,12 +1157,7 @@ private static void TraceCore(int nodeId, string message) { lock (s_traceLock) { - s_debugDumpPath ??= -#if CLR2COMPATIBILITY - Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); -#else - DebugUtils.DebugPath; -#endif + s_debugDumpPath ??= DebugUtils.DebugPath; if (String.IsNullOrEmpty(s_debugDumpPath)) { diff --git a/src/Shared/CopyOnWriteDictionary.cs b/src/Shared/CopyOnWriteDictionary.cs index b0948ed17a2..f11c87f5b5c 100644 --- a/src/Shared/CopyOnWriteDictionary.cs +++ b/src/Shared/CopyOnWriteDictionary.cs @@ -28,7 +28,6 @@ namespace Microsoft.Build.Collections [Serializable] internal class CopyOnWriteDictionary : IDictionary, IDictionary, ISerializable { -#if !NET35 // MSBuildNameIgnoreCaseComparer not compiled into MSBuildTaskHost but also allocations not interesting there. /// /// Empty dictionary with a , /// used as the basis of new dictionaries with that comparer to avoid @@ -42,8 +41,6 @@ internal class CopyOnWriteDictionary : IDictionary, IDictionary, I /// allocating new comparers objects. /// private static readonly ImmutableDictionary OrdinalIgnoreCaseComparerDictionaryPrototype = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); -#endif - /// /// The backing dictionary. @@ -84,15 +81,11 @@ protected CopyOnWriteDictionary(SerializationInfo info, StreamingContext context private static ImmutableDictionary GetInitialDictionary(IEqualityComparer? keyComparer) { -#if NET35 - return ImmutableDictionary.Create(keyComparer); -#else return keyComparer is MSBuildNameIgnoreCaseComparer - ? NameComparerDictionaryPrototype - : keyComparer == StringComparer.OrdinalIgnoreCase - ? OrdinalIgnoreCaseComparerDictionaryPrototype - : ImmutableDictionary.Create(keyComparer); -#endif + ? NameComparerDictionaryPrototype + : keyComparer == StringComparer.OrdinalIgnoreCase + ? OrdinalIgnoreCaseComparerDictionaryPrototype + : ImmutableDictionary.Create(keyComparer); } /// diff --git a/src/Shared/Debugging/DebugUtils.cs b/src/Shared/Debugging/DebugUtils.cs index a642dfc2bd8..b4643cda115 100644 --- a/src/Shared/Debugging/DebugUtils.cs +++ b/src/Shared/Debugging/DebugUtils.cs @@ -2,8 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; +using System.Text; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared.FileSystem; #nullable disable @@ -100,6 +105,58 @@ internal static void SetDebugPath() DebugPath = debugDirectory; } + private static readonly string s_debugDumpPath = GetDebugDumpPath(); + + /// + /// Gets the location of the directory used for diagnostic log files. + /// + /// + private static string GetDebugDumpPath() + { + string debugPath = DebugPath; + + return !string.IsNullOrEmpty(debugPath) + ? debugPath + : FileUtilities.TempFileDirectory; + } + + private static string s_debugDumpPathInRunningTests = GetDebugDumpPath(); + internal static bool ResetDebugDumpPathInRunningTests = false; + + /// + /// The directory used for diagnostic log files. + /// + internal static string DebugDumpPath + { + get + { + if (BuildEnvironmentHelper.Instance.RunningTests) + { + if (ResetDebugDumpPathInRunningTests) + { + s_debugDumpPathInRunningTests = GetDebugDumpPath(); + // reset dump file name so new one is created in new path + s_dumpFileName = null; + ResetDebugDumpPathInRunningTests = false; + } + + return s_debugDumpPathInRunningTests; + } + + return s_debugDumpPath; + } + } + + /// + /// The file used for diagnostic log files. + /// + internal static string DumpFilePath => s_dumpFileName; + + /// + /// The filename that exceptions will be dumped to + /// + private static string s_dumpFileName; + private static readonly Lazy ProcessNodeMode = new( () => NodeModeHelper.ExtractFromCommandLine(Environment.CommandLine)); @@ -157,5 +214,111 @@ public static string FindNextAvailableDebugFilePath(string fileName) return fullPath; } + + + /// + /// Dump any unhandled exceptions to a file so they can be diagnosed + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It is called by the CLR")] + internal static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) + { + Exception ex = (Exception)e.ExceptionObject; + DumpExceptionToFile(ex); +#if !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS + RecordCrashTelemetryForUnhandledException(ex); +#endif + } + +#if !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS + /// + /// Records and immediately flushes crash telemetry for an unhandled exception. + /// Best effort - must never throw, as the process is already crashing. + /// + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static void RecordCrashTelemetryForUnhandledException(Exception ex) + { + CrashTelemetryRecorder.RecordAndFlushCrashTelemetry( + ex, + exitType: CrashExitType.UnhandledException, + isUnhandled: true, + isCritical: ExceptionHandling.IsCriticalException(ex)); + } +#endif + + /// + /// Dump the exception information to a file + /// + internal static void DumpExceptionToFile(Exception ex) + { + try + { + // Locking on a type is not recommended. However, we are doing it here to be extra cautious about compatibility because + // this method previously had a [MethodImpl(MethodImplOptions.Synchronized)] attribute, which does lock on the type when + // applied to a static method. + lock (typeof(ExceptionHandling)) + { + if (s_dumpFileName == null) + { + Guid guid = Guid.NewGuid(); + + // For some reason we get Watson buckets because GetTempPath gives us a folder here that doesn't exist. + // Either because %TMP% is misdefined, or because they deleted the temp folder during the build. + // If this throws, no sense catching it, we can't log it now, and we're here + // because we're a child node with no console to log to, so die + Directory.CreateDirectory(DebugDumpPath); + + var pid = EnvironmentUtilities.CurrentProcessId; + // This naming pattern is assumed in ReadAnyExceptionFromFile + s_dumpFileName = Path.Combine(DebugDumpPath, $"MSBuild_pid-{pid}_{guid:n}.failure.txt"); + + using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) + { + writer.WriteLine("UNHANDLED EXCEPTIONS FROM PROCESS {0}:", pid); + writer.WriteLine("====================="); + } + } + + using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) + { + // "G" format is, e.g., 6/15/2008 9:15:07 PM + writer.WriteLine(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)); + writer.WriteLine(ex.ToString()); + writer.WriteLine("==================="); + } + } + } + + // Some customers experience exceptions such as 'OutOfMemory' errors when msbuild attempts to log errors to a local file. + // This catch helps to prevent the application from crashing in this best-effort dump-diagnostics path, + // but doesn't prevent the overall crash from going to Watson. + catch + { + } + } + + /// + /// Returns the content of any exception dump files modified + /// since the provided time, otherwise returns an empty string. + /// + internal static string ReadAnyExceptionFromFile(DateTime fromTimeUtc) + { + var builder = new StringBuilder(); + IEnumerable files = FileSystems.Default.EnumerateFiles(DebugDumpPath, "MSBuild*failure.txt"); + + foreach (string file in files) + { + if (FileSystems.Default.GetLastWriteTimeUtc(file) >= fromTimeUtc) + { + builder.Append(Environment.NewLine); + builder.Append(file); + builder.Append(':'); + builder.Append(Environment.NewLine); + builder.Append(FileSystems.Default.ReadFileAllText(file)); + builder.Append(Environment.NewLine); + } + } + + return builder.ToString(); + } } } diff --git a/src/Shared/Debugging/PrintLineDebuggerWriters.cs b/src/Shared/Debugging/PrintLineDebuggerWriters.cs index bcdadc22a24..5f97665568f 100644 --- a/src/Shared/Debugging/PrintLineDebuggerWriters.cs +++ b/src/Shared/Debugging/PrintLineDebuggerWriters.cs @@ -71,7 +71,7 @@ public CompositeWriter(IEnumerable writers) private static readonly Lazy _artifactsLogs = new Lazy( () => { - var executingAssembly = FileUtilities.ExecutingAssemblyPath; + var executingAssembly = BuildEnvironmentHelper.ExecutingAssemblyPath; var binPart = $"bin"; diff --git a/src/Shared/EnvironmentUtilities.cs b/src/Shared/EnvironmentUtilities.cs deleted file mode 100644 index a66cbb26251..00000000000 --- a/src/Shared/EnvironmentUtilities.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System; -using System.Diagnostics; -using System.Threading; - -namespace Microsoft.Build.Shared -{ - internal static partial class EnvironmentUtilities - { -#if !NETCOREAPP - private static volatile int s_processId; - private static volatile string? s_processPath; -#endif - private static volatile string? s_processName; - - /// Gets the unique identifier for the current process. - public static int CurrentProcessId - { - get - { -#if NETCOREAPP - return Environment.ProcessId; -#else - // copied from Environment.ProcessPath - int processId = s_processId; - if (processId == 0) - { - using Process currentProcess = Process.GetCurrentProcess(); - s_processId = processId = currentProcess.Id; - - // Assume that process Id zero is invalid for user processes. It holds for all mainstream operating systems. - Debug.Assert(processId != 0); - } - - return processId; -#endif - } - } - - /// - /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. - /// - /// Path of the executable that started the currently executing process - /// - /// If the executable is renamed or deleted before this property is first accessed, the return value is undefined and depends on the operating system. - /// - public static string? ProcessPath - { - get - { -#if NETCOREAPP - return Environment.ProcessPath; -#else - // copied from Environment.ProcessPath - string? processPath = s_processPath; - if (processPath == null) - { - // The value is cached both as a performance optimization and to ensure that the API always returns - // the same path in a given process. - using Process currentProcess = Process.GetCurrentProcess(); - Interlocked.CompareExchange(ref s_processPath, currentProcess?.MainModule?.FileName ?? "", null); - processPath = s_processPath; - Debug.Assert(processPath != null); - } - - return (processPath?.Length != 0) ? processPath : null; -#endif - } - } - - public static string ProcessName - { - get - { - string? processName = s_processName; - if (processName == null) - { - using Process currentProcess = Process.GetCurrentProcess(); - Interlocked.CompareExchange(ref s_processName, currentProcess.ProcessName, null); - processName = s_processName; - } - - return processName; - } - } - - public static bool IsWellKnownEnvironmentDerivedProperty(string propertyName) - { - return propertyName.StartsWith("MSBUILD", StringComparison.OrdinalIgnoreCase) || - propertyName.StartsWith("COMPLUS_", StringComparison.OrdinalIgnoreCase) || - propertyName.StartsWith("DOTNET_", StringComparison.OrdinalIgnoreCase); - } - } -} diff --git a/src/Shared/ErrorUtilities.cs b/src/Shared/ErrorUtilities.cs index ed4a38b1650..81d609cee19 100644 --- a/src/Shared/ErrorUtilities.cs +++ b/src/Shared/ErrorUtilities.cs @@ -132,12 +132,10 @@ internal static void VerifyThrowInternalNull([NotNull] object? parameter, [Calle /// The object that should already have been used as a lock. internal static void VerifyThrowInternalLockHeld(object locker) { -#if !CLR2COMPATIBILITY if (!Monitor.IsEntered(locker)) { ThrowInternalError("Lock should already have been taken"); } -#endif } /// @@ -521,7 +519,6 @@ internal static void VerifyThrowArgumentLength([NotNull] string? parameter, [Cal } } -#if !CLR2COMPATIBILITY /// /// Throws an ArgumentNullException if the given collection is null /// and ArgumentException if it has zero length. @@ -546,7 +543,6 @@ internal static void VerifyThrowArgumentLengthIfNotNull([MaybeNull] IReadOnly ThrowArgumentLength(parameterName); } } -#endif [DoesNotReturn] private static void ThrowArgumentLength(string? parameterName) diff --git a/src/Shared/ExceptionHandling.cs b/src/Shared/ExceptionHandling.cs deleted file mode 100644 index 77383b611cd..00000000000 --- a/src/Shared/ExceptionHandling.cs +++ /dev/null @@ -1,461 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -#if BUILDINGAPPXTASKS -namespace Microsoft.Build.AppxPackage.Shared -#else -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security; -using System.Text; -using System.Threading; -using System.Xml; -using Microsoft.Build.Shared.FileSystem; -using System.Xml.Schema; -using System.Runtime.Serialization; -#if !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS -using Microsoft.Build.Shared.Debugging; -using Microsoft.Build.Framework.Telemetry; -#endif -using Microsoft.Build.Framework; - -namespace Microsoft.Build.Shared -#endif -{ - /// - /// Utility methods for classifying and handling exceptions. - /// - internal static class ExceptionHandling - { - private static readonly string s_debugDumpPath = GetDebugDumpPath(); - - /// - /// Gets the location of the directory used for diagnostic log files. - /// - /// - private static string GetDebugDumpPath() - { - string debugPath = - - /* Unmerged change from project 'Microsoft.Build.Engine.OM.UnitTests (net7.0)' - Before: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - After: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - */ - /* Unmerged change from project 'Microsoft.Build.Engine.OM.UnitTests (net472)' - Before: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - After: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - */ - /* Unmerged change from project 'MSBuildTaskHost' - Before: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - After: - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) - */ - // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) -#if CLR2COMPATIBILITY || MICROSOFT_BUILD_ENGINE_OM_UNITTESTS - Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); -#else - DebugUtils.DebugPath; -#endif - - return !string.IsNullOrEmpty(debugPath) - ? debugPath - : FileUtilities.TempFileDirectory; - } - - private static string s_debugDumpPathInRunningTests = GetDebugDumpPath(); - internal static bool ResetDebugDumpPathInRunningTests = false; - - /// - /// The directory used for diagnostic log files. - /// - internal static string DebugDumpPath - { - get - { - if (BuildEnvironmentHelper.Instance.RunningTests) - { - if (ResetDebugDumpPathInRunningTests) - { - s_debugDumpPathInRunningTests = GetDebugDumpPath(); - // reset dump file name so new one is created in new path - s_dumpFileName = null; - ResetDebugDumpPathInRunningTests = false; - } - - return s_debugDumpPathInRunningTests; - } - - return s_debugDumpPath; - } - } - - /// - /// The file used for diagnostic log files. - /// - internal static string DumpFilePath => s_dumpFileName; - -#if !BUILDINGAPPXTASKS - /// - /// The filename that exceptions will be dumped to - /// - private static string s_dumpFileName; -#endif - /// - /// If the given exception is "ignorable under some circumstances" return false. - /// Otherwise it's "really bad", and return true. - /// This makes it possible to catch(Exception ex) without catching disasters. - /// - /// The exception to check. - /// True if exception is critical. - internal static bool IsCriticalException(Exception e) - { - if (e is OutOfMemoryException - || e is StackOverflowException - || e is ThreadAbortException - || e is ThreadInterruptedException - || e is AccessViolationException -#if !TASKHOST - || e is CriticalTaskException -#endif -#if !BUILDINGAPPXTASKS - || e is InternalErrorException -#endif - ) - { - // Ideally we would include NullReferenceException, because it should only ever be thrown by CLR (use ArgumentNullException for arguments) - // but we should handle it if tasks and loggers throw it. - - // ExecutionEngineException has been deprecated by the CLR - return true; - } - -#if !CLR2COMPATIBILITY - // Check if any critical exceptions - var aggregateException = e as AggregateException; - - if (aggregateException != null) - { - // If the aggregate exception contains a critical exception it is considered a critical exception - if (aggregateException.InnerExceptions.Any(innerException => IsCriticalException(innerException))) - { - return true; - } - } -#endif - - return false; - } - - /// - /// If the given exception is file IO related or expected return false. - /// Otherwise, return true. - /// - /// The exception to check. - /// True if exception is not IO related or expected otherwise false. - internal static bool NotExpectedException(Exception e) - { - return !IsIoRelatedException(e); - } - - /// - /// Determine whether the exception is file-IO related. - /// - /// The exception to check. - /// True if exception is IO related. - internal static bool IsIoRelatedException(Exception e) - { - // These all derive from IOException - // DirectoryNotFoundException - // DriveNotFoundException - // EndOfStreamException - // FileLoadException - // FileNotFoundException - // PathTooLongException - // PipeException - return e is UnauthorizedAccessException - || e is NotSupportedException - || (e is ArgumentException && !(e is ArgumentNullException)) - || e is SecurityException - || e is IOException; - } - - /// Checks if the exception is an XML one. - /// Exception to check. - /// True if exception is related to XML parsing. - internal static bool IsXmlException(Exception e) - { - return e is XmlException -#if FEATURE_SECURITY_PERMISSIONS - || e is System.Security.XmlSyntaxException -#endif - || e is XmlSchemaException - || e is UriFormatException; // XmlTextReader for example uses this under the covers - } - - /// Extracts line and column numbers from the exception if it is XML-related one. - /// XML-related exception. - /// Line and column numbers if available, (0,0) if not. - /// This function works around the fact that XmlException and XmlSchemaException are not directly related. - internal static LineAndColumn GetXmlLineAndColumn(Exception e) - { - var line = 0; - var column = 0; - - var xmlException = e as XmlException; - if (xmlException != null) - { - line = xmlException.LineNumber; - column = xmlException.LinePosition; - } - else - { - var schemaException = e as XmlSchemaException; - if (schemaException != null) - { - line = schemaException.LineNumber; - column = schemaException.LinePosition; - } - } - - return new LineAndColumn - { - Line = line, - Column = column - }; - } - -#if !BUILDINGAPPXTASKS - - /// - /// If the given exception is file IO related or Xml related return false. - /// Otherwise, return true. - /// - /// The exception to check. - internal static bool NotExpectedIoOrXmlException(Exception e) - { - if - ( - IsXmlException(e) - || !NotExpectedException(e)) - { - return false; - } - - return true; - } - - /// - /// If the given exception is reflection-related return false. - /// Otherwise, return true. - /// - /// The exception to check. - internal static bool NotExpectedReflectionException(Exception e) - { - // We are explicitly not handling TargetInvocationException. Those are just wrappers around - // exceptions thrown by the called code (such as a task or logger) which callers will typically - // want to treat differently. - if - ( - e is TypeLoadException // thrown when the common language runtime cannot find the assembly, the type within the assembly, or cannot load the type - || e is MethodAccessException // thrown when a class member is not found or access to the member is not permitted - || e is MissingMethodException // thrown when code in a dependent assembly attempts to access a missing method in an assembly that was modified - || e is MemberAccessException // thrown when a class member is not found or access to the member is not permitted - || e is BadImageFormatException // thrown when the file image of a DLL or an executable program is invalid - || e is ReflectionTypeLoadException // thrown by the Module.GetTypes method if any of the classes in a module cannot be loaded - || e is TargetParameterCountException // thrown when the number of parameters for an invocation does not match the number expected - || e is InvalidCastException - || e is AmbiguousMatchException // thrown when binding to a member results in more than one member matching the binding criteria - || e is CustomAttributeFormatException // thrown if a custom attribute on a data type is formatted incorrectly - || e is InvalidFilterCriteriaException // thrown in FindMembers when the filter criteria is not valid for the type of filter you are using - || e is TargetException // thrown when an attempt is made to invoke a non-static method on a null object. This may occur because the caller does not - // have access to the member, or because the target does not define the member, and so on. - || e is MissingFieldException // thrown when code in a dependent assembly attempts to access a missing field in an assembly that was modified. - || !NotExpectedException(e)) // Reflection can throw IO exceptions if the assembly cannot be opened - { - return false; - } - - return true; - } - - /// - /// Serialization has been observed to throw TypeLoadException as - /// well as SerializationException and IO exceptions. (Obviously - /// it has to do reflection but it ought to be wrapping the exceptions.) - /// - internal static bool NotExpectedSerializationException(Exception e) - { - if - ( - e is SerializationException || - !NotExpectedReflectionException(e)) - { - return false; - } - - return true; - } - - /// - /// Returns false if this is a known exception thrown by the registry API. - /// - internal static bool NotExpectedRegistryException(Exception e) - { - if (e is SecurityException - || e is UnauthorizedAccessException - || e is IOException - || e is ObjectDisposedException - || e is ArgumentException) - { - return false; - } - - return true; - } - - /// - /// Returns false if this is a known exception thrown by function evaluation - /// - internal static bool NotExpectedFunctionException(Exception e) - { - if (e is InvalidCastException - || e is ArgumentNullException - || e is FormatException - || e is InvalidOperationException - || !NotExpectedReflectionException(e)) - { - return false; - } - - return true; - } - - /// - /// Dump any unhandled exceptions to a file so they can be diagnosed - /// - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It is called by the CLR")] - internal static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) - { - Exception ex = (Exception)e.ExceptionObject; - DumpExceptionToFile(ex); -#if !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS - RecordCrashTelemetryForUnhandledException(ex); -#endif - } - -#if !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS - /// - /// Records and immediately flushes crash telemetry for an unhandled exception. - /// Best effort - must never throw, as the process is already crashing. - /// - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private static void RecordCrashTelemetryForUnhandledException(Exception ex) - { - CrashTelemetryRecorder.RecordAndFlushCrashTelemetry( - ex, - exitType: CrashExitType.UnhandledException, - isUnhandled: true, - isCritical: IsCriticalException(ex)); - } -#endif - - /// - /// Dump the exception information to a file - /// - internal static void DumpExceptionToFile(Exception ex) - { - try - { - // Locking on a type is not recommended. However, we are doing it here to be extra cautious about compatibility because - // this method previously had a [MethodImpl(MethodImplOptions.Synchronized)] attribute, which does lock on the type when - // applied to a static method. - lock (typeof(ExceptionHandling)) - { - if (s_dumpFileName == null) - { - Guid guid = Guid.NewGuid(); - - // For some reason we get Watson buckets because GetTempPath gives us a folder here that doesn't exist. - // Either because %TMP% is misdefined, or because they deleted the temp folder during the build. - // If this throws, no sense catching it, we can't log it now, and we're here - // because we're a child node with no console to log to, so die - Directory.CreateDirectory(DebugDumpPath); - - var pid = EnvironmentUtilities.CurrentProcessId; - // This naming pattern is assumed in ReadAnyExceptionFromFile - s_dumpFileName = Path.Combine(DebugDumpPath, $"MSBuild_pid-{pid}_{guid:n}.failure.txt"); - - using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) - { - writer.WriteLine("UNHANDLED EXCEPTIONS FROM PROCESS {0}:", pid); - writer.WriteLine("====================="); - } - } - - using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) - { - // "G" format is, e.g., 6/15/2008 9:15:07 PM - writer.WriteLine(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)); - writer.WriteLine(ex.ToString()); - writer.WriteLine("==================="); - } - } - } - - // Some customers experience exceptions such as 'OutOfMemory' errors when msbuild attempts to log errors to a local file. - // This catch helps to prevent the application from crashing in this best-effort dump-diagnostics path, - // but doesn't prevent the overall crash from going to Watson. - catch - { - } - } - - /// - /// Returns the content of any exception dump files modified - /// since the provided time, otherwise returns an empty string. - /// - internal static string ReadAnyExceptionFromFile(DateTime fromTimeUtc) - { - var builder = new StringBuilder(); - IEnumerable files = FileSystems.Default.EnumerateFiles(DebugDumpPath, "MSBuild*failure.txt"); - - foreach (string file in files) - { - if (FileSystems.Default.GetLastWriteTimeUtc(file) >= fromTimeUtc) - { - builder.Append(Environment.NewLine); - builder.Append(file); - builder.Append(':'); - builder.Append(Environment.NewLine); - builder.Append(FileSystems.Default.ReadFileAllText(file)); - builder.Append(Environment.NewLine); - } - } - - return builder.ToString(); - } -#endif - - /// Line and column pair. - internal struct LineAndColumn - { - /// Gets or sets line number. - internal int Line { get; set; } - - /// Gets or sets column position. - internal int Column { get; set; } - } - } -} diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index d08e6549791..584d0b955d9 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -46,7 +46,7 @@ internal class FileMatcher #endif // on OSX both System.IO.Path separators are '/', so we have to use the literals - internal static readonly char[] directorySeparatorCharacters = FrameworkFileUtilities.Slashes; + internal static readonly char[] directorySeparatorCharacters = FileUtilities.Slashes; // until Cloudbuild switches to EvaluationContext, we need to keep their dependence on global glob caching via an environment variable private static readonly Lazy>> s_cachedGlobExpansions = new Lazy>>(() => new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase)); @@ -233,7 +233,7 @@ internal static bool HasPropertyOrItemReferences(string filespec) /// private static IReadOnlyList GetAccessibleFileSystemEntries(IFileSystem fileSystem, FileSystemEntity entityType, string path, string pattern, string projectDirectory, bool stripProjectDirectory) { - path = FrameworkFileUtilities.FixFilePath(path); + path = FileUtilities.FixFilePath(path); switch (entityType) { case FileSystemEntity.Files: return GetAccessibleFiles(fileSystem, path, pattern, projectDirectory, stripProjectDirectory); @@ -592,7 +592,7 @@ private static void PreprocessFileSpecForSplitting( out string wildcardDirectoryPart, out string filenamePart) { - filespec = FrameworkFileUtilities.FixFilePath(filespec); + filespec = FileUtilities.FixFilePath(filespec); int indexOfLastDirectorySeparator = filespec.LastIndexOfAny(directorySeparatorCharacters); if (-1 == indexOfLastDirectorySeparator) { @@ -2145,7 +2145,7 @@ private SearchAction GetFileSearchData( wildcard[wildcardLength - 1] == '*') { // Check that there are no other slashes in the wildcard. - if (wildcard.IndexOfAny(FrameworkFileUtilities.Slashes, 3, wildcardLength - 6) == -1) + if (wildcard.IndexOfAny(FileUtilities.Slashes, 3, wildcardLength - 6) == -1) { directoryPattern = wildcard.Substring(3, wildcardLength - 6); } @@ -2679,7 +2679,7 @@ private static bool IsSubdirectoryOf(string possibleChild, string possibleParent /// True in case of a match (e.g. directoryPath = "dir/subdir" and pattern = "s*"), false otherwise. private static bool DirectoryEndsWithPattern(string directoryPath, string pattern) { - int index = directoryPath.LastIndexOfAny(FrameworkFileUtilities.Slashes); + int index = directoryPath.LastIndexOfAny(FileUtilities.Slashes); return (index != -1 && IsMatch(directoryPath.AsSpan(index + 1), pattern)); } diff --git a/src/Shared/FileSystemSources.proj b/src/Shared/FileSystemSources.proj deleted file mode 100644 index f86f3cfbd1c..00000000000 --- a/src/Shared/FileSystemSources.proj +++ /dev/null @@ -1,7 +0,0 @@ - - - - FileSystem\%(Filename).cs - - - \ No newline at end of file diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs deleted file mode 100644 index dbb88c31088..00000000000 --- a/src/Shared/FileUtilities.cs +++ /dev/null @@ -1,1587 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -#if !CLR2COMPATIBILITY -using System.Collections.Concurrent; -#else -using Microsoft.Build.Shared.Concurrent; -#endif -#if NET -using System.Buffers; -#endif -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared.FileSystem; - -#nullable disable - -namespace Microsoft.Build.Shared -{ - /// - /// This class contains utility methods for file IO. - /// PERF\COVERAGE NOTE: Try to keep classes in 'shared' as granular as possible. All the methods in - /// each class get pulled into the resulting assembly. - /// - internal static partial class FileUtilities - { - // A list of possible test runners. If the program running has one of these substrings in the name, we assume - // this is a test harness. - - // This flag, when set, indicates that we are running tests. Initially assume it's true. It also implies that - // the currentExecutableOverride is set to a path (that is non-null). Assume this is not initialized when we - // have the impossible combination of runningTests = false and currentExecutableOverride = null. - - // This is the fake current executable we use in case we are running tests. - - /// - /// The directory where MSBuild stores cache information used during the build. - /// - internal static string cacheDirectory = null; - -#if CLR2COMPATIBILITY - internal static string TempFileDirectory => Path.GetTempPath(); -#endif - - /// - /// FOR UNIT TESTS ONLY - /// Clear out the static variable used for the cache directory so that tests that - /// modify it can validate their modifications. - /// - internal static void ClearCacheDirectoryPath() - { - cacheDirectory = null; - } - - internal static readonly StringComparison PathComparison = GetIsFileSystemCaseSensitive() ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; - - internal static readonly StringComparer PathComparer = GetIsFileSystemCaseSensitive() ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; - - /// - /// Determines whether the file system is case sensitive. - /// Copied from https://github.com/dotnet/runtime/blob/73ba11f3015216b39cb866d9fb7d3d25e93489f2/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs#L41-L59 - /// - public static bool GetIsFileSystemCaseSensitive() - { - try - { - string pathWithUpperCase = Path.Combine(Path.GetTempPath(), $"CASESENSITIVETEST{Guid.NewGuid():N}"); - using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) - { - string lowerCased = pathWithUpperCase.ToLowerInvariant(); - return !FileSystems.Default.FileExists(lowerCased); - } - } - catch (Exception exc) - { - // In case something goes terribly wrong, we don't want to fail just because - // of a casing test, so we assume case-insensitive-but-preserving. - Debug.Fail("Casing test failed: " + exc); - return false; - } - } - - /// - /// Copied from https://github.com/dotnet/corefx/blob/056715ff70e14712419d82d51c8c50c54b9ea795/src/Common/src/System/IO/PathInternal.Windows.cs#L61 - /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 - /// -#if NET - internal static readonly SearchValues InvalidPathChars = SearchValues.Create( -#else - internal static readonly char[] InvalidPathChars = ( -#endif - [ - '|', '\0', - (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, - (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, - (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, - (char)31 - ]); - - /// - /// Copied from https://github.com/dotnet/corefx/blob/387cf98c410bdca8fd195b28cbe53af578698f94/src/System.Runtime.Extensions/src/System/IO/Path.Windows.cs#L18 - /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 - /// - internal static readonly char[] InvalidFileNameCharsArray = - [ - '\"', '<', '>', '|', '\0', - (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, - (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, - (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, - (char)31, ':', '*', '?', '\\', '/' - ]; - -#if NET - internal static readonly SearchValues InvalidFileNameChars = SearchValues.Create(InvalidFileNameCharsArray); -#else - internal static char[] InvalidFileNameChars => InvalidFileNameCharsArray; -#endif - - internal static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString(); - - private static readonly ConcurrentDictionary FileExistenceCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private static readonly IFileSystem DefaultFileSystem = FileSystems.Default; - - /// - /// Retrieves the MSBuild runtime cache directory - /// - internal static string GetCacheDirectory() - { - if (cacheDirectory == null) - { - cacheDirectory = Path.Combine(TempFileDirectory, string.Format(CultureInfo.CurrentUICulture, "MSBuild{0}-{1}", EnvironmentUtilities.CurrentProcessId, AppDomain.CurrentDomain.Id)); - } - - return cacheDirectory; - } - - /// - /// Get the hex hash string for the string - /// - internal static string GetHexHash(string stringToHash) - { - return stringToHash.GetHashCode().ToString("X", CultureInfo.InvariantCulture); - } - - /// - /// Get the hash for the assemblyPaths - /// - internal static int GetPathsHash(IEnumerable assemblyPaths) - { - StringBuilder builder = new StringBuilder(); - - foreach (string path in assemblyPaths) - { - if (path != null) - { - string directoryPath = path.Trim(); - if (directoryPath.Length > 0) - { - DateTime lastModifiedTime; - if (NativeMethodsShared.GetLastWriteDirectoryUtcTime(directoryPath, out lastModifiedTime)) - { - builder.Append(lastModifiedTime.Ticks); - builder.Append('|'); - builder.Append(directoryPath.ToUpperInvariant()); - builder.Append('|'); - } - } - } - } - - return builder.ToString().GetHashCode(); - } - - /// - /// Returns whether MSBuild can write to the given directory. Throws for PathTooLongExceptions - /// but not other exceptions. - /// - internal static bool CanWriteToDirectory(string directory) - { - try - { - string testFilePath = Path.Combine(directory, $"MSBuild_{Guid.NewGuid():N}_testFile.txt"); - FileInfo file = new(testFilePath); - file.Directory.Create(); // If the directory already exists, this method does nothing. - File.WriteAllText(testFilePath, $"MSBuild process {EnvironmentUtilities.CurrentProcessId} successfully wrote to file."); - File.Delete(testFilePath); - return true; - } - catch (PathTooLongException) - { - ErrorUtilities.ThrowArgument("DebugPathTooLong", directory); - return false; // Should never reach here. - } - catch (Exception) - { - return false; - } - } - - /// - /// Clears the MSBuild runtime cache - /// - internal static void ClearCacheDirectory() - { - string cacheDirectory = GetCacheDirectory(); - - if (DefaultFileSystem.DirectoryExists(cacheDirectory)) - { - DeleteDirectoryNoThrow(cacheDirectory, true); - } - } - - /// - /// Ensures the path does not have a leading or trailing slash after removing the first 'start' characters. - /// - internal static string EnsureNoLeadingOrTrailingSlash(string path, int start) - { - int stop = path.Length; - while (start < stop && FrameworkFileUtilities.IsSlash(path[start])) - { - start++; - } - while (start < stop && FrameworkFileUtilities.IsSlash(path[stop - 1])) - { - stop--; - } - - return FrameworkFileUtilities.FixFilePath(path.Substring(start, stop - start)); - } - - /// - /// Ensures the path does not have a leading slash after removing the first 'start' characters but does end in a slash. - /// - internal static string EnsureTrailingNoLeadingSlash(string path, int start) - { - int stop = path.Length; - while (start < stop && FrameworkFileUtilities.IsSlash(path[start])) - { - start++; - } - - return FrameworkFileUtilities.FixFilePath(start < stop && FrameworkFileUtilities.IsSlash(path[stop - 1]) ? - path.Substring(start) : -#if NET - string.Concat(path.AsSpan(start), new(in Path.DirectorySeparatorChar))); -#else - path.Substring(start) + Path.DirectorySeparatorChar); -#endif - } - - /// - /// Ensures the path is enclosed within single quotes. - /// - /// The path to check. - /// The path enclosed by quotes. - internal static string EnsureSingleQuotes(string path) - { - return EnsureQuotes(path); - } - - /// - /// Ensures the path is enclosed within double quotes. - /// - /// The path to check. - /// The path enclosed by quotes. - internal static string EnsureDoubleQuotes(string path) - { - return EnsureQuotes(path, isSingleQuote: false); - } - - /// - /// Ensures the path is enclosed within quotes. - /// - /// The path to check. - /// Indicates if single or double quotes should be used - /// The path enclosed by quotes. - internal static string EnsureQuotes(string path, bool isSingleQuote = true) - { - path = FrameworkFileUtilities.FixFilePath(path); - - const char singleQuote = '\''; - const char doubleQuote = '\"'; - var targetQuote = isSingleQuote ? singleQuote : doubleQuote; - var convertQuote = isSingleQuote ? doubleQuote : singleQuote; - - if (!string.IsNullOrEmpty(path)) - { - // Special case: convert the quotes. - if (path.Length > 1 && path[0] == convertQuote && path[path.Length - 1] == convertQuote) - { -#if NET - path = $"{targetQuote}{path.AsSpan(1, path.Length - 2)}{targetQuote}"; -#else - path = $"{targetQuote}{path.Substring(1, path.Length - 2)}{targetQuote}"; -#endif - } - // Enclose the path in a set of the 'target' quote unless the string is already quoted with the 'target' quotes. - else if (path.Length == 1 || path[0] != targetQuote || path[path.Length - 1] != targetQuote) - { - path = $"{targetQuote}{path}{targetQuote}"; - } - } - - return path; - } - - /// - /// Trims the string and removes any double quotes around it. - /// - internal static string TrimAndStripAnyQuotes(string path) - { - if (path is null) - { - return path; - } - - // Trim returns the same string if trimming isn't needed - path = path.Trim(); - path = path.Trim(['"']); - - return path; - } - - /// - /// Get the directory name of a rooted full path - /// - /// - /// - internal static String GetDirectoryNameOfFullPath(String fullPath) - { - if (fullPath != null) - { - int i = fullPath.Length; - while (i > 0 && fullPath[--i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) - { - ; - } - - return FrameworkFileUtilities.FixFilePath(fullPath.Substring(0, i)); - } - return null; - } - - internal static string TruncatePathToTrailingSegments(string path, int trailingSegmentsToKeep) - { -#if !CLR2COMPATIBILITY - ErrorUtilities.VerifyThrowInternalLength(path, nameof(path)); - ErrorUtilities.VerifyThrow(trailingSegmentsToKeep >= 0, "trailing segments must be positive"); - - var segments = path.Split(FrameworkFileUtilities.Slashes, StringSplitOptions.RemoveEmptyEntries); - - var headingSegmentsToRemove = Math.Max(0, segments.Length - trailingSegmentsToKeep); - - return string.Join(DirectorySeparatorString, segments.Skip(headingSegmentsToRemove)); -#else - return path; -#endif - } - - internal static bool ContainsRelativePathSegments(string path) - { - for (int i = 0; i < path.Length; i++) - { - if (i + 1 < path.Length && path[i] == '.' && path[i + 1] == '.') - { - if (RelativePathBoundsAreValid(path, i, i + 1)) - { - return true; - } - else - { - i += 2; - continue; - } - } - - if (path[i] == '.' && RelativePathBoundsAreValid(path, i, i)) - { - return true; - } - } - - return false; - } - -#if !CLR2COMPATIBILITY - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - private static bool RelativePathBoundsAreValid(string path, int leftIndex, int rightIndex) - { - var leftBound = leftIndex - 1 >= 0 - ? path[leftIndex - 1] - : (char?)null; - - var rightBound = rightIndex + 1 < path.Length - ? path[rightIndex + 1] - : (char?)null; - - return IsValidRelativePathBound(leftBound) && IsValidRelativePathBound(rightBound); - } - -#if !CLR2COMPATIBILITY - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - private static bool IsValidRelativePathBound(char? c) - { - return c == null || IsAnySlash(c.Value); - } - - /// - /// Gets the canonicalized full path of the provided path. - /// Guidance for use: call this on all paths accepted through public entry - /// points that need normalization. After that point, only verify the path - /// is rooted, using ErrorUtilities.VerifyThrowPathRooted. - /// ASSUMES INPUT IS ALREADY UNESCAPED. - /// - internal static string NormalizePath(string path) - { - ErrorUtilities.VerifyThrowArgumentLength(path); - string fullPath = GetFullPath(path); - return FrameworkFileUtilities.FixFilePath(fullPath); - } - - internal static string NormalizePath(string directory, string file) - { - return NormalizePath(Path.Combine(directory, file)); - } - -#if !CLR2COMPATIBILITY - internal static string NormalizePath(params string[] paths) - { - return NormalizePath(Path.Combine(paths)); - } -#endif - - private static string GetFullPath(string path) - { -#if FEATURE_LEGACY_GETFULLPATH - if (NativeMethodsShared.IsWindows) - { - string uncheckedFullPath = NativeMethodsShared.GetFullPath(path); - - if (IsPathTooLong(uncheckedFullPath)) - { - string message = ResourceUtilities.FormatString(AssemblyResources.GetString("Shared.PathTooLong"), path, NativeMethodsShared.MaxPath); - throw new PathTooLongException(message); - } - - // We really don't care about extensions here, but Path.HasExtension provides a great way to - // invoke the CLR's invalid path checks (these are independent of path length) - Path.HasExtension(uncheckedFullPath); - - // If we detect we are a UNC path then we need to use the regular get full path in order to do the correct checks for UNC formatting - // and security checks for strings like \\?\GlobalRoot - return IsUNCPath(uncheckedFullPath) ? Path.GetFullPath(uncheckedFullPath) : uncheckedFullPath; - } -#endif - return Path.GetFullPath(path); - } - -#if FEATURE_LEGACY_GETFULLPATH - private static bool IsUNCPath(string path) - { - if (!NativeMethodsShared.IsWindows || !path.StartsWith(@"\\", StringComparison.Ordinal)) - { - return false; - } - bool isUNC = true; - for (int i = 2; i < path.Length - 1; i++) - { - if (path[i] == '\\') - { - isUNC = false; - break; - } - } - - /* - From Path.cs in the CLR - - Throw an ArgumentException for paths like \\, \\server, \\server\ - This check can only be properly done after normalizing, so - \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\ - (an internal kernel path) because it provides aliases for drives. - - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); - - // Check for \\?\Globalroot, an internal mechanism to the kernel - // that provides aliases for drives and other undocumented stuff. - // The kernel team won't even describe the full set of what - // is available here - we don't want managed apps mucking - // with this for security reasons. - */ - return isUNC || path.IndexOf(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase) != -1; - } -#endif // FEATURE_LEGACY_GETFULLPATH - - /// - /// Normalizes all path separators (both forward and back slashes) to forward slashes. - /// This is platform-independent, unlike FrameworkFileUtilities.FixFilePath which only normalizes on non-Windows platforms. - /// Use this when you need consistent path comparison regardless of which separator style is used. - /// - /// The path to normalize - /// The path with all backslashes replaced by forward slashes, or the original path if null/empty - internal static string NormalizePathSeparatorsToForwardSlash(string path) - { - return string.IsNullOrEmpty(path) ? path : path.Replace('\\', '/'); - } - -#if !CLR2COMPATIBILITY - /// - /// If on Unix, convert backslashes to slashes for strings that resemble paths. - /// The heuristic is if something resembles paths (contains slashes) check if the - /// first segment exists and is a directory. - /// Use a native shared method to massage file path. If the file is adjusted, - /// that qualifies is as a path. - /// - /// @baseDirectory is just passed to LooksLikeUnixFilePath, to help with the check - /// - internal static string MaybeAdjustFilePath(string value, string baseDirectory = "") - { - var comparisonType = StringComparison.Ordinal; - - // Don't bother with arrays or properties or network paths, or those that - // have no slashes. - if (NativeMethodsShared.IsWindows || string.IsNullOrEmpty(value) - || value.StartsWith("$(", comparisonType) || value.StartsWith("@(", comparisonType) - || value.StartsWith("\\\\", comparisonType)) - { - return value; - } - - // For Unix-like systems, we may want to convert backslashes to slashes - Span newValue = ConvertToUnixSlashes(value.ToCharArray()); - - // Find the part of the name we want to check, that is remove quotes, if present - bool shouldAdjust = newValue.IndexOf('/') != -1 && LooksLikeUnixFilePath(RemoveQuotes(newValue), baseDirectory); - return shouldAdjust ? newValue.ToString() : value; - } - - /// - /// If on Unix, convert backslashes to slashes for strings that resemble paths. - /// This overload takes and returns ReadOnlyMemory of characters. - /// - internal static ReadOnlyMemory MaybeAdjustFilePath(ReadOnlyMemory value, string baseDirectory = "") - { - if (NativeMethodsShared.IsWindows || value.IsEmpty) - { - return value; - } - - // Don't bother with arrays or properties or network paths. - if (value.Length >= 2) - { - var span = value.Span; - - // The condition is equivalent to span.StartsWith("$(") || span.StartsWith("@(") || span.StartsWith("\\\\") - if ((span[1] == '(' && (span[0] == '$' || span[0] == '@')) || - (span[1] == '\\' && span[0] == '\\')) - { - return value; - } - } - - // For Unix-like systems, we may want to convert backslashes to slashes - Span newValue = ConvertToUnixSlashes(value.ToArray()); - - // Find the part of the name we want to check, that is remove quotes, if present - bool shouldAdjust = newValue.IndexOf('/') != -1 && LooksLikeUnixFilePath(RemoveQuotes(newValue), baseDirectory); - return shouldAdjust ? newValue.ToString().AsMemory() : value; - } - - private static Span ConvertToUnixSlashes(Span path) - { - return path.IndexOf('\\') == -1 ? path : CollapseSlashes(path); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span CollapseSlashes(Span str) - { - int sliceLength = 0; - - // Performs Regex.Replace(str, @"[\\/]+", "/") - for (int i = 0; i < str.Length; i++) - { - bool isCurSlash = IsAnySlash(str[i]); - bool isPrevSlash = i > 0 && IsAnySlash(str[i - 1]); - - if (!isCurSlash || !isPrevSlash) - { - str[sliceLength] = str[i] == '\\' ? '/' : str[i]; - sliceLength++; - } - } - - return str.Slice(0, sliceLength); - } - - private static Span RemoveQuotes(Span path) - { - int endId = path.Length - 1; - char singleQuote = '\''; - char doubleQuote = '\"'; - - bool hasQuotes = path.Length > 2 - && ((path[0] == singleQuote && path[endId] == singleQuote) - || (path[0] == doubleQuote && path[endId] == doubleQuote)); - - return hasQuotes ? path.Slice(1, endId - 1) : path; - } -#endif - -#if !CLR2COMPATIBILITY - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - internal static bool IsAnySlash(char c) => c == '/' || c == '\\'; - -#if !CLR2COMPATIBILITY - /// - /// If on Unix, check if the string looks like a file path. - /// The heuristic is if something resembles paths (contains slashes) check if the - /// first segment exists and is a directory. - /// - /// If @baseDirectory is not null, then look for the first segment exists under - /// that - /// - internal static bool LooksLikeUnixFilePath(string value, string baseDirectory = "") - => LooksLikeUnixFilePath(value.AsSpan(), baseDirectory); - - internal static bool LooksLikeUnixFilePath(ReadOnlySpan value, string baseDirectory = "") - { - if (NativeMethodsShared.IsWindows) - { - return false; - } - - // The first slash will either be at the beginning of the string or after the first directory name - int directoryLength = value.Slice(1).IndexOf('/') + 1; - bool shouldCheckDirectory = directoryLength != 0; - - // Check for actual files or directories under / that get missed by the above logic - bool shouldCheckFileOrDirectory = !shouldCheckDirectory && value.Length > 0 && value[0] == '/'; - ReadOnlySpan directory = value.Slice(0, directoryLength); - - return (shouldCheckDirectory && DefaultFileSystem.DirectoryExists(Path.Combine(baseDirectory, directory.ToString()))) - || (shouldCheckFileOrDirectory && DefaultFileSystem.FileOrDirectoryExists(value.ToString())); - } -#endif - - /// - /// Extracts the directory from the given file-spec. - /// - /// The filespec. - /// directory path - internal static string GetDirectory(string fileSpec) - { - string directory = Path.GetDirectoryName(FrameworkFileUtilities.FixFilePath(fileSpec)); - - // if file-spec is a root directory e.g. c:, c:\, \, \\server\share - // NOTE: Path.GetDirectoryName also treats invalid UNC file-specs as root directories e.g. \\, \\server - if (directory == null) - { - // just use the file-spec as-is - directory = fileSpec; - } - else if ((directory.Length > 0) && !FrameworkFileUtilities.EndsWithSlash(directory)) - { - // restore trailing slash if Path.GetDirectoryName has removed it (this happens with non-root directories) - directory += Path.DirectorySeparatorChar; - } - - return directory; - } - -#if !CLR2COMPATIBILITY - /// - /// Deletes all subdirectories within the specified directory without throwing exceptions. - /// This method enumerates all subdirectories in the given directory and attempts to delete - /// each one recursively. If any IO-related exceptions occur during enumeration or deletion, - /// they are silently ignored. - /// - /// The directory whose subdirectories should be deleted. - /// - /// This method is useful for cleanup operations where partial failure is acceptable. - /// It will not delete the root directory itself, only its subdirectories. - /// IO exceptions during directory enumeration or deletion are caught and ignored. - /// - internal static void DeleteSubdirectoriesNoThrow(string directory) - { - try - { - foreach (string dir in FileSystems.Default.EnumerateDirectories(directory)) - { - DeleteDirectoryNoThrow(dir, recursive: true, retryCount: 1); - } - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - // If we can't enumerate the directories, ignore. Other cases should be handled by DeleteDirectoryNoThrow. - } - } -#endif - - /// - /// Determines whether the given assembly file name has one of the listed extensions. - /// - /// The name of the file - /// Array of extensions to consider. - /// - internal static bool HasExtension(string fileName, string[] allowedExtensions) - { - Debug.Assert(allowedExtensions?.Length > 0); - - // Easiest way to invoke invalid path chars - // check, which callers are relying on. - if (Path.HasExtension(fileName)) - { - foreach (string extension in allowedExtensions) - { - Debug.Assert(!String.IsNullOrEmpty(extension) && extension[0] == '.'); - - if (fileName.EndsWith(extension, PathComparison)) - { - return true; - } - } - } - - return false; - } - - // ISO 8601 Universal time with sortable format - internal const string FileTimeFormat = "yyyy'-'MM'-'dd HH':'mm':'ss'.'fffffff"; - - /// - /// Get the currently executing assembly path - /// - internal static string ExecutingAssemblyPath => Path.GetFullPath(AssemblyUtilities.GetAssemblyLocation(typeof(FileUtilities).GetTypeInfo().Assembly)); - - /// - /// Determines the full path for the given file-spec. - /// ASSUMES INPUT IS STILL ESCAPED - /// - /// The file spec to get the full path of. - /// - /// Whether to escape the path after getting the full path. - /// Full path to the file, escaped if not specified otherwise. - internal static string GetFullPath(string fileSpec, string currentDirectory, bool escape = true) - { - // Sending data out of the engine into the filesystem, so time to unescape. - fileSpec = FrameworkFileUtilities.FixFilePath(EscapingUtilities.UnescapeAll(fileSpec)); - - string fullPath = NormalizePath(Path.Combine(currentDirectory, fileSpec)); - // In some cases we might want to NOT escape in order to preserve symbols like @, %, $ etc. - if (escape) - { - // Data coming back from the filesystem into the engine, so time to escape it back. - fullPath = EscapingUtilities.Escape(fullPath); - } - - if (NativeMethodsShared.IsWindows && !FrameworkFileUtilities.EndsWithSlash(fullPath)) - { - if (FileUtilitiesRegex.IsDrivePattern(fileSpec) || - FileUtilitiesRegex.IsUncPattern(fullPath)) - { - // append trailing slash if Path.GetFullPath failed to (this happens with drive-specs and UNC shares) - fullPath += Path.DirectorySeparatorChar; - } - } - - return fullPath; - } - - /// - /// A variation of Path.GetFullPath that will return the input value - /// instead of throwing any IO exception. - /// Useful to get a better path for an error message, without the risk of throwing - /// if the error message was itself caused by the path being invalid! - /// - internal static string GetFullPathNoThrow(string path) - { - try - { - path = NormalizePath(path); - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - } - - return path; - } - - /// - /// Compare if two paths, relative to the given currentDirectory are equal. - /// Does not throw IO exceptions. See - /// - /// - /// - /// - /// - /// - internal static bool ComparePathsNoThrow(string first, string second, string currentDirectory, bool alwaysIgnoreCase = false) - { - StringComparison pathComparison = alwaysIgnoreCase ? StringComparison.OrdinalIgnoreCase : PathComparison; - // perf: try comparing the bare strings first - if (string.Equals(first, second, pathComparison)) - { - return true; - } - - var firstFullPath = NormalizePathForComparisonNoThrow(first, currentDirectory); - var secondFullPath = NormalizePathForComparisonNoThrow(second, currentDirectory); - - return string.Equals(firstFullPath, secondFullPath, pathComparison); - } - - /// - /// Normalizes a path for path comparison - /// Does not throw IO exceptions. See - /// - /// - internal static string NormalizePathForComparisonNoThrow(string path, string currentDirectory) - { - // file is invalid, return early to avoid triggering an exception - if (PathIsInvalid(path)) - { - return path; - } - - var normalizedPath = path.NormalizeForPathComparison(); - var fullPath = GetFullPathNoThrow(Path.Combine(currentDirectory, normalizedPath)); - - return fullPath; - } - - internal static bool PathIsInvalid(string path) - { - // Path.GetFileName does not react well to malformed filenames. - // For example, Path.GetFileName("a/b/foo:bar") returns bar instead of foo:bar - // It also throws exceptions on illegal path characters -#if NET - if (!path.AsSpan().ContainsAny(InvalidPathChars)) - { - int lastDirectorySeparator = path.LastIndexOfAny(FrameworkFileUtilities.Slashes); - return path.AsSpan(lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0).ContainsAny(InvalidFileNameChars); - } -#else - if (path.IndexOfAny(InvalidPathChars) < 0) - { - int lastDirectorySeparator = path.LastIndexOfAny(FrameworkFileUtilities.Slashes); - return path.IndexOfAny(InvalidFileNameChars, lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0) >= 0; - } -#endif - return true; - } - - /// - /// A variation on File.Delete that will throw ExceptionHandling.NotExpectedException exceptions - /// - internal static void DeleteNoThrow(string path) - { - try - { - File.Delete(FrameworkFileUtilities.FixFilePath(path)); - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - } - } - - /// - /// A variation on Directory.Delete that will throw ExceptionHandling.NotExpectedException exceptions - /// - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Int32.TryParse(System.String,System.Int32@)", Justification = "We expect the out value to be 0 if the parse fails and compensate accordingly")] - internal static void DeleteDirectoryNoThrow(string path, bool recursive, int retryCount = 0, int retryTimeOut = 0) - { - // Try parse will set the out parameter to 0 if the string passed in is null, or is outside the range of an int. - if (!int.TryParse(Environment.GetEnvironmentVariable("MSBUILDDIRECTORYDELETERETRYCOUNT"), out retryCount)) - { - retryCount = 0; - } - - if (!int.TryParse(Environment.GetEnvironmentVariable("MSBUILDDIRECTORYDELETRETRYTIMEOUT"), out retryTimeOut)) - { - retryTimeOut = 0; - } - - retryCount = retryCount < 1 ? 2 : retryCount; - retryTimeOut = retryTimeOut < 1 ? 500 : retryTimeOut; - - path = FrameworkFileUtilities.FixFilePath(path); - - for (int i = 0; i < retryCount; i++) - { - try - { - if (DefaultFileSystem.DirectoryExists(path)) - { - Directory.Delete(path, recursive); - break; - } - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - } - - if (i + 1 < retryCount) // should not wait for the final iteration since we not gonna check anyway - { - Thread.Sleep(retryTimeOut); - } - } - } - - /// - /// Deletes a directory, ensuring that Directory.Delete does not get a path ending in a slash. - /// - /// - /// This is a workaround for https://github.com/dotnet/corefx/issues/3780, which clashed with a common - /// pattern in our tests. - /// - internal static void DeleteWithoutTrailingBackslash(string path, bool recursive = false) - { - // Some tests (such as FileMatcher and Evaluation tests) were failing with an UnauthorizedAccessException or directory not empty. - // This retry logic works around that issue. - const int NUM_TRIES = 3; - for (int i = 0; i < NUM_TRIES; i++) - { - try - { - Directory.Delete(FrameworkFileUtilities.EnsureNoTrailingSlash(path), recursive); - - // If we got here, the directory was successfully deleted - return; - } - catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) - { - if (i == NUM_TRIES - 1) - { - // var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories); - // string fileString = string.Join(Environment.NewLine, files); - // string message = $"Unable to delete directory '{path}'. Contents:" + Environment.NewLine + fileString; - // throw new IOException(message, ex); - throw; - } - } - - Thread.Sleep(10); - } - } - - /// - /// Gets a file info object for the specified file path. If the file path - /// is invalid, or is a directory, or cannot be accessed, or does not exist, - /// it returns null rather than throwing or returning a FileInfo around a non-existent file. - /// This allows it to be called where File.Exists() (which never throws, and returns false - /// for directories) was called - but with the advantage that a FileInfo object is returned - /// that can be queried (e.g., for LastWriteTime) without hitting the disk again. - /// - /// - /// FileInfo around path if it is an existing /file/, else null - internal static FileInfo GetFileInfoNoThrow(string filePath) - { - filePath = AttemptToShortenPath(filePath); - - FileInfo fileInfo; - - try - { - fileInfo = new FileInfo(filePath); - } - catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) - { - // Invalid or inaccessible path: treat as if nonexistent file, just as File.Exists does - return null; - } - - if (fileInfo.Exists) - { - // It's an existing file - return fileInfo; - } - else - { - // Nonexistent, or existing but a directory, just as File.Exists behaves - return null; - } - } - - /// - /// Returns if the directory exists - /// - /// Full path to the directory in the filesystem - /// The file system - /// - internal static bool DirectoryExistsNoThrow(string fullPath, IFileSystem fileSystem = null) - { - fullPath = AttemptToShortenPath(fullPath); - - try - { - fileSystem ??= DefaultFileSystem; - - return Traits.Instance.CacheFileExistence - ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.DirectoryExists(fullPath)) - : fileSystem.DirectoryExists(fullPath); - } - catch - { - return false; - } - } - - /// - /// Returns if the directory exists - /// - /// Full path to the file in the filesystem - /// The file system - /// - internal static bool FileExistsNoThrow(string fullPath, IFileSystem fileSystem = null) - { - fullPath = AttemptToShortenPath(fullPath); - - try - { - fileSystem ??= DefaultFileSystem; - - return Traits.Instance.CacheFileExistence - ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.FileExists(fullPath)) - : fileSystem.FileExists(fullPath); - } - catch - { - return false; - } - } - - /// - /// If there is a directory or file at the specified path, returns true. - /// Otherwise, returns false. - /// Does not throw IO exceptions, to match Directory.Exists and File.Exists. - /// Unlike calling each of those in turn it only accesses the disk once, which is faster. - /// - internal static bool FileOrDirectoryExistsNoThrow(string fullPath, IFileSystem fileSystem = null) - { - fullPath = AttemptToShortenPath(fullPath); - - try - { - fileSystem ??= DefaultFileSystem; - - return Traits.Instance.CacheFileExistence - ? FileExistenceCache.GetOrAdd(fullPath, fullPath => fileSystem.FileOrDirectoryExists(fullPath)) - : fileSystem.FileOrDirectoryExists(fullPath); - } - catch - { - return false; - } - } - - /// - /// This method returns true if the specified filename is a solution file (.sln) or - /// solution filter file (.slnf); otherwise, it returns false. - /// - /// - /// Solution filters are included because they are a thin veneer over solutions, just - /// with a more limited set of projects to build, and should be treated the same way. - /// - internal static bool IsSolutionFilename(string filename) - { - return HasExtension(filename, ".sln") || - HasExtension(filename, ".slnf") || - HasExtension(filename, ".slnx"); - } - - internal static bool IsSolutionFilterFilename(string filename) - { - return HasExtension(filename, ".slnf"); - } - - internal static bool IsSolutionXFilename(string filename) - { - return HasExtension(filename, ".slnx"); - } - - /// - /// Returns true if the specified filename is a VC++ project file, otherwise returns false - /// - internal static bool IsVCProjFilename(string filename) - { - return HasExtension(filename, ".vcproj"); - } - - internal static bool IsDspFilename(string filename) - { - return HasExtension(filename, ".dsp"); - } - - /// - /// Returns true if the specified filename is a metaproject file (.metaproj), otherwise false. - /// - internal static bool IsMetaprojectFilename(string filename) - { - return HasExtension(filename, ".metaproj"); - } - - internal static bool IsBinaryLogFilename(string filename) - { - return HasExtension(filename, ".binlog"); - } - - private static bool HasExtension(string filename, string extension) - { - if (String.IsNullOrEmpty(filename)) - { - return false; - } - - return filename.EndsWith(extension, PathComparison); - } - - /// - /// Given the absolute location of a file, and a disc location, returns relative file path to that disk location. - /// Throws UriFormatException. - /// - /// - /// The base path we want to be relative to. Must be absolute. - /// Should not include a filename as the last segment will be interpreted as a directory. - /// - /// - /// The path we need to make relative to basePath. The path can be either absolute path or a relative path in which case it is relative to the base path. - /// If the path cannot be made relative to the base path (for example, it is on another drive), it is returned verbatim. - /// If the basePath is an empty string, returns the path. - /// - /// relative path (can be the full path) - internal static string MakeRelative(string basePath, string path) - { - ErrorUtilities.VerifyThrowArgumentNull(basePath); - ErrorUtilities.VerifyThrowArgumentLength(path); - - string fullBase = Path.GetFullPath(basePath); - string fullPath = Path.GetFullPath(path); - - string[] splitBase = fullBase.Split(MSBuildConstants.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries); - string[] splitPath = fullPath.Split(MSBuildConstants.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries); - - ErrorUtilities.VerifyThrow(splitPath.Length > 0, "Cannot call MakeRelative on a path of only slashes."); - - // On a mac, the path could start with any number of slashes and still be valid. We have to check them all. - int indexOfFirstNonSlashChar = 0; - while (path[indexOfFirstNonSlashChar] == Path.DirectorySeparatorChar) - { - indexOfFirstNonSlashChar++; - } - if (path.IndexOf(splitPath[0]) != indexOfFirstNonSlashChar) - { - // path was already relative so just return it - return FrameworkFileUtilities.FixFilePath(path); - } - - int index = 0; - while (index < splitBase.Length && index < splitPath.Length && splitBase[index].Equals(splitPath[index], PathComparison)) - { - index++; - } - - if (index == splitBase.Length && index == splitPath.Length) - { - return "."; - } - - // If the paths have no component in common, the only valid relative path is the full path. - if (index == 0) - { - return fullPath; - } - - StringBuilder sb = StringBuilderCache.Acquire(); - - for (int i = index; i < splitBase.Length; i++) - { - sb.Append("..").Append(Path.DirectorySeparatorChar); - } - for (int i = index; i < splitPath.Length; i++) - { - sb.Append(splitPath[i]).Append(Path.DirectorySeparatorChar); - } - - if (fullPath[fullPath.Length - 1] != Path.DirectorySeparatorChar) - { - sb.Length--; - } - - return StringBuilderCache.GetStringAndRelease(sb); - } - - /// - /// Normalizes the path if and only if it is longer than max path, - /// or would be if rooted by the current directory. - /// This may make it shorter by removing ".."'s. - /// - internal static string AttemptToShortenPath(string path) - { - if (IsPathTooLong(path) || IsPathTooLongIfRooted(path)) - { - // Attempt to make it shorter -- perhaps there are some \..\ elements - path = GetFullPathNoThrow(path); - } - return FrameworkFileUtilities.FixFilePath(path); - } - - public static bool IsPathTooLong(string path) - { - // >= not > because MAX_PATH assumes a trailing null - return path.Length >= NativeMethodsShared.MaxPath; - } - - private static bool IsPathTooLongIfRooted(string path) - { - bool hasMaxPath = NativeMethodsShared.HasMaxPath; - int maxPath = NativeMethodsShared.MaxPath; - // >= not > because MAX_PATH assumes a trailing null - return hasMaxPath && !IsRootedNoThrow(path) && NativeMethodsShared.GetCurrentDirectory().Length + path.Length + 1 /* slash */ >= maxPath; - } - - /// - /// A variation of Path.IsRooted that not throw any IO exception. - /// - private static bool IsRootedNoThrow(string path) - { - try - { - return Path.IsPathRooted(FrameworkFileUtilities.FixFilePath(path)); - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - return false; - } - } - - /// - /// Get the folder N levels above the given. Will stop and return current path when rooted. - /// - /// Path to get the folder above. - /// Number of levels up to walk. - /// Full path to the folder N levels above the path. - internal static string GetFolderAbove(string path, int count = 1) - { - if (count < 1) - { - return path; - } - - var parent = Directory.GetParent(path); - - while (count > 1 && parent?.Parent != null) - { - parent = parent.Parent; - count--; - } - - return parent?.FullName ?? path; - } - - /// - /// Combine multiple paths. Should only be used when compiling against .NET 2.0. - /// - /// Only use in .NET 2.0. Otherwise, use System.IO.Path.Combine(...) - /// - /// - /// Root path. - /// Paths to concatenate. - /// Combined path. - internal static string CombinePaths(string root, params string[] paths) - { - ErrorUtilities.VerifyThrowArgumentNull(root); - ErrorUtilities.VerifyThrowArgumentNull(paths); - - return paths.Aggregate(root, Path.Combine); - } - - internal static string TrimTrailingSlashes(this string s) - { - return s.TrimEnd(FrameworkFileUtilities.Slashes); - } - - /// - /// Replace all backward slashes to forward slashes - /// - internal static string ToSlash(this string s) - { - return s.Replace('\\', '/'); - } - - internal static string ToBackslash(this string s) - { - return s.Replace('/', '\\'); - } - - /// - /// Ensure all slashes are the current platform's slash - /// - /// - /// - internal static string ToPlatformSlash(this string s) - { - var separator = Path.DirectorySeparatorChar; - - return s.Replace(separator == '/' ? '\\' : '/', separator); - } - - internal static string WithTrailingSlash(this string s) - { - return FrameworkFileUtilities.EnsureTrailingSlash(s); - } - - internal static string NormalizeForPathComparison(this string s) => s.ToPlatformSlash().TrimTrailingSlashes(); - - // TODO: assumption on file system case sensitivity: https://github.com/dotnet/msbuild/issues/781 - internal static bool PathsEqual(string path1, string path2) - { - if (path1 == null && path2 == null) - { - return true; - } - if (path1 == null || path2 == null) - { - return false; - } - - var endA = path1.Length - 1; - var endB = path2.Length - 1; - - // Trim trailing slashes - for (var i = endA; i >= 0; i--) - { - var c = path1[i]; - if (c == '/' || c == '\\') - { - endA--; - } - else - { - break; - } - } - - for (var i = endB; i >= 0; i--) - { - var c = path2[i]; - if (c == '/' || c == '\\') - { - endB--; - } - else - { - break; - } - } - - if (endA != endB) - { - // Lengths not the same - return false; - } - - for (var i = 0; i <= endA; i++) - { - var charA = (uint)path1[i]; - var charB = (uint)path2[i]; - - if ((charA | charB) > 0x7F) - { - // Non-ascii chars move to non fast path - return PathsEqualNonAscii(path1, path2, i, endA - i + 1); - } - - // uppercase both chars - notice that we need just one compare per char - if ((uint)(charA - 'a') <= (uint)('z' - 'a')) - { - charA -= 0x20; - } - - if ((uint)(charB - 'a') <= (uint)('z' - 'a')) - { - charB -= 0x20; - } - - // Set path delimiters the same - if (charA == '\\') - { - charA = '/'; - } - if (charB == '\\') - { - charB = '/'; - } - - if (charA != charB) - { - return false; - } - } - - return true; - } - - internal static StreamWriter OpenWrite(string path, bool append, Encoding encoding = null) - { - const int DefaultFileStreamBufferSize = 4096; - FileMode mode = append ? FileMode.Append : FileMode.Create; - Stream fileStream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); - if (encoding == null) - { - return new StreamWriter(fileStream); - } - else - { - return new StreamWriter(fileStream, encoding); - } - } - - internal static StreamReader OpenRead(string path, Encoding encoding = null, bool detectEncodingFromByteOrderMarks = true) - { - const int DefaultFileStreamBufferSize = 4096; - Stream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); - if (encoding == null) - { - return new StreamReader(fileStream); - } - else - { - return new StreamReader(fileStream, encoding, detectEncodingFromByteOrderMarks); - } - } - - /// - /// Locate a file in either the directory specified or a location in the - /// directory structure above that directory. - /// - internal static string GetDirectoryNameOfFileAbove(string startingDirectory, string fileName, IFileSystem fileSystem = null) - { - fileSystem ??= DefaultFileSystem; - - // Canonicalize our starting location - string lookInDirectory = GetFullPath(startingDirectory); - - do - { - // Construct the path that we will use to test against - string possibleFileDirectory = Path.Combine(lookInDirectory, fileName); - - // If we successfully locate the file in the directory that we're - // looking in, simply return that location. Otherwise we'll - // keep moving up the tree. - if (fileSystem.FileExists(possibleFileDirectory)) - { - // We've found the file, return the directory we found it in - return lookInDirectory; - } - else - { - // GetDirectoryName will return null when we reach the root - // terminating our search - lookInDirectory = Path.GetDirectoryName(lookInDirectory); - } - } - while (lookInDirectory != null); - - // When we didn't find the location, then return an empty string - return string.Empty; - } - - /// - /// Searches for a file based on the specified starting directory. - /// - /// The file to search for. - /// An optional directory to start the search in. The default location is the directory - /// of the file containing the property function. - /// The filesystem - /// The full path of the file if it is found, otherwise an empty string. - internal static string GetPathOfFileAbove(string file, string startingDirectory, IFileSystem fileSystem = null) - { - // This method does not accept a path, only a file name - if (file.Any(i => i.Equals(Path.DirectorySeparatorChar) || i.Equals(Path.AltDirectorySeparatorChar))) - { - ErrorUtilities.ThrowArgument("InvalidGetPathOfFileAboveParameter", file); - } - - // Search for a directory that contains that file - string directoryName = GetDirectoryNameOfFileAbove(startingDirectory, file, fileSystem); - - return String.IsNullOrEmpty(directoryName) ? String.Empty : NormalizePath(directoryName, file); - } - - internal static void EnsureDirectoryExists(string directoryPath) - { - if (!string.IsNullOrEmpty(directoryPath) && !DefaultFileSystem.DirectoryExists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - } - - // Method is simple set of function calls and may inline; - // we don't want it inlining into the tight loop that calls it as an exit case, - // so mark as non-inlining - [MethodImpl(MethodImplOptions.NoInlining)] - 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; - } - - var slash1 = strA.ToSlash(); - var slash2 = strB.ToSlash(); - - if (string.Compare(slash1, i, slash2, i, length, StringComparison.OrdinalIgnoreCase) == 0) - { - return true; - } - - return false; - } - -#if !CLR2COMPATIBILITY - /// - /// Clears the file existence cache. - /// - internal static void ClearFileExistenceCache() - { - FileExistenceCache.Clear(); - } -#endif - - internal static void ReadFromStream(this Stream stream, byte[] content, int startIndex, int length) - { - stream.ReadExactly(content, startIndex, length); - } - } -} - -#if !NET -namespace System.IO -{ - internal static class StreamExtensions - { - internal static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - if ((uint)count > buffer.Length - offset) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - while (count > 0) - { - int read = stream.Read(buffer, offset, count); - if (read <= 0) - { - throw new EndOfStreamException(); - } - offset +=read; - count -= read; - } - } - } -} -#endif diff --git a/src/Shared/FrameworkLocationHelper.cs b/src/Shared/FrameworkLocationHelper.cs index 6762aad8d66..9945522ae59 100644 --- a/src/Shared/FrameworkLocationHelper.cs +++ b/src/Shared/FrameworkLocationHelper.cs @@ -1574,7 +1574,7 @@ public virtual string GetPathToDotNetFrameworkReferenceAssemblies() string referencePath = GenerateReferenceAssemblyPath(FrameworkLocationHelper.programFilesReferenceAssemblyLocation, this.FrameworkName); if (FileSystems.Default.DirectoryExists(referencePath)) { - this._pathToDotNetFrameworkReferenceAssemblies = FrameworkFileUtilities.EnsureTrailingSlash(referencePath); + this._pathToDotNetFrameworkReferenceAssemblies = FileUtilities.EnsureTrailingSlash(referencePath); } } diff --git a/src/Shared/IMSBuildElementLocation.cs b/src/Shared/IMSBuildElementLocation.cs deleted file mode 100644 index bd329f0580b..00000000000 --- a/src/Shared/IMSBuildElementLocation.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.Build.Shared -{ - /// - /// Represents the location information for error reporting purposes. This is normally used to - /// associate a run-time error with the original XML. - /// This is not used for arbitrary errors from tasks, which store location in a BuildXXXXEventArgs. - /// All implementations should be IMMUTABLE. - /// Any editing of the project XML through the MSBuild API's will invalidate locations in that XML until the XML is reloaded. - /// - public interface IMSBuildElementLocation - { - /// - /// The file from which this particular element originated. It may - /// differ from the ProjectFile if, for instance, it was part of - /// an import or originated in a targets file. - /// Should always have a value. - /// If not known, returns empty string. - /// - string File - { - get; - } - - /// - /// The line number where this element exists in its file. - /// The first line is numbered 1. - /// Zero indicates "unknown location". - /// - int Line - { - get; - } - - /// - /// The column number where this element exists in its file. - /// The first column is numbered 1. - /// Zero indicates "unknown location". - /// - int Column - { - get; - } - - /// - /// The location in a form suitable for replacement - /// into a message. - /// - string LocationString - { - get; - } - } -} diff --git a/src/Shared/InterningBinaryReader.cs b/src/Shared/InterningBinaryReader.cs index 307cc68bdc9..3ce9cf42c2b 100644 --- a/src/Shared/InterningBinaryReader.cs +++ b/src/Shared/InterningBinaryReader.cs @@ -2,18 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; -using System.IO; +using System.Buffers; using System.Diagnostics; +using System.IO; +using System.Text; using System.Threading; - -#if !CLR2COMPATIBILITY -using System.Buffers; -#endif - -using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; - using Microsoft.NET.StringTools; +using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; #nullable disable @@ -152,12 +147,8 @@ public override String ReadString() charsRead = _decoder.GetChars(rawBuffer, rawPosition, n, charBuffer, 0); return Strings.WeakIntern(charBuffer.AsSpan(0, charsRead)); } -#if !CLR2COMPATIBILITY + resultBuffer ??= ArrayPool.Shared.Rent(stringLength); // Actual string length in chars may be smaller. -#else - // Since NET35 is only used in rare TaskHost processes, we decided to leave it as-is. - resultBuffer ??= new char[stringLength]; // Actual string length in chars may be smaller. -#endif charsRead += _decoder.GetChars(rawBuffer, rawPosition, n, resultBuffer, charsRead); currPos += n; @@ -173,7 +164,6 @@ public override String ReadString() Debug.Assert(false, e.ToString()); throw; } -#if !CLR2COMPATIBILITY finally { // resultBuffer shall always be either Rented or null @@ -182,7 +172,6 @@ public override String ReadString() ArrayPool.Shared.Return(resultBuffer); } } -#endif } /// diff --git a/src/Shared/LoadedType.cs b/src/Shared/LoadedType.cs index 18f59bd164f..a108e072da7 100644 --- a/src/Shared/LoadedType.cs +++ b/src/Shared/LoadedType.cs @@ -56,7 +56,6 @@ internal LoadedType( LoadedAssembly = loadedAssembly; -#if !NET35 // This block is reflection only loaded type implementation. Net35 does not support it, and fall backs to former implementation in #else // Property `Properties` set in this block aren't used by TaskHosts. Properties below are only used on the NodeProvider side to get information about the // properties and reflect over them without needing them to be fully loaded, so it also isn't need for TaskHosts. @@ -161,12 +160,6 @@ internal LoadedType( } } } -#else - // For v3.5 fallback to old full type approach, as oppose to reflection only - HasLoadInSeparateAppDomainAttribute = Type.GetTypeInfo().IsDefined(typeof(LoadInSeparateAppDomainAttribute), true /* inherited */); - HasSTAThreadAttribute = Type.GetTypeInfo().IsDefined(typeof(RunInSTAAttribute), true /* inherited */); - IsMarshalByRef = Type.IsMarshalByRef; -#endif } #endregion @@ -236,9 +229,7 @@ private bool CheckForHardcodedSTARequirement() /// internal Assembly LoadedAssembly { get; private set; } -#if !NET35 internal ReflectableTaskPropertyInfo[] Properties { get; private set; } -#endif /// /// Assembly-qualified names for properties. Only has a value if this type was loaded using MetadataLoadContext. diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 57c7ffe3c46..17589c57473 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -5,14 +5,10 @@ using System.Collections.Generic; using System.IO; using System.Reflection; - using Microsoft.Build.BackEnd; +using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Framework; - -#if !TASKHOST using Microsoft.Build.Framework.Telemetry; -using Microsoft.Build.Experimental.BuildCheck; -#endif #nullable disable @@ -263,14 +259,7 @@ internal class LogMessagePacketBase : INodePacket /// private static readonly int s_defaultPacketVersion = (Environment.Version.Major * 10) + Environment.Version.Minor; -#if TASKHOST - /// - /// Dictionary of methods used to read BuildEventArgs. - /// - private static readonly Dictionary s_readMethodCache = new Dictionary(); - -#endif - /// + /// /// Dictionary of methods used to write BuildEventArgs. /// private static readonly Dictionary s_writeMethodCache = new Dictionary(); @@ -444,26 +433,7 @@ internal void ReadFromStream(ITranslator translator) if (eventCanSerializeItself) { - -#if TASKHOST - MethodInfo methodInfo = null; - lock (s_readMethodCache) - { - if (!s_readMethodCache.TryGetValue(_eventType, out methodInfo)) - { - Type eventDerivedType = _buildEvent.GetType(); - methodInfo = eventDerivedType.GetMethod("CreateFromStream", BindingFlags.NonPublic | BindingFlags.Instance); - s_readMethodCache.Add(_eventType, methodInfo); - } - } - - ArgsReaderDelegate readerMethod = (ArgsReaderDelegate)CreateDelegateRobust(typeof(ArgsReaderDelegate), _buildEvent, methodInfo); - - readerMethod(translator.Reader, packetVersion); - -#else _buildEvent.CreateFromStream(translator.Reader, packetVersion); -#endif TranslateAdditionalProperties(translator, _eventType, _buildEvent); } @@ -509,11 +479,7 @@ private static Delegate CreateDelegateRobust(Type type, Object firstArgument, Me { try { -#if CLR2COMPATIBILITY - delegateMethod = Delegate.CreateDelegate(type, firstArgument, methodInfo); -#else delegateMethod = methodInfo.CreateDelegate(type, firstArgument); -#endif } catch (FileLoadException) when (i < 5) { @@ -548,8 +514,6 @@ private BuildEventArgs GetBuildEventArgFromId() LoggingEventType.TaskFinishedEvent => new TaskFinishedEventArgs(null, null, null, null, null, false), LoggingEventType.TaskCommandLineEvent => new TaskCommandLineEventArgs(null, null, MessageImportance.Normal), LoggingEventType.ResponseFileUsedEvent => new ResponseFileUsedEventArgs(null), - -#if !TASKHOST // MSBuildTaskHost is targeting Microsoft.Build.Framework.dll 3.5 LoggingEventType.AssemblyLoadEvent => new AssemblyLoadBuildEventArgs(), LoggingEventType.TaskParameterEvent => new TaskParameterEventArgs(0, null, null, true, default), LoggingEventType.ProjectEvaluationStartedEvent => new ProjectEvaluationStartedEventArgs(), @@ -579,7 +543,7 @@ private BuildEventArgs GetBuildEventArgFromId() LoggingEventType.BuildSubmissionStartedEvent => new BuildSubmissionStartedEventArgs(), LoggingEventType.BuildCanceledEvent => new BuildCanceledEventArgs("Build canceled."), LoggingEventType.WorkerNodeTelemetryEvent => new WorkerNodeTelemetryEventArgs(), -#endif + _ => throw new InternalErrorException("Should not get to the default of GetBuildEventArgFromId ID: " + _eventType) }; } @@ -602,12 +566,10 @@ private LoggingEventType GetLoggingEventId(BuildEventArgs eventArg) { return LoggingEventType.TaskCommandLineEvent; } -#if !TASKHOST else if (eventType == typeof(TaskParameterEventArgs)) { return LoggingEventType.TaskParameterEvent; } -#endif else if (eventType == typeof(ProjectFinishedEventArgs)) { return LoggingEventType.ProjectFinishedEvent; @@ -624,8 +586,6 @@ private LoggingEventType GetLoggingEventId(BuildEventArgs eventArg) { return LoggingEventType.ExternalProjectFinishedEvent; } - -#if !TASKHOST else if (eventType == typeof(ProjectEvaluationFinishedEventArgs)) { return LoggingEventType.ProjectEvaluationFinishedEvent; @@ -730,7 +690,6 @@ private LoggingEventType GetLoggingEventId(BuildEventArgs eventArg) { return LoggingEventType.WorkerNodeTelemetryEvent; } -#endif else if (eventType == typeof(TargetStartedEventArgs)) { return LoggingEventType.TargetStartedEvent; @@ -903,10 +862,8 @@ private void WriteResponseFileUsedEventToStream(ResponseFileUsedEventArgs respon translator.Translate(ref filePath); -#if !CLR2COMPATIBILITY DateTime timestamp = responseFileUsedEventArgs.RawTimestamp; translator.Translate(ref timestamp); -#endif } #endregion @@ -1062,11 +1019,9 @@ private ResponseFileUsedEventArgs ReadResponseFileUsedEventFromStream(ITranslato translator.Translate(ref responseFilePath); ResponseFileUsedEventArgs buildEvent = new ResponseFileUsedEventArgs(responseFilePath); -#if !CLR2COMPATIBILITY DateTime timestamp = default; translator.Translate(ref timestamp); buildEvent.RawTimestamp = timestamp; -#endif return buildEvent; } diff --git a/src/Shared/Modifiers.cs b/src/Shared/Modifiers.cs deleted file mode 100644 index d7b9a9fc95f..00000000000 --- a/src/Shared/Modifiers.cs +++ /dev/null @@ -1,474 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -#if !CLR2COMPATIBILITY -using System.Collections.Frozen; -#else -using System.Collections.Generic; -#endif -using System.Diagnostics.CodeAnalysis; -using System.IO; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared.FileSystem; - -#nullable disable - -namespace Microsoft.Build.Shared -{ - /// - /// This class contains utility methods for file IO. - /// - /// - /// Partial class in order to reduce the amount of sharing into different assemblies - /// - internal static partial class FileUtilities - { - /// - /// Encapsulates the definitions of the item-spec modifiers a.k.a. reserved item metadata. - /// - internal static class ItemSpecModifiers - { - internal const string FullPath = "FullPath"; - internal const string RootDir = "RootDir"; - internal const string Filename = "Filename"; - internal const string Extension = "Extension"; - internal const string RelativeDir = "RelativeDir"; - internal const string Directory = "Directory"; - internal const string RecursiveDir = "RecursiveDir"; - internal const string Identity = "Identity"; - internal const string ModifiedTime = "ModifiedTime"; - internal const string CreatedTime = "CreatedTime"; - internal const string AccessedTime = "AccessedTime"; - internal const string DefiningProjectFullPath = "DefiningProjectFullPath"; - internal const string DefiningProjectDirectory = "DefiningProjectDirectory"; - internal const string DefiningProjectName = "DefiningProjectName"; - internal const string DefiningProjectExtension = "DefiningProjectExtension"; - - // These are all the well-known attributes. - internal static readonly string[] All = - { - FullPath, - RootDir, - Filename, - Extension, - RelativeDir, - Directory, - RecursiveDir, // <-- Not derivable. - Identity, - ModifiedTime, - CreatedTime, - AccessedTime, - DefiningProjectFullPath, - DefiningProjectDirectory, - DefiningProjectName, - DefiningProjectExtension - }; - -#if CLR2COMPATIBILITY - private static readonly HashSet s_tableOfItemSpecModifiers = new HashSet(All, StringComparer.OrdinalIgnoreCase); - private static readonly HashSet s_tableOfDefiningProjectModifiers = new HashSet( - [ - DefiningProjectFullPath, - DefiningProjectDirectory, - DefiningProjectName, - DefiningProjectExtension, - ], StringComparer.OrdinalIgnoreCase); -#else - private static readonly FrozenSet s_tableOfItemSpecModifiers = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, All); - private static readonly FrozenSet s_tableOfDefiningProjectModifiers = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, - [ - DefiningProjectFullPath, - DefiningProjectDirectory, - DefiningProjectName, - DefiningProjectExtension, - ]); -#endif - - /// - /// Indicates if the given name is reserved for an item-spec modifier. - /// - internal static bool IsItemSpecModifier(string name) - { - if (name == null) - { - return false; - } - - // Could still be a case-insensitive match. - bool result = s_tableOfItemSpecModifiers.Contains(name); - - return result; - } - - /// - /// Indicates if the given name is reserved for one of the specific subset of itemspec - /// modifiers to do with the defining project of the item. - /// - internal static bool IsDefiningProjectModifier(string name) => s_tableOfDefiningProjectModifiers.Contains(name); - - /// - /// Indicates if the given name is reserved for a derivable item-spec modifier. - /// Derivable means it can be computed given a file name. - /// - /// Name to check. - /// true, if name of a derivable modifier - internal static bool IsDerivableItemSpecModifier(string name) - { - bool isItemSpecModifier = IsItemSpecModifier(name); - - if (isItemSpecModifier) - { - if (name.Length == 12) - { - if (name[0] == 'R' || name[0] == 'r') - { - // The only 12 letter ItemSpecModifier that starts with 'R' is 'RecursiveDir' - return false; - } - } - } - - return isItemSpecModifier; - } - - /// - /// Performs path manipulations on the given item-spec as directed. - /// Does not cache the result. - /// - internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string definingProjectEscaped, string modifier) - { - string dummy = null; - return GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, modifier, ref dummy); - } - - /// - /// Performs path manipulations on the given item-spec as directed. - /// - /// Supported modifiers: - /// %(FullPath) = full path of item - /// %(RootDir) = root directory of item - /// %(Filename) = item filename without extension - /// %(Extension) = item filename extension - /// %(RelativeDir) = item directory as given in item-spec - /// %(Directory) = full path of item directory relative to root - /// %(RecursiveDir) = portion of item path that matched a recursive wildcard - /// %(Identity) = item-spec as given - /// %(ModifiedTime) = last write time of item - /// %(CreatedTime) = creation time of item - /// %(AccessedTime) = last access time of item - /// - /// NOTES: - /// 1) This method always returns an empty string for the %(RecursiveDir) modifier because it does not have enough - /// information to compute it -- only the BuildItem class can compute this modifier. - /// 2) All but the file time modifiers could be cached, but it's not worth the space. Only full path is cached, as the others are just string manipulations. - /// - /// - /// Methods of the Path class "normalize" slashes and periods. For example: - /// 1) successive slashes are combined into 1 slash - /// 2) trailing periods are discarded - /// 3) forward slashes are changed to back-slashes - /// - /// As a result, we cannot rely on any file-spec that has passed through a Path method to remain the same. We will - /// therefore not bother preserving slashes and periods when file-specs are transformed. - /// - /// Never returns null. - /// - /// The root directory for relative item-specs. When called on the Engine thread, this is the project directory. When called as part of building a task, it is null, indicating that the current directory should be used. - /// The item-spec to modify. - /// The path to the project that defined this item (may be null). - /// The modifier to apply to the item-spec. - /// Full path if any was previously computed, to cache. - /// The modified item-spec (can be empty string, but will never be null). - /// Thrown when the item-spec is not a path. - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Pre-existing")] - internal static string GetItemSpecModifier(string currentDirectory, string itemSpec, string definingProjectEscaped, string modifier, ref string fullPath) - { - ErrorUtilities.VerifyThrow(itemSpec != null, "Need item-spec to modify."); - ErrorUtilities.VerifyThrow(modifier != null, "Need modifier to apply to item-spec."); - - string modifiedItemSpec = null; - - try - { - if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.FullPath, StringComparison.OrdinalIgnoreCase)) - { - if (fullPath != null) - { - return fullPath; - } - - if (currentDirectory == null) - { -#if !TASKHOST - currentDirectory = FrameworkFileUtilities.CurrentThreadWorkingDirectory ?? string.Empty; -#else - currentDirectory = string.Empty; -#endif - } - - modifiedItemSpec = GetFullPath(itemSpec, currentDirectory); - fullPath = modifiedItemSpec; - - ThrowForUrl(modifiedItemSpec, itemSpec, currentDirectory); - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.RootDir, StringComparison.OrdinalIgnoreCase)) - { - GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, ItemSpecModifiers.FullPath, ref fullPath); - - modifiedItemSpec = Path.GetPathRoot(fullPath); - - if (!FrameworkFileUtilities.EndsWithSlash(modifiedItemSpec)) - { - ErrorUtilities.VerifyThrow(FileUtilitiesRegex.StartsWithUncPattern(modifiedItemSpec), - "Only UNC shares should be missing trailing slashes."); - - // restore/append trailing slash if Path.GetPathRoot() has either removed it, or failed to add it - // (this happens with UNC shares) - modifiedItemSpec += Path.DirectorySeparatorChar; - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.Filename, StringComparison.OrdinalIgnoreCase)) - { - // if the item-spec is a root directory, it can have no filename - if (IsRootDirectory(itemSpec)) - { - // NOTE: this is to prevent Path.GetFileNameWithoutExtension() from treating server and share elements - // in a UNC file-spec as filenames e.g. \\server, \\server\share - modifiedItemSpec = String.Empty; - } - else - { - // Fix path to avoid problem with Path.GetFileNameWithoutExtension when backslashes in itemSpec on Unix - modifiedItemSpec = Path.GetFileNameWithoutExtension(FrameworkFileUtilities.FixFilePath(itemSpec)); - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.Extension, StringComparison.OrdinalIgnoreCase)) - { - // if the item-spec is a root directory, it can have no extension - if (IsRootDirectory(itemSpec)) - { - // NOTE: this is to prevent Path.GetExtension() from treating server and share elements in a UNC - // file-spec as filenames e.g. \\server.ext, \\server\share.ext - modifiedItemSpec = String.Empty; - } - else - { - modifiedItemSpec = Path.GetExtension(itemSpec); - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.RelativeDir, StringComparison.OrdinalIgnoreCase)) - { - modifiedItemSpec = GetDirectory(itemSpec); - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.Directory, StringComparison.OrdinalIgnoreCase)) - { - GetItemSpecModifier(currentDirectory, itemSpec, definingProjectEscaped, ItemSpecModifiers.FullPath, ref fullPath); - - modifiedItemSpec = GetDirectory(fullPath); - - if (NativeMethodsShared.IsWindows) - { - int length = -1; - if (FileUtilitiesRegex.StartsWithDrivePattern(modifiedItemSpec)) - { - length = 2; - } - else - { - length = FileUtilitiesRegex.StartsWithUncPatternMatchLength(modifiedItemSpec); - } - - if (length != -1) - { - ErrorUtilities.VerifyThrow((modifiedItemSpec.Length > length) && FrameworkFileUtilities.IsSlash(modifiedItemSpec[length]), - "Root directory must have a trailing slash."); - - modifiedItemSpec = modifiedItemSpec.Substring(length + 1); - } - } - else - { - ErrorUtilities.VerifyThrow(!string.IsNullOrEmpty(modifiedItemSpec) && FrameworkFileUtilities.IsSlash(modifiedItemSpec[0]), - "Expected a full non-windows path rooted at '/'."); - - // A full unix path is always rooted at - // `/`, and a root-relative path is the - // rest of the string. - modifiedItemSpec = modifiedItemSpec.Substring(1); - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.RecursiveDir, StringComparison.OrdinalIgnoreCase)) - { - // only the BuildItem class can compute this modifier -- so leave empty - modifiedItemSpec = String.Empty; - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.Identity, StringComparison.OrdinalIgnoreCase)) - { - modifiedItemSpec = itemSpec; - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.ModifiedTime, StringComparison.OrdinalIgnoreCase)) - { - // About to go out to the filesystem. This means data is leaving the engine, so need - // to unescape first. - string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); - - FileInfo info = FileUtilities.GetFileInfoNoThrow(unescapedItemSpec); - - if (info != null) - { - modifiedItemSpec = info.LastWriteTime.ToString(FileTimeFormat, null); - } - else - { - // File does not exist, or path is a directory - modifiedItemSpec = String.Empty; - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.CreatedTime, StringComparison.OrdinalIgnoreCase)) - { - // About to go out to the filesystem. This means data is leaving the engine, so need - // to unescape first. - string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); - - if (FileSystems.Default.FileExists(unescapedItemSpec)) - { - modifiedItemSpec = File.GetCreationTime(unescapedItemSpec).ToString(FileTimeFormat, null); - } - else - { - // File does not exist, or path is a directory - modifiedItemSpec = String.Empty; - } - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.AccessedTime, StringComparison.OrdinalIgnoreCase)) - { - // About to go out to the filesystem. This means data is leaving the engine, so need - // to unescape first. - string unescapedItemSpec = EscapingUtilities.UnescapeAll(itemSpec); - - if (FileSystems.Default.FileExists(unescapedItemSpec)) - { - modifiedItemSpec = File.GetLastAccessTime(unescapedItemSpec).ToString(FileTimeFormat, null); - } - else - { - // File does not exist, or path is a directory - modifiedItemSpec = String.Empty; - } - } - else if (IsDefiningProjectModifier(modifier)) - { - if (String.IsNullOrEmpty(definingProjectEscaped)) - { - // We have nothing to work with, but that's sometimes OK -- so just return String.Empty - modifiedItemSpec = String.Empty; - } - else - { - if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectDirectory, StringComparison.OrdinalIgnoreCase)) - { - // ItemSpecModifiers.Directory does not contain the root directory - modifiedItemSpec = Path.Combine( - GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, ItemSpecModifiers.RootDir), - GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, ItemSpecModifiers.Directory)); - } - else - { - string additionalModifier = null; - - if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectFullPath, StringComparison.OrdinalIgnoreCase)) - { - additionalModifier = ItemSpecModifiers.FullPath; - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectName, StringComparison.OrdinalIgnoreCase)) - { - additionalModifier = ItemSpecModifiers.Filename; - } - else if (string.Equals(modifier, FileUtilities.ItemSpecModifiers.DefiningProjectExtension, StringComparison.OrdinalIgnoreCase)) - { - additionalModifier = ItemSpecModifiers.Extension; - } - else - { - ErrorUtilities.ThrowInternalError("\"{0}\" is not a valid item-spec modifier.", modifier); - } - - modifiedItemSpec = GetItemSpecModifier(currentDirectory, definingProjectEscaped, null, additionalModifier); - } - } - } - else - { - ErrorUtilities.ThrowInternalError("\"{0}\" is not a valid item-spec modifier.", modifier); - } - } - catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) - { - ErrorUtilities.ThrowInvalidOperation("Shared.InvalidFilespecForTransform", modifier, itemSpec, e.Message); - } - - return modifiedItemSpec; - } - - /// - /// Indicates whether the given path is a UNC or drive pattern root directory. - /// Note: This function mimics the behavior of checking if Path.GetDirectoryName(path) == null. - /// - /// - /// - private static bool IsRootDirectory(string path) - { - // Eliminate all non-rooted paths - if (!Path.IsPathRooted(path)) - { - return false; - } - - int uncMatchLength = FileUtilitiesRegex.StartsWithUncPatternMatchLength(path); - - // Determine if the given path is a standard drive/unc pattern root - if (FileUtilitiesRegex.IsDrivePattern(path) || - FileUtilitiesRegex.IsDrivePatternWithSlash(path) || - uncMatchLength == path.Length) - { - return true; - } - - // Eliminate all non-root unc paths. - if (uncMatchLength != -1) - { - return false; - } - - // Eliminate any drive patterns that don't have a slash after the colon or where the 4th character is a non-slash - // A non-slash at [3] is specifically checked here because Path.GetDirectoryName - // considers "C:///" a valid root. - if (FileUtilitiesRegex.StartsWithDrivePattern(path) && - ((path.Length >= 3 && path[2] != '\\' && path[2] != '/') || - (path.Length >= 4 && path[3] != '\\' && path[3] != '/'))) - { - return false; - } - - // There are some edge cases that can get to this point. - // After eliminating valid / invalid roots, fall back on original behavior. - return Path.GetDirectoryName(path) == null; - } - - /// - /// Temporary check for something like http://foo which will end up like c:\foo\bar\http://foo - /// We should either have no colon, or exactly one colon. - /// UNDONE: This is a minimal safe change for Dev10. The correct fix should be to make GetFullPath/NormalizePath throw for this. - /// - private static void ThrowForUrl(string fullPath, string itemSpec, string currentDirectory) - { - if (fullPath.IndexOf(':') != fullPath.LastIndexOf(':')) - { - // Cause a better error to appear - fullPath = Path.GetFullPath(Path.Combine(currentDirectory, itemSpec)); - } - } - } - } -} diff --git a/src/Shared/NamedPipeUtil.cs b/src/Shared/NamedPipeUtil.cs index 0b85b05bacd..b62192b4449 100644 --- a/src/Shared/NamedPipeUtil.cs +++ b/src/Shared/NamedPipeUtil.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Internal; namespace Microsoft.Build.Shared @@ -30,13 +31,7 @@ internal static string GetPlatformSpecificPipeName(string pipeName) // can be quite long, leaving very little room for the actual pipe name. Fortunately, // '/tmp' is mandated by POSIX to always be a valid temp directory, so we can use that // instead. -#if !CLR2COMPATIBILITY return Path.Combine("/tmp", pipeName); -#else - // We should never get here. This would be a net35 task host running on unix. - ErrorUtilities.ThrowInternalError("Task host used on unix in retrieving the pipe name."); - return string.Empty; -#endif } else { diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index ae422e25e31..b7ff1eeffee 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -5,25 +5,22 @@ #if NET using System.Collections.Frozen; #endif -using System.Diagnostics.CodeAnalysis; -#if CLR2COMPATIBILITY -using Microsoft.Build.Shared.Concurrent; -#else using System.Collections.Concurrent; -#endif +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Pipes; using System.Threading; +using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using System.IO.Pipes; -using System.IO; -using System.Collections.Generic; +using Microsoft.Build.Shared.Debugging; #if FEATURE_SECURITY_PERMISSIONS || FEATURE_PIPE_SECURITY using System.Security.AccessControl; #endif #if FEATURE_PIPE_SECURITY && FEATURE_NAMED_PIPE_SECURITY_CONSTRUCTOR using System.Security.Principal; - #endif #if NET451_OR_GREATER || NETCOREAPP using System.Threading.Tasks; @@ -137,7 +134,7 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint nameof(HandshakeComponents.FileVersionPrivate)]; #endif -#endregion + #endregion #region INodeEndpoint Events @@ -318,11 +315,7 @@ private void InternalDisconnect() ErrorUtilities.VerifyThrow(_packetPump.ManagedThreadId != Thread.CurrentThread.ManagedThreadId, "Can't join on the same thread."); _terminatePacketPump.Set(); _packetPump.Join(); -#if CLR2COMPATIBILITY - _terminatePacketPump.Close(); -#else _terminatePacketPump.Dispose(); -#endif _pipeServer.Dispose(); _packetPump = null; ChangeLinkStatus(LinkStatus.Inactive); @@ -421,7 +414,7 @@ private void PacketPumpProc() int index = 0; foreach (var component in handshakeComponents.EnumerateComponents()) { - + if (!_pipeServer.TryReadIntForHandshake( byteToAccept: index == 0 ? (byte?)CommunicationsUtilities.handshakeVersion : null, /* this will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard */ #if NETCOREAPP2_1_OR_GREATER @@ -522,7 +515,7 @@ private void PacketPumpProc() localPipeServer.Disconnect(); } - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); return; } @@ -671,7 +664,7 @@ private void RunReadLoop( { // Lost communications. Abort (but allow node reuse) CommunicationsUtilities.Trace("Exception reading from server. {0}", e); - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Inactive); exitLoop = true; break; @@ -730,7 +723,7 @@ private void RunReadLoop( { // Error while deserializing or handling packet. Abort. CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e); - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; break; @@ -789,7 +782,7 @@ private void RunReadLoop( { // Error while deserializing or handling packet. Abort. CommunicationsUtilities.Trace("Exception while serializing packets: {0}", e); - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; break; @@ -812,8 +805,8 @@ private void RunReadLoop( while (!exitLoop); } -#endregion + #endregion -#endregion + #endregion } } diff --git a/src/Shared/NodePipeBase.cs b/src/Shared/NodePipeBase.cs index 00cfed04af6..d0a8f3c7cbf 100644 --- a/src/Shared/NodePipeBase.cs +++ b/src/Shared/NodePipeBase.cs @@ -2,17 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.IO; using System.IO.Pipes; using System.Threading; -using Microsoft.Build.BackEnd; -using Microsoft.Build.Framework; - -#if !TASKHOST -using System.Buffers.Binary; using System.Threading.Tasks; +using Microsoft.Build.BackEnd; using Microsoft.Build.Eventing; -#endif +using Microsoft.Build.Framework; namespace Microsoft.Build.Internal { @@ -124,12 +121,8 @@ internal INodePacket ReadPacket() throw new IOException($"Incomplete header read. {headerBytesRead} of {HeaderLength} bytes read."); } -#if TASKHOST - int packetLength = BitConverter.ToInt32(_headerData, 1); -#else int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerData, 1, 4)); MSBuildEventSource.Log.PacketReadSize(packetLength); -#endif // Read the packet. Set the buffer length now to avoid additional resizing during the read. _readBuffer.Position = 0; @@ -144,7 +137,6 @@ internal INodePacket ReadPacket() return DeserializePacket(); } -#if !TASKHOST internal async Task WritePacketAsync(INodePacket packet, CancellationToken cancellationToken = default) { int messageLength = WritePacketToBuffer(packet); @@ -194,7 +186,6 @@ internal async Task ReadPacketAsync(CancellationToken cancellationT return DeserializePacket(); } -#endif private int WritePacketToBuffer(INodePacket packet) { @@ -233,7 +224,6 @@ private int Read(byte[] buffer, int bytesToRead) return totalBytesRead; } -#if !TASKHOST private async ValueTask ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) { int totalBytesRead = 0; @@ -256,7 +246,6 @@ private async ValueTask ReadAsync(byte[] buffer, int bytesToRead, Cancellat return totalBytesRead; } -#endif private INodePacket DeserializePacket() { diff --git a/src/Shared/NodePipeServer.cs b/src/Shared/NodePipeServer.cs index e5ff7c2ae25..8187ca4db59 100644 --- a/src/Shared/NodePipeServer.cs +++ b/src/Shared/NodePipeServer.cs @@ -8,13 +8,11 @@ using System.Security.AccessControl; using System.Security.Principal; #endif -using Microsoft.Build.BackEnd; -using Microsoft.Build.Shared; - -#if !TASKHOST using System.Threading; using System.Threading.Tasks; -#endif +using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared.Debugging; namespace Microsoft.Build.Internal { @@ -80,11 +78,7 @@ internal NodePipeServer(string pipeName, Handshake handshake, int maxNumberOfSer protected override PipeStream NodeStream => _pipeServer; -#if TASKHOST - internal LinkStatus WaitForConnection() -#else internal async Task WaitForConnectionAsync(CancellationToken cancellationToken) -#endif { DateTime originalWaitStartTime = DateTime.UtcNow; bool gotValidConnection = false; @@ -102,12 +96,6 @@ internal async Task WaitForConnectionAsync(CancellationToken cancell try { // Wait for a connection -#if TASKHOST - IAsyncResult resultForConnection = _pipeServer.BeginWaitForConnection(null, null); - CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); - bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false); - _pipeServer.EndWaitForConnection(resultForConnection); -#else using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(waitTimeRemaining); bool connected = false; @@ -121,7 +109,7 @@ internal async Task WaitForConnectionAsync(CancellationToken cancell { connected = false; } -#endif + if (!connected) { CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us. Exiting comm thread."); @@ -168,7 +156,7 @@ internal async Task WaitForConnectionAsync(CancellationToken cancell _pipeServer.Disconnect(); } - ExceptionHandling.DumpExceptionToFile(e); + DebugUtils.DumpExceptionToFile(e); return LinkStatus.Failed; } } diff --git a/src/Shared/ReadOnlyEmptyDictionary.cs b/src/Shared/ReadOnlyEmptyDictionary.cs index 46b1b2738e9..a769a5156e8 100644 --- a/src/Shared/ReadOnlyEmptyDictionary.cs +++ b/src/Shared/ReadOnlyEmptyDictionary.cs @@ -70,22 +70,12 @@ public bool IsReadOnly /// /// Gets empty collection /// - public ICollection Keys => -#if CLR2COMPATIBILITY - new K[0]; -#else - Array.Empty(); -#endif + public ICollection Keys => Array.Empty(); /// /// Gets empty collection /// - public ICollection Values => -#if CLR2COMPATIBILITY - new V[0]; -#else - Array.Empty(); -#endif + public ICollection Values => Array.Empty(); /// /// Is it fixed size @@ -308,22 +298,3 @@ public void CopyTo(System.Array array, int index) } } } - -#if NET35 -namespace System.Collections.Generic -{ - public interface IReadOnlyCollection : IEnumerable - { - int Count { get; } - } - - public interface IReadOnlyDictionary : IReadOnlyCollection> - { - TValue this[TKey key] { get; } - IEnumerable Keys { get; } - IEnumerable Values { get; } - bool ContainsKey(TKey key); - bool TryGetValue(TKey key, out TValue value); - } -} -#endif diff --git a/src/Shared/RegisteredTaskObjectCacheBase.cs b/src/Shared/RegisteredTaskObjectCacheBase.cs index 10391f5d336..fa91fb2a6ae 100644 --- a/src/Shared/RegisteredTaskObjectCacheBase.cs +++ b/src/Shared/RegisteredTaskObjectCacheBase.cs @@ -8,7 +8,6 @@ #nullable disable #if BUILD_ENGINE -using Microsoft.Build.Shared; namespace Microsoft.Build.BackEnd.Components.Caching #else namespace Microsoft.Build.Shared diff --git a/src/Shared/Resources/Strings.shared.resx b/src/Shared/Resources/Strings.shared.resx index 6e29c8e68af..1ee972f2dab 100644 --- a/src/Shared/Resources/Strings.shared.resx +++ b/src/Shared/Resources/Strings.shared.resx @@ -334,9 +334,6 @@ MSB5024: Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio. {StrBegin="MSB5024: "} - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - An exception occurred while expanding a fileSpec with globs: fileSpec: "{0}", assuming it is a file name. Exception: {1} diff --git a/src/Shared/Resources/xlf/Strings.shared.cs.xlf b/src/Shared/Resources/xlf/Strings.shared.cs.xlf index efc33efebe0..7c27da24ffb 100644 --- a/src/Shared/Resources/xlf/Strings.shared.cs.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.cs.xlf @@ -85,11 +85,6 @@ Nástroj MSBuild očekává platný objekt {0}. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Cesta: {0} překračuje maximální limit pro cestu k OS. Plně kvalifikovaný název souboru musí být kratší než {1} znaků. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: Soubor filtru řešení v {0} obsahuje projekt {1}, který není v souboru řešení v {2}. diff --git a/src/Shared/Resources/xlf/Strings.shared.de.xlf b/src/Shared/Resources/xlf/Strings.shared.de.xlf index a63a5c4e29d..96e44a116e6 100644 --- a/src/Shared/Resources/xlf/Strings.shared.de.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.de.xlf @@ -85,11 +85,6 @@ MSBuild erwartet ein gültiges {0}-Objekt. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Der Pfad "{0}" überschreitet das maximale Pfadlimit des Betriebssystems. Der vollqualifizierte Dateiname muss weniger als {1} Zeichen umfassen. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: Die Projektmappenfilter-Datei unter "{0}" enthält das Projekt "{1}", das in der Projektmappendatei unter "{2}" nicht enthalten ist. diff --git a/src/Shared/Resources/xlf/Strings.shared.es.xlf b/src/Shared/Resources/xlf/Strings.shared.es.xlf index b7936f94976..28992cf81d7 100644 --- a/src/Shared/Resources/xlf/Strings.shared.es.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.es.xlf @@ -85,11 +85,6 @@ MSBuild espera un objeto "{0}" válido. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - La ruta de acceso {0} supera el límite máximo para la ruta de acceso del sistema operativo. El nombre de archivo completo debe ser inferior a {1} caracteres. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: El archivo de filtro de soluciones en "{0}" incluye el proyecto "{1}", que no está en el archivo de solución en "{2}". diff --git a/src/Shared/Resources/xlf/Strings.shared.fr.xlf b/src/Shared/Resources/xlf/Strings.shared.fr.xlf index abbc14e5158..c33ef71d145 100644 --- a/src/Shared/Resources/xlf/Strings.shared.fr.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.fr.xlf @@ -85,11 +85,6 @@ MSBuild attend un objet "{0}" valide. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Le chemin {0} dépasse la limite maximale de chemin du système d'exploitation. Le nom du fichier qualifié complet doit contenir moins de {1} caractères. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: le fichier de filtre de solution sur "{0}" inclut le projet "{1}", qui ne figure pas dans le fichier solution sur "{2}". diff --git a/src/Shared/Resources/xlf/Strings.shared.it.xlf b/src/Shared/Resources/xlf/Strings.shared.it.xlf index 082c3faab51..13d9fc42900 100644 --- a/src/Shared/Resources/xlf/Strings.shared.it.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.it.xlf @@ -85,11 +85,6 @@ MSBuild prevede un oggetto "{0}" valido. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Il percorso {0} supera il limite massimo dei percorsi del sistema operativo. Il nome completo del file deve essere composto da meno di {1}. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: il file di filtro della soluzione in "{0}" include il progetto "{1}" che non è presente nel file di soluzione in "{2}". diff --git a/src/Shared/Resources/xlf/Strings.shared.ja.xlf b/src/Shared/Resources/xlf/Strings.shared.ja.xlf index 8e33727f2fb..bf5724129aa 100644 --- a/src/Shared/Resources/xlf/Strings.shared.ja.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.ja.xlf @@ -85,11 +85,6 @@ MSBuild は有効な "{0}" オブジェクトを必要としています。 - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - パス: {0} は OS のパスの上限を越えています。完全修飾のファイル名は {1} 文字以下にする必要があります。 - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: "{0}" のソリューション フィルター ファイルには、"{2}" のソリューション ファイルにないプロジェクト "{1}" が含まれています。 diff --git a/src/Shared/Resources/xlf/Strings.shared.ko.xlf b/src/Shared/Resources/xlf/Strings.shared.ko.xlf index 102c6ecfb97..f36fecb0d5d 100644 --- a/src/Shared/Resources/xlf/Strings.shared.ko.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.ko.xlf @@ -85,11 +85,6 @@ MSBuild에 올바른 "{0}" 개체가 필요합니다. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - 경로: {0}은(는) OS 최대 경로 제한을 초과합니다. 정규화된 파일 이름은 {1}자 이하여야 합니다. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: "{0}"의 솔루션 필터 파일에 "{2}"의 솔루션 파일에 없는 "{1}" 프로젝트가 포함되어 있습니다. diff --git a/src/Shared/Resources/xlf/Strings.shared.pl.xlf b/src/Shared/Resources/xlf/Strings.shared.pl.xlf index f5c531296d1..e0cf71f3188 100644 --- a/src/Shared/Resources/xlf/Strings.shared.pl.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.pl.xlf @@ -85,11 +85,6 @@ Program MSBuild oczekuje prawidłowego obiektu „{0}”. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Ścieżka: {0} przekracza limit maksymalnej długości ścieżki w systemie operacyjnym. W pełni kwalifikowana nazwa pliku musi się składać z mniej niż {1} znaków. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: Plik filtru rozwiązania w lokalizacji „{0}” obejmuje projekt „{1}”, który nie znajduje się w pliku rozwiązania w lokalizacji „{2}”. diff --git a/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf b/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf index cf78ee34200..01ca48b9f29 100644 --- a/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf @@ -85,11 +85,6 @@ O MSBuild está esperando um objeto "{0}" válido. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Caminho: {0} excede o limite máximo do caminho do SO. O nome do arquivo totalmente qualificado deve ter menos de {1} caracteres. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: o arquivo de filtro da solução em "{0}" inclui o projeto "{1}" que não está no arquivo da solução em "{2}". diff --git a/src/Shared/Resources/xlf/Strings.shared.ru.xlf b/src/Shared/Resources/xlf/Strings.shared.ru.xlf index a40cb28ad14..d0f2c1e081f 100644 --- a/src/Shared/Resources/xlf/Strings.shared.ru.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.ru.xlf @@ -85,11 +85,6 @@ Для MSBuild требуется допустимый объект "{0}". - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Длина пути {0} превышает максимально допустимую в ОС. Символов в полном имени файла должно быть не больше {1}. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: файл фильтра решения в "{0}" включает проект "{1}", который отсутствует в файле решения в "{2}". diff --git a/src/Shared/Resources/xlf/Strings.shared.tr.xlf b/src/Shared/Resources/xlf/Strings.shared.tr.xlf index e5e76c65345..359306dbdc8 100644 --- a/src/Shared/Resources/xlf/Strings.shared.tr.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.tr.xlf @@ -85,11 +85,6 @@ MSBuild, geçerli bir "{0}" nesnesi bekliyor. - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Yol: {0}, işletim sisteminin en yüksek yol sınırını aşıyor. Tam dosya adı en fazla {1} karakter olmalıdır. - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: "{0}" konumundaki çözüm filtresi dosyası, "{2}" konumundaki çözüm dosyasında bulunmayan "{1}" projesini içeriyor. diff --git a/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf b/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf index f0540b9f5e0..6b1f3ae581e 100644 --- a/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf @@ -85,11 +85,6 @@ MSBuild 需要有效的“{0}”对象。 - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - 路径: {0} 超过 OS 最大路径限制。完全限定的文件名必须少于 {1} 个字符。 - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: 位于“{0}”的解决方案筛选器文件包含“{2}”处的解决方案文件中没有的项目“{1}”。 diff --git a/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf b/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf index a7e8187bddb..518e09fdb4c 100644 --- a/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf @@ -85,11 +85,6 @@ MSBuild 需要有效的 "{0}" 物件。 - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - 路徑: {0} 超過 OS 路徑上限。完整檔案名稱必須少於 {1} 個字元。 - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". MSB5028: 位於 "{0}" 的解決方案篩選檔案包含專案 "{1}",該專案不在位於 "{2}" 的解決方案檔案中。 diff --git a/src/Shared/SolutionConfiguration.cs b/src/Shared/SolutionConfiguration.cs index 7242a8ca89f..22b086a8c71 100644 --- a/src/Shared/SolutionConfiguration.cs +++ b/src/Shared/SolutionConfiguration.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml; +using Microsoft.Build.Framework; namespace Microsoft.Build.Shared { diff --git a/src/Shared/TaskEngineAssemblyResolver.cs b/src/Shared/TaskEngineAssemblyResolver.cs index 44b243ab0f0..153ce441e68 100644 --- a/src/Shared/TaskEngineAssemblyResolver.cs +++ b/src/Shared/TaskEngineAssemblyResolver.cs @@ -113,12 +113,7 @@ private Assembly ResolveAssembly(AssemblyLoadContext assemblyLoadContext, Assemb if (taskAssemblyName.Equals(argAssemblyName)) { -#if (!CLR2COMPATIBILITY) return Assembly.UnsafeLoadFrom(_taskAssemblyFile); -#else - return Assembly.LoadFrom(_taskAssemblyFile); -#endif - } #else // !FEATURE_APPDOMAIN AssemblyNameExtension taskAssemblyName = new AssemblyNameExtension(AssemblyLoadContext.GetAssemblyName(_taskAssemblyFile)); diff --git a/src/Shared/TaskHostConfiguration.cs b/src/Shared/TaskHostConfiguration.cs index 168648efb56..0383ca84911 100644 --- a/src/Shared/TaskHostConfiguration.cs +++ b/src/Shared/TaskHostConfiguration.cs @@ -95,9 +95,7 @@ internal class TaskHostConfiguration : INodePacket /// private string _projectFile; -#if !NET35 private HostServices _hostServices; -#endif /// /// The set of parameters to apply to the task prior to execution. @@ -167,9 +165,7 @@ public TaskHostConfiguration( IDictionary buildProcessEnvironment, CultureInfo culture, CultureInfo uiCulture, -#if !NET35 HostServices hostServices, -#endif #if FEATURE_APPDOMAIN AppDomainSetup appDomainSetup, #endif @@ -206,9 +202,7 @@ public TaskHostConfiguration( _culture = culture; _uiCulture = uiCulture; -#if !NET35 _hostServices = hostServices; -#endif #if FEATURE_APPDOMAIN _appDomainSetup = appDomainSetup; #endif @@ -308,7 +302,6 @@ public AppDomainSetup AppDomainSetup } #endif -#if !NET35 /// /// The HostServices to be used by the task host. /// @@ -318,7 +311,6 @@ public HostServices HostServices get { return _hostServices; } } -#endif /// /// Line number where the instance of this task is defined. @@ -525,25 +517,13 @@ public void Translate(ITranslator translator) translator.TranslateDictionary(ref _globalParameters, StringComparer.OrdinalIgnoreCase); translator.Translate(collection: ref _warningsAsErrors, objectTranslator: (ITranslator t, ref string s) => t.Translate(ref s), -#if CLR2COMPATIBILITY - collectionFactory: count => new HashSet(StringComparer.OrdinalIgnoreCase)); -#else collectionFactory: count => new HashSet(count, StringComparer.OrdinalIgnoreCase)); -#endif translator.Translate(collection: ref _warningsNotAsErrors, objectTranslator: (ITranslator t, ref string s) => t.Translate(ref s), -#if CLR2COMPATIBILITY - collectionFactory: count => new HashSet(StringComparer.OrdinalIgnoreCase)); -#else collectionFactory: count => new HashSet(count, StringComparer.OrdinalIgnoreCase)); -#endif translator.Translate(collection: ref _warningsAsMessages, objectTranslator: (ITranslator t, ref string s) => t.Translate(ref s), -#if CLR2COMPATIBILITY - collectionFactory: count => new HashSet(StringComparer.OrdinalIgnoreCase)); -#else collectionFactory: count => new HashSet(count, StringComparer.OrdinalIgnoreCase)); -#endif } /// diff --git a/src/Shared/TaskHostTaskComplete.cs b/src/Shared/TaskHostTaskComplete.cs index 8255ca19865..aaa903827a2 100644 --- a/src/Shared/TaskHostTaskComplete.cs +++ b/src/Shared/TaskHostTaskComplete.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -#if !CLR2COMPATIBILITY && FEATURE_REPORTFILEACCESSES +#if FEATURE_REPORTFILEACCESSES using Microsoft.Build.Experimental.FileAccess; #endif using Microsoft.Build.Shared; diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index 602a36871ed..262d3b0988b 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -4,6 +4,9 @@ using System; using System.Reflection; using Microsoft.Build.Framework; +#if FEATURE_APPDOMAIN +using Microsoft.Build.Shared.Debugging; +#endif namespace Microsoft.Build.Shared { @@ -115,7 +118,7 @@ bool isOutOfProc } // Hook up last minute dumping of any exceptions - taskAppDomain.UnhandledException += ExceptionHandling.UnhandledExceptionHandler; + taskAppDomain.UnhandledException += DebugUtils.UnhandledExceptionHandler; appDomainCreated?.Invoke(taskAppDomain); } } diff --git a/src/Shared/TaskParameter.cs b/src/Shared/TaskParameter.cs index 6c54e0e8f06..4c4849f5605 100644 --- a/src/Shared/TaskParameter.cs +++ b/src/Shared/TaskParameter.cs @@ -541,10 +541,8 @@ private class TaskParameterTaskItem : #endif ITaskItem, ITaskItem2, - ITranslatable -#if !TASKHOST - , IMetadataContainer -#endif + ITranslatable, + IMetadataContainer { /// /// The item spec @@ -574,7 +572,7 @@ internal TaskParameterTaskItem(ITaskItem copyFrom) if (copyFrom is ITaskItem2 copyFromAsITaskItem2) { _escapedItemSpec = copyFromAsITaskItem2.EvaluatedIncludeEscaped; - _escapedDefiningProject = copyFromAsITaskItem2.GetMetadataValueEscaped(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath); + _escapedDefiningProject = copyFromAsITaskItem2.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath); IDictionary nonGenericEscapedMetadata = copyFromAsITaskItem2.CloneCustomMetadataEscaped(); _customEscapedMetadata = nonGenericEscapedMetadata as Dictionary; @@ -595,7 +593,7 @@ internal TaskParameterTaskItem(ITaskItem copyFrom) // TaskParameterTaskItem's constructor expects escaped values, so escaping them all // is the closest approximation to correct we can get. _escapedItemSpec = EscapingUtilities.Escape(copyFrom.ItemSpec); - _escapedDefiningProject = EscapingUtilities.EscapeWithCaching(copyFrom.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath)); + _escapedDefiningProject = EscapingUtilities.EscapeWithCaching(copyFrom.GetMetadata(ItemSpecModifiers.DefiningProjectFullPath)); IDictionary customMetadata = copyFrom.CloneCustomMetadata(); _customEscapedMetadata = new Dictionary(MSBuildNameIgnoreCaseComparer.Default); @@ -646,7 +644,7 @@ public ICollection MetadataNames get { List metadataNames = (_customEscapedMetadata == null) ? new List() : new List(_customEscapedMetadata.Keys); - metadataNames.AddRange(FileUtilities.ItemSpecModifiers.All); + metadataNames.AddRange(ItemSpecModifiers.All); return metadataNames; } @@ -662,7 +660,7 @@ public int MetadataCount get { int count = (_customEscapedMetadata == null) ? 0 : _customEscapedMetadata.Count; - return count + FileUtilities.ItemSpecModifiers.All.Length; + return count + ItemSpecModifiers.All.Length; } } @@ -708,7 +706,7 @@ public void SetMetadata(string metadataName, string metadataValue) // Non-derivable metadata can only be set at construction time. // That's why this is IsItemSpecModifier and not IsDerivableItemSpecModifier. - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); _customEscapedMetadata ??= new Dictionary(MSBuildNameIgnoreCaseComparer.Default); @@ -722,7 +720,7 @@ public void SetMetadata(string metadataName, string metadataValue) public void RemoveMetadata(string metadataName) { ErrorUtilities.VerifyThrowArgumentNull(metadataName); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); if (_customEscapedMetadata == null) { @@ -750,7 +748,6 @@ public void CopyMetadataTo(ITaskItem destinationItem) // between items, and need to know the source item where the metadata came from string originalItemSpec = destinationItem.GetMetadata("OriginalItemSpec"); -#if !TASKHOST if (_customEscapedMetadata != null && destinationItem is IMetadataContainer destinationItemAsMetadataContainer) { // The destination implements IMetadataContainer so we can use the ImportMetadata bulk-set operation. @@ -767,9 +764,7 @@ public void CopyMetadataTo(ITaskItem destinationItem) destinationItemAsMetadataContainer.ImportMetadata(metadataToImport); } - else -#endif - if (_customEscapedMetadata != null) + else if (_customEscapedMetadata != null) { foreach (KeyValuePair entry in _customEscapedMetadata) { @@ -834,11 +829,11 @@ string ITaskItem2.GetMetadataValueEscaped(string metadataName) string metadataValue = null; - if (FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) + if (ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) { // FileUtilities.GetItemSpecModifier is expecting escaped data, which we assume we already are. // Passing in a null for currentDirectory indicates we are already in the correct current directory - metadataValue = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(null, _escapedItemSpec, _escapedDefiningProject, metadataName, ref _fullPath); + metadataValue = ItemSpecModifiers.GetItemSpecModifier(null, _escapedItemSpec, _escapedDefiningProject, metadataName, ref _fullPath); } else if (_customEscapedMetadata != null) { diff --git a/src/Shared/TaskParameterTypeVerifier.cs b/src/Shared/TaskParameterTypeVerifier.cs index 2b7d6160d84..0390b5c1de4 100644 --- a/src/Shared/TaskParameterTypeVerifier.cs +++ b/src/Shared/TaskParameterTypeVerifier.cs @@ -4,9 +4,6 @@ using System; using System.Reflection; using Microsoft.Build.Framework; -#if NET35 -using Microsoft.Build.Shared; -#endif #nullable disable diff --git a/src/Shared/TempFileUtilities.cs b/src/Shared/TempFileUtilities.cs deleted file mode 100644 index 3533bf22763..00000000000 --- a/src/Shared/TempFileUtilities.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared.FileSystem; - -#nullable disable - -namespace Microsoft.Build.Shared -{ - /// - /// This class contains utility methods for file IO. - /// It is in a separate file so that it can be selectively included into an assembly. - /// - internal static partial class FileUtilities - { - private static Lazy tempFileDirectory = CreateTempFileDirectoryLazy(); - - private const string msbuildTempFolderPrefix = "MSBuildTemp"; - - internal static string TempFileDirectory => tempFileDirectory.Value; - - private static Lazy CreateTempFileDirectoryLazy() - { - return new Lazy( - () => - { - string path = CreateFolderUnderTemp(); - RegisterCleanupOnExit(path); - return path; - }, - LazyThreadSafetyMode.ExecutionAndPublication); - } - - private static void RegisterCleanupOnExit(string pathToCleanup) - { - AppDomain.CurrentDomain.ProcessExit += (_, _) => - { - try - { - if (Directory.Exists(pathToCleanup)) - { - Directory.Delete(pathToCleanup, recursive: true); - } - } - catch - { - // Best effort - ignore failures during cleanup - } - }; - } - - internal static void ClearTempFileDirectory() - { - tempFileDirectory = CreateTempFileDirectoryLazy(); - } - - private static string CreateFolderUnderTemp() - { - string path; - -#if NET - path = Directory.CreateTempSubdirectory(msbuildTempFolderPrefix).FullName; -#else - // CreateTempSubdirectory API is not available in .NET Framework - path = Path.Combine(Path.GetTempPath(), $"{msbuildTempFolderPrefix}{Guid.NewGuid():N}"); - Directory.CreateDirectory(path); -#endif - - return FrameworkFileUtilities.EnsureTrailingSlash(path); - } - - /// - /// Generates a unique directory name in the temporary folder. - /// Caller must delete when finished. - /// - /// - /// - internal static string GetTemporaryDirectory(bool createDirectory = true, string subfolder = null) - { - string temporaryDirectory = Path.Combine(TempFileDirectory, $"Temporary{Guid.NewGuid():N}", subfolder ?? string.Empty); - - if (createDirectory) - { - Directory.CreateDirectory(temporaryDirectory); - } - - return temporaryDirectory; - } - - /// - /// Generates a unique temporary file name with a given extension in the temporary folder. - /// File is guaranteed to be unique. - /// Extension may have an initial period. - /// File will NOT be created. - /// May throw IOException. - /// - internal static string GetTemporaryFileName() - { - return GetTemporaryFileName(".tmp"); - } - - /// - /// Generates a unique temporary file name with a given extension in the temporary folder. - /// File is guaranteed to be unique. - /// Extension may have an initial period. - /// File will NOT be created. - /// May throw IOException. - /// - internal static string GetTemporaryFileName(string extension) - { - return GetTemporaryFile(null, null, extension, false); - } - - /// - /// Generates a unique temporary file name with a given extension in the temporary folder. - /// If no extension is provided, uses ".tmp". - /// File is guaranteed to be unique. - /// Caller must delete it when finished. - /// - internal static string GetTemporaryFile() - { - return GetTemporaryFile(".tmp"); - } - - /// - /// Generates a unique temporary file name with a given extension in the temporary folder. - /// File is guaranteed to be unique. - /// Caller must delete it when finished. - /// - internal static string GetTemporaryFile(string fileName, string extension, bool createFile) - { - return GetTemporaryFile(null, fileName, extension, createFile); - } - - /// - /// Generates a unique temporary file name with a given extension in the temporary folder. - /// File is guaranteed to be unique. - /// Extension may have an initial period. - /// Caller must delete it when finished. - /// May throw IOException. - /// - internal static string GetTemporaryFile(string extension) - { - return GetTemporaryFile(null, null, extension); - } - - /// - /// Creates a file with unique temporary file name with a given extension in the specified folder. - /// File is guaranteed to be unique. - /// Extension may have an initial period. - /// If folder is null, the temporary folder will be used. - /// Caller must delete it when finished. - /// May throw IOException. - /// - internal static string GetTemporaryFile(string directory, string fileName, string extension, bool createFile = true) - { - ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(directory, nameof(directory)); - - try - { - directory ??= TempFileDirectory; - - // If the extension needs a dot prepended, do so. - if (extension is null) - { - extension = string.Empty; - } - else if (extension.Length > 0 && extension[0] != '.') - { - extension = '.' + extension; - } - - // If the fileName is null, use tmp{Guid}; otherwise use fileName. - if (string.IsNullOrEmpty(fileName)) - { - fileName = $"tmp{Guid.NewGuid():N}"; - } - - Directory.CreateDirectory(directory); - - string file = Path.Combine(directory, $"{fileName}{extension}"); - - ErrorUtilities.VerifyThrow(!FileSystems.Default.FileExists(file), "Guid should be unique"); - - if (createFile) - { - File.WriteAllText(file, string.Empty); - } - - return file; - } - catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) - { - throw new IOException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Shared.FailedCreatingTempFile", ex.Message), ex); - } - } - - internal static void CopyDirectory(string source, string dest) - { - Directory.CreateDirectory(dest); - - DirectoryInfo sourceInfo = new DirectoryInfo(source); - foreach (var fileInfo in sourceInfo.GetFiles()) - { - string destFile = Path.Combine(dest, fileInfo.Name); - fileInfo.CopyTo(destFile); - } - foreach (var subdirInfo in sourceInfo.GetDirectories()) - { - string destDir = Path.Combine(dest, subdirInfo.Name); - CopyDirectory(subdirInfo.FullName, destDir); - } - } - - public sealed class TempWorkingDirectory : IDisposable - { - public string Path { get; } - - public TempWorkingDirectory(string sourcePath, -#if !CLR2COMPATIBILITY - [CallerMemberName] -#endif - string name = null) - { - Path = name == null - ? GetTemporaryDirectory() - : System.IO.Path.Combine(TempFileDirectory, name); - - if (FileSystems.Default.DirectoryExists(Path)) - { - Directory.Delete(Path, true); - } - - CopyDirectory(sourcePath, Path); - } - - public void Dispose() - { - Directory.Delete(Path, true); - } - } - } -} diff --git a/src/Shared/ToolsetElement.cs b/src/Shared/ToolsetElement.cs index 913bbbf1a5c..9b6945eaf86 100644 --- a/src/Shared/ToolsetElement.cs +++ b/src/Shared/ToolsetElement.cs @@ -6,6 +6,7 @@ using System.Configuration; using System.IO; using Microsoft.Build.Collections; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Shared/TranslatorHelpers.cs b/src/Shared/TranslatorHelpers.cs index 52bf3941657..b195d17daf1 100644 --- a/src/Shared/TranslatorHelpers.cs +++ b/src/Shared/TranslatorHelpers.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -#if !TASKHOST using System.Collections.Frozen; using System.Collections.Immutable; -#endif using System.Collections.Generic; using System.Configuration.Assemblies; using System.Globalization; @@ -178,7 +176,6 @@ public static void TranslateDictionary( translator.TranslateDictionary(ref dictionary, AdaptFactory(valueFactory), valueFactory, collectionCreator); } -#if !TASKHOST public static void TranslateDictionary( this ITranslator translator, ref FrozenDictionary dictionary, @@ -257,7 +254,6 @@ public static void TranslateDictionary( dictionary = (ImmutableDictionary)localDict; } } -#endif public static void TranslateHashSet( this ITranslator translator, diff --git a/src/Shared/UnitTests/EscapingUtilities_Tests.cs b/src/Shared/UnitTests/EscapingUtilities_Tests.cs deleted file mode 100644 index 9b756d3a3da..00000000000 --- a/src/Shared/UnitTests/EscapingUtilities_Tests.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Build.Shared; -using Xunit; - -#nullable disable - -namespace Microsoft.Build.UnitTests -{ - public sealed class EscapingUtilities_Tests - { - /// - /// - [Fact] - public void Unescape() - { - Assert.Equal("", EscapingUtilities.UnescapeAll("")); - Assert.Equal("foo", EscapingUtilities.UnescapeAll("foo")); - Assert.Equal("foo space", EscapingUtilities.UnescapeAll("foo%20space")); - Assert.Equal("foo2;", EscapingUtilities.UnescapeAll("foo2%3B")); - Assert.Equal(";foo3", EscapingUtilities.UnescapeAll("%3bfoo3")); - Assert.Equal(";", EscapingUtilities.UnescapeAll("%3b")); - Assert.Equal(";;;;;", EscapingUtilities.UnescapeAll("%3b%3B;%3b%3B")); - Assert.Equal("%3B", EscapingUtilities.UnescapeAll("%253B")); - Assert.Equal("===%ZZ %%%===", EscapingUtilities.UnescapeAll("===%ZZ%20%%%===")); - Assert.Equal("hello; escaping% how( are) you?", EscapingUtilities.UnescapeAll("hello%3B escaping%25 how%28 are%29 you%3f")); - - Assert.Equal("%*?*%*", EscapingUtilities.UnescapeAll("%25*?*%25*")); - Assert.Equal("%*?*%*", EscapingUtilities.UnescapeAll("%25%2a%3f%2a%25%2a")); - - Assert.Equal("*Star*craft or *War*cr@ft??", EscapingUtilities.UnescapeAll("%2aStar%2Acraft%20or %2aWar%2Acr%40ft%3f%3F")); - } - - /// - /// - [Fact] - public void Escape() - { - Assert.Equal("%2a", EscapingUtilities.Escape("*")); - Assert.Equal("%3f", EscapingUtilities.Escape("?")); - Assert.Equal("#%2a%3f%2a#%2a", EscapingUtilities.Escape("#*?*#*")); - Assert.Equal("%25%2a%3f%2a%25%2a", EscapingUtilities.Escape("%*?*%*")); - } - - /// - /// - [Fact] - public void UnescapeEscape() - { - string text = "*"; - Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); - - text = "?"; - Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); - - text = "#*?*#*"; - Assert.Equal(text, EscapingUtilities.UnescapeAll(EscapingUtilities.Escape(text))); - } - - /// - /// - [Fact] - public void EscapeUnescape() - { - string text = "%2a"; - Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); - - text = "%3f"; - Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); - - text = "#%2a%3f%2a#%2a"; - Assert.Equal(text, EscapingUtilities.Escape(EscapingUtilities.UnescapeAll(text))); - } - - [Fact] - public void ContainsEscapedWildcards() - { - Assert.False(EscapingUtilities.ContainsEscapedWildcards("NoStarOrQMark")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%%")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%2")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%4")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%3A")); - Assert.False(EscapingUtilities.ContainsEscapedWildcards("%2B")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2a")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%2A")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3F")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3f")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%%3f")); - Assert.True(EscapingUtilities.ContainsEscapedWildcards("%3%3f")); - } - } -} diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index dc90922a428..c0bea972965 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -1873,8 +1873,8 @@ public void GetFileSpecInfoCommon( { if (NativeMethodsShared.IsUnixLike) { - expectedFixedDirectoryPart = FrameworkFileUtilities.FixFilePath(expectedFixedDirectoryPart); - expectedWildcardDirectoryPart = FrameworkFileUtilities.FixFilePath(expectedWildcardDirectoryPart); + expectedFixedDirectoryPart = FileUtilities.FixFilePath(expectedFixedDirectoryPart); + expectedWildcardDirectoryPart = FileUtilities.FixFilePath(expectedWildcardDirectoryPart); } TestGetFileSpecInfo( filespec, @@ -2299,11 +2299,11 @@ private bool IsMatchingDirectory(string path, string candidate) { if (String.Compare(normalizedPath, 0, normalizedCandidate, 0, normalizedPath.Length, StringComparison.OrdinalIgnoreCase) == 0) { - if (FrameworkFileUtilities.EndsWithSlash(normalizedPath)) + if (FileUtilities.EndsWithSlash(normalizedPath)) { return true; } - else if (FrameworkFileUtilities.IsSlash(normalizedCandidate[normalizedPath.Length])) + else if (FileUtilities.IsSlash(normalizedCandidate[normalizedPath.Length])) { return true; } @@ -2508,9 +2508,9 @@ private static void ValidateSplitFileSpec( out wildcardDirectoryPart, out filenamePart); - expectedFixedDirectoryPart = FrameworkFileUtilities.FixFilePath(expectedFixedDirectoryPart); - expectedWildcardDirectoryPart = FrameworkFileUtilities.FixFilePath(expectedWildcardDirectoryPart); - expectedFilenamePart = FrameworkFileUtilities.FixFilePath(expectedFilenamePart); + expectedFixedDirectoryPart = FileUtilities.FixFilePath(expectedFixedDirectoryPart); + expectedWildcardDirectoryPart = FileUtilities.FixFilePath(expectedWildcardDirectoryPart); + expectedFilenamePart = FileUtilities.FixFilePath(expectedFilenamePart); if ( diff --git a/src/Shared/UnitTests/FileUtilities_Tests.cs b/src/Shared/UnitTests/FileUtilities_Tests.cs deleted file mode 100644 index b7e677e2e4a..00000000000 --- a/src/Shared/UnitTests/FileUtilities_Tests.cs +++ /dev/null @@ -1,1058 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared; -using Shouldly; -using Xunit; - -#nullable disable - -namespace Microsoft.Build.UnitTests -{ - public class FileUtilities_Tests - { - /// - /// Exercises FileUtilities.ItemSpecModifiers.GetItemSpecModifier - /// - [Fact] - [Trait("Category", "netcore-osx-failing")] - [Trait("Category", "netcore-linux-failing")] - public void GetItemSpecModifier() - { - TestGetItemSpecModifier(Directory.GetCurrentDirectory()); - TestGetItemSpecModifier(null); - } - - private static void TestGetItemSpecModifier(string currentDirectory) - { - string cache = null; - string modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, FileUtilities.ItemSpecModifiers.RecursiveDir, ref cache); - Assert.Equal(String.Empty, modifier); - - cache = null; - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, "foo", String.Empty, FileUtilities.ItemSpecModifiers.ModifiedTime, ref cache); - Assert.Equal(String.Empty, modifier); - - cache = null; - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, FileUtilities.ItemSpecModifiers.RelativeDir, ref cache); - Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier); - - // confirm we get the same thing back the second time - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"foo\goo", String.Empty, FileUtilities.ItemSpecModifiers.RelativeDir, ref cache); - Assert.Equal(@"foo" + Path.DirectorySeparatorChar, modifier); - - cache = null; - string itemSpec = NativeMethodsShared.IsWindows ? @"c:\foo.txt" : "/foo.txt"; - string itemSpecDir = NativeMethodsShared.IsWindows ? @"c:\" : "/"; - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.FullPath, ref cache); - Assert.Equal(itemSpec, modifier); - Assert.Equal(itemSpec, cache); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.RootDir, ref cache); - Assert.Equal(itemSpecDir, modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.Filename, ref cache); - Assert.Equal(@"foo", modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.Extension, ref cache); - Assert.Equal(@".txt", modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.Directory, ref cache); - Assert.Equal(String.Empty, modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, String.Empty, FileUtilities.ItemSpecModifiers.Identity, ref cache); - Assert.Equal(itemSpec, modifier); - - string projectPath = NativeMethodsShared.IsWindows ? @"c:\abc\goo.proj" : @"/abc/goo.proj"; - string projectPathDir = NativeMethodsShared.IsWindows ? @"c:\abc\" : @"/abc/"; - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, FileUtilities.ItemSpecModifiers.DefiningProjectDirectory, ref cache); - Assert.Equal(projectPathDir, modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, FileUtilities.ItemSpecModifiers.DefiningProjectExtension, ref cache); - Assert.Equal(@".proj", modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, FileUtilities.ItemSpecModifiers.DefiningProjectFullPath, ref cache); - Assert.Equal(projectPath, modifier); - - modifier = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, itemSpec, projectPath, FileUtilities.ItemSpecModifiers.DefiningProjectName, ref cache); - Assert.Equal(@"goo", modifier); - } - - [Fact] - public void MakeRelativeTests() - { - if (NativeMethodsShared.IsWindows) - { - Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\foo.cpp")); - Assert.Equal(@"def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\foo.cpp")); - Assert.Equal(@"..\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\foo.cpp")); - Assert.Equal(@"..\ttt\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\foo.cpp")); - Assert.Equal(@"e:\abc\def\foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"e:\abc\def\foo.cpp")); - Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"\\aaa\abc\def", @"\\aaa\abc\def\foo.cpp")); - Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"c:\abc\def", @"foo.cpp")); - Assert.Equal(@"\\host\path\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\path\file")); - Assert.Equal(@"\\host\d$\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\d$\file")); - Assert.Equal(@"..\fff\ggg.hh", FileUtilities.MakeRelative(@"c:\foo\bar\..\abc\cde", @"c:\foo\bar\..\abc\fff\ggg.hh")); - - /* Directories */ - Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\")); - Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\")); - Assert.Equal(@"..\ttt\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\")); - Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\def\")); - - /* Directory + File */ - Assert.Equal(@"def", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def")); - Assert.Equal(@"..\..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\ghi")); - Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ghi")); - Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\ghi")); - - /* File + Directory */ - Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc", @"c:\abc\def\")); - Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\")); - Assert.Equal(@"..\ghi\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\ghi\")); - Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\")); - } - else - { - Assert.Equal(@"bar.cpp", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/bar.cpp")); - Assert.Equal(@"def/foo.cpp", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/foo.cpp")); - Assert.Equal(@"../foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/foo.cpp")); - Assert.Equal(@"../ttt/foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/foo.cpp")); - Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"/abc/def", @"foo.cpp")); - Assert.Equal(@"../fff/ggg.hh", FileUtilities.MakeRelative(@"/foo/bar/../abc/cde", @"/foo/bar/../abc/fff/ggg.hh")); - - /* Directories */ - Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/")); - Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/")); - Assert.Equal(@"../ttt/", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/")); - Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/def/")); - - /* Directory + File */ - Assert.Equal(@"def", FileUtilities.MakeRelative(@"/abc/", @"/abc/def")); - Assert.Equal(@"../../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/ghi")); - Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ghi")); - Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/ghi")); - - /* File + Directory */ - Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc", @"/abc/def/")); - Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/")); - Assert.Equal(@"../ghi/", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/ghi/")); - Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/")); - } - } - - /// - /// Exercises FileUtilities.ItemSpecModifiers.GetItemSpecModifier on a bad path. - /// - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void GetItemSpecModifierOnBadPath() - { - Assert.Throws(() => - { - TestGetItemSpecModifierOnBadPath(Directory.GetCurrentDirectory()); - }); - } - /// - /// Exercises FileUtilities.ItemSpecModifiers.GetItemSpecModifier on a bad path. - /// - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void GetItemSpecModifierOnBadPath2() - { - Assert.Throws(() => - { - TestGetItemSpecModifierOnBadPath(null); - }); - } - - private static void TestGetItemSpecModifierOnBadPath(string currentDirectory) - { - try - { - string cache = null; - FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, @"http://www.microsoft.com", String.Empty, FileUtilities.ItemSpecModifiers.RootDir, ref cache); - } - catch (Exception e) - { - // so I can see the exception message in NUnit's "Standard Out" window - Console.WriteLine(e.Message); - throw; - } - } - - [Fact] - public void GetFileInfoNoThrowBasic() - { - string file = null; - try - { - file = FileUtilities.GetTemporaryFile(); - FileInfo info = FileUtilities.GetFileInfoNoThrow(file); - Assert.Equal(info.LastWriteTime, new FileInfo(file).LastWriteTime); - } - finally - { - if (file != null) - { - File.Delete(file); - } - } - } - - [Fact] - public void GetFileInfoNoThrowNonexistent() - { - FileInfo info = FileUtilities.GetFileInfoNoThrow("this_file_is_nonexistent"); - Assert.Null(info); - } - - /// - /// Exercises FrameworkFileUtilities.EndsWithSlash - /// - [Fact] - [Trait("Category", "netcore-osx-failing")] - [Trait("Category", "netcore-linux-failing")] - public void EndsWithSlash() - { - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"C:\foo\")); - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"C:\")); - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"\")); - - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"http://www.microsoft.com/")); - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"//server/share/")); - Assert.True(FrameworkFileUtilities.EndsWithSlash(@"/")); - - Assert.False(FrameworkFileUtilities.EndsWithSlash(@"C:\foo")); - Assert.False(FrameworkFileUtilities.EndsWithSlash(@"C:")); - Assert.False(FrameworkFileUtilities.EndsWithSlash(@"foo")); - - // confirm that empty string doesn't barf - Assert.False(FrameworkFileUtilities.EndsWithSlash(String.Empty)); - } - - /// - /// Exercises FileUtilities.GetDirectory - /// - [Fact] - [Trait("Category", "netcore-osx-failing")] - [Trait("Category", "netcore-linux-failing")] - public void GetDirectoryWithTrailingSlash() - { - Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\" : "/")); - Assert.Equal(NativeMethodsShared.IsWindows ? @"c:\" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:\foo" : "/foo")); - Assert.Equal(NativeMethodsShared.IsWindows ? @"c:" : "/", FileUtilities.GetDirectory(NativeMethodsShared.IsWindows ? @"c:" : "/")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\"), FileUtilities.GetDirectory(@"\foo")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"..\"), FileUtilities.GetDirectory(@"..\foo")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\foo\"), FileUtilities.GetDirectory(@"\foo\")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\\server\share"), FileUtilities.GetDirectory(@"\\server\share")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\\server\share\"), FileUtilities.GetDirectory(@"\\server\share\file")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\\server\share\directory\"), FileUtilities.GetDirectory(@"\\server\share\directory\")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\"), FileUtilities.GetDirectory(@"foo\bar")); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"\foo\bar\"), FileUtilities.GetDirectory(@"\foo\bar\")); - Assert.Equal(String.Empty, FileUtilities.GetDirectory("foo")); - } - - [Theory] - [InlineData("foo.txt", new[] { ".txt" })] - [InlineData("foo.txt", new[] { ".TXT" })] - [InlineData("foo.txt", new[] { ".EXE", ".TXT" })] - public void HasExtension_WhenFileNameHasExtension_ReturnsTrue(string fileName, string[] allowedExtensions) - { - var result = FileUtilities.HasExtension(fileName, allowedExtensions); - - if (!FileUtilities.GetIsFileSystemCaseSensitive() || allowedExtensions.Any(extension => fileName.Contains(extension))) - { - result.ShouldBeTrue(); - } - } - - [Theory] - [InlineData("foo.txt", new[] { ".DLL" })] - [InlineData("foo.txt", new[] { ".EXE", ".DLL" })] - [InlineData("foo.exec", new[] { ".exe", })] - [InlineData("foo.exe", new[] { ".exec", })] - [InlineData("foo", new[] { ".exe", })] - [InlineData("", new[] { ".exe" })] - [InlineData(null, new[] { ".exe" })] - public void HasExtension_WhenFileNameDoesNotHaveExtension_ReturnsFalse(string fileName, string[] allowedExtensions) - { - var result = FileUtilities.HasExtension(fileName, allowedExtensions); - - Assert.False(result); - } - - [WindowsFullFrameworkOnlyFact] - public void HasExtension_WhenInvalidFileName_ThrowsArgumentException() - { - Assert.Throws(() => - { - FileUtilities.HasExtension("|/", new[] { ".exe" }); - }); - } - - [Fact] - public void HasExtension_UsesOrdinalIgnoreCase() - { - var currentCulture = Thread.CurrentThread.CurrentCulture; - try - { - Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR"); // Turkish - - var result = FileUtilities.HasExtension("foo.ini", new string[] { ".INI" }); - - result.ShouldBe(!FileUtilities.GetIsFileSystemCaseSensitive()); - } - finally - { - Thread.CurrentThread.CurrentCulture = currentCulture; - } - } - - /// - /// Exercises FrameworkFileUtilities.EnsureTrailingSlash - /// - [Fact] - public void EnsureTrailingSlash() - { - // Doesn't have a trailing slash to start with. - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\bar\"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo\bar")); // "test 1" - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo/bar\"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo/bar")); // "test 2" - - // Already has a trailing slash to start with. - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo/bar/"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo/bar/")); // test 3" - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\bar\"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo\bar\")); // test 4" - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo/bar\"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo/bar\")); // test 5" - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\bar/"), FrameworkFileUtilities.EnsureTrailingSlash(@"foo\bar/")); // "test 5" - } - - /// - /// Exercises FileUtilities.ItemSpecModifiers.IsItemSpecModifier - /// - [Fact] - public void IsItemSpecModifier() - { - // Positive matches using exact case. - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("FullPath")); // "test 1" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("RootDir")); // "test 2" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Filename")); // "test 3" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Extension")); // "test 4" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("RelativeDir")); // "test 5" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Directory")); // "test 6" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("RecursiveDir")); // "test 7" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Identity")); // "test 8" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("ModifiedTime")); // "test 9" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("CreatedTime")); // "test 10" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("AccessedTime")); // "test 11" - - // Positive matches using different case. - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("fullPath")); // "test 21" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("rootDir")); // "test 22" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("filename")); // "test 23" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("extension")); // "test 24" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("relativeDir")); // "test 25" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("directory")); // "test 26" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("recursiveDir")); // "test 27" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("identity")); // "test 28" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("modifiedTime")); // "test 29" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("createdTime")); // "test 30" - Assert.True(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("accessedTime")); // "test 31" - - // Negative tests to get maximum code coverage inside the many different branches - // of FileUtilities.ItemSpecModifiers.IsItemSpecModifier. - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("rootxxx")); // "test 41" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Rootxxx")); // "test 42" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxx")); // "test 43" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("filexxxx")); // "test 44" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Filexxxx")); // "test 45" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("idenxxxx")); // "test 46" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Idenxxxx")); // "test 47" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxxx")); // "test 48" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("extenxxxx")); // "test 49" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Extenxxxx")); // "test 50" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("direcxxxx")); // "test 51" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Direcxxxx")); // "test 52" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxx")); // "test 53" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxx")); // "test 54" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("relativexxx")); // "test 55" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Relativexxx")); // "test 56" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("createdxxxx")); // "test 57" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Createdxxxx")); // "test 58" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxx")); // "test 59" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("recursivexxx")); // "test 60" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Recursivexxx")); // "test 61" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("accessedxxxx")); // "test 62" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Accessedxxxx")); // "test 63" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("modifiedxxxx")); // "test 64" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("Modifiedxxxx")); // "test 65" - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier("xxxxxxxxxxxx")); // "test 66" - - Assert.False(FileUtilities.ItemSpecModifiers.IsItemSpecModifier(null)); // "test 67" - } - - [Fact] - public void CheckDerivableItemSpecModifiers() - { - Assert.True(FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier("Filename")); - Assert.False(FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier("RecursiveDir")); - Assert.False(FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier("recursivedir")); - } - - [WindowsOnlyFact] - public void NormalizePathThatFitsIntoMaxPath() - { - string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; - string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; - string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; - - Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath))); - } - - [LongPathSupportDisabledFact(fullFrameworkOnly: true, additionalMessage: "https://github.com/dotnet/msbuild/issues/4363")] - public void NormalizePathThatDoesntFitIntoMaxPath() - { - Assert.Throws(() => - { - string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; - string filePath = @"..\..\..\..\..\..\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; - - // This path ends up over 420 characters long - string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; - - Assert.Equal(fullPath, FileUtilities.NormalizePath(Path.Combine(currentDirectory, filePath))); - }); - } - - [WindowsOnlyFact] - public void GetItemSpecModifierRootDirThatFitsIntoMaxPath() - { - string currentDirectory = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890"; - string fullPath = @"c:\aardvark\aardvark\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\1234567890\a.cs"; - string cache = fullPath; - - Assert.Equal(@"c:\", FileUtilities.ItemSpecModifiers.GetItemSpecModifier(currentDirectory, fullPath, String.Empty, FileUtilities.ItemSpecModifiers.RootDir, ref cache)); - } - - [Fact] - public void NormalizePathNull() - { - Assert.Throws(() => - { - Assert.Null(FileUtilities.NormalizePath(null, null)); - }); - } - - [Fact] - public void NormalizePathEmpty() - { - Assert.Throws(() => - { - Assert.Null(FileUtilities.NormalizePath(String.Empty)); - }); - } - - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void NormalizePathBadUNC1() - { - Assert.Throws(() => - { - Assert.Null(FileUtilities.NormalizePath(@"\\")); - }); - } - - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void NormalizePathBadUNC2() - { - Assert.Throws(() => - { - Assert.Null(FileUtilities.NormalizePath(@"\\XXX\")); - }); - } - - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void NormalizePathBadUNC3() - { - Assert.Throws(() => - { - Assert.Equal(@"\\localhost", FileUtilities.NormalizePath(@"\\localhost")); - }); - } - - [WindowsOnlyFact] - public void NormalizePathGoodUNC() - { - Assert.Equal(@"\\localhost\share", FileUtilities.NormalizePath(@"\\localhost\share")); - } - - [WindowsOnlyFact] - public void NormalizePathTooLongWithDots() - { - string longPart = new string('x', 300); - Assert.Equal(@"c:\abc\def", FileUtilities.NormalizePath(@"c:\abc\" + longPart + @"\..\def")); - } - - [WindowsFullFrameworkOnlyFact(additionalMessage: ".NET Core 2.1+ no longer validates paths: https://github.com/dotnet/corefx/issues/27779#issuecomment-371253486.")] - public void NormalizePathInvalid() - { - string filePath = @"c:\aardvark\|||"; - - Assert.Throws(() => - { - FileUtilities.NormalizePath(filePath); - }); - } - - [WindowsOnlyFact] - public void CannotNormalizePathWithNewLineAndSpace() - { - string filePath = "\r\n C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll\r\n "; - -#if FEATURE_LEGACY_GETFULLPATH - Assert.Throws(() => FileUtilities.NormalizePath(filePath)); -#else - Assert.NotEqual("C:\\work\\sdk3\\artifacts\\tmp\\Debug\\SimpleNamesWi---6143883E\\NETFrameworkLibrary\\bin\\Debug\\net462\\NETFrameworkLibrary.dll", FileUtilities.NormalizePath(filePath)); -#endif - } - - [Fact] - public void FileOrDirectoryExistsNoThrow() - { - var isWindows = NativeMethodsShared.IsWindows; - - Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow("||")); - Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\doesnot_exist" : "/doesnot_exist")); - Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(isWindows ? @"c:\" : "/")); - Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(Path.GetTempPath())); - - string path = null; - - try - { - path = FileUtilities.GetTemporaryFile(); - Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(path)); - } - finally - { - File.Delete(path); - } - } - -#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY - // These tests will need to be redesigned for Linux - - [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] - public void FileOrDirectoryExistsNoThrowTooLongWithDots() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath)); - Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X'))); - } - - [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] - public void FileOrDirectoryExistsNoThrowTooLongWithDotsRelative() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - - string currentDirectory = Directory.GetCurrentDirectory(); - - try - { - Directory.SetCurrentDirectory(Environment.SystemDirectory); - - Assert.True(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath)); - Assert.False(FileUtilities.FileOrDirectoryExistsNoThrow(inputPath.Replace('\\', 'X'))); - } - finally - { - Directory.SetCurrentDirectory(currentDirectory); - } - } - - [Fact] - public void DirectoryExistsNoThrowTooLongWithDots() - { - string path = Path.Combine(Environment.SystemDirectory, "..", "..", "..") + Path.DirectorySeparatorChar; - if (NativeMethodsShared.IsWindows) - { - path += Environment.SystemDirectory.Substring(3); - } - - int length = path.Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = Path.Combine(new[] { Environment.SystemDirectory, longPart, "..", "..", ".." }) - + Path.DirectorySeparatorChar; - if (NativeMethodsShared.IsWindows) - { - path += Environment.SystemDirectory.Substring(3); - } - - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - Assert.True(FileUtilities.DirectoryExistsNoThrow(inputPath)); - } - - [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] - public void DirectoryExistsNoThrowTooLongWithDotsRelative() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3)).Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3); - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\..\windows\system32" exists - - string currentDirectory = Directory.GetCurrentDirectory(); - - try - { - Directory.SetCurrentDirectory(Environment.SystemDirectory); - - FileUtilities.DirectoryExistsNoThrow(inputPath).ShouldBeTrue(); - FileUtilities.DirectoryExistsNoThrow(inputPath.Replace('\\', 'X')).ShouldBeFalse(); - } - finally - { - Directory.SetCurrentDirectory(currentDirectory); - } - } - - public static bool RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241() - { - // Run these tests only when we're not on Windows - return !NativeMethodsShared.IsWindows || - // OR we're on Windows and long paths aren't enabled - // https://github.com/dotnet/msbuild/issues/4241 - NativeMethodsShared.IsMaxPathLegacyWindows(); - } - - [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] - public void FileExistsNoThrowTooLongWithDots() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; - Console.WriteLine(inputPath.Length); - Console.WriteLine(inputPath); - - // "c:\windows\system32\\..\..\windows\system32" exists - Assert.True(FileUtilities.FileExistsNoThrow(inputPath)); - } - - [ConditionalFact(nameof(RunTestsThatDependOnWindowsShortPathBehavior_Workaround4241))] - public void FileExistsNoThrowTooLongWithDotsRelative() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - - string currentDirectory = Directory.GetCurrentDirectory(); - - try - { - Directory.SetCurrentDirectory(Environment.SystemDirectory); - - Assert.True(FileUtilities.FileExistsNoThrow(inputPath)); - Assert.False(FileUtilities.FileExistsNoThrow(inputPath.Replace('\\', 'X'))); - } - finally - { - Directory.SetCurrentDirectory(currentDirectory); - } - } - - [Fact] - public void GetFileInfoNoThrowTooLongWithDots() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = Environment.SystemDirectory + @"\" + longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null); - Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null); - } - - [Fact] - public void GetFileInfoNoThrowTooLongWithDotsRelative() - { - int length = (Environment.SystemDirectory + @"\" + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe").Length; - - string longPart = new string('x', 260 - length); // We want the shortest that is > max path. - - string inputPath = longPart + @"\..\..\..\" + Environment.SystemDirectory.Substring(3) + @"\..\explorer.exe"; - Console.WriteLine(inputPath.Length); - - // "c:\windows\system32\\..\..\windows\system32" exists - - string currentDirectory = Directory.GetCurrentDirectory(); - - try - { - Directory.SetCurrentDirectory(Environment.SystemDirectory); - - Assert.True(FileUtilities.GetFileInfoNoThrow(inputPath) != null); - Assert.False(FileUtilities.GetFileInfoNoThrow(inputPath.Replace('\\', 'X')) != null); - } - finally - { - Directory.SetCurrentDirectory(currentDirectory); - } - } -#endif - - /// - /// Simple test, neither the base file nor retry files exist - /// - [Fact] - public void GenerateTempFileNameSimple() - { - string path = null; - - try - { - path = FileUtilities.GetTemporaryFile(); - - Assert.EndsWith(".tmp", path); - Assert.True(File.Exists(path)); - Assert.StartsWith(Path.GetTempPath(), path); - } - finally - { - File.Delete(path); - } - } - - /// - /// Choose an extension - /// - [Fact] - public void GenerateTempFileNameWithExtension() - { - string path = null; - - try - { - path = FileUtilities.GetTemporaryFile(".bat"); - - Assert.EndsWith(".bat", path); - Assert.True(File.Exists(path)); - Assert.StartsWith(Path.GetTempPath(), path); - } - finally - { - File.Delete(path); - } - } - - /// - /// Choose a (missing) directory and extension - /// - [Fact] - public void GenerateTempFileNameWithDirectoryAndExtension() - { - string path = null; - string directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "subfolder"); - - try - { - path = FileUtilities.GetTemporaryFile(directory, null, ".bat"); - - Assert.EndsWith(".bat", path); - Assert.True(File.Exists(path)); - Assert.StartsWith(directory, path); - } - finally - { - File.Delete(path); - FileUtilities.DeleteWithoutTrailingBackslash(directory); - } - } - - /// - /// Extension without a period - /// - [Fact] - public void GenerateTempFileNameWithExtensionNoPeriod() - { - string path = null; - - try - { - path = FileUtilities.GetTemporaryFile("bat"); - - Assert.EndsWith(".bat", path); - Assert.True(File.Exists(path)); - Assert.StartsWith(Path.GetTempPath(), path); - } - finally - { - File.Delete(path); - } - } - - /// - /// Extension is invalid - /// - [Fact] - [Trait("Category", "netcore-osx-failing")] - [Trait("Category", "netcore-linux-failing")] - public void GenerateTempBatchFileWithBadExtension() - { - Assert.Throws(() => - { - FileUtilities.GetTemporaryFile("|"); - }); - } - - /// - /// Directory is invalid - /// - [Fact] - [Trait("Category", "netcore-osx-failing")] - [Trait("Category", "netcore-linux-failing")] - public void GenerateTempBatchFileWithBadDirectory() - { - Assert.Throws(() => - { - FileUtilities.GetTemporaryFile("|", null, ".tmp"); - }); - } - - [UnixOnlyFact] - public void AbsolutePathLooksLikeUnixPathOnUnix() - { - var secondSlash = SystemSpecificAbsolutePath.Substring(1).IndexOf(Path.DirectorySeparatorChar) + 1; - var rootLevelPath = SystemSpecificAbsolutePath.Substring(0, secondSlash); - - Assert.True(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath)); - Assert.True(FileUtilities.LooksLikeUnixFilePath(rootLevelPath)); - } - - [WindowsOnlyFact] - public void PathDoesNotLookLikeUnixPathOnWindows() - { - Assert.False(FileUtilities.LooksLikeUnixFilePath(SystemSpecificAbsolutePath)); - Assert.False(FileUtilities.LooksLikeUnixFilePath("/path/that/looks/unixy")); - Assert.False(FileUtilities.LooksLikeUnixFilePath("/root")); - } - - [UnixOnlyFact] - public void RelativePathLooksLikeUnixPathOnUnixWithBaseDirectory() - { - string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty); - string oldCWD = Directory.GetCurrentDirectory(); - - try - { - // /first - string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath)); - string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath))); - - Directory.SetCurrentDirectory(tmpDirectory); - - // We are in and second is not under that, so this will be false - Assert.False(FileUtilities.LooksLikeUnixFilePath("second/file.txt")); - - // .. but if we have baseDirectory:firstDirectory, then it will be true - Assert.True(FileUtilities.LooksLikeUnixFilePath("second/file.txt", firstDirectory)); - } - finally - { - if (filePath != null) - { - File.Delete(filePath); - } - Directory.SetCurrentDirectory(oldCWD); - } - } - - [UnixOnlyFact] - public void RelativePathMaybeAdjustFilePathWithBaseDirectory() - { - // /first/second/file.txt - string filePath = ObjectModelHelpers.CreateFileInTempProjectDirectory("first/second/file.txt", String.Empty); - string oldCWD = Directory.GetCurrentDirectory(); - - try - { - // /first - string firstDirectory = Path.GetDirectoryName(Path.GetDirectoryName(filePath)); - string tmpDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(filePath))); - - Directory.SetCurrentDirectory(tmpDirectory); - - // We are in and second is not under that, so this won't convert - Assert.Equal("second\\file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt")); - - // .. but if we have baseDirectory:firstDirectory, then it will - Assert.Equal("second/file.txt", FileUtilities.MaybeAdjustFilePath("second\\file.txt", firstDirectory)); - } - finally - { - if (filePath != null) - { - File.Delete(filePath); - } - Directory.SetCurrentDirectory(oldCWD); - } - } - - private static string SystemSpecificAbsolutePath => FileUtilities.ExecutingAssemblyPath; - - - [Fact] - public void GetFolderAboveTest() - { - string root = NativeMethodsShared.IsWindows ? @"c:\" : "/"; - string path = Path.Combine(root, "1", "2", "3", "4", "5"); - - Assert.Equal(Path.Combine(root, "1", "2", "3", "4", "5"), FileUtilities.GetFolderAbove(path, 0)); - Assert.Equal(Path.Combine(root, "1", "2", "3", "4"), FileUtilities.GetFolderAbove(path)); - Assert.Equal(Path.Combine(root, "1", "2", "3"), FileUtilities.GetFolderAbove(path, 2)); - Assert.Equal(Path.Combine(root, "1", "2"), FileUtilities.GetFolderAbove(path, 3)); - Assert.Equal(Path.Combine(root, "1"), FileUtilities.GetFolderAbove(path, 4)); - Assert.Equal(root, FileUtilities.GetFolderAbove(path, 5)); - Assert.Equal(root, FileUtilities.GetFolderAbove(path, 99)); - - Assert.Equal(root, FileUtilities.GetFolderAbove(root, 99)); - } - - [Fact] - public void CombinePathsTest() - { - // These tests run in .NET 4+, so we can cheat - var root = @"c:\"; - - Assert.Equal( - Path.Combine(root, "path1"), - FileUtilities.CombinePaths(root, "path1")); - - Assert.Equal( - Path.Combine(root, "path1", "path2", "file.txt"), - FileUtilities.CombinePaths(root, "path1", "path2", "file.txt")); - } - - [Theory] - [InlineData(@"c:\a\.\b", true)] - [InlineData(@"c:\a\..\b", true)] - [InlineData(@"c:\a\..", true)] - [InlineData(@"c:\a\.", true)] - [InlineData(@".\a", true)] - [InlineData(@"..\b", true)] - [InlineData(@"..", true)] - [InlineData(@".", true)] - [InlineData(@"..\", true)] - [InlineData(@".\", true)] - [InlineData(@"\..", true)] - [InlineData(@"\.", true)] - [InlineData(@"..\..\a", true)] - [InlineData(@"..\..\..\a", true)] - [InlineData(@"b..\", false)] - [InlineData(@"b.\", false)] - [InlineData(@"\b..", false)] - [InlineData(@"\b.", false)] - [InlineData(@"\b..\", false)] - [InlineData(@"\b.\", false)] - [InlineData(@"...", false)] - [InlineData(@"....", false)] - public void ContainsRelativeSegmentsTest(string path, bool expectedResult) - { - FileUtilities.ContainsRelativePathSegments(path).ShouldBe(expectedResult); - } - - [Theory] - [InlineData("a/b/c/d", 0, "")] - [InlineData("a/b/c/d", 1, "d")] - [InlineData("a/b/c/d", 2, "c/d")] - [InlineData("a/b/c/d", 3, "b/c/d")] - [InlineData("a/b/c/d", 4, "a/b/c/d")] - [InlineData("a/b/c/d", 5, "a/b/c/d")] - [InlineData(@"a\/\/\//b/\/\/\//c//\/\/\/d/\//\/\/", 2, "c/d")] - public static void TestTruncatePathToTrailingSegments(string path, int trailingSegments, string expectedTruncatedPath) - { - expectedTruncatedPath = expectedTruncatedPath.Replace('/', Path.DirectorySeparatorChar); - - FileUtilities.TruncatePathToTrailingSegments(path, trailingSegments).ShouldBe(expectedTruncatedPath); - } - - /// - /// Exercises FileUtilities.EnsureSingleQuotes - /// - [Theory] - [InlineData(null, null)] // Null test - [InlineData("", "")] // Empty string test - [InlineData(@" ", @"' '")] // One character which is a space - [InlineData(@"'", @"'''")] // One character which is a single quote - [InlineData(@"""", @"'""'")] // One character which is a double quote - [InlineData(@"example", @"'example'")] // Unquoted string - [InlineData(@"'example'", @"'example'")] // Single quoted string - [InlineData(@"""example""", @"'example'")] // Double quoted string - [InlineData(@"'example""", @"''example""'")] // Mixed Quotes - Leading Single - [InlineData(@"""example'", @"'""example''")] // Mixed Quotes - Leading Double - [InlineData(@"ex""am'ple", @"'ex""am'ple'")] // Interior Quotes - public void EnsureSingleQuotesTest(string path, string expectedResult) - { - FileUtilities.EnsureSingleQuotes(path).ShouldBe(expectedResult); - } - - /// - /// Exercises FileUtilities.EnsureDoubleQuotes - /// - [Theory] - [InlineData(null, null)] // Null test - [InlineData("", "")] // Empty string test - [InlineData(@" ", @""" """)] // One character which is a space - [InlineData(@"'", @"""'""")] // One character which is a single quote - [InlineData(@"""", @"""""""")] // One character which is a double quote - [InlineData(@"example", @"""example""")] // Unquoted string - [InlineData(@"'example'", @"""example""")] // Single quoted string - [InlineData(@"""example""", @"""example""")] // Double quoted string - [InlineData(@"'example""", @"""'example""""")] // Mixed Quotes - Leading Single - [InlineData(@"""example'", @"""""example'""")] // Mixed Quotes - Leading Double - [InlineData(@"ex""am'ple", @"""ex""am'ple""")] // Interior Quotes - public void EnsureDoubleQuotesTest(string path, string expectedResult) - { - FileUtilities.EnsureDoubleQuotes(path).ShouldBe(expectedResult); - } - } -} diff --git a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs index fd5e9a82ed8..579229649ce 100644 --- a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs +++ b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Shared/UnitTests/TypeLoader_Dependencies_Tests.cs b/src/Shared/UnitTests/TypeLoader_Dependencies_Tests.cs index 191642f2806..5f21b94fbe8 100644 --- a/src/Shared/UnitTests/TypeLoader_Dependencies_Tests.cs +++ b/src/Shared/UnitTests/TypeLoader_Dependencies_Tests.cs @@ -3,6 +3,7 @@ using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.Shared; using Shouldly; diff --git a/src/Shared/UnitTests/TypeLoader_Tests.cs b/src/Shared/UnitTests/TypeLoader_Tests.cs index 764f5c4104a..048ba6ceebd 100644 --- a/src/Shared/UnitTests/TypeLoader_Tests.cs +++ b/src/Shared/UnitTests/TypeLoader_Tests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.Shared; using Shouldly; diff --git a/src/Shared/XMakeAttributes.cs b/src/Shared/XMakeAttributes.cs index 47e15477b90..fbe39e96b32 100644 --- a/src/Shared/XMakeAttributes.cs +++ b/src/Shared/XMakeAttributes.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -#if !CLR2COMPATIBILITY using System.Runtime.InteropServices; -#endif #nullable disable @@ -420,7 +418,6 @@ internal static bool TryMergeArchitectureValues(string architectureA, string arc /// internal static string GetCurrentMSBuildArchitecture() { -#if !CLR2COMPATIBILITY string currentArchitecture = string.Empty; switch (RuntimeInformation.ProcessArchitecture) { @@ -440,9 +437,7 @@ internal static string GetCurrentMSBuildArchitecture() currentArchitecture = (IntPtr.Size == sizeof(Int64)) ? MSBuildArchitectureValues.x64 : MSBuildArchitectureValues.x86; break; } -#else - string currentArchitecture = (IntPtr.Size == sizeof(Int64)) ? MSBuildArchitectureValues.x64 : MSBuildArchitectureValues.x86; -#endif + return currentArchitecture; } diff --git a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs index 5607a413d21..641a8164086 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Runtime.Versioning; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; diff --git a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs index 99df3fbf783..f40dd8d215d 100644 --- a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs +++ b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; diff --git a/src/Tasks.UnitTests/CodeTaskFactoryTests.cs b/src/Tasks.UnitTests/CodeTaskFactoryTests.cs index 3b9b1740d64..2ab31ff10f8 100644 --- a/src/Tasks.UnitTests/CodeTaskFactoryTests.cs +++ b/src/Tasks.UnitTests/CodeTaskFactoryTests.cs @@ -1206,15 +1206,15 @@ public void BuildTaskSimpleCodeFactoryTempDirectoryDoesntExist() { // Ensure we're getting the right temp path (%TMP% == GetTempPath()) Assert.Equal( - FrameworkFileUtilities.EnsureTrailingSlash(Path.GetTempPath()), - FrameworkFileUtilities.EnsureTrailingSlash(Path.GetFullPath(oldTempPath))); + FileUtilities.EnsureTrailingSlash(Path.GetTempPath()), + FileUtilities.EnsureTrailingSlash(Path.GetFullPath(oldTempPath))); Assert.False(Directory.Exists(newTempPath)); Environment.SetEnvironmentVariable("TMP", newTempPath); Assert.Equal( - FrameworkFileUtilities.EnsureTrailingSlash(newTempPath), - FrameworkFileUtilities.EnsureTrailingSlash(Path.GetTempPath())); + FileUtilities.EnsureTrailingSlash(newTempPath), + FileUtilities.EnsureTrailingSlash(Path.GetTempPath())); MockLogger mockLogger = Helpers.BuildProjectWithNewOMExpectSuccess(projectFileContents); mockLogger.AssertLogContains("Hello, World!"); diff --git a/src/Tasks.UnitTests/Copy_Tests.cs b/src/Tasks.UnitTests/Copy_Tests.cs index 63062fcf3a5..4cafeba9313 100644 --- a/src/Tasks.UnitTests/Copy_Tests.cs +++ b/src/Tasks.UnitTests/Copy_Tests.cs @@ -2508,8 +2508,8 @@ public void SuccessAfterOneRetryContinueToNextFile(bool isUseHardLinks, bool isU // Copy calls to different destinations can come in any order when running in parallel. // Use .OriginalValue to compare against the original input path (before Path.GetFullPath resolution). // TaskItem normalizes paths via FileUtilities.FixFilePath, so we need to do the same for comparison. - Assert.Contains(copyFunctor.FilesCopiedSuccessfully, f => f.Path.OriginalValue == FrameworkFileUtilities.FixFilePath("c:\\source")); - Assert.Contains(copyFunctor.FilesCopiedSuccessfully, f => f.Path.OriginalValue == FrameworkFileUtilities.FixFilePath("c:\\source2")); + Assert.Contains(copyFunctor.FilesCopiedSuccessfully, f => f.Path.OriginalValue == FileUtilities.FixFilePath("c:\\source")); + Assert.Contains(copyFunctor.FilesCopiedSuccessfully, f => f.Path.OriginalValue == FileUtilities.FixFilePath("c:\\source2")); } /// @@ -3162,7 +3162,7 @@ internal CopyFunctor(int countOfSuccess, bool throwOnFailure) public void CopyToFileWithSameCaseInsensitiveNameAsExistingDirectoryOnUnix() { // Skip this test on case-insensitive file systems (Windows, macOS with default APFS/HFS+) - if (!FileUtilities.GetIsFileSystemCaseSensitive()) + if (!FileUtilities.IsFileSystemCaseSensitive) { return; } diff --git a/src/Tasks.UnitTests/CreateCSharpManifestResourceName_Tests.cs b/src/Tasks.UnitTests/CreateCSharpManifestResourceName_Tests.cs index c0b37c2f639..a29f5b938fc 100644 --- a/src/Tasks.UnitTests/CreateCSharpManifestResourceName_Tests.cs +++ b/src/Tasks.UnitTests/CreateCSharpManifestResourceName_Tests.cs @@ -326,7 +326,7 @@ public void CulturedBitmapWithRootNamespace() binaryStream: null, log: null); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"fr\RootNamespace.SubFolder.SplashScreen.bmp"), result); + Assert.Equal(FileUtilities.FixFilePath(@"fr\RootNamespace.SubFolder.SplashScreen.bmp"), result); } /// diff --git a/src/Tasks.UnitTests/CreateVisualBasicManifestResourceName_Tests.cs b/src/Tasks.UnitTests/CreateVisualBasicManifestResourceName_Tests.cs index d7ce809ecfa..567eea82ab6 100644 --- a/src/Tasks.UnitTests/CreateVisualBasicManifestResourceName_Tests.cs +++ b/src/Tasks.UnitTests/CreateVisualBasicManifestResourceName_Tests.cs @@ -217,7 +217,7 @@ public void RootnamespaceWithCulture() { string result = CreateVisualBasicManifestResourceName.CreateManifestNameImpl( - fileName: FrameworkFileUtilities.FixFilePath(@"SubFolder\MyForm.en-GB.ResX"), + fileName: FileUtilities.FixFilePath(@"SubFolder\MyForm.en-GB.ResX"), linkFileName: null, // Link file name prependCultureAsDirectory: @@ -283,7 +283,7 @@ public void BitmapWithRootNamespace() { string result = CreateVisualBasicManifestResourceName.CreateManifestNameImpl( - fileName: FrameworkFileUtilities.FixFilePath(@"SubFolder\SplashScreen.bmp"), + fileName: FileUtilities.FixFilePath(@"SubFolder\SplashScreen.bmp"), linkFileName: null, // Link file name prependCultureAsDirectory: true, rootNamespace: "RootNamespace", // Root namespace @@ -303,7 +303,7 @@ public void CulturedBitmapWithRootNamespace() { string result = CreateVisualBasicManifestResourceName.CreateManifestNameImpl( - fileName: FrameworkFileUtilities.FixFilePath(@"SubFolder\SplashScreen.fr.bmp"), + fileName: FileUtilities.FixFilePath(@"SubFolder\SplashScreen.fr.bmp"), linkFileName: null, // Link file name prependCultureAsDirectory: true, rootNamespace: "RootNamespace", // Root namespace @@ -312,7 +312,7 @@ public void CulturedBitmapWithRootNamespace() binaryStream: null, log: null); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"fr\RootNamespace.SplashScreen.bmp"), result); + Assert.Equal(FileUtilities.FixFilePath(@"fr\RootNamespace.SplashScreen.bmp"), result); } /// @@ -323,7 +323,7 @@ public void CulturedBitmapWithRootNamespaceNoDirectoryPrefix() { string result = CreateVisualBasicManifestResourceName.CreateManifestNameImpl( - fileName: FrameworkFileUtilities.FixFilePath(@"SubFolder\SplashScreen.fr.bmp"), + fileName: FileUtilities.FixFilePath(@"SubFolder\SplashScreen.fr.bmp"), linkFileName: null, // Link file name prependCultureAsDirectory: false, rootNamespace: "RootNamespace", // Root namespace @@ -614,7 +614,7 @@ public void CulturedResourcesFileWithRootNamespaceWithinSubfolder() { string result = CreateVisualBasicManifestResourceName.CreateManifestNameImpl( - fileName: FrameworkFileUtilities.FixFilePath(@"SubFolder\MyResource.fr.resources"), + fileName: FileUtilities.FixFilePath(@"SubFolder\MyResource.fr.resources"), linkFileName: null, // Link file name prependCultureAsDirectory: false, rootNamespace: "RootNamespace", // Root namespace diff --git a/src/Tasks.UnitTests/FindAppConfigFile_Tests.cs b/src/Tasks.UnitTests/FindAppConfigFile_Tests.cs index 7f6a1a10600..0965bb7c461 100644 --- a/src/Tasks.UnitTests/FindAppConfigFile_Tests.cs +++ b/src/Tasks.UnitTests/FindAppConfigFile_Tests.cs @@ -48,7 +48,7 @@ public void FoundInSecondBelowProjectDirectory() f.SecondaryList = new ITaskItem[] { new TaskItem("foo\\app.config"), new TaskItem("xxx") }; f.TargetPath = "targetpath"; Assert.True(f.Execute()); - Assert.Equal(FrameworkFileUtilities.FixFilePath("foo\\app.config"), f.AppConfigFile.ItemSpec); + Assert.Equal(FileUtilities.FixFilePath("foo\\app.config"), f.AppConfigFile.ItemSpec); Assert.Equal("targetpath", f.AppConfigFile.GetMetadata("TargetPath")); } @@ -74,7 +74,7 @@ public void MatchFileNameOnlyWithAnInvalidPath() f.TargetPath = "targetpath"; Assert.True(f.Execute()); // Should ignore the invalid paths - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\\app.config"), f.AppConfigFile.ItemSpec); + Assert.Equal(FileUtilities.FixFilePath(@"foo\\app.config"), f.AppConfigFile.ItemSpec); } // For historical reasons, we should return the last one in the list diff --git a/src/Tasks.UnitTests/FindInList_Tests.cs b/src/Tasks.UnitTests/FindInList_Tests.cs index 6cdece8d848..c051964fdfa 100644 --- a/src/Tasks.UnitTests/FindInList_Tests.cs +++ b/src/Tasks.UnitTests/FindInList_Tests.cs @@ -117,7 +117,7 @@ public void MatchFileNameOnly() f.MatchFileNameOnly = true; f.List = new ITaskItem[] { new TaskItem(@"c:\foo\a.cs"), new TaskItem("b.cs") }; Assert.True(f.Execute()); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"c:\foo\a.cs"), f.ItemFound.ItemSpec); + Assert.Equal(FileUtilities.FixFilePath(@"c:\foo\a.cs"), f.ItemFound.ItemSpec); } [Fact] @@ -132,7 +132,7 @@ public void MatchFileNameOnlyWithAnInvalidPath() Assert.True(f.Execute()); Console.WriteLine(e.Log); // Should ignore the invalid paths - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"foo\a.cs"), f.ItemFound.ItemSpec); + Assert.Equal(FileUtilities.FixFilePath(@"foo\a.cs"), f.ItemFound.ItemSpec); } } } diff --git a/src/Tasks.UnitTests/FindUnderPath_Tests.cs b/src/Tasks.UnitTests/FindUnderPath_Tests.cs index 0f9197579ab..d8b98b63ce5 100644 --- a/src/Tasks.UnitTests/FindUnderPath_Tests.cs +++ b/src/Tasks.UnitTests/FindUnderPath_Tests.cs @@ -33,8 +33,8 @@ public void BasicFilter() Assert.True(success); Assert.Single(t.InPath); Assert.Single(t.OutOfPath); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"C:\MyProject\File1.txt"), t.InPath[0].ItemSpec); - Assert.Equal(FrameworkFileUtilities.FixFilePath(@"C:\SomeoneElsesProject\File2.txt"), t.OutOfPath[0].ItemSpec); + Assert.Equal(FileUtilities.FixFilePath(@"C:\MyProject\File1.txt"), t.InPath[0].ItemSpec); + Assert.Equal(FileUtilities.FixFilePath(@"C:\SomeoneElsesProject\File2.txt"), t.OutOfPath[0].ItemSpec); } /// diff --git a/src/Tasks.UnitTests/GetReferencePaths_Tests.cs b/src/Tasks.UnitTests/GetReferencePaths_Tests.cs index 056f0080963..f4352c0322a 100644 --- a/src/Tasks.UnitTests/GetReferencePaths_Tests.cs +++ b/src/Tasks.UnitTests/GetReferencePaths_Tests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; diff --git a/src/Tasks.UnitTests/HintPathResolver_Tests.cs b/src/Tasks.UnitTests/HintPathResolver_Tests.cs index 9548e32f421..7849b653fdb 100644 --- a/src/Tasks.UnitTests/HintPathResolver_Tests.cs +++ b/src/Tasks.UnitTests/HintPathResolver_Tests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Shouldly; diff --git a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj index 0ae65d5be14..b5d9c668d35 100644 --- a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj +++ b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj @@ -44,7 +44,6 @@ - diff --git a/src/Tasks.UnitTests/OutputPathTests.cs b/src/Tasks.UnitTests/OutputPathTests.cs index b1b897c20dc..ea7cfbb1f33 100644 --- a/src/Tasks.UnitTests/OutputPathTests.cs +++ b/src/Tasks.UnitTests/OutputPathTests.cs @@ -5,6 +5,7 @@ using System.IO; using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; diff --git a/src/Tasks.UnitTests/ResolveCodeAnalysisRuleSet_Tests.cs b/src/Tasks.UnitTests/ResolveCodeAnalysisRuleSet_Tests.cs index a8eb139f408..c4635039b1b 100644 --- a/src/Tasks.UnitTests/ResolveCodeAnalysisRuleSet_Tests.cs +++ b/src/Tasks.UnitTests/ResolveCodeAnalysisRuleSet_Tests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Xunit; diff --git a/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs b/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs index b676ff9817c..acd43836a7f 100644 --- a/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs +++ b/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Reflection; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Tasks.ResourceHandling; diff --git a/src/Tasks/AppConfig/AppConfig.cs b/src/Tasks/AppConfig/AppConfig.cs index 498f2a23d10..47790663d04 100644 --- a/src/Tasks/AppConfig/AppConfig.cs +++ b/src/Tasks/AppConfig/AppConfig.cs @@ -4,8 +4,7 @@ using System; using System.IO; using System.Xml; - -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; #nullable disable diff --git a/src/Tasks/AppConfig/BindingRedirect.cs b/src/Tasks/AppConfig/BindingRedirect.cs index d29808b9b59..6691b543a16 100644 --- a/src/Tasks/AppConfig/BindingRedirect.cs +++ b/src/Tasks/AppConfig/BindingRedirect.cs @@ -3,6 +3,7 @@ using System; using System.Xml; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs b/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs index ded0c8b46bf..45f0d257c12 100644 --- a/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs +++ b/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssemblyDependency/AssemblyInformation.cs b/src/Tasks/AssemblyDependency/AssemblyInformation.cs index 2a3c9c6aef3..5ecfd2e4705 100644 --- a/src/Tasks/AssemblyDependency/AssemblyInformation.cs +++ b/src/Tasks/AssemblyDependency/AssemblyInformation.cs @@ -16,8 +16,10 @@ using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; #if FEATURE_ASSEMBLYLOADCONTEXT -using System.Reflection.PortableExecutable; using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +#else +using Microsoft.Build.Framework; #endif using Microsoft.Build.Tasks.AssemblyDependency; diff --git a/src/Tasks/AssemblyDependency/CandidateAssemblyFilesResolver.cs b/src/Tasks/AssemblyDependency/CandidateAssemblyFilesResolver.cs index 328bb6984a2..99bda929a54 100644 --- a/src/Tasks/AssemblyDependency/CandidateAssemblyFilesResolver.cs +++ b/src/Tasks/AssemblyDependency/CandidateAssemblyFilesResolver.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssemblyDependency/HintPathResolver.cs b/src/Tasks/AssemblyDependency/HintPathResolver.cs index 96a3b920491..08c6d97ef52 100644 --- a/src/Tasks/AssemblyDependency/HintPathResolver.cs +++ b/src/Tasks/AssemblyDependency/HintPathResolver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index 1c68ec4d840..37de75c0dec 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -870,7 +870,7 @@ private static AssemblyNameExtension GetAssemblyNameFromItemMetadata(ITaskItem i if (string.IsNullOrEmpty(name)) { // Fall back to inferring assembly name from file name. - name = item.GetMetadata(FileUtilities.ItemSpecModifiers.Filename); + name = item.GetMetadata(ItemSpecModifiers.Filename); } return new AssemblyNameExtension($"{name}, Version={version}, Culture=neutral, PublicKeyToken={publicKeyToken}"); @@ -1720,7 +1720,7 @@ private bool FindAssociatedFiles() { // We don't look for associated files for FX assemblies. bool hasFrameworkPath = false; - string referenceDirectoryName = FrameworkFileUtilities.EnsureTrailingSlash(reference.DirectoryName); + string referenceDirectoryName = FileUtilities.EnsureTrailingSlash(reference.DirectoryName); foreach (string frameworkPath in _frameworkPaths) { @@ -2768,7 +2768,7 @@ private ITaskItem SetItemMetadata(List relatedItems, List // Set up the satellites. foreach (string satelliteFile in satellites) { - relatedItemBase.SetMetadata(ItemMetadataNames.destinationSubDirectory, FrameworkFileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(satelliteFile))); + relatedItemBase.SetMetadata(ItemMetadataNames.destinationSubDirectory, FileUtilities.EnsureTrailingSlash(Path.GetDirectoryName(satelliteFile))); AddRelatedItem(satelliteItems, relatedItemBase, Path.Combine(reference.DirectoryName, satelliteFile)); } } diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 9c685aa4bbb..30d999a4ec4 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -2246,7 +2246,7 @@ internal bool Execute( { for (int i = 0; i < _targetFrameworkDirectories.Length; i++) { - _targetFrameworkDirectories[i] = FrameworkFileUtilities.EnsureTrailingSlash(_targetFrameworkDirectories[i]); + _targetFrameworkDirectories[i] = FileUtilities.EnsureTrailingSlash(_targetFrameworkDirectories[i]); } } diff --git a/src/Tasks/AssemblyDependency/Resolver.cs b/src/Tasks/AssemblyDependency/Resolver.cs index 5cebad377ce..62a455c983d 100644 --- a/src/Tasks/AssemblyDependency/Resolver.cs +++ b/src/Tasks/AssemblyDependency/Resolver.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssignLinkMetadata.cs b/src/Tasks/AssignLinkMetadata.cs index d880dc094c4..e49208769ef 100644 --- a/src/Tasks/AssignLinkMetadata.cs +++ b/src/Tasks/AssignLinkMetadata.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; #nullable disable @@ -45,9 +44,9 @@ public override bool Execute() { try { - string definingProject = item.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath); - string definingProjectDirectory = item.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectDirectory); - string fullPath = item.GetMetadata(FileUtilities.ItemSpecModifiers.FullPath); + string definingProject = item.GetMetadata(ItemSpecModifiers.DefiningProjectFullPath); + string definingProjectDirectory = item.GetMetadata(ItemSpecModifiers.DefiningProjectDirectory); + string fullPath = item.GetMetadata(ItemSpecModifiers.FullPath); if ( String.IsNullOrEmpty(item.GetMetadata("Link")) diff --git a/src/Tasks/AssignTargetPath.cs b/src/Tasks/AssignTargetPath.cs index f017a02fefe..f8f42c9945d 100644 --- a/src/Tasks/AssignTargetPath.cs +++ b/src/Tasks/AssignTargetPath.cs @@ -67,10 +67,10 @@ public override bool Execute() // Ensure trailing slash otherwise c:\bin appears to match part of c:\bin2\foo // Also ensure that relative segments in the path are resolved. fullRootPath = - TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.EnsureTrailingSlash(RootFolder)).GetCanonicalForm(); + TaskEnvironment.GetAbsolutePath(FileUtilities.EnsureTrailingSlash(RootFolder)).GetCanonicalForm(); // Ensure trailing slash for comparison. Current directory is already canonical, so we don't need to call GetCanonicalForm on it. - AbsolutePath currentDirectory = FrameworkFileUtilities.EnsureTrailingSlash(TaskEnvironment.ProjectDirectory); + AbsolutePath currentDirectory = FileUtilities.EnsureTrailingSlash(TaskEnvironment.ProjectDirectory); // Check if the root folder is the same as the current directory - AbsolutePath handles OS-aware case sensitivity. isRootFolderSameAsCurrentDirectory = fullRootPath == currentDirectory; @@ -82,10 +82,10 @@ public override bool Execute() // Ensure trailing slash otherwise c:\bin appears to match part of c:\bin2\foo // Also ensure that relative segments in the path are resolved and throw on illegal characters in Path.GetFullPath to preserve pre-existing behavior. fullRootPathString = - Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.EnsureTrailingSlash(RootFolder))); + Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FileUtilities.EnsureTrailingSlash(RootFolder))); // Ensure trailing slash for comparison. Current directory is already canonical, so we don't need to call GetCanonicalForm on it. - AbsolutePath currentDirectory = FrameworkFileUtilities.EnsureTrailingSlash(TaskEnvironment.ProjectDirectory); + AbsolutePath currentDirectory = FileUtilities.EnsureTrailingSlash(TaskEnvironment.ProjectDirectory); // Check if the root folder is the same as the current directory. // Perform a case-insensitive comparison to match Path.GetFullPath behavior on Windows, even on case-sensitive file systems, diff --git a/src/Tasks/BootstrapperUtil/ResourceUpdater.cs b/src/Tasks/BootstrapperUtil/ResourceUpdater.cs index afee6d8a725..99b9c5ad0db 100644 --- a/src/Tasks/BootstrapperUtil/ResourceUpdater.cs +++ b/src/Tasks/BootstrapperUtil/ResourceUpdater.cs @@ -7,8 +7,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using Microsoft.Build.Shared; - +using Microsoft.Build.Framework; namespace Microsoft.Build.Tasks.Deployment.Bootstrapper { diff --git a/src/Tasks/Copy.cs b/src/Tasks/Copy.cs index 853fe698800..36d2a3d20f9 100644 --- a/src/Tasks/Copy.cs +++ b/src/Tasks/Copy.cs @@ -847,7 +847,7 @@ private bool InitializeDestinationFiles() foreach (ITaskItem sourceFolder in SourceFolders) { ErrorUtilities.VerifyThrowArgumentLength(sourceFolder.ItemSpec); - AbsolutePath src = FrameworkFileUtilities.NormalizePath(TaskEnvironment.GetAbsolutePath(sourceFolder.ItemSpec)); + AbsolutePath src = FileUtilities.NormalizePath(TaskEnvironment.GetAbsolutePath(sourceFolder.ItemSpec)); string srcName = Path.GetFileName(src); (string[] filesInFolder, _, _, string globFailure) = FileMatcher.Default.GetFiles(src, "**"); diff --git a/src/Tasks/CreateCSharpManifestResourceName.cs b/src/Tasks/CreateCSharpManifestResourceName.cs index 85a5b1b1ec2..c7f838b16ef 100644 --- a/src/Tasks/CreateCSharpManifestResourceName.cs +++ b/src/Tasks/CreateCSharpManifestResourceName.cs @@ -97,13 +97,13 @@ internal static string CreateManifestNameImpl( bool enableCustomCulture = false) { // Use the link file name if there is one, otherwise, fall back to file name. - string embeddedFileName = FrameworkFileUtilities.FixFilePath(linkFileName); + string embeddedFileName = FileUtilities.FixFilePath(linkFileName); if (string.IsNullOrEmpty(embeddedFileName)) { - embeddedFileName = FrameworkFileUtilities.FixFilePath(fileName); + embeddedFileName = FileUtilities.FixFilePath(fileName); } - dependentUponFileName = FrameworkFileUtilities.FixFilePath(dependentUponFileName); + dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; if (!string.IsNullOrEmpty(culture) && enableCustomCulture) diff --git a/src/Tasks/CreateItem.cs b/src/Tasks/CreateItem.cs index 56cb7b1c021..cfb61b0919f 100644 --- a/src/Tasks/CreateItem.cs +++ b/src/Tasks/CreateItem.cs @@ -117,7 +117,7 @@ private List CreateOutputItems(Dictionary metadataTab // 2. If there is no existing metadata then apply the new if ((!PreserveExistingMetadata) || String.IsNullOrEmpty(newItem.GetMetadata(nameAndValue.Key))) { - if (FileUtilities.ItemSpecModifiers.IsItemSpecModifier(nameAndValue.Key)) + if (ItemSpecModifiers.IsItemSpecModifier(nameAndValue.Key)) { // Explicitly setting built-in metadata, is not allowed. Log.LogErrorWithCodeFromResources("CreateItem.AdditionalMetadataError", nameAndValue.Key); @@ -194,7 +194,7 @@ private List CreateOutputItems(Dictionary metadataTab { if (!string.IsNullOrEmpty(match.wildcardDirectoryPart)) { - newItem.SetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir, match.wildcardDirectoryPart); + newItem.SetMetadata(ItemSpecModifiers.RecursiveDir, match.wildcardDirectoryPart); } } diff --git a/src/Tasks/CreateVisualBasicManifestResourceName.cs b/src/Tasks/CreateVisualBasicManifestResourceName.cs index 24c59241ee0..d2cf7f405ef 100644 --- a/src/Tasks/CreateVisualBasicManifestResourceName.cs +++ b/src/Tasks/CreateVisualBasicManifestResourceName.cs @@ -102,7 +102,7 @@ internal static string CreateManifestNameImpl( embeddedFileName = fileName; } - dependentUponFileName = FrameworkFileUtilities.FixFilePath(dependentUponFileName); + dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; if (!string.IsNullOrEmpty(culture) && enableCustomCulture) diff --git a/src/Tasks/DependencyFile.cs b/src/Tasks/DependencyFile.cs index 0e591aa7f97..a69f6938c05 100644 --- a/src/Tasks/DependencyFile.cs +++ b/src/Tasks/DependencyFile.cs @@ -4,7 +4,6 @@ using System; using System.IO; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; #nullable disable @@ -61,7 +60,7 @@ internal bool Exists /// The file name. internal DependencyFile(string filename) { - this.filename = FrameworkFileUtilities.FixFilePath(filename); + this.filename = FileUtilities.FixFilePath(filename); if (FileSystems.Default.FileExists(FileName)) { diff --git a/src/Tasks/FileIO/WriteLinesToFile.cs b/src/Tasks/FileIO/WriteLinesToFile.cs index 560264ad909..82c6517eb93 100644 --- a/src/Tasks/FileIO/WriteLinesToFile.cs +++ b/src/Tasks/FileIO/WriteLinesToFile.cs @@ -72,7 +72,7 @@ public override bool Execute() } ErrorUtilities.VerifyThrowArgumentLength(File.ItemSpec); - AbsolutePath filePath = FrameworkFileUtilities.NormalizePath(TaskEnvironment.GetAbsolutePath(File.ItemSpec)); + AbsolutePath filePath = FileUtilities.NormalizePath(TaskEnvironment.GetAbsolutePath(File.ItemSpec)); string contentsAsString = string.Empty; if (Lines != null && Lines.Length > 0) diff --git a/src/Tasks/FindInList.cs b/src/Tasks/FindInList.cs index 493990ca1a9..84538f53b6e 100644 --- a/src/Tasks/FindInList.cs +++ b/src/Tasks/FindInList.cs @@ -105,7 +105,7 @@ private bool IsMatchingItem(StringComparison comparison, ITaskItem item) { try { - var path = FrameworkFileUtilities.FixFilePath(item.ItemSpec); + var path = FileUtilities.FixFilePath(item.ItemSpec); string filename = (MatchFileNameOnly ? Path.GetFileName(path) : path); if (String.Equals(filename, ItemSpecToFind, comparison)) diff --git a/src/Tasks/GetFrameworkSDKPath.cs b/src/Tasks/GetFrameworkSDKPath.cs index 0161001d0e8..15e327abb92 100644 --- a/src/Tasks/GetFrameworkSDKPath.cs +++ b/src/Tasks/GetFrameworkSDKPath.cs @@ -63,7 +63,7 @@ public string Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -110,7 +110,7 @@ public string FrameworkSdkVersion20Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -153,7 +153,7 @@ public string FrameworkSdkVersion35Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -196,7 +196,7 @@ public string FrameworkSdkVersion40Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -239,7 +239,7 @@ public string FrameworkSdkVersion45Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -282,7 +282,7 @@ public string FrameworkSdkVersion451Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -325,7 +325,7 @@ public string FrameworkSdkVersion46Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } @@ -368,7 +368,7 @@ public string FrameworkSdkVersion461Path } else { - path = FrameworkFileUtilities.EnsureTrailingSlash(path); + path = FileUtilities.EnsureTrailingSlash(path); Log.LogMessageFromResources(MessageImportance.Low, "GetFrameworkSdkPath.FoundSDK", path); } diff --git a/src/Tasks/GetReferenceAssemblyPaths.cs b/src/Tasks/GetReferenceAssemblyPaths.cs index 50e3f54d4c4..81b02cdf5a0 100644 --- a/src/Tasks/GetReferenceAssemblyPaths.cs +++ b/src/Tasks/GetReferenceAssemblyPaths.cs @@ -4,10 +4,11 @@ using System; using System.Collections.Generic; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using FrameworkNameVersioning = System.Runtime.Versioning.FrameworkName; + #if FEATURE_GAC +using Microsoft.Build.Shared; using SystemProcessorArchitecture = System.Reflection.ProcessorArchitecture; #endif diff --git a/src/Tasks/GetSDKReferenceFiles.cs b/src/Tasks/GetSDKReferenceFiles.cs index 6099546f9af..d1786ad7f1f 100644 --- a/src/Tasks/GetSDKReferenceFiles.cs +++ b/src/Tasks/GetSDKReferenceFiles.cs @@ -567,7 +567,7 @@ private void GenerateOutputItems() /// private void GatherReferenceAssemblies(HashSet resolvedFiles, ITaskItem sdkReference, string path, SDKInfo info) { - if (info.DirectoryToFileList != null && info.DirectoryToFileList.TryGetValue(FrameworkFileUtilities.EnsureNoTrailingSlash(path), out List referenceFiles) && referenceFiles != null) + if (info.DirectoryToFileList != null && info.DirectoryToFileList.TryGetValue(FileUtilities.EnsureNoTrailingSlash(path), out List referenceFiles) && referenceFiles != null) { foreach (string file in referenceFiles) { @@ -619,7 +619,7 @@ private void GatherRedistFiles(HashSet resolvedRedistFiles, foreach (KeyValuePair> directoryToFileList in info.DirectoryToFileList) { // Add a trailing slash to ensure we don't match the start of a platform (e.g. ...\ARM matching ...\ARM64) - if (FrameworkFileUtilities.EnsureTrailingSlash(directoryToFileList.Key).StartsWith(FrameworkFileUtilities.EnsureTrailingSlash(redistFilePath), StringComparison.OrdinalIgnoreCase)) + if (FileUtilities.EnsureTrailingSlash(directoryToFileList.Key).StartsWith(FileUtilities.EnsureTrailingSlash(redistFilePath), StringComparison.OrdinalIgnoreCase)) { List redistFiles = directoryToFileList.Value; string targetPathRoot = sdkReference.GetMetadata("CopyRedistToSubDirectory"); diff --git a/src/Tasks/ListOperators/FindUnderPath.cs b/src/Tasks/ListOperators/FindUnderPath.cs index e7370e40a39..46d280dd332 100644 --- a/src/Tasks/ListOperators/FindUnderPath.cs +++ b/src/Tasks/ListOperators/FindUnderPath.cs @@ -67,16 +67,16 @@ public override bool Execute() { conePath = Strings.WeakIntern( - TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(Path.ItemSpec)).GetCanonicalForm()); + TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(Path.ItemSpec)).GetCanonicalForm()); } else { conePath = Strings.WeakIntern( - System.IO.Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(Path.ItemSpec)))); + System.IO.Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(Path.ItemSpec)))); } - conePath = FrameworkFileUtilities.EnsureTrailingSlash(conePath); + conePath = FileUtilities.EnsureTrailingSlash(conePath); } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) { @@ -98,13 +98,13 @@ public override bool Execute() { fullPath = Strings.WeakIntern( - TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(item.ItemSpec)).GetCanonicalForm()); + TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(item.ItemSpec)).GetCanonicalForm()); } else { fullPath = Strings.WeakIntern( - System.IO.Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(item.ItemSpec)))); + System.IO.Path.GetFullPath(TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(item.ItemSpec)))); } } catch (Exception e) when (ExceptionHandling.IsIoRelatedException(e)) diff --git a/src/Tasks/MSBuild.cs b/src/Tasks/MSBuild.cs index d3c24bd0f1d..df5ca14e163 100644 --- a/src/Tasks/MSBuild.cs +++ b/src/Tasks/MSBuild.cs @@ -654,7 +654,7 @@ internal static bool ExecuteTargets( // Set a metadata on the output items called "MSBuildProjectFile" which tells you which project file produced this item. if (String.IsNullOrEmpty(outputItemFromTarget.GetMetadata(ItemMetadataNames.msbuildSourceProjectFile))) { - outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceProjectFile, projects[i].GetMetadata(FileUtilities.ItemSpecModifiers.FullPath)); + outputItemFromTarget.SetMetadata(ItemMetadataNames.msbuildSourceProjectFile, projects[i].GetMetadata(ItemSpecModifiers.FullPath)); } } diff --git a/src/Tasks/MakeDir.cs b/src/Tasks/MakeDir.cs index bf0c67ffa95..b16b810d5b2 100644 --- a/src/Tasks/MakeDir.cs +++ b/src/Tasks/MakeDir.cs @@ -63,7 +63,7 @@ public override bool Execute() // For speed, eliminate duplicates caused by poor targets authoring, don't absolutize yet to save allocation if (!directoriesSet.Contains(directory.ItemSpec)) { - absolutePath = TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(directory.ItemSpec)); + absolutePath = TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(directory.ItemSpec)); // Only log a message if we actually need to create the folder if (!FileUtilities.DirectoryExistsNoThrow(absolutePath)) { diff --git a/src/Tasks/ManifestUtil/LauncherBuilder.cs b/src/Tasks/ManifestUtil/LauncherBuilder.cs index c65068ddc1f..e441c548957 100644 --- a/src/Tasks/ManifestUtil/LauncherBuilder.cs +++ b/src/Tasks/ManifestUtil/LauncherBuilder.cs @@ -3,7 +3,7 @@ using System; using System.IO; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.Deployment.Bootstrapper; diff --git a/src/Tasks/ManifestUtil/ManifestWriter.cs b/src/Tasks/ManifestUtil/ManifestWriter.cs index 71d3a1478b6..13dd871912b 100644 --- a/src/Tasks/ManifestUtil/ManifestWriter.cs +++ b/src/Tasks/ManifestUtil/ManifestWriter.cs @@ -6,7 +6,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Xml.Serialization; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; #nullable disable diff --git a/src/Tasks/ManifestUtil/Util.cs b/src/Tasks/ManifestUtil/Util.cs index a2d89b437c7..22d48fab01c 100644 --- a/src/Tasks/ManifestUtil/Util.cs +++ b/src/Tasks/ManifestUtil/Util.cs @@ -15,7 +15,6 @@ using System.Security.Cryptography; using System.Text; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Win32; diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index d00e4b81005..9699f49dec9 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -1,6 +1,5 @@ - + - @@ -112,29 +111,16 @@ TaskLoggingHelperExtension.cs - MetadataConversionUtilities.cs StreamMappedString.cs - - ExceptionHandling.cs - - - FileUtilities.cs - StringUtils.cs - - EscapingUtilities.cs - - - Modifiers.cs - @@ -147,7 +133,6 @@ - @@ -667,7 +652,6 @@ - @@ -710,7 +694,6 @@ - diff --git a/src/Tasks/Move.cs b/src/Tasks/Move.cs index a957107e942..5e0a13b3742 100644 --- a/src/Tasks/Move.cs +++ b/src/Tasks/Move.cs @@ -6,7 +6,6 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; diff --git a/src/Tasks/RedistList.cs b/src/Tasks/RedistList.cs index d89440d05aa..91fc81d0921 100644 --- a/src/Tasks/RedistList.cs +++ b/src/Tasks/RedistList.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Text; using System.Xml; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; diff --git a/src/Tasks/ResGenDependencies.cs b/src/Tasks/ResGenDependencies.cs index 4c329b8824c..8e3811d00da 100644 --- a/src/Tasks/ResGenDependencies.cs +++ b/src/Tasks/ResGenDependencies.cs @@ -8,14 +8,13 @@ #if FEATURE_RESXREADER_LIVEDESERIALIZATION using System.Collections; using System.Resources; -using Microsoft.Build.Shared; #endif using System.Xml; using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.ResourceHandling; using Microsoft.Build.Utilities; -using Microsoft.Build.Framework; #nullable disable @@ -371,7 +370,7 @@ internal bool AllOutputFilesAreUpToDate() Debug.Assert(outputFiles != null, "OutputFiles hasn't been set"); foreach (string outputFileName in outputFiles) { - var outputFile = new FileInfo(FrameworkFileUtilities.FixFilePath(outputFileName)); + var outputFile = new FileInfo(FileUtilities.FixFilePath(outputFileName)); if (!outputFile.Exists || outputFile.LastWriteTime < LastModified) { return false; diff --git a/src/Tasks/ResolveKeySource.cs b/src/Tasks/ResolveKeySource.cs index 366f0badba0..23f33794861 100644 --- a/src/Tasks/ResolveKeySource.cs +++ b/src/Tasks/ResolveKeySource.cs @@ -10,7 +10,6 @@ using System.Globalization; using System.Security.Cryptography; using Microsoft.Runtime.Hosting; -using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; #endif diff --git a/src/Tasks/ResolveSDKReference.cs b/src/Tasks/ResolveSDKReference.cs index 7b759a91a79..9d2c288a93e 100644 --- a/src/Tasks/ResolveSDKReference.cs +++ b/src/Tasks/ResolveSDKReference.cs @@ -916,7 +916,7 @@ public void Resolve(Dictionary sdks, string targetConfigurati _prefer32BitFromProject = prefer32Bit; // There must be a trailing slash or else the ExpandSDKReferenceAssemblies will not work. - ResolvedPath = FrameworkFileUtilities.EnsureTrailingSlash(sdk.ItemSpec); + ResolvedPath = FileUtilities.EnsureTrailingSlash(sdk.ItemSpec); System.Version.TryParse(sdk.GetMetadata(SDKPlatformVersion), out Version targetPlatformVersionFromItem); diff --git a/src/Tasks/ResourceHandling/MSBuildResXReader.cs b/src/Tasks/ResourceHandling/MSBuildResXReader.cs index 9de27e4c4b7..b4166560bfe 100644 --- a/src/Tasks/ResourceHandling/MSBuildResXReader.cs +++ b/src/Tasks/ResourceHandling/MSBuildResXReader.cs @@ -229,7 +229,7 @@ private static void AddLinkedResource(string resxFilename, bool pathsRelativeToB { string[] fileRefInfo = ParseResxFileRefString(value); - string fileName = FrameworkFileUtilities.FixFilePath(fileRefInfo[0]); + string fileName = FileUtilities.FixFilePath(fileRefInfo[0]); string fileRefType = fileRefInfo[1]; if (pathsRelativeToBasePath) diff --git a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs index 64f18fab383..84de01be4a8 100644 --- a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs +++ b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs @@ -9,7 +9,6 @@ using Microsoft.Build.Utilities; #if RUNTIME_TYPE_NETCORE using System.Runtime.InteropServices; -using Microsoft.Build.Shared; using Constants = Microsoft.Build.Framework.Constants; #endif diff --git a/src/Tasks/StateFileBase.cs b/src/Tasks/StateFileBase.cs index e44fbf5f2bf..01e2a102f06 100644 --- a/src/Tasks/StateFileBase.cs +++ b/src/Tasks/StateFileBase.cs @@ -4,7 +4,7 @@ using System; using System.IO; using Microsoft.Build.BackEnd; -using Microsoft.Build.Shared; +using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; diff --git a/src/Tasks/TlbReference.cs b/src/Tasks/TlbReference.cs index 30acc7f8e38..eed290e1bb0 100644 --- a/src/Tasks/TlbReference.cs +++ b/src/Tasks/TlbReference.cs @@ -11,7 +11,6 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; // TYPELIBATTR clashes with the one in InteropServices. using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; diff --git a/src/Tasks/Touch.cs b/src/Tasks/Touch.cs index 7e1e68f175c..3ca82c7c951 100644 --- a/src/Tasks/Touch.cs +++ b/src/Tasks/Touch.cs @@ -98,7 +98,7 @@ internal bool ExecuteImpl( AbsolutePath path; try { - path = TaskEnvironment.GetAbsolutePath(FrameworkFileUtilities.FixFilePath(file.ItemSpec)); + path = TaskEnvironment.GetAbsolutePath(FileUtilities.FixFilePath(file.ItemSpec)); } catch (ArgumentException ex) { diff --git a/src/Tasks/Unzip.cs b/src/Tasks/Unzip.cs index b45d66de1a4..fd262b1007c 100644 --- a/src/Tasks/Unzip.cs +++ b/src/Tasks/Unzip.cs @@ -176,7 +176,7 @@ public override bool Execute() /// The to extract files to. private void Extract(ZipArchive sourceArchive, DirectoryInfo destinationDirectory) { - string fullDestinationDirectoryPath = Path.GetFullPath(FrameworkFileUtilities.EnsureTrailingSlash(destinationDirectory.FullName)); + string fullDestinationDirectoryPath = Path.GetFullPath(FileUtilities.EnsureTrailingSlash(destinationDirectory.FullName)); foreach (ZipArchiveEntry zipArchiveEntry in sourceArchive.Entries.TakeWhile(i => !_cancellationToken.IsCancellationRequested)) { diff --git a/src/Tasks/XamlTaskFactory/TaskParser.cs b/src/Tasks/XamlTaskFactory/TaskParser.cs index ff723700f11..1c0f3165ec7 100644 --- a/src/Tasks/XamlTaskFactory/TaskParser.cs +++ b/src/Tasks/XamlTaskFactory/TaskParser.cs @@ -7,6 +7,7 @@ using System.IO; using System.Text; using System.Xaml; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using XamlTypes = Microsoft.Build.Framework.XamlTypes; diff --git a/src/UnitTests.Shared/RequiresSymbolicLinksFactAttribute.cs b/src/UnitTests.Shared/RequiresSymbolicLinksFactAttribute.cs index 8f98ab38e0a..f824ac9d495 100644 --- a/src/UnitTests.Shared/RequiresSymbolicLinksFactAttribute.cs +++ b/src/UnitTests.Shared/RequiresSymbolicLinksFactAttribute.cs @@ -3,7 +3,7 @@ using System; using System.IO; - +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Utilities.UnitTests/CommandLineBuilder_Tests.cs b/src/Utilities.UnitTests/CommandLineBuilder_Tests.cs index 7f07523a3dc..ae1686a23ca 100644 --- a/src/Utilities.UnitTests/CommandLineBuilder_Tests.cs +++ b/src/Utilities.UnitTests/CommandLineBuilder_Tests.cs @@ -108,11 +108,11 @@ public void AppendLiteralSwitchWithSpacesInParameter() public void AppendTwoStringsEnsureNoSpace() { CommandLineBuilder c = new CommandLineBuilder(); - c.AppendFileNamesIfNotNull(new[] { "Form1.resx", FrameworkFileUtilities.FixFilePath("built\\Form1.resources") }, ","); + c.AppendFileNamesIfNotNull(new[] { "Form1.resx", FileUtilities.FixFilePath("built\\Form1.resources") }, ","); // There shouldn't be a space before or after the comma // Tools like resgen require comma-delimited lists to be bumped up next to each other. - c.ShouldBe(FrameworkFileUtilities.FixFilePath(@"Form1.resx,built\Form1.resources")); + c.ShouldBe(FileUtilities.FixFilePath(@"Form1.resx,built\Form1.resources")); } /* diff --git a/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj b/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj index 971c9183beb..9f5dc1cf08d 100644 --- a/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj +++ b/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj @@ -23,7 +23,6 @@ - diff --git a/src/Utilities.UnitTests/PlatformManifest_Tests.cs b/src/Utilities.UnitTests/PlatformManifest_Tests.cs index 2a0cdb3360d..2b313ef8249 100644 --- a/src/Utilities.UnitTests/PlatformManifest_Tests.cs +++ b/src/Utilities.UnitTests/PlatformManifest_Tests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using Shouldly; diff --git a/src/Utilities.UnitTests/TaskItem_Tests.cs b/src/Utilities.UnitTests/TaskItem_Tests.cs index a7db155e2ce..6bdb86eccf7 100644 --- a/src/Utilities.UnitTests/TaskItem_Tests.cs +++ b/src/Utilities.UnitTests/TaskItem_Tests.cs @@ -41,9 +41,9 @@ public void ConstructWithITaskItem() to.GetMetadata("Cat").ShouldBe(""); // manipulate the item-spec a bit - to.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("Monkey"); - to.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".txt"); - to.GetMetadata(FileUtilities.ItemSpecModifiers.RelativeDir).ShouldBe(string.Empty); + to.GetMetadata(ItemSpecModifiers.Filename).ShouldBe("Monkey"); + to.GetMetadata(ItemSpecModifiers.Extension).ShouldBe(".txt"); + to.GetMetadata(ItemSpecModifiers.RelativeDir).ShouldBe(string.Empty); } // Make sure metadata can be cloned from an existing ITaskItem @@ -88,14 +88,14 @@ public void MetadataNamesAndCount() TaskItem taskItem = new TaskItem("x"); // Without custom metadata, should return the built in metadata - taskItem.MetadataNames.Cast().ShouldBeSetEquivalentTo(FileUtilities.ItemSpecModifiers.All); - taskItem.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length); + taskItem.MetadataNames.Cast().ShouldBeSetEquivalentTo(ItemSpecModifiers.All); + taskItem.MetadataCount.ShouldBe(ItemSpecModifiers.All.Length); // Now add one taskItem.SetMetadata("m", "m1"); - taskItem.MetadataNames.Cast().ShouldBeSetEquivalentTo(FileUtilities.ItemSpecModifiers.All.Concat(new[] { "m" })); - taskItem.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length + 1); + taskItem.MetadataNames.Cast().ShouldBeSetEquivalentTo(ItemSpecModifiers.All.Concat(new[] { "m" })); + taskItem.MetadataCount.ShouldBe(ItemSpecModifiers.All.Length + 1); } [Fact] @@ -113,15 +113,15 @@ public void NullITaskItemCast() public void ConstructFromDictionary() { Hashtable h = new Hashtable(); - h[FileUtilities.ItemSpecModifiers.Filename] = "foo"; - h[FileUtilities.ItemSpecModifiers.Extension] = "bar"; + h[ItemSpecModifiers.Filename] = "foo"; + h[ItemSpecModifiers.Extension] = "bar"; h["custom"] = "hello"; TaskItem t = new TaskItem("bamboo.baz", h); // item-spec modifiers were not overridden by dictionary passed to constructor - t.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("bamboo"); - t.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".baz"); + t.GetMetadata(ItemSpecModifiers.Filename).ShouldBe("bamboo"); + t.GetMetadata(ItemSpecModifiers.Extension).ShouldBe(".baz"); t.GetMetadata("CUSTOM").ShouldBe("hello"); } @@ -134,7 +134,7 @@ public void CannotChangeModifiers() try { - t.SetMetadata(FileUtilities.ItemSpecModifiers.FullPath, "bazbaz"); + t.SetMetadata(ItemSpecModifiers.FullPath, "bazbaz"); } catch (Exception e) { @@ -154,7 +154,7 @@ public void CannotRemoveModifiers() try { - t.RemoveMetadata(FileUtilities.ItemSpecModifiers.RootDir); + t.RemoveMetadata(ItemSpecModifiers.RootDir); } catch (Exception e) { @@ -169,11 +169,11 @@ public void CheckMetadataCount() { TaskItem t = new TaskItem("foo"); - t.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length); + t.MetadataCount.ShouldBe(ItemSpecModifiers.All.Length); t.SetMetadata("grog", "RUM"); - t.MetadataCount.ShouldBe(FileUtilities.ItemSpecModifiers.All.Length + 1); + t.MetadataCount.ShouldBe(ItemSpecModifiers.All.Length + 1); } [Fact] @@ -181,7 +181,7 @@ public void NonexistentRequestFullPath() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.FullPath).ShouldBe( + from.GetMetadata(ItemSpecModifiers.FullPath).ShouldBe( Path.Combine( Directory.GetCurrentDirectory(), "Monkey.txt")); @@ -192,7 +192,7 @@ public void NonexistentRequestRootDir() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.RootDir).ShouldBe(Path.GetPathRoot(from.GetMetadata(FileUtilities.ItemSpecModifiers.FullPath))); + from.GetMetadata(ItemSpecModifiers.RootDir).ShouldBe(Path.GetPathRoot(from.GetMetadata(ItemSpecModifiers.FullPath))); } [Fact] @@ -200,7 +200,7 @@ public void NonexistentRequestFilename() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.Filename).ShouldBe("Monkey"); + from.GetMetadata(ItemSpecModifiers.Filename).ShouldBe("Monkey"); } [Fact] @@ -208,7 +208,7 @@ public void NonexistentRequestExtension() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.Extension).ShouldBe(".txt"); + from.GetMetadata(ItemSpecModifiers.Extension).ShouldBe(".txt"); } [Fact] @@ -216,7 +216,7 @@ public void NonexistentRequestRelativeDir() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.RelativeDir).Length.ShouldBe(0); + from.GetMetadata(ItemSpecModifiers.RelativeDir).Length.ShouldBe(0); } [Fact] @@ -224,7 +224,7 @@ public void NonexistentRequestDirectory() { TaskItem from = new TaskItem(); from.ItemSpec = NativeMethodsShared.IsWindows ? @"c:\subdir\Monkey.txt" : "/subdir/Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.Directory).ShouldBe(NativeMethodsShared.IsWindows ? @"subdir\" : "subdir/"); + from.GetMetadata(ItemSpecModifiers.Directory).ShouldBe(NativeMethodsShared.IsWindows ? @"subdir\" : "subdir/"); } [WindowsOnlyFact("UNC is not implemented except under Windows.")] @@ -232,7 +232,7 @@ public void NonexistentRequestDirectoryUNC() { TaskItem from = new TaskItem(); from.ItemSpec = @"\\local\share\subdir\Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.Directory).ShouldBe(@"subdir\"); + from.GetMetadata(ItemSpecModifiers.Directory).ShouldBe(@"subdir\"); } [Fact] @@ -241,7 +241,7 @@ public void NonexistentRequestRecursiveDir() TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.RecursiveDir).Length.ShouldBe(0); + from.GetMetadata(ItemSpecModifiers.RecursiveDir).Length.ShouldBe(0); } [Fact] @@ -249,7 +249,7 @@ public void NonexistentRequestIdentity() { TaskItem from = new TaskItem(); from.ItemSpec = "Monkey.txt"; - from.GetMetadata(FileUtilities.ItemSpecModifiers.Identity).ShouldBe("Monkey.txt"); + from.GetMetadata(ItemSpecModifiers.Identity).ShouldBe("Monkey.txt"); } [Fact] @@ -258,19 +258,19 @@ public void RequestTimeStamps() TaskItem from = new TaskItem(); from.ItemSpec = FileUtilities.GetTemporaryFile(); - from.GetMetadata(FileUtilities.ItemSpecModifiers.ModifiedTime).Length.ShouldBeGreaterThan(0); + from.GetMetadata(ItemSpecModifiers.ModifiedTime).Length.ShouldBeGreaterThan(0); - from.GetMetadata(FileUtilities.ItemSpecModifiers.CreatedTime).Length.ShouldBeGreaterThan(0); + from.GetMetadata(ItemSpecModifiers.CreatedTime).Length.ShouldBeGreaterThan(0); - from.GetMetadata(FileUtilities.ItemSpecModifiers.AccessedTime).Length.ShouldBeGreaterThan(0); + from.GetMetadata(ItemSpecModifiers.AccessedTime).Length.ShouldBeGreaterThan(0); File.Delete(from.ItemSpec); - from.GetMetadata(FileUtilities.ItemSpecModifiers.ModifiedTime).Length.ShouldBe(0); + from.GetMetadata(ItemSpecModifiers.ModifiedTime).Length.ShouldBe(0); - from.GetMetadata(FileUtilities.ItemSpecModifiers.CreatedTime).Length.ShouldBe(0); + from.GetMetadata(ItemSpecModifiers.CreatedTime).Length.ShouldBe(0); - from.GetMetadata(FileUtilities.ItemSpecModifiers.AccessedTime).Length.ShouldBe(0); + from.GetMetadata(ItemSpecModifiers.AccessedTime).Length.ShouldBe(0); } /// diff --git a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs index a1b959497c8..d18d9a48ff6 100644 --- a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs +++ b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs @@ -2198,7 +2198,7 @@ public void GetPathToStandardLibraries64Bit40() } string pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v4.0", string.Empty, "x86"); - string dotNet40Path = FrameworkFileUtilities.EnsureNoTrailingSlash(referencePaths[0]); + string dotNet40Path = FileUtilities.EnsureNoTrailingSlash(referencePaths[0]); pathToFramework.ShouldBe(dotNet40Path, StringCompareShould.IgnoreCase); pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v4.0", string.Empty, "x64"); @@ -2281,7 +2281,7 @@ public void GetPathToStandardLibraries32Bit40() } string pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v4.0", string.Empty, "x86"); - string dotNet40Path = FrameworkFileUtilities.EnsureNoTrailingSlash(referencePaths[0]); + string dotNet40Path = FileUtilities.EnsureNoTrailingSlash(referencePaths[0]); pathToFramework.ShouldBe(dotNet40Path, StringCompareShould.IgnoreCase); pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v4.0", string.Empty, "x64"); diff --git a/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs b/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs index f5cdd7f1e79..6eae1c71a1a 100644 --- a/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs +++ b/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs @@ -900,12 +900,12 @@ public void FileTrackerFileIsExcludedFromDependencies() // The short path to temp string tempShortPath = NativeMethodsShared.IsUnixLike ? tempPath - : FrameworkFileUtilities.EnsureTrailingSlash( + : FileUtilities.EnsureTrailingSlash( NativeMethodsShared.GetShortFilePath(tempPath).ToUpperInvariant()); // The long path to temp string tempLongPath = NativeMethodsShared.IsUnixLike ? tempPath - : FrameworkFileUtilities.EnsureTrailingSlash( + : FileUtilities.EnsureTrailingSlash( NativeMethodsShared.GetLongFilePath(tempPath).ToUpperInvariant()); // We don't want to be including these as dependencies or outputs: diff --git a/src/Utilities/CommandLineBuilder.cs b/src/Utilities/CommandLineBuilder.cs index 481dc35c62e..07ac12544e9 100644 --- a/src/Utilities/CommandLineBuilder.cs +++ b/src/Utilities/CommandLineBuilder.cs @@ -336,7 +336,7 @@ protected void AppendFileNameWithQuoting(string fileName) // their own quotes. Quotes are illegal. VerifyThrowNoEmbeddedDoubleQuotes(string.Empty, fileName); - fileName = FrameworkFileUtilities.FixFilePath(fileName); + fileName = FileUtilities.FixFilePath(fileName); if (fileName.Length != 0 && fileName[0] == '-') { AppendTextWithQuoting("." + Path.DirectorySeparatorChar + fileName); diff --git a/src/Utilities/Microsoft.Build.Utilities.csproj b/src/Utilities/Microsoft.Build.Utilities.csproj index c287f5b9f6a..91325b42c49 100644 --- a/src/Utilities/Microsoft.Build.Utilities.csproj +++ b/src/Utilities/Microsoft.Build.Utilities.csproj @@ -1,6 +1,5 @@ - + - @@ -20,7 +19,6 @@ - @@ -33,7 +31,6 @@ - @@ -72,33 +69,18 @@ Shared\ErrorUtilities.cs - - Shared\EscapingUtilities.cs - Shared\EventArgsFormatting.cs - - Shared\ExceptionHandling.cs - - - Shared\FileUtilities.cs - Shared\FileMatcher.cs - - Shared\FileUtilitiesRegex.cs - Shared\FrameworkLocationHelper.cs Shared\IKeyed.cs - - Shared\Modifiers.cs - Shared\InprocTrackingNativeMethods.cs @@ -123,9 +105,6 @@ Shared\TaskLoggingHelper.cs - - Shared\TempFileUtilities.cs - Shared\Tracing.cs diff --git a/src/Utilities/PlatformManifest.cs b/src/Utilities/PlatformManifest.cs index fec3fedbfca..c5e8f29cb84 100644 --- a/src/Utilities/PlatformManifest.cs +++ b/src/Utilities/PlatformManifest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Xml; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; diff --git a/src/Utilities/SDKManifest.cs b/src/Utilities/SDKManifest.cs index d8c06a2c234..c6aa44563a7 100644 --- a/src/Utilities/SDKManifest.cs +++ b/src/Utilities/SDKManifest.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Xml; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; diff --git a/src/Utilities/TargetPlatformSDK.cs b/src/Utilities/TargetPlatformSDK.cs index ea7b3408cd7..3aa41ed671e 100644 --- a/src/Utilities/TargetPlatformSDK.cs +++ b/src/Utilities/TargetPlatformSDK.cs @@ -105,7 +105,7 @@ public Version MinOSVersion public string Path { get => _path; - set => _path = value != null ? FrameworkFileUtilities.EnsureTrailingSlash(value) : null; + set => _path = value != null ? FileUtilities.EnsureTrailingSlash(value) : null; } /// diff --git a/src/Utilities/TaskItem.cs b/src/Utilities/TaskItem.cs index 2fa8a3789a1..2cfb2f9d97b 100644 --- a/src/Utilities/TaskItem.cs +++ b/src/Utilities/TaskItem.cs @@ -103,7 +103,7 @@ public TaskItem( { ErrorUtilities.VerifyThrowArgumentNull(itemSpec); - _itemSpec = treatAsFilePath ? FrameworkFileUtilities.FixFilePath(itemSpec) : itemSpec; + _itemSpec = treatAsFilePath ? FileUtilities.FixFilePath(itemSpec) : itemSpec; } /// @@ -130,7 +130,7 @@ public TaskItem( { // don't import metadata whose names clash with the names of reserved metadata string key = (string)singleMetadata.Key; - if (!FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(key)) + if (!ItemSpecModifiers.IsDerivableItemSpecModifier(key)) { builder[key] = (string)singleMetadata.Value ?? string.Empty; } @@ -153,12 +153,12 @@ public TaskItem( if (!(sourceItem is ITaskItem2 sourceItemAsITaskItem2)) { _itemSpec = EscapingUtilities.Escape(sourceItem.ItemSpec); - _definingProject = EscapingUtilities.EscapeWithCaching(sourceItem.GetMetadata(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath)); + _definingProject = EscapingUtilities.EscapeWithCaching(sourceItem.GetMetadata(ItemSpecModifiers.DefiningProjectFullPath)); } else { _itemSpec = sourceItemAsITaskItem2.EvaluatedIncludeEscaped; - _definingProject = sourceItemAsITaskItem2.GetMetadataValueEscaped(FileUtilities.ItemSpecModifiers.DefiningProjectFullPath); + _definingProject = sourceItemAsITaskItem2.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath); } sourceItem.CopyMetadataTo(this); @@ -185,7 +185,7 @@ public string ItemSpec { ErrorUtilities.VerifyThrowArgumentNull(value, nameof(ItemSpec)); - _itemSpec = FrameworkFileUtilities.FixFilePath(value); + _itemSpec = FileUtilities.FixFilePath(value); _fullPath = null; } } @@ -204,7 +204,7 @@ string ITaskItem2.EvaluatedIncludeEscaped set { - _itemSpec = FrameworkFileUtilities.FixFilePath(value); + _itemSpec = FileUtilities.FixFilePath(value); _fullPath = null; } } @@ -217,7 +217,7 @@ public ICollection MetadataNames { get { - int count = (_metadata?.Count ?? 0) + FileUtilities.ItemSpecModifiers.All.Length; + int count = (_metadata?.Count ?? 0) + ItemSpecModifiers.All.Length; var metadataNames = new List(capacity: count); @@ -226,7 +226,7 @@ public ICollection MetadataNames metadataNames.AddRange(_metadata.Keys); } - metadataNames.AddRange(FileUtilities.ItemSpecModifiers.All); + metadataNames.AddRange(ItemSpecModifiers.All); return metadataNames; } @@ -236,7 +236,7 @@ public ICollection MetadataNames /// Gets the number of metadata set on the item. /// /// Count of metadata. - public int MetadataCount => (_metadata?.Count ?? 0) + FileUtilities.ItemSpecModifiers.All.Length; + public int MetadataCount => (_metadata?.Count ?? 0) + ItemSpecModifiers.All.Length; /// /// Gets the backing metadata dictionary in a serializable wrapper. @@ -271,7 +271,7 @@ private SerializableMetadata Metadata public void RemoveMetadata(string metadataName) { ErrorUtilities.VerifyThrowArgumentNull(metadataName); - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(metadataName), + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); _metadata = _metadata?.Remove(metadataName); @@ -307,7 +307,7 @@ public void SetMetadata( // Non-derivable metadata can only be set at construction time. // That's why this is IsItemSpecModifier and not IsDerivableItemSpecModifier. - ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName), + ErrorUtilities.VerifyThrowArgument(!ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName), "Shared.CannotChangeItemSpecModifiers", metadataName); _metadata ??= ImmutableDictionaryExtensions.EmptyMetadata; @@ -504,11 +504,11 @@ string ITaskItem2.GetMetadataValueEscaped(string metadataName) string metadataValue = null; - if (FileUtilities.ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) + if (ItemSpecModifiers.IsDerivableItemSpecModifier(metadataName)) { // FileUtilities.GetItemSpecModifier is expecting escaped data, which we assume we already are. // Passing in a null for currentDirectory indicates we are already in the correct current directory - metadataValue = FileUtilities.ItemSpecModifiers.GetItemSpecModifier(null, _itemSpec, _definingProject, metadataName, ref _fullPath); + metadataValue = ItemSpecModifiers.GetItemSpecModifier(null, _itemSpec, _definingProject, metadataName, ref _fullPath); } else { diff --git a/src/Utilities/ToolLocationHelper.cs b/src/Utilities/ToolLocationHelper.cs index 9e7ebbf82cd..d84f3954661 100644 --- a/src/Utilities/ToolLocationHelper.cs +++ b/src/Utilities/ToolLocationHelper.cs @@ -664,7 +664,7 @@ public static IList GetSDKReferenceFolders(string sdkRoot, string target string legacyWindowsMetadataLocation = Path.Combine(sdkRoot, "Windows Metadata"); if (FileUtilities.DirectoryExistsNoThrow(legacyWindowsMetadataLocation)) { - legacyWindowsMetadataLocation = FrameworkFileUtilities.EnsureTrailingSlash(legacyWindowsMetadataLocation); + legacyWindowsMetadataLocation = FileUtilities.EnsureTrailingSlash(legacyWindowsMetadataLocation); referenceDirectories.Add(legacyWindowsMetadataLocation); } @@ -1767,7 +1767,7 @@ public static string GetPathToStandardLibraries(string targetFrameworkIdentifier { // We found the framework reference assembly directory with mscorlib in it // that's our standard lib path, so return it, with no trailing slash. - return FrameworkFileUtilities.EnsureNoTrailingSlash(referenceAssemblyDirectory); + return FileUtilities.EnsureNoTrailingSlash(referenceAssemblyDirectory); } } @@ -1841,7 +1841,7 @@ public static string GetPathToStandardLibraries(string targetFrameworkIdentifier { // We found the framework reference assembly directory with mscorlib in it // that's our standard lib path, so return it, with no trailing slash. - return FrameworkFileUtilities.EnsureNoTrailingSlash(legacyMsCorlib20Path); + return FileUtilities.EnsureNoTrailingSlash(legacyMsCorlib20Path); } // If for some reason the 2.0 framework is not installed in its default location then maybe someone is using the ".net 4.0" reference assembly @@ -1858,7 +1858,7 @@ public static string GetPathToStandardLibraries(string targetFrameworkIdentifier { // We found the framework reference assembly directory with mscorlib in it // that's our standard lib path, so return it, with no trailing slash. - return FrameworkFileUtilities.EnsureNoTrailingSlash(referenceAssemblyDirectory); + return FileUtilities.EnsureNoTrailingSlash(referenceAssemblyDirectory); } } @@ -2427,7 +2427,7 @@ private static void AddSDKPath(string sdkRoot, string contentFolderName, string if (FileUtilities.DirectoryExistsNoThrow(referenceAssemblyPath)) { - referenceAssemblyPath = FrameworkFileUtilities.EnsureTrailingSlash(referenceAssemblyPath); + referenceAssemblyPath = FileUtilities.EnsureTrailingSlash(referenceAssemblyPath); contentDirectories.Add(referenceAssemblyPath); } } @@ -2553,7 +2553,7 @@ internal static void GatherExtensionSDKs(DirectoryInfo extensionSdksDirectory, T string pathToSDKManifest = Path.Combine(sdkVersionDirectory.FullName, "SDKManifest.xml"); if (FileUtilities.FileExistsNoThrow(pathToSDKManifest)) { - targetPlatformSDK.ExtensionSDKs.Add(SDKKey, FrameworkFileUtilities.EnsureTrailingSlash(sdkVersionDirectory.FullName)); + targetPlatformSDK.ExtensionSDKs.Add(SDKKey, FileUtilities.EnsureTrailingSlash(sdkVersionDirectory.FullName)); } else { @@ -2826,7 +2826,7 @@ internal static void GatherSDKsFromRegistryImpl(Dictionary - private static readonly string s_tempPath = FrameworkFileUtilities.EnsureTrailingSlash(Path.GetTempPath()); + private static readonly string s_tempPath = FileUtilities.EnsureTrailingSlash(Path.GetTempPath()); // The short path to temp - private static readonly string s_tempShortPath = FrameworkFileUtilities.EnsureTrailingSlash(NativeMethodsShared.GetShortFilePath(s_tempPath).ToUpperInvariant()); + private static readonly string s_tempShortPath = FileUtilities.EnsureTrailingSlash(NativeMethodsShared.GetShortFilePath(s_tempPath).ToUpperInvariant()); // The long path to temp - private static readonly string s_tempLongPath = FrameworkFileUtilities.EnsureTrailingSlash(NativeMethodsShared.GetLongFilePath(s_tempPath).ToUpperInvariant()); + private static readonly string s_tempLongPath = FileUtilities.EnsureTrailingSlash(NativeMethodsShared.GetLongFilePath(s_tempPath).ToUpperInvariant()); // The path to ApplicationData (is equal to %USERPROFILE%\Application Data folder in Windows XP and %USERPROFILE%\AppData\Roaming in Vista and later) - private static readonly string s_applicationDataPath = FrameworkFileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).ToUpperInvariant()); + private static readonly string s_applicationDataPath = FileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).ToUpperInvariant()); // The path to LocalApplicationData (is equal to %USERPROFILE%\Local Settings\Application Data folder in Windows XP and %USERPROFILE%\AppData\Local in Vista and later). - private static readonly string s_localApplicationDataPath = FrameworkFileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).ToUpperInvariant()); + private static readonly string s_localApplicationDataPath = FileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).ToUpperInvariant()); // The path to the LocalLow folder. In Vista and later, user application data is organized across %USERPROFILE%\AppData\LocalLow, %USERPROFILE%\AppData\Local (%LOCALAPPDATA%) // and %USERPROFILE%\AppData\Roaming (%APPDATA%). The LocalLow folder is not present in XP. - private static readonly string s_localLowApplicationDataPath = FrameworkFileUtilities.EnsureTrailingSlash(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData\\LocalLow").ToUpperInvariant()); + private static readonly string s_localLowApplicationDataPath = FileUtilities.EnsureTrailingSlash(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData\\LocalLow").ToUpperInvariant()); // The path to the common Application Data, which is also used by some programs (e.g. antivirus) that we wish to ignore. // Is equal to C:\Documents and Settings\All Users\Application Data on XP, and C:\ProgramData on Vista+. @@ -127,18 +127,18 @@ private static List InitializeCommonApplicationDataPaths() { List commonApplicationDataPaths = new(); - string defaultCommonApplicationDataPath = FrameworkFileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData).ToUpperInvariant()); + string defaultCommonApplicationDataPath = FileUtilities.EnsureTrailingSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData).ToUpperInvariant()); commonApplicationDataPaths.Add(defaultCommonApplicationDataPath); string defaultRootDirectory = Path.GetPathRoot(defaultCommonApplicationDataPath); - string alternativeCommonApplicationDataPath1 = FrameworkFileUtilities.EnsureTrailingSlash(Path.Combine(defaultRootDirectory, @"Documents and Settings\All Users\Application Data").ToUpperInvariant()); + string alternativeCommonApplicationDataPath1 = FileUtilities.EnsureTrailingSlash(Path.Combine(defaultRootDirectory, @"Documents and Settings\All Users\Application Data").ToUpperInvariant()); if (!alternativeCommonApplicationDataPath1.Equals(defaultCommonApplicationDataPath, StringComparison.Ordinal)) { commonApplicationDataPaths.Add(alternativeCommonApplicationDataPath1); } - string alternativeCommonApplicationDataPath2 = FrameworkFileUtilities.EnsureTrailingSlash(Path.Combine(defaultRootDirectory, @"Users\All Users\Application Data").ToUpperInvariant()); + string alternativeCommonApplicationDataPath2 = FileUtilities.EnsureTrailingSlash(Path.Combine(defaultRootDirectory, @"Users\All Users\Application Data").ToUpperInvariant()); if (!alternativeCommonApplicationDataPath2.Equals(defaultCommonApplicationDataPath, StringComparison.Ordinal)) { @@ -281,7 +281,7 @@ public static bool FileIsUnderPath(string fileName, string path) // Ensure that the path has a trailing slash that we are checking under // By default the paths that we check for most often will have, so this will // return fast and not allocate memory in the process - return FileIsUnderNormalizedPath(fileName, FrameworkFileUtilities.EnsureTrailingSlash(path)); + return FileIsUnderNormalizedPath(fileName, FileUtilities.EnsureTrailingSlash(path)); } internal static bool FileIsUnderNormalizedPath(string fileName, string path) @@ -615,7 +615,7 @@ public static string TrackerResponseFileArguments(string dllName, string interme { intermediateDirectory = FileUtilities.NormalizePath(intermediateDirectory); // If the intermediate directory ends up with a trailing slash, then be rid of it! - if (FrameworkFileUtilities.EndsWithSlash(intermediateDirectory)) + if (FileUtilities.EndsWithSlash(intermediateDirectory)) { intermediateDirectory = Path.GetDirectoryName(intermediateDirectory); } diff --git a/src/Utilities/TrackedDependencies/FlatTrackingData.cs b/src/Utilities/TrackedDependencies/FlatTrackingData.cs index e12cee660ff..633dc4c02ac 100644 --- a/src/Utilities/TrackedDependencies/FlatTrackingData.cs +++ b/src/Utilities/TrackedDependencies/FlatTrackingData.cs @@ -323,7 +323,7 @@ private void InternalConstruct(ITask ownerTask, ITaskItem[] tlogFilesLocal, ITas // our "starts with" comparison doesn't pick up incomplete matches, such as C:\Foo matching C:\FooFile.txt foreach (string excludePath in excludedInputPaths) { - string fullexcludePath = FrameworkFileUtilities.EnsureTrailingSlash(FileUtilities.NormalizePath(excludePath)).ToUpperInvariant(); + string fullexcludePath = FileUtilities.EnsureTrailingSlash(FileUtilities.NormalizePath(excludePath)).ToUpperInvariant(); _excludedInputPaths.Add(fullexcludePath); } }