MSBuild: звездочки и странное поведение ItemGroup Exclude

У меня есть скрипт, который пытается построить ItemGroup из всех файлов в определенном каталоге, исключая файлы с определенными именами (независимо от расширения).

Список файлов, которые нужно исключить, изначально содержит расширения файлов, и я использую RegexReplace Заданий сообщества, чтобы заменить расширения на звездочку. Затем я использую этот список в атрибуте Exclude элемента. По какой-то причине файлы не исключаются должным образом, хотя список кажется правильным.

Чтобы попытаться найти причину, я создал тестовый скрипт (ниже), который имеет две задачи: первая инициализирует два свойства со списком шаблонов файлов двумя разными способами. Вторая задача выводит оба свойства и файлы, полученные в результате использования обоих этих свойств в атрибуте Exclude.

Значения свойств кажутся идентичными, однако результирующие группы различаются. Как это возможно?

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         DefaultTargets="Init;Test" ToolsVersion="3.5">
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

  <Target Name="Init">
    <ItemGroup>
      <OriginalFilenames Include="TestDir\SampleProj.exe"/>
      <OriginalFilenames Include="TestDir\SampleLib1.dll"/>
    </ItemGroup>
    <RegexReplace Input="@(OriginalFilenames)" Expression="\.\w+$" Replacement=".*">
      <Output TaskParameter="Output" ItemName="PatternedFilenames"/>
    </RegexReplace>
    <PropertyGroup>
      <ExcludeFilesA>TestDir\SampleProj.*;TestDir\SampleLib1.*</ExcludeFilesA>
      <ExcludeFilesB>@(PatternedFilenames)</ExcludeFilesB>
    </PropertyGroup>
  </Target>

  <Target Name="Test">
    <Message Text='ExcludeFilesA: $(ExcludeFilesA)' />
    <Message Text='ExcludeFilesB: $(ExcludeFilesB)' />
    <ItemGroup>
      <AllFiles Include="TestDir\**"/>
      <RemainingFilesA Include="TestDir\**" Exclude="$(ExcludeFilesA)"/>
      <RemainingFilesB Include="TestDir\**" Exclude="$(ExcludeFilesB)"/>
    </ItemGroup>
    <Message Text="&#xA;**AllFiles**&#xA;@(AllFiles, '&#xA;')" />
    <Message Text="&#xA;**PatternedFilenames**&#xA;@(PatternedFilenames, '&#xA;')" />
    <Message Text="&#xA;**RemainingFilesA**&#xA;@(RemainingFilesA, '&#xA;')" />
    <Message Text="&#xA;**RemainingFilesB**&#xA;@(RemainingFilesB, '&#xA;')" />
  </Target>

</Project>

Вывод (несколько переформатирован для ясности):

ExcludeFilesA: TestDir\SampleProj.*;TestDir\SampleLib1.*
ExcludeFilesB: TestDir\SampleProj.*;TestDir\SampleLib1.*

AllFiles:
  TestDir\SampleLib1.dll
  TestDir\SampleLib1.pdb
  TestDir\SampleLib2.dll
  TestDir\SampleLib2.pdb
  TestDir\SampleProj.exe
  TestDir\SampleProj.pdb

PatternedFilenames:
  TestDir\SampleProj.*
  TestDir\SampleLib1.*

RemainingFilesA:
  TestDir\SampleLib2.dll
  TestDir\SampleLib2.pdb

RemainingFilesB:
  TestDir\SampleLib1.dll
  TestDir\SampleLib1.pdb
  TestDir\SampleLib2.dll
  TestDir\SampleLib2.pdb
  TestDir\SampleProj.exe
  TestDir\SampleProj.pdb

Обратите внимание, что ExcludeFilesA и ExcludeFilesB выглядят одинаково, но результирующие группы RemainingFilesA и RemainingFilesB различаются.

В конечном итоге я хочу получить список RemainingFilesA, используя шаблон, сгенерированный так же, как ExcludeFilesB. Можете ли вы предложить способ, или я должен полностью переосмыслить свой подход?


person Roman Starkov    schedule 19.12.2009    source источник


Ответы (2)


Истинная причина этого обнаружилась случайно, когда пользовательская задача выдала исключение.

Фактическое значение ExcludeFilesA равно TestDir\SampleProj.*;TestDir\SampleLib1.*, как и следовало ожидать. Однако фактическое значение ExcludeFilesB равно TestDir\SampleProj.%2a;TestDir\SampleLib1.%2a.

Предположительно Message отключает экранирование строки перед ее использованием, но Include и Exclude этого не делают. Это объясняет, почему строки выглядят одинаково, но ведут себя по-разному.

Кстати, порядок выполнения, кажется, не имеет к этому никакого отношения, и я почти уверен (после длительных экспериментов), что все выполняется и оценивается именно в том порядке, в котором оно появляется в этом сценарии.

person Roman Starkov    schedule 20.12.2009
comment
Большое спасибо! Я попытался фактически избежать звездочки в <FooItem Include="bar*baz" />, чтобы позже получить ее дословно без подстановки файлов, и я не смог найти никакой информации об этом. Ваше исследование помогло мне - использование %2a отключило расширение, и все преобразования @-> в более поздней команде получили звездочку. - person quetzalcoatl; 23.10.2017
comment
aи сразу после написания этого комментария я нашел docs.microsoft .com/en-us/visualstudio/msbuild/, где это явно указано для включения и звездочки, да. - person quetzalcoatl; 23.10.2017

ItemGroups должны быть оценены перед выполнением целей, и PatternedFilenames ItemGroup создается на лету в своем целевом контейнере. Вы можете обойти это с помощью задачи CreateItem, которая обеспечит PatternedFilenames охват на протяжении всего исполнения:

<RegexReplace Input="@(OriginalFilenames)" Expression="\.\w+$" Replacement=".*">
  <Output TaskParameter="Output" ItemName="PatternedFilenames_tmp"/>
</RegexReplace>
<CreateItem Include="@(PatternedFilenames_tmp)">
  <Output TaskParameter="Include" ItemName="PatternedFilenames"/>
</CreateItem>
person KMoraz    schedule 19.12.2009
comment
О... спасибо за обходной путь, я попробую. Не могли бы вы объяснить порядок, в котором происходят события более подробно, пожалуйста? Ясно, что ExcludeFilesA инициализируется до создания ItemGroup RemainingFilesA; почему то же самое не происходит с RemainingFilesB? Это выглядит примерно так: 1. Заканчивается инициализация; 2. Оцениваются группы элементов из теста; 3. RegexReplace оценивается; 4. Тест начинается и печатает сообщения. Но разве этого не может быть?.. - person Roman Starkov; 20.12.2009
comment
Прочтите раздел «Порядок оценки свойств и элементов» здесь: msdn.microsoft. com/en-us/library/dd997067(VS.100).aspx Это сообщение связано с вашей проблемой: elegantcode.com/2006/12/11/ - person KMoraz; 20.12.2009