9

I have a situation where I have a bunch of absolute paths, and I would like to convert them to relative paths based on another directory with MSBuild. Here is the code I have so far:

<PropertyGroup>
    <FromPath>$(Bar)</FromPath>
</PropertyGroup>
<ItemGroup>
    <AbsolutePaths Include="@(Foo)" Exclude="@(Baz)" />
    <PathsRelativeToBar Include="@(AbsolutePaths->'???')" /> <!-- What goes here? -->
</ItemGroup>

Any help would be appreciated, thanks!

edit: I found a C#-based solution in this StackOverflow question, but I'm not sure how (or if it's possible) to convert it to MSBuild.

Community
  • 1
  • 1
James Ko
  • 32,215
  • 30
  • 128
  • 239
  • If you have the absolute path and relative working directory path, this could help : http://stackoverflow.com/questions/703281/getting-path-relative-to-the-current-working-directory – mojorisinify Feb 18 '16 at 03:06
  • @mojorisinify Thanks for helping! I had seen that solution already. Unfortunately, I'm not really sure how I would translate that to MSBuild code. – James Ko Feb 18 '16 at 03:47

1 Answers1

16

There is a native function in MSBuild called "MakeRelative"

Here is how you can use it.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="4.5" >
    <PropertyGroup>
        <Bar>c:\temp\msbuild-sample\2</Bar>
        <FromPath>$(Bar)</FromPath>
    </PropertyGroup>
    <ItemGroup>
        <AbsolutePaths Include="c:\temp\msbuild-sample\1\**\*.txt" />
    </ItemGroup>
    <Target Name="Build">
        <ItemGroup>
            <PathsRelativeToBar Include="@(AbsolutePaths)">
                <!-- Here is the magic... we're adding a piece of metadata with the relative path -->
                <RelativePath>$([MSBuild]::MakeRelative($(FromPath), %(AbsolutePaths.FullPath)))</RelativePath>
            </PathsRelativeToBar>
        </ItemGroup>
        <Message Text="----- Absolute paths -----" />
        <Message Text="%(AbsolutePaths.FullPath)" />
        <Message Text="----- Relative paths (showing full path) -----" />
        <Message Text="%(PathsRelativeToBar.FullPath)" />
        <Message Text="----- Relative paths (relative to $(FromPath)) -----" />
        <Message Text="%(PathsRelativeToBar.RelativePath)" />
    </Target>
</Project>

Here is a quick view of my current environment

C:\temp\msbuild-sample>dir /s /b

C:\temp\msbuild-sample\1
C:\temp\msbuild-sample\sample.build
C:\temp\msbuild-sample\1\1.1
C:\temp\msbuild-sample\1\1.txt
C:\temp\msbuild-sample\1\2.txt
C:\temp\msbuild-sample\1\1.1\3.txt
C:\temp\msbuild-sample\1\1.1\4.txt

and here is the output.

----- Absolute paths -----
c:\temp\msbuild-sample\1\1.1\3.txt
c:\temp\msbuild-sample\1\1.1\4.txt
c:\temp\msbuild-sample\1\1.txt
c:\temp\msbuild-sample\1\2.txt
----- Relative paths (showing full path) -----
c:\temp\msbuild-sample\1\1.1\3.txt
c:\temp\msbuild-sample\1\1.1\4.txt
c:\temp\msbuild-sample\1\1.txt
c:\temp\msbuild-sample\1\2.txt
----- Relative paths (relative to c:\temp\msbuild-sample\2) -----
..\1\1.1\3.txt
..\1\1.1\4.txt
..\1\1.txt
..\1\2.txt

Hope this helps :)

duncanc
  • 321
  • 2
  • 5