трудно сказать точно, что происходит под капотом. Я не состою в MSBuild, поэтому я лишь приблизительно знаком с фактической реализацией. Нам понадобится разработчик MSBuild, чтобы дать 100% правильный ответ. Но вот что, как я предполагаю, происходит (читай: остальная часть этого содержит спекуляции с моей стороны).
Внутри вы нацеливаетесь, когда используете оператор
Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj"
MSBuild замечает, что используется расширение свойства $(BaseDirectory) и что тип параметра для проектов в MSBuild — это массив. Также MSBuild замечает, что BaseDirectory — это свойство, содержащее элемент. Эти свойства не ведут себя как обычные свойства. Вы можете думать о них как о «виртуальных свойствах» (да, я только что придумал этот термин). Когда эти свойства используются вместо поиска значения, происходит встроенная замена. Таким образом, ваш атрибут Projects изменится на:
Projects="@(Base->'%(FullPath)')\DebugConsoleApp\DebugConsoleApp.csproj"
Поскольку Projects является массивом, MSBuild попытается выполнить преобразование предоставленного выражения. Поскольку это недопустимое преобразование, возникает ошибка. Какую ошибку вы получаете.
Теперь, чтобы обойти это, вы можете изменить цель сборки, чтобы она выглядела следующим образом:
<Target Name="Build">
<PropertyGroup>
<_BaseDir>$(BaseDirectory)</_BaseDir>
<_DeployDir>@(Base->'%(FullPath)')</_DeployDir>
</PropertyGroup>
<Message Text="_BaseDir: $(_BaseDir)"/>
<Message Text="DeployDirectory: $(DeployDirectory)"/>
<MSBuild Projects="$(_BaseDir)\DebugConsoleApp\DebugConsoleApp.csproj"
Properties="Configuration=$(Configuration);OutputPath=$(_Tmp2)"
ContinueOnError="false" />
<!--<MSBuild Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj"
Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)"
ContinueOnError="false" />-->
</Target>
При таком подходе я создал группу свойств внутри самой цели и присвоил значение этих «виртуальных свойств» новым свойствам. Эти новые свойства являются не виртуальными, а реальными свойствами, поэтому вы можете без проблем использовать их по своему усмотрению.
Теперь перейдем к вашему вопросу: "Почему задача с сообщением работает, чёрт возьми?!!!" Внутри цели Hello у вас есть следующее:
<Message Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)" />
Который работает без проблем. Ранее я упоминал, что эти виртуальные свойства будут по существу заменены встроенным определением, поддерживающим их, так что это фактически станет.
<Message Text="Hello world. BaseDirectory=@(Base->'%(FullPath)'), DeployDirectory=@(Base->'%(FullPath)')\Deploy" />
Хорошо, держи эту мысль.
Свойство Text
в задаче MSBuild определяется как строка, представляющая собой скалярное значение. Если вы помните, свойство Projects в задаче MSBuild определяется как ITaskItem[], так как это массив и его векторное значение. Когда @(...)
находится в свойстве векторных значений, все выражение используется как преобразование элемента. В этом случае оператор @(Base->'%(FullPath)')\DebugConsoleApp\DebugConsoleApp.csproj
не является допустимым выражением преобразования. Когда '@(..)' находится внутри объявления свойства скалярных значений, значения выравниваются в строку. Таким образом, каждый экземпляр '@(...)' обрабатывается и сводится к одному строковому значению. Если значений несколько, используются разделители.
Так что, надеюсь, это объясняет поведение, которое вы видите, и на самом деле это может быть ошибка. Вы можете зарегистрировать его на странице http://connect.microsoft.com/, и команда MSBuild проверит его.
Подробнее о виртуальных свойствах Ранее я упоминал, что эти виртуальные свойства не ведут себя как обычные свойства в том смысле, что значение не ищется, а вместо этого использование $(...) заменяется на выражение свойств. Не верьте мне на слово, посмотрите сами. Вот пример файла, который я создал
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<ItemGroup>
<MyItem Include="C:\temp\01.txt"></MyItem>
</ItemGroup>
<PropertyGroup>
<MyProperty>@(MyItem->'%(FullPath)')</MyProperty>
</PropertyGroup>
<Target Name="Demo">
<Message Text="MyProperty: $(MyProperty)" />
<!-- Add to the item -->
<ItemGroup>
<MyItem Include="C:\temp\01.txt"></MyItem>
</ItemGroup>
<Message Text="MyProperty: $(MyProperty)" />
</Target>
</Project>
Здесь у меня объявлен список элементов MyItem и зависимое свойство MyProperty. Внутри демонстрационной цели я печатаю значение для MyProperty, затем добавляю другое значение в список элементов MyItem и снова распечатываю значение для MyProperty. Вот результат.
PS C:\temp\MSBuild\SO> msbuild .\Build.proj /nologo
Build started 4/26/2011 10:17:08 PM.
Project "C:\temp\MSBuild\SO\Build.proj" on node 1 (default targets).
First:
MyProperty: C:\temp\01.txt
MyProperty: C:\temp\01.txt;C:\temp\01.txt
Done Building Project "C:\temp\MSBuild\SO\Build.proj" (default targets).
Как видите, он ведет себя именно так, как я сказал.
person
Sayed Ibrahim Hashimi
schedule
27.04.2011