Элемент MSBuild нельзя использовать в задаче MSBuild, ошибка MSB4012 - PullRequest
2 голосов
/ 16 апреля 2011

У меня есть следующий файл проекта MSBuild:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Deploy" ToolsVersion="4.0">
  <ItemGroup>
    <Base Include="$(MSBuildProjectDirectory)\.." />
  </ItemGroup>

  <PropertyGroup>
    <BaseDirectory>@(Base->'%(FullPath)')</BaseDirectory>
    <DeployDirectory>$(BaseDirectory)\Deploy</DeployDirectory>
    <Configuration>Release</Configuration>
  </PropertyGroup>

  <Target Name="Deploy" DependsOnTargets="Hello;Clean;Build" />

  <Target Name="Hello">
    <Message Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)" />
  </Target>

  <Target Name="Clean">
    <RemoveDir Directories="$(DeployDirectory)" />
  </Target>

  <Target Name="Build">
    <MSBuild Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj" Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)" ContinueOnError="false" />
  </Target>

</Project>

И когда я запускаю его, я получаю сообщение об ошибке:

C: \ Репозитории \ Project \ Build \ Build.proj (22,16): ошибка MSB4012: выражение "@ (Base -> '% (FullPath)') \ Deploy" не может использоваться в этом контекст. Списки элементов не могут быть объединены с другими строками, где есть элемент т ожидается. Используйте точку с запятой для разделения нескольких списков элементов.

Почему я получаю эту ошибку и как ее можно избежать? Я использую элемент Base в ItemGroup, потому что мне нужно избавиться от .. в пути, а Items разрешить сделать это с помощью %FullPath метаданных. Если я использую только PropertyGroup, тогда все работает нормально, но у меня есть .. во всех путях.

Ответы [ 2 ]

3 голосов
/ 27 апреля 2011

Трудно точно сказать, что происходит под капотом.Я не нахожусь в 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>

При таком подходе я имеюсоздал группу свойств внутри самой цели и присвоил значение этих «виртуальных свойств» новым свойствам.Эти новые свойства - не виртуальные свойства, а реальные свойства, поэтому вы можете использовать их, как и ожидалось, без проблем.

Теперь перейдем к вашему вопросу: «Почему задача сообщения работает WTF? !!!" Внутри цели Hello у вас есть следующее:

<Message Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)" />

, которое работает без проблем.Ранее я упоминал, что эти виртуальные свойства будут по существу заменены встроенными определениями, поддерживающими их, так что это фактически станет.

<Message Text="Hello world. BaseDirectory=@(Base->'%(FullPath)'), DeployDirectory=@(Base->'%(FullPath)')\Deploy" />

OK, удерживайте эту мысль.

Свойство 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 .Внутри цели Demo я печатаю значение для 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).

Как вы можете видеть, он ведет себя так, как я сказал.

2 голосов
/ 17 апреля 2011

Вы боретесь с оценочным заказом.Переместите объявление группы свойств внутри цели «Hello», и она будет работать так, как вы ожидаете.Еще лучше, переместите его в свою собственную цель и установите эту цель в любых DependsOnTargets для других целей, которые требуют выполнения оценки перед выполнением, или, наоборот, установите эти цели в качестве «BeforeTargets» для новой цели.

(редактировать)

Это будет работать для всех целей:

<ItemGroup>
  <Base Include="$(MSBuildProjectDirectory)\.." />
</ItemGroup>

<Target Name="Deploy" DependsOnTargets="Hello;Clean;Build" />

<Target Name="CalcProps">
  <PropertyGroup>
    <BaseDirectory>@(Base->'%(FullPath)')</BaseDirectory>
    <DeployDirectory>$(BaseDirectory)\Deploy</DeployDirectory>
    <Configuration>Release</Configuration>
  </PropertyGroup>
</Target>

<Target Name="Hello" DependsOnTargets="CalcProps">
  <Message
    Text="Hello world. BaseDirectory=$(BaseDirectory), DeployDirectory=$(DeployDirectory)"
    />
</Target>

<Target Name="Clean" DependsOnTargets="CalcProps">
  <RemoveDir Directories="$(DeployDirectory)" />
</Target>

<Target Name="Build" DependsOnTargets="CalcProps">
  <MSBuild 
    Projects="$(BaseDirectory)\DebugConsoleApp\DebugConsoleApp.csproj" 
    Properties="Configuration=$(Configuration);OutputPath=$(DeployDirectory)" 
    ContinueOnError="false"
    />  
</Target>

Я бы предположил, что оценка аргумента Projects для задачи MSBuild, так как она имеет тип ITaskItem[], возможно, использует неоцененную строку в $ (BaseDirectory), и, поскольку это преобразование элемента, происходит сбой, поскольку в случае, когда преобразовываемый элемент имеет более одного члена (даже если в этом случае это не так),Использование вами одного и того же свойства в задаче «Сообщение» передается аргументу типа System.String, который может иметь другую последовательность оценки.

...