From b26323dd6fe3c50b8d55b1b8608a2c1b36630163 Mon Sep 17 00:00:00 2001 From: "Ryland 41491307+ryalanms@users.noreply.github.com" <41491307+ryalanms@users.noreply.github.com> Date: Wed, 17 Jul 2019 13:51:04 -0700 Subject: [PATCH 1/7] Adding a default 'true' for unset properties owned by WPF --- .../targets/Microsoft.NET.Sdk.WindowsDesktop.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.props b/packaging/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.props index 985437424fe..923c2d9bdf3 100644 --- a/packaging/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.props +++ b/packaging/Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.props @@ -1,6 +1,8 @@ <_MicrosoftNetSdkWindowsDesktop>true + true + true From 8a777ad8caff39ce4fe94dadc6117b281b33ab38 Mon Sep 17 00:00:00 2001 From: "Ryland 41491307+ryalanms@users.noreply.github.com" <41491307+ryalanms@users.noreply.github.com> Date: Mon, 30 Sep 2019 13:17:29 -0700 Subject: [PATCH 2/7] Add CoreFX GetRelativePath implementation for .NET Framework code generation --- .../NETFrameworkGetRelativePath.cs | 370 ++++++++++++++++++ .../PresentationBuildTasks.csproj | 2 + 2 files changed, 372 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs new file mode 100644 index 00000000000..23f8f9a8250 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs @@ -0,0 +1,370 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//--------------------------------------------------------------------------- +// +// Description: +// Returns a relative path from one path to another. +// +// Paths are resolved by calling the GetFullPath method before calculating +// the difference. The method uses the default file path comparison for the +// current platform (StringComparison.OrdinalIgnoreCase for Windows.) +// +//--------------------------------------------------------------------------- + +#pragma warning disable 1634, 1691 + +using System; +using System.Xml; +using System.IO; +using System.Text; +using System.Reflection; +using System.Globalization; +using System.ComponentModel; +using System.Security.Cryptography; + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.Design.Serialization; + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +using System.Threading; +using MS.Internal.Markup; +using MS.Internal.Tasks; +using MS.Utility; // for SR +using Microsoft.Build.Utilities; +using Microsoft.Build.Tasks.Windows; +using System.Runtime.CompilerServices; + +namespace MS.Internal +{ + internal sealed class PathInternal + { + internal static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) + { + if (relativeTo == null) + throw new ArgumentNullException(nameof(relativeTo)); + + if (PathInternal.IsEffectivelyEmpty(relativeTo.AsSpan())) + throw new ArgumentException(nameof(relativeTo)); + + if (path == null) + throw new ArgumentNullException(nameof(path)); + + if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) + throw new ArgumentException(nameof(path)); + + Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); + + relativeTo = Path.GetFullPath(relativeTo); + path = Path.GetFullPath(path); + + // Need to check if the roots are different- if they are we need to return the "to" path. + if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType)) + return path; + + int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase); + + // If there is nothing in common they can't share the same root, return the "to" path as is. + if (commonLength == 0) + return path; + + // Trailing separators aren't significant for comparison + int relativeToLength = relativeTo.Length; + if (EndsInDirectorySeparator(relativeTo.AsSpan())) + relativeToLength--; + + bool pathEndsInSeparator = EndsInDirectorySeparator(path.AsSpan()); + int pathLength = path.Length; + if (pathEndsInSeparator) + pathLength--; + + // If we have effectively the same path, return "." + if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; + + // We have the same root, we need to calculate the difference now using the + // common Length and Segment count past the length. + // + // Some examples: + // + // C:\Foo C:\Bar L3, S1 -> ..\Bar + // C:\Foo C:\Foo\Bar L6, S0 -> Bar + // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar + // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar + + StringBuilder sb = new StringBuilder(); + + // Add parent segments for segments past the common on the "from" path + if (commonLength < relativeToLength) + { + sb.Append(".."); + + for (int i = commonLength + 1; i < relativeToLength; i++) + { + if (PathInternal.IsDirectorySeparator(relativeTo[i])) + { + sb.Append(DirectorySeparatorChar); + sb.Append(".."); + } + } + } + else if (PathInternal.IsDirectorySeparator(path[commonLength])) + { + // No parent segments and we need to eat the initial separator + // (C:\Foo C:\Foo\Bar case) + commonLength++; + } + + // Now add the rest of the "to" path, adding back the trailing separator + int differenceLength = pathLength - commonLength; + if (pathEndsInSeparator) + differenceLength++; + + if (differenceLength > 0) + { + if (sb.Length > 0) + { + sb.Append(DirectorySeparatorChar); + } + + sb.Append(path, commonLength, differenceLength); + } + + return sb.ToString(); + } + + /// + /// True if the given character is a directory separator. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsDirectorySeparator(char c) + { + return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; + } + + /// + /// Get the common path length from the start of the string. + /// + internal static int GetCommonPathLength(string first, string second, bool ignoreCase) + { + int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase); + + // If nothing matches + if (commonChars == 0) + return commonChars; + + // Or we're a full string and equal length or match to a separator + if (commonChars == first.Length + && (commonChars == second.Length || IsDirectorySeparator(second[commonChars]))) + return commonChars; + + if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) + return commonChars; + + // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar. + while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) + commonChars--; + + return commonChars; + } + + /// + /// Returns true if the two paths have the same root + /// + internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType) + { + int firstRootLength = GetRootLength(first.AsSpan()); + int secondRootLength = GetRootLength(second.AsSpan()); + + return firstRootLength == secondRootLength + && string.Compare( + strA: first, + indexA: 0, + strB: second, + indexB: 0, + length: firstRootLength, + comparisonType: comparisonType) == 0; + } + + /// + /// Returns true if the path is effectively empty for the current OS. + /// For unix, this is empty or null. For Windows, this is empty, null, or + /// just spaces ((char)32). + /// + internal static bool IsEffectivelyEmpty(ReadOnlySpan path) + { + if (path.IsEmpty) + return true; + + foreach (char c in path) + { + if (c != ' ') + return false; + } + return true; + } + + + /// + /// Gets the length of the root of the path (drive, share, etc.). + /// + internal static int GetRootLength(ReadOnlySpan path) + { + int pathLength = path.Length; + int i = 0; + + bool deviceSyntax = IsDevice(path); + bool deviceUnc = deviceSyntax && IsDeviceUNC(path); + + if ((!deviceSyntax || deviceUnc) && pathLength > 0 && IsDirectorySeparator(path[0])) + { + // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo") + if (deviceUnc || (pathLength > 1 && IsDirectorySeparator(path[1]))) + { + // UNC (\\?\UNC\ or \\), scan past server\share + + // Start past the prefix ("\\" or "\\?\UNC\") + i = deviceUnc ? UncExtendedPrefixLength : UncPrefixLength; + + // Skip two separators at most + int n = 2; + while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) + i++; + } + else + { + // Current drive rooted (e.g. "\foo") + i = 1; + } + } + else if (deviceSyntax) + { + // Device path (e.g. "\\?\.", "\\.\") + // Skip any characters following the prefix that aren't a separator + i = DevicePrefixLength; + while (i < pathLength && !IsDirectorySeparator(path[i])) + i++; + + // If there is another separator take it, as long as we have had at least one + // non-separator after the prefix (e.g. don't take "\\?\\", but take "\\?\a\") + if (i < pathLength && i > DevicePrefixLength && IsDirectorySeparator(path[i])) + i++; + } + else if (pathLength >= 2 + && path[1] == VolumeSeparatorChar + && IsValidDriveChar(path[0])) + { + // Valid drive specified path ("C:", "D:", etc.) + i = 2; + + // If the colon is followed by a directory separator, move past it (e.g "C:\") + if (pathLength > 2 && IsDirectorySeparator(path[2])) + i++; + } + + return i; + } + + /// + /// Gets the count of common characters from the left optionally ignoring case + /// + internal static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) + { + if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0; + + // Current index + int index = 0; + + while (index != first.Length && index != second.Length && + first[index] == second[index] || (ignoreCase && char.ToUpperInvariant(first[index]) == char.ToUpperInvariant(second[index]))) + { + index++; + } + + return index; + } + + /// + /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\") + /// + internal static bool IsDevice(ReadOnlySpan path) + { + // If the path begins with any two separators is will be recognized and normalized and prepped with + // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. + return IsExtended(path) + || + ( + path.Length >= DevicePrefixLength + && IsDirectorySeparator(path[0]) + && IsDirectorySeparator(path[1]) + && (path[2] == '.' || path[2] == '?') + && IsDirectorySeparator(path[3]) + ); + } + + /// + /// Returns true if the path is a device UNC (\\?\UNC\, \\.\UNC\) + /// + internal static bool IsDeviceUNC(ReadOnlySpan path) + { + return path.Length >= UncExtendedPrefixLength + && IsDevice(path) + && IsDirectorySeparator(path[7]) + && path[4] == 'U' + && path[5] == 'N' + && path[6] == 'C'; + } + + /// + /// Returns true if the given character is a valid drive letter + /// + internal static bool IsValidDriveChar(char value) + { + return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z')); + } + + /// + /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the + /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization + /// and path length checks. + /// + internal static bool IsExtended(ReadOnlySpan path) + { + // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. + // Skipping of normalization will *only* occur if back slashes ('\') are used. + return path.Length >= DevicePrefixLength + && path[0] == '\\' + && (path[1] == '\\' || path[1] == '?') + && path[2] == '?' + && path[3] == '\\'; + } + + /// + /// Returns true if the path ends in a directory separator. + /// + public static bool EndsInDirectorySeparator(ReadOnlySpan path) + => path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); + + /// + /// Returns true if the path ends in a directory separator. + /// + public static bool EndsInDirectorySeparator(string path) + => path != null && path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); + + internal const char DirectorySeparatorChar = '\\'; + internal const char AltDirectorySeparatorChar = '/'; + internal const char VolumeSeparatorChar = ':'; + // \\?\UNC\, \\.\UNC\ + internal const int UncExtendedPrefixLength = 8; + // \\?\, \\.\, \??\ + internal const int DevicePrefixLength = 4; + // \\ + internal const int UncPrefixLength = 2; + } +} + diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj index 61dbb1e744d..cf3cd1e6d9d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj @@ -277,6 +277,7 @@ + @@ -314,6 +315,7 @@ Provide specific/old versions for PresentationBuildTasks which needs to run inside MSBuild --> + From 900b82d2665fa65da8484405ddafcbe9dfa3f8bf Mon Sep 17 00:00:00 2001 From: "Ryland 41491307+ryalanms@users.noreply.github.com" <41491307+ryalanms@users.noreply.github.com> Date: Mon, 30 Sep 2019 14:06:37 -0700 Subject: [PATCH 3/7] Use more robust relative path calculation in markup code generation --- .../Internal/MarkupCompiler/MarkupCompiler.cs | 17 ++++--------- .../NETFrameworkGetRelativePath.cs | 24 +++++++++++++------ .../PresentationBuildTasks.csproj | 1 + 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs index 76a0aa297a8..9a47bb3102a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs @@ -1568,19 +1568,12 @@ private string ParentFolderPrefix { get { - string parentFolderPrefix = string.Empty; - if (TargetPath.StartsWith(SourceFileInfo.SourcePath, StringComparison.OrdinalIgnoreCase)) - { - string relPath = TargetPath.Substring(SourceFileInfo.SourcePath.Length); - relPath += SourceFileInfo.RelativeSourceFilePath; - string[] dirs = relPath.Split(new Char[] { ESCAPED_BACKSLASH_CHAR }); - for (int i = 1; i < dirs.Length; i++) - { - parentFolderPrefix += PARENTFOLDER; - } - } +#if NETFX && !NETCOREAPP + return PathInternal.GetRelativePath(TargetPath, SourceFileInfo.SourcePath, StringComparison.OrdinalIgnoreCase); +#else - return parentFolderPrefix; + return Path.GetRelativePath(TargetPath, SourceFileInfo.SourcePath); +#endif } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs index 23f8f9a8250..245403747ce 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs @@ -273,20 +273,30 @@ internal static int GetRootLength(ReadOnlySpan path) /// /// Gets the count of common characters from the left optionally ignoring case /// - internal static int EqualStartingCharacterCount(string first, string second, bool ignoreCase) + internal static unsafe int EqualStartingCharacterCount(string first, string second, bool ignoreCase) { if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0; - // Current index - int index = 0; + int commonChars = 0; - while (index != first.Length && index != second.Length && - first[index] == second[index] || (ignoreCase && char.ToUpperInvariant(first[index]) == char.ToUpperInvariant(second[index]))) + fixed (char* f = first) + fixed (char* s = second) { - index++; + char* l = f; + char* r = s; + char* leftEnd = l + first.Length; + char* rightEnd = r + second.Length; + + while (l != leftEnd && r != rightEnd + && (*l == *r || (ignoreCase && char.ToUpperInvariant((*l)) == char.ToUpperInvariant((*r))))) + { + commonChars++; + l++; + r++; + } } - return index; + return commonChars; } /// diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj index cf3cd1e6d9d..04b5bd87873 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj @@ -27,6 +27,7 @@ true true AnyCPU;x64 + true From 0eaf8c8a7c43909acfc8444d473c170d32173a55 Mon Sep 17 00:00:00 2001 From: Ryland Alaniz Date: Wed, 10 Jun 2020 12:04:26 -0700 Subject: [PATCH 4/7] Revive 2019 fix --- .../src/PresentationBuildTasks/PresentationBuildTasks.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj index f8a17ebab50..5d8f2349931 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj @@ -280,6 +280,7 @@ + From 94d96b1961650a5b62900592a697a7ab4d7899ac Mon Sep 17 00:00:00 2001 From: Ryland <41491307+ryalanms@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:09:39 -0700 Subject: [PATCH 5/7] Address PR feedback --- .../MS/Internal/MarkupCompiler/MarkupCompiler.cs | 2 +- .../{NETFrameworkGetRelativePath.cs => PathInternal.cs} | 0 .../src/PresentationBuildTasks/PresentationBuildTasks.csproj | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/{NETFrameworkGetRelativePath.cs => PathInternal.cs} (100%) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs index eb241da4949..a2dce6f3559 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/MarkupCompiler.cs @@ -1575,7 +1575,7 @@ private string ParentFolderPrefix { get { -#if NETFX && !NETCOREAPP +#if NETFX return PathInternal.GetRelativePath(TargetPath, SourceFileInfo.SourcePath, StringComparison.OrdinalIgnoreCase); #else return Path.GetRelativePath(TargetPath, SourceFileInfo.SourcePath); diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs similarity index 100% rename from src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/NETFrameworkGetRelativePath.cs rename to src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj index 7c36b7db9c5..38b33730d47 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj @@ -279,7 +279,7 @@ - + From 484790e47a44c97a6e898a5b49432e619d263559 Mon Sep 17 00:00:00 2001 From: Ryland <41491307+ryalanms@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:10:28 -0700 Subject: [PATCH 6/7] Address PR feedback --- .../MS/Internal/MarkupCompiler/PathInternal.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs index 245403747ce..9b8361f347f 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs @@ -76,10 +76,10 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom // Trailing separators aren't significant for comparison int relativeToLength = relativeTo.Length; - if (EndsInDirectorySeparator(relativeTo.AsSpan())) + if (DoesEndInDirectorySeparator(relativeTo.AsSpan())) relativeToLength--; - bool pathEndsInSeparator = EndsInDirectorySeparator(path.AsSpan()); + bool pathEndsInSeparator = DoesEndInDirectorySeparator(path.AsSpan()); int pathLength = path.Length; if (pathEndsInSeparator) pathLength--; @@ -97,7 +97,7 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); // Add parent segments for segments past the common on the "from" path if (commonLength < relativeToLength) @@ -152,7 +152,7 @@ internal static bool IsDirectorySeparator(char c) /// internal static int GetCommonPathLength(string first, string second, bool ignoreCase) { - int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase); + int commonChars = EqualStartingCharacterCount(first, second, ignoreCase); // If nothing matches if (commonChars == 0) @@ -357,13 +357,13 @@ internal static bool IsExtended(ReadOnlySpan path) /// /// Returns true if the path ends in a directory separator. /// - public static bool EndsInDirectorySeparator(ReadOnlySpan path) + public static bool DoesEndInDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); /// /// Returns true if the path ends in a directory separator. /// - public static bool EndsInDirectorySeparator(string path) + public static bool DoesEndInDirectorySeparator(string path) => path != null && path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); internal const char DirectorySeparatorChar = '\\'; From e70e971e135f2fa98f29c8cc3c9c4f7526254d21 Mon Sep 17 00:00:00 2001 From: Ryland <41491307+ryalanms@users.noreply.github.com> Date: Wed, 9 Sep 2020 12:41:21 -0700 Subject: [PATCH 7/7] PR feedback --- .../MS/Internal/MarkupCompiler/PathInternal.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs index 9b8361f347f..661aa18714e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/MS/Internal/MarkupCompiler/PathInternal.cs @@ -85,7 +85,7 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom pathLength--; // If we have effectively the same path, return "." - if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; + if (relativeToLength == pathLength && commonLength >= relativeToLength) return CurrentDir.ToString(); // We have the same root, we need to calculate the difference now using the // common Length and Segment count past the length. @@ -102,14 +102,14 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom // Add parent segments for segments past the common on the "from" path if (commonLength < relativeToLength) { - sb.Append(".."); + sb.Append(ParentDir); for (int i = commonLength + 1; i < relativeToLength; i++) { if (PathInternal.IsDirectorySeparator(relativeTo[i])) { - sb.Append(DirectorySeparatorChar); - sb.Append(".."); + sb.Append(Path.DirectorySeparatorChar); + sb.Append(ParentDir); } } } @@ -129,7 +129,7 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom { if (sb.Length > 0) { - sb.Append(DirectorySeparatorChar); + sb.Append(Path.DirectorySeparatorChar); } sb.Append(path, commonLength, differenceLength); @@ -144,7 +144,7 @@ internal static string GetRelativePath(string relativeTo, string path, StringCom [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsDirectorySeparator(char c) { - return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; + return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; } /// @@ -366,8 +366,6 @@ public static bool DoesEndInDirectorySeparator(ReadOnlySpan path) public static bool DoesEndInDirectorySeparator(string path) => path != null && path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]); - internal const char DirectorySeparatorChar = '\\'; - internal const char AltDirectorySeparatorChar = '/'; internal const char VolumeSeparatorChar = ':'; // \\?\UNC\, \\.\UNC\ internal const int UncExtendedPrefixLength = 8; @@ -375,6 +373,10 @@ public static bool DoesEndInDirectorySeparator(string path) internal const int DevicePrefixLength = 4; // \\ internal const int UncPrefixLength = 2; + // ".." + internal const string ParentDir = ".."; + // '.' + internal const char CurrentDir = '.'; } }