Проблема с шаблоном MSBuild ItemGroup Include/Exclude

Проблема: массив ItemGroups неправильно строится на основе значения, переданного в атрибуте exclude.

Если вы запустите этот скрипт, он создаст какой-то образец файла, а затем попытается создать массив с именем TheFiles на основе атрибутов Include/Exclude, проблема в том, что когда Exclude является чем-то другим, кроме жестко запрограммированного или очень простого свойства, он ошибается.

Целевой список DynamicExcludeList неправильно выбирает следующие файлы:
.\AFolder\test.cs;.\AFolder\test.txt

Целевой список HardcodedExcludeList правильно выбирает следующие файлы:
.\AFolder\test.txt

Любая помощь приветствуется, это сводит меня с ума.

(обратите внимание на его msbuild v4)

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Run">

      <Target Name="Run" >
        <CallTarget Targets="CreateSampleFiles" />
        <CallTarget Targets="DynamicExcludeList" />
        <CallTarget Targets="HardcodedExcludeList" />
      </Target>

      <Target Name="CreateSampleFiles" >
        <MakeDir Directories="AFolder" />
        <WriteLinesToFile Lines="Test" File="AFolder\test.cs" Overwrite="true" />
        <WriteLinesToFile Lines="Test" File="AFolder\test.txt" Overwrite="true" />
      </Target>

      <Target Name="DynamicExcludeList" >

        <PropertyGroup>
          <CommonFileExclusion>.\DIRECTORY_NAME_TOKEN\**\*.cs</CommonFileExclusion>
          <FinalExcludes>$(CommonFileExclusion.Replace('DIRECTORY_NAME_TOKEN', 'AFolder'))</FinalExcludes>
        </PropertyGroup>

        <Message Text="FinalExcludes: $(FinalExcludes)" />
        <ItemGroup>
          <TheFiles 
            Include=".\AFolder\**\*;" 
            Exclude="$(FinalExcludes)"
          />
        </ItemGroup>
        <Message Text="TheFiles: @(TheFiles)" />

      </Target>

      <Target Name="HardcodedExcludeList" >

        <PropertyGroup>
          <FinalExcludes>.\AFolder\**\*.cs</FinalExcludes>
        </PropertyGroup>

        <Message Text="FinalExcludes: $(FinalExcludes)" />
        <ItemGroup>
          <TheFilesWithHardcodedExcludes
            Include=".\AFolder\**\*;"
            Exclude="$(FinalExcludes)"
          />
        </ItemGroup>
        <Message Text="TheFilesWithHardcodedExcludes: @(TheFilesWithHardcodedExcludes)" />

      </Target>  
    </Project>

Это вывод, обратите внимание на различия между «TheFiles» и «TheFilesWithHardcodedExcludes».

PS C:\SVN\TrunkDeployment\TestMsBuild> msbuild .\Test.build.xml
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 8/10/2010 2:30:42 PM.
Project "C:\SVN\TrunkDeployment\TestMsBuild\Test.build.xml" on node 1 (default targets).
DynamicExcludeList:
  FinalExcludes: .\AFolder\**\*.cs
  TheFiles: .\AFolder\test.cs;.\AFolder\test.txt
HardcodedExcludeList:
  FinalExcludes: .\AFolder\**\*.cs
  TheFilesWithHardcodedExcludes: .\AFolder\test.txt
Done Building Project "C:\SVN\TrunkDeployment\TestMsBuild\Test.build.xml" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.06

РЕДАКТИРОВАНИЕ

Я обновил приведенный выше скрипт, чтобы использовать CreateItem, однако все еще остается проблема, когда список элементов для исключения содержит более 1 пути (т. е. значение CommonFileExclusion изменилось):

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Run">

      <Target Name="Run" >
        <CallTarget Targets="CreateSampleFiles" />
        <CallTarget Targets="DynamicExcludeList" />
        <CallTarget Targets="HardcodedExcludeList" />
      </Target>

      <Target Name="CreateSampleFiles" >
        <MakeDir Directories="AFolder" />
        <WriteLinesToFile Lines="Test" File="AFolder\test.cs" Overwrite="true" />
        <WriteLinesToFile Lines="Test" File="AFolder\test.txt" Overwrite="true" />
        <WriteLinesToFile Lines="Test" File="AFolder\test.vb" Overwrite="true" />
      </Target>

      <Target Name="DynamicExcludeList" >

        <PropertyGroup>
          <CommonFileExclusion>.\DIRECTORY_NAME_TOKEN\**\*.cs;.\DIRECTORY_NAME_TOKEN\**\*.vb;</CommonFileExclusion>
          <FinalExcludes>$(CommonFileExclusion.Replace('DIRECTORY_NAME_TOKEN', 'AFolder'))</FinalExcludes>
        </PropertyGroup>

        <Message Text="FinalExcludes: $(FinalExcludes)" />
        <CreateItem Include=".\AFolder\**\*;"
                     Exclude="$(FinalExcludes)">
          <Output TaskParameter="Include" ItemName="TheFiles"/>
        </CreateItem>
        <Message Text="TheFiles: @(TheFiles)" />

      </Target>

      <Target Name="HardcodedExcludeList" >

        <PropertyGroup>
          <FinalExcludes>.\AFolder\**\*.cs;.\AFolder\**\*.vb</FinalExcludes>
        </PropertyGroup>

        <Message Text="FinalExcludes: $(FinalExcludes)" />
        <CreateItem Include=".\AFolder\**\*;"
                     Exclude="$(FinalExcludes)">
          <Output TaskParameter="Include" ItemName="TheFilesWithHardcodedExcludes"/>
        </CreateItem>
        <Message Text="TheFilesWithHardcodedExcludes: @(TheFilesWithHardcodedExcludes)" />

      </Target>
    </Project>

person Keith    schedule 08.10.2010    source источник


Ответы (2)


Хорошо, я немного попробовал, и я думаю, что проблема связана с тем, что вы используете свойство, которое представляет значение SCALAR для нескольких значений. Я бы рекомендовал группировать и преобразовывать (см. http://scottlaw.knot.org/blog/?p=402 и http://msdn.microsoft.com/en-us/library/ms171476.aspx). Например, работает следующий код:

<Target Name="DynamicExcludeList" >
  <ItemGroup>
    <ExtensionsExcluded Include="cs;vb" />
  </ItemGroup>

  <CreateItem Include=".\AFolder\**\*"
          Exclude="@(ExtensionsExcluded->'.\AFolder\**\*.%(identity)')">
    <Output TaskParameter="Include" ItemName="TheFiles"/>
  </CreateItem>
  <Message Text="TheFiles: @(TheFiles)" />
</Target>
person Benjamin Baumann    schedule 08.10.2010
comment
Спасибо за исправление, это всего лишь одна из тех вещей, которые неприятны в MSBuild (IMO), поскольку чем больше исключений вы хотите добавить, тем сложнее (например, некоторые файлы в одних каталогах, но не в других и т. д.), может искать другое решение. - person Keith; 11.10.2010

В цели DynamicExcludeList используйте CreateItem задачу вместо ItemGroup для динамического создания вашего элемента:

<Target Name="DynamicExcludeList" >

  <PropertyGroup>
    <CommonFileExclusion>.\DIRECTORY_NAME_TOKEN\**\*.cs</CommonFileExclusion>
    <FinalExcludes>$(CommonFileExclusion.Replace('DIRECTORY_NAME_TOKEN', 'AFolder'))</FinalExcludes>
  </PropertyGroup>

  <Message Text="FinalExcludes: $(FinalExcludes)" />

  <CreateItem Include=".\AFolder\**\*;"
              Exclude="$(FinalExcludes)">
    <Output TaskParameter="Include" ItemName="TheFiles"/>
  </CreateItem>

  <Message Text="TheFiles: @(TheFiles)" />
</Target>

Теоретически ItemGroup и CreateItem эквивалентны, но я видел подобный случай (динамическая ситуация), когда нужно использовать CreateItem.

person Julien Hoarau    schedule 08.10.2010
comment
На самом деле все свойства и группа элементов оцениваются при анализе вашего скрипта перед запуском каждой цели. Вот почему ваши группы элементов заполняются не тем, что вы думали, а тем, что было до выполнения вашего сценария msbuild. Подробнее об этом можно прочитать здесь: blogs.msdn. com/b/msbuild/archive/2006/01/03/508629.aspx и sedodream.com/ . Хороший способ избежать этой проблемы — использовать ItemGroup вне целей (для статических элементов) и createitem внутри (для динамических элементов). - person Benjamin Baumann; 08.10.2010
comment
Спасибо за объяснение. Но если вы посмотрите документацию по CreateItem (msdn.microsoft.com/en-us /library/s2y3e43x.aspx), вы видите, что эта задача устарела, поэтому вы можете подумать, что ItemGroup внутри цели будет оцениваться динамически, как для CreateItem. - person Julien Hoarau; 08.10.2010
comment
Спасибо за отзыв, у меня все еще есть проблема (сообщение через секунду), в соответствии с этим: stackoverflow.com/questions/937681/createitem-vs-itemgroup, CreateItem устарел в 3.5 - person Keith; 08.10.2010
comment
CreateItem устарел, начиная с .NET 3.5, но вы все еще можете его использовать. И это решает вашу проблему - person Julien Hoarau; 08.10.2010
comment
это решило это в моем первоначальном примере, который был сокращен для простоты, я начал внедрять ваше исправление, но столкнулся с той же проблемой, когда добавил больше путей к значению CommonFileExclusion, если у вас все еще есть какие-либо идеи, это было бы фантастически ( я обновил свой пост с проблемой выше). Спасибо за помощь - person Keith; 08.10.2010
comment
Я посмотрел на вашу другую проблему, но для этого я понятия не имею. Я думаю, это ошибка. - person Julien Hoarau; 08.10.2010
comment
Я думаю, что это может быть та же самая проблема. Когда MSBuild сначала анализирует ваш сценарий, он оценивает CommonFileExclusion и FinalExcludes. Таким образом, FinalExcludes=NULL, потому что каталог AFolder еще не существует. Но если вы жестко закодируете FinalExcludes (без установки свойства), это должно сработать. Чтобы решить вашу проблему, вы можете изменить свою группу свойств на задачу CreateProperty, как показано в msdn .microsoft.com/en-us/library/63ckb9s9.aspx. И будьте осторожны, вы устанавливаете значение FinalExcludes два раза, а MSBuild читает ваш файл только один раз! - person Benjamin Baumann; 08.10.2010
comment
Я пробовал использовать CreateProperty вместо propertyGroup, но это не сработало. - person Julien Hoarau; 08.10.2010
comment
Я думаю, что проблема заключается в использовании свойства для нескалярного значения. Вы должны использовать элемент для FinalExcludes. Я выложил версию, которая работает. - person Benjamin Baumann; 08.10.2010