diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs
index e124863a3ac..647841369fa 100644
--- a/src/Shared/FileUtilities.cs
+++ b/src/Shared/FileUtilities.cs
@@ -413,8 +413,8 @@ internal static string MaybeAdjustFilePath(string value)
return value;
}
- // For Unix-like systems, we may want to convert backslashes to slashes
- string newValue = Regex.Replace(value, @"[\\/]+", "/");
+ // For Unix-like systems, we want to convert backslashes to slashes
+ string newValue = value.ToSlash();
string quote = string.Empty;
// Find the part of the name we want to check, that is remove quotes, if present
diff --git a/src/XMakeBuildEngine/UnitTests/Evaluation/EvaluatorFileNormalization_Tests.cs b/src/XMakeBuildEngine/UnitTests/Evaluation/EvaluatorFileNormalization_Tests.cs
new file mode 100644
index 00000000000..86b0275979b
--- /dev/null
+++ b/src/XMakeBuildEngine/UnitTests/Evaluation/EvaluatorFileNormalization_Tests.cs
@@ -0,0 +1,98 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//-----------------------------------------------------------------------
+//
+// Tests for evaluation
+//-----------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Xml;
+
+using Microsoft.Build.Collections;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Shared;
+using Xunit;
+
+using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException;
+using ProjectHelpers = Microsoft.Build.UnitTests.BackEnd.ProjectHelpers;
+
+namespace Microsoft.Build.UnitTests.Evaluation
+{
+ ///
+ /// Tests mainly for how evaluation normalizes input for cross-platform paths
+ ///
+ public class EvaluatorFileNormalization_Tests : IDisposable
+ {
+ public EvaluatorFileNormalization_Tests()
+ {
+ ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
+ GC.Collect();
+ }
+
+ ///
+ /// Cleanup
+ ///
+ public void Dispose()
+ {
+ ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
+ GC.Collect();
+ }
+
+ ///
+ /// Basic verification -- with TreatAsLocalProperty set, but to a different property than is being passed as a global, the
+ /// global property overrides the local property.
+ ///
+ [Fact]
+ public void MultipleForwardSlashesShouldNotGetCollapsedWhenPathLooksLikeUnixPath()
+ {
+ string content = ObjectModelHelpers.CleanupFileContents(@"
+
+
+ /tmp/a//x\\c;ba://
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ");
+
+
+ IDictionary globalProperties = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ globalProperties.Add("GP", @"/tmp/a//x\\c;ba://");
+
+ Project project = new Project(XmlReader.Create(new StringReader(content)), globalProperties, null);
+
+ MockLogger logger = new MockLogger();
+ bool result = project.Build(logger);
+ Assert.Equal(true, result);
+
+ var expectedString = NativeMethodsShared.IsWindows ? @"/tmp/a//x\\c;ba://" : @"/tmp/a//x//c;ba://";
+
+ logger.AssertLogContains($"GP[{expectedString}]");
+ logger.AssertLogContains($"P[{expectedString}]");
+ logger.AssertLogContains($"I[{expectedString}]");
+ logger.AssertLogContains($"T[{expectedString}]");
+
+ Assert.Equal(expectedString, project.GetPropertyValue("GP"));
+ Assert.Equal(expectedString, project.GetPropertyValue("P"));
+ Assert.Equal(expectedString, string.Join(";", project.Items.Select(i => i.EvaluatedInclude)));
+ }
+ }
+}