Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get MSBUILD to evaluate and print the full path when given a relative path?

How can I get MSBuild to evaluate and print in a <Message /> task an absolute path given a relative path?

Property Group

<Source_Dir>..\..\..\Public\Server\</Source_Dir>
<Program_Dir>c:\Program Files (x86)\Program\</Program_Dir>

Task

<Message Importance="low" Text="Copying '$(Source_Dir.FullPath)' to '$(Program_Dir)'" />

Output

Copying '' to 'c:\Program Files (x86)\Program\'

like image 590
Eric Schoonover Avatar asked Oct 07 '08 01:10

Eric Schoonover


3 Answers

In MSBuild 4.0, the easiest way is the following:

$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\your\path'))

This method works even if the script is <Import>ed into another script; the path is relative to the file containing the above code.

(consolidated from Aaron's answer as well as the last part of Sayed's answer)


In MSBuild 3.5, you can use the ConvertToAbsolutePath task:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         DefaultTargets="Test"
         ToolsVersion="3.5">
  <PropertyGroup>
    <Source_Dir>..\..\..\Public\Server\</Source_Dir>
    <Program_Dir>c:\Program Files (x86)\Program\</Program_Dir>
  </PropertyGroup>

  <Target Name="Test">
    <ConvertToAbsolutePath Paths="$(Source_Dir)">
      <Output TaskParameter="AbsolutePaths" PropertyName="Source_Dir_Abs"/>
    </ConvertToAbsolutePath>
    <Message Text='Copying "$(Source_Dir_Abs)" to "$(Program_Dir)".' />
  </Target>
</Project>

Relevant output:

Project "P:\software\perforce1\main\XxxxxxXxxx\Xxxxx.proj" on node 0 (default targets).
  Copying "P:\software\Public\Server\" to "c:\Program Files (x86)\Program\".

A little long-winded if you ask me, but it works. This will be relative to the "original" project file, so if placed inside a file that gets <Import>ed, this won't be relative to that file.


In MSBuild 2.0, there is an approach which doesn't resolve "..". It does however behave just like an absolute path:

<PropertyGroup>
    <Source_Dir_Abs>$(MSBuildProjectDirectory)\$(Source_Dir)</Source_Dir_Abs>
</PropertyGroup>

The $(MSBuildProjectDirectory) reserved property is always the directory of the script that contains this reference.

This will also be relative to the "original" project file, so if placed inside a file that gets <Import>ed, this won't be relative to that file.

like image 87
Roman Starkov Avatar answered Nov 11 '22 14:11

Roman Starkov


MSBuild 4.0 added Property Functions which allow you to call into static functions in some of the .net system dlls. A really nice thing about Property Functions is that they will evaluate out side of a target.

To evaluate a full path you can use System.IO.Path.GetFullPath when defining a property like so:

<PropertyGroup>
  <Source_Dir>$([System.IO.Path]::GetFullPath('..\..\..\Public\Server\'))</Source_Dir>
</PropertyGroup>

The syntax is a little ugly but very powerful.

like image 27
Aaron Carlson Avatar answered Nov 11 '22 16:11

Aaron Carlson


Wayne is correct that well-known metadata does not apply to properties - only to items. Using properties such as "MSBuildProjectDirectory" will work, but I'm not aware of a built in way to resolve the full path.

Another option is to write a simple, custom task that will take a relative path and spit out the fully-resolved path. It would look something like this:

public class ResolveRelativePath : Task
{
    [Required]
    public string RelativePath { get; set; }

    [Output]
    public string FullPath { get; private set; }

    public override bool Execute()
    {
        try
        {
            DirectoryInfo dirInfo = new DirectoryInfo(RelativePath);
            FullPath = dirInfo.FullName;
        }
        catch (Exception ex)
        {
            Log.LogErrorFromException(ex);
        }
        return !Log.HasLoggedErrors;
    }
}

And your MSBuild lines would look something like:

<PropertyGroup>
    <TaskAssembly>D:\BuildTasks\Build.Tasks.dll</TaskAssembly>
    <Source_Dir>..\..\..\Public\Server\</Source_Dir>
    <Program_Dir>c:\Program Files (x86)\Program\</Program_Dir>
</PropertyGroup>
<UsingTask AssemblyFile="$(TaskAssembly)" TaskName="ResolveRelativePath" />

<Target Name="Default">
    <ResolveRelativePath RelativePath="$(Source_Dir)">
    <Output TaskParameter="FullPath" PropertyName="_FullPath" />
    </ResolveRelativePath>
    <Message Importance="low" Text="Copying '$(_FullPath)' to '$(Program_Dir)'" />
</Target>
like image 7
brock.holum Avatar answered Nov 11 '22 16:11

brock.holum