Как я могу заставить MSBUILD оценивать и распечатывать полный путь при указании относительного пути?

Как я могу заставить MSBuild оценивать и печатать в <Message /> задаче абсолютный путь с учетом относительного пути?

Группа свойств

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

Задача

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

Вывод

Копирование '' в 'c: \ Program Files (x86) \ Program \'


person Eric Schoonover    schedule 07.10.2008    source источник
comment
Я думаю, что метаданные FullPath применяются только к элементам ‹ItemGroup›, а не к свойствам ‹PropertyGroup›.   -  person lesscode    schedule 07.10.2008
comment
Я думаю, что вы правы, кто-нибудь знает способ перейти от Property к полному / абсолютному пути?   -  person Eric Schoonover    schedule 07.10.2008
comment
Вы могли бы попасть туда, используя $ (ProjectDir) $ (Source_Dir), но у вас были бы лишние '..'   -  person lesscode    schedule 07.10.2008


Ответы (5)


В MSBuild 4.0 самый простой способ:

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

Этот метод работает, даже если сценарий <Import> вставлен в другой сценарий; путь указывается относительно файла, содержащего приведенный выше код.

(собрано из ответа Аарона, а также последней части Ответ Саеда)


В MSBuild 3.5 можно использовать Задача ConvertToAbsolutePath:

<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>

Соответствующий вывод:

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

Немного многословно, если вы спросите меня, но это работает. Он будет относиться к «исходному» файлу проекта, поэтому, если он будет помещен в файл, который получает <Import>ed, он не будет относиться к этому файлу.


В MSBuild 2.0 есть подход, который не разрешает "..". Однако он ведет себя так же, как абсолютный путь:

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

Зарезервированное свойство $ (MSBuildProjectDirectory) всегда каталог сценария, который содержит эту ссылку.

Это также будет относиться к «исходному» файлу проекта, поэтому, если оно будет помещено в файл, который получает <Import>ed, он не будет относиться к этому файлу.

person Roman Starkov    schedule 09.08.2009
comment
Будьте осторожны с [System.IO.Path]::GetFullPath. Я столкнулся с ошибкой построения в Visual Studio, потому что текущий рабочий каталог в msbuild был C:\Windows\System32. GetFullPath разрешается относительно рабочего каталога, а не каталога проекта. - person Chris Chilvers; 22.12.2015
comment
@ChrisChilvers Это прискорбно. В прошлый раз, когда я тестировал это (правда, очень давно), это относилось к файлу, содержащему код. - person Roman Starkov; 25.12.2015
comment
Он также работает для обратной трассировки пути в стиле ..\..\your\path. Также обратите внимание, что макрос $(MSBuildThisFileDirectory) уже включает в себя завершающую косую черту, поэтому вы должны указать your\path без ведущей косой черты, т.е. $(MSBuildThisFileDirectory)your\path. Один случай, когда это имеет значение, - это когда вы используете его для OutDir для теста Microsoft Unit Testing Framework Test, когда вы пытаетесь запустить тесты, он не сможет оценить `\\` в объединенном пути, чтобы найти построенную dll. - person Adam Yaxley; 30.06.2016
comment
@AdamYaxley GetFullPath автоматически сворачивает двойную обратную косую черту, поэтому он будет работать, даже если у вас есть эта дополнительная обратная косая черта. Ваша проблема должна быть связана с чем-то другим. - person Roman Starkov; 30.06.2016

В MSBuild 4.0 добавлены функции свойств, которые позволяют вызывать статические функции в некоторых системных dll .net. Что действительно хорошо в функциях свойств, так это то, что они оценивают их вне целевой области.

Чтобы оценить полный путь, вы можете использовать System.IO.Path .GetFullPath при определении такого свойства:

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

Синтаксис немного уродливый, но очень мощный.

person Aaron Carlson    schedule 04.02.2011
comment
+1 Ой, чувак. Отлично! Именно то, что я искал. Спасибо. - person Asaf; 25.07.2011
comment
Он также оценивает путь относительно (насколько я понимаю) файла проекта, в котором определено свойство, что очень удобно, если вы хотите включить это свойство в другие файлы. Для меня это было идеальное решение. - person Jean Hominal; 23.04.2012
comment
@JeanHominal. Если вы используете <Import>, он все равно будет относиться к месту, куда он импортирован, что очень досадно. Тем не менее, это единственный подход, который также работает за пределами целей, что дает мне +1. См. этот ответ, чтобы узнать, как обойти <Import> проблему. - person Roman Starkov; 02.09.2012
comment
Да, я ошибся, когда сказал, что сделал - все относительные пути всегда относятся к пути исполняемого проекта (проекта, который в настоящее время выполняется MSBuild); однако вы можете использовать $(MSBuildThisFileDirectory), чтобы получить полный путь к каталогу текущего исполняемого файла. - person Jean Hominal; 03.09.2012
comment
Именно то, что мне нужно. Спасибо! - person Cameron Chapman; 13.02.2015

Уэйн прав в том, что хорошо известные метаданные не относятся к свойствам - только к элементам. Использование таких свойств, как «MSBuildProjectDirectory», будет работать, но я не знаю встроенного способа разрешения полного пути.

Другой вариант - написать простую настраиваемую задачу, которая будет использовать относительный путь и выдавать полностью разрешенный путь. Это выглядело бы примерно так:

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;
    }
}

И ваши строки MSBuild будут выглядеть примерно так:

<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>
person brock.holum    schedule 07.10.2008
comment
Чувак, я узнал больше о том, как создать задачу MS из этого фрагмента кода выше, чем когда-либо в документации MSBuild. Спасибо! :-) - person evilhomer; 06.11.2008

Вы пытаетесь получить доступ к свойству метаданных элемента через свойство, что невозможно. Вы хотите сделать что-то вроде этого:

<PropertyGroup>
  <Program_Dir>c:\Program Files (x86)\Program\</Program_Dir>
</PropertyGroup>
<ItemGroup>
   <Source_Dir Include="..\Desktop"/>
</ItemGroup>     
<Target Name="BuildAll">
   <Message Text="Copying '%(Source_Dir.FullPath)' to '$(Program_Dir)'" />
</Target>

Что будет генерировать вывод как:

 Copying 'C:\Users\sdorman\Desktop' to 'c:\Program Files (x86)\Program\'

(Сценарий был запущен из моей папки Documents, поэтому .. \ Desktop - это правильный относительный путь для доступа к моему рабочему столу.)

В вашем случае замените ".. \ Desktop" на "...... \ Public \ Server" в элементе Source_Dir, и все будет готово.

person Scott Dorman    schedule 07.10.2008
comment
+1 Это прекрасно работает, и я (как и другие) приземлился здесь в поисках способа канонизировать ItemGroup (в значительной степени предполагая, что пакет и копирование в новую ItemGroup были необходимы) - синтаксис, который вы показываете, делает это без этой путаницы. IOW Я забыл о FullPath хорошо известных метаданных - person Ruben Bartelink; 11.04.2012

Если вам нужно преобразовать свойства в элементы, у вас есть два варианта. В msbuild 2 вы можете использовать задачу CreateItem

  <Target Name='Build'>
    <CreateItem Include='$(Source_Dir)'>
      <Output ItemName='SRCDIR' TaskParameter='Include' />
    </CreateItem>

а с MSBuild 3.5 вы можете иметь ItemGroups внутри задачи

  <Target Name='Build'>
    <ItemGroup>
      <SRCDIR2 Include='$(Source_Dir)' />
    </ItemGroup>
    <Message Text="%(SRCDIR2.FullPath)" />
    <Message Text="%(SRCDIR.FullPath)" />
  </Target>
person Scott Weinstein    schedule 07.10.2008