2016-07-15 08:31:50 -07:00
// 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.
2016-06-30 17:11:33 -07:00
using System ;
using System.IO ;
using Microsoft.Build.Utilities ;
using Microsoft.Build.Framework ;
using Microsoft.DotNet.Cli.Build.Framework ;
namespace Microsoft.DotNet.Cli.Build
{
public class MakeRelative : Task
{
[Required]
public string Path1 { get ; set ; }
[Required]
public string Path2 { get ; set ; }
public char SeparatorChar { get ; set ; }
[Output]
public ITaskItem RelativePath { get ; set ; }
public override bool Execute ( )
{
if ( SeparatorChar = = default ( char ) )
{
SeparatorChar = Path . DirectorySeparatorChar ;
}
var relativePath = GetRelativePath ( Path1 , Path2 , SeparatorChar ) ;
RelativePath = ToTaskItem ( Path1 , Path2 , relativePath ) ;
return true ;
}
private static TaskItem ToTaskItem ( string path1 , string path2 , string relativePath )
{
var framework = new TaskItem ( ) ;
framework . ItemSpec = relativePath ;
framework . SetMetadata ( "Path1" , path1 ) ;
framework . SetMetadata ( "Path2" , path2 ) ;
framework . SetMetadata ( "RelativePath" , relativePath ) ;
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 ;
}
}
}