Merge pull request #7842 from peterhuene/fix-7699

Fix relative path handling on Windows.
This commit is contained in:
Livar 2017-10-27 11:07:11 -07:00 committed by GitHub
commit 1e70555569
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 86 deletions

View file

@ -17,21 +17,12 @@ namespace Microsoft.DotNet.Cli.Build
[Required] [Required]
public string Path2 { get; set; } public string Path2 { get; set; }
public char SeparatorChar { get; set; }
[Output] [Output]
public ITaskItem RelativePath { get; set; } public ITaskItem RelativePath { get; set; }
public override bool Execute() public override bool Execute()
{ {
if (SeparatorChar == default(char)) RelativePath = ToTaskItem(Path1, Path2, Path.GetRelativePath(Path1, Path2));
{
SeparatorChar = Path.DirectorySeparatorChar;
}
var relativePath = GetRelativePath(Path1, Path2, SeparatorChar);
RelativePath = ToTaskItem(Path1, Path2, relativePath);
return true; return true;
} }
@ -47,75 +38,5 @@ namespace Microsoft.DotNet.Cli.Build
return framework; return framework;
} }
private static string GetRelativePath(string path1, string path2, char separator = default(char))
{
StringComparison compare;
if (CurrentPlatform.IsWindows)
{
compare = StringComparison.OrdinalIgnoreCase;
// check if paths are on the same volume
if (!string.Equals(Path.GetPathRoot(path1), Path.GetPathRoot(path2)))
{
// on different volumes, "relative" path is just Path2
return path2;
}
}
else
{
compare = StringComparison.Ordinal;
}
var index = 0;
var path1Segments = path1.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var path2Segments = path2.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
// if path1 does not end with / it is assumed the end is not a directory
// we will assume that is isn't a directory by ignoring the last split
var len1 = path1Segments.Length - 1;
var len2 = path2Segments.Length;
// find largest common absolute path between both paths
var min = Math.Min(len1, len2);
while (min > index)
{
if (!string.Equals(path1Segments[index], path2Segments[index], compare))
{
break;
}
// Handle scenarios where folder and file have same name (only if os supports same name for file and directory)
// e.g. /file/name /file/name/app
else if ((len1 == index && len2 > index + 1) || (len1 > index && len2 == index + 1))
{
break;
}
++index;
}
var path = "";
// check if path2 ends with a non-directory separator and if path1 has the same non-directory at the end
if (len1 + 1 == len2 && !string.IsNullOrEmpty(path1Segments[index]) &&
string.Equals(path1Segments[index], path2Segments[index], compare))
{
return path;
}
for (var i = index; len1 > i; ++i)
{
path += ".." + separator;
}
for (var i = index; len2 - 1 > i; ++i)
{
path += path2Segments[i] + separator;
}
// if path2 doesn't end with an empty string it means it ended with a non-directory name, so we add it back
if (!string.IsNullOrEmpty(path2Segments[len2 - 1]))
{
path += path2Segments[len2 - 1];
}
return path;
}
} }
} }

View file

@ -14,6 +14,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NETCore.App" Version="$(CLI_SharedFrameworkVersion)" />
<PackageReference Include="Microsoft.Build" Version="$(CLI_MSBuild_Version)" /> <PackageReference Include="Microsoft.Build" Version="$(CLI_MSBuild_Version)" />
<PackageReference Include="Microsoft.CSharp" Version="4.0.1" /> <PackageReference Include="Microsoft.CSharp" Version="4.0.1" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.0.11" /> <PackageReference Include="System.Dynamic.Runtime" Version="4.0.11" />

View file

@ -151,7 +151,7 @@ namespace Microsoft.DotNet.Tools.Common
{ {
compare = StringComparison.OrdinalIgnoreCase; compare = StringComparison.OrdinalIgnoreCase;
// check if paths are on the same volume // check if paths are on the same volume
if (!string.Equals(Path.GetPathRoot(path1), Path.GetPathRoot(path2))) if (!string.Equals(Path.GetPathRoot(path1), Path.GetPathRoot(path2), compare))
{ {
// on different volumes, "relative" path is just path2 // on different volumes, "relative" path is just path2
return path2; return path2;
@ -273,7 +273,22 @@ namespace Microsoft.DotNet.Tools.Common
foreach (var component in components) foreach (var component in components)
{ {
if (!string.IsNullOrEmpty(component)) if (string.IsNullOrEmpty(component))
{
continue;
}
if (string.IsNullOrEmpty(result))
{
result = component;
// On Windows, manually append a separator for drive references because Path.Combine won't do so
if (result.EndsWith(":") && RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
{
result += Path.DirectorySeparatorChar;
}
}
else
{ {
result = Path.Combine(result, component); result = Path.Combine(result, component);
} }

View file

@ -256,7 +256,7 @@ namespace Microsoft.DotNet.Tools
string fullPath = Path.GetFullPath(reference); string fullPath = Path.GetFullPath(reference);
ret.Add(fullPath); ret.Add(fullPath);
ret.Add(PathUtility.GetRelativePath(ProjectDirectory, fullPath)); ret.Add(Path.GetRelativePath(ProjectDirectory, fullPath));
return ret; return ret;
} }

View file

@ -23,7 +23,7 @@ namespace Microsoft.DotNet.Tools.Common
throw new ArgumentException(); throw new ArgumentException();
} }
var relativeProjectPath = PathUtility.GetRelativePath( var relativeProjectPath = Path.GetRelativePath(
PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory),
fullProjectPath); fullProjectPath);

View file

@ -91,7 +91,7 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
} }
var relativePathReferences = _appliedCommand.Arguments.Select((r) => var relativePathReferences = _appliedCommand.Arguments.Select((r) =>
PathUtility.GetRelativePath(msbuildProj.ProjectDirectory, Path.GetFullPath(r))) Path.GetRelativePath(msbuildProj.ProjectDirectory, Path.GetFullPath(r)))
.ToList(); .ToList();
int numberOfAddedReferences = msbuildProj.AddProjectToProjectReferences( int numberOfAddedReferences = msbuildProj.AddProjectToProjectReferences(

View file

@ -41,7 +41,7 @@ namespace Microsoft.DotNet.Tools.Sln.Remove
SlnFile slnFile = SlnFileFactory.CreateFromFileOrDirectory(_fileOrDirectory); SlnFile slnFile = SlnFileFactory.CreateFromFileOrDirectory(_fileOrDirectory);
var relativeProjectPaths = _appliedCommand.Arguments.Select(p => var relativeProjectPaths = _appliedCommand.Arguments.Select(p =>
PathUtility.GetRelativePath( Path.GetRelativePath(
PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory),
Path.GetFullPath(p))) Path.GetFullPath(p)))
.ToList(); .ToList();

View file

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Cli.Utils
{
public class PathUtilityTests : TestBase
{
/// <summary>
/// Tests that PathUtility.GetRelativePath treats drive references as case insensitive on Windows.
/// </summary>
[WindowsOnlyFact]
public void GetRelativePathWithCaseInsensitiveDrives()
{
Assert.Equal(@"bar\", PathUtility.GetRelativePath(@"C:\foo\", @"C:\foo\bar\"));
Assert.Equal(@"Bar\Baz\", PathUtility.GetRelativePath(@"c:\foo\", @"C:\Foo\Bar\Baz\"));
Assert.Equal(@"baz\Qux\", PathUtility.GetRelativePath(@"C:\fOO\bar\", @"c:\foo\BAR\baz\Qux\"));
Assert.Equal(@"d:\foo\", PathUtility.GetRelativePath(@"C:\foo\", @"d:\foo\"));
}
/// <summary>
/// Tests that PathUtility.RemoveExtraPathSeparators works correctly with drive references on Windows.
/// </summary>
[WindowsOnlyFact]
public void RemoveExtraPathSeparatorsWithDrives()
{
Assert.Equal(@"c:\foo\bar\baz\", PathUtility.RemoveExtraPathSeparators(@"c:\\\foo\\\\bar\baz\\"));
Assert.Equal(@"D:\QUX\", PathUtility.RemoveExtraPathSeparators(@"D:\\\\\QUX\"));
}
}
}