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ů.
+
+