MSBuild:星号和奇怪的ItemGroup排除行为

3
我有一个脚本,试图从特定目录中的所有文件中构建一个ItemGroup,同时排除具有特定名称的文件(无论扩展名如何)。
要排除的文件列表最初包含文件扩展名,我使用Community Tasks的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

请注意,ExcludeFilesAExcludeFilesB看起来相同,但生成的组RemainingFilesARemainingFilesB不同。
最终我想使用以与ExcludeFilesB相同方式生成的模式来获取列表RemainingFilesA。你能提供一种方法吗?还是我必须完全重新考虑我的方法?
2个回答

2
这个问题的真正原因是一个自定义任务抛出了异常。 ExcludeFilesA 的实际值是 TestDir\SampleProj.*;TestDir\SampleLib1.*,就像人们所期望的那样。然而,ExcludeFilesB 的实际值是 TestDir\SampleProj.%2a;TestDir\SampleLib1.%2a
可能是 Message 在使用字符串之前对其进行了转义,但是 IncludeExclude 没有这样做。这就解释了为什么字符串看起来相同但行为不同。
顺便说一下,执行顺序似乎与此无关,并且我非常确定(通过广泛的实验)脚本中的所有内容都按照出现顺序精确执行和评估。

非常感谢!我尝试在<FooItem Include="bar*baz" />中实际转义星号,以便稍后可以直接获取它而不进行文件全局匹配,但我找不到任何相关信息。您的研究帮助了我 - 使用%2a关闭了扩展,并且稍后命令中的所有@->转换都得到了星号。 - quetzalcoatl
在写完这条评论后,我立刻发现了https://learn.microsoft.com/en-us/visualstudio/msbuild/special-characters-to-escape,其中明确指出了Include和星号需要转义。 - quetzalcoatl

0

在执行目标之前需要评估 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>

哦...谢谢你的解决方法,我会试一下。你能否详细解释一下事情发生的顺序吗?显然,在创建ItemGroup“RemainingFilesA”之前,ExcludeFilesA已经被初始化了;为什么对于RemainingFilesB不是这样呢?它看起来几乎是这样的顺序:1. “Init”完成;2. 从“Test”评估ItemGroups;3. RegexReplace被评估;4. “Test”开始并打印消息。但这肯定不可能是这样的,对吧?... - Roman Starkov
请阅读此处的“属性和项评估顺序”部分: http://msdn.microsoft.com/zh-cn/library/dd997067(VS.100).aspx 这篇文章与您的问题有关: http://elegantcode.com/2006/12/11/msbuild-items-run-time-evaluation-behavior/ - KMoraz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接