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

发布于 2024-08-16 06:07:16 字数 3009 浏览 2 评论 0原文

我有一个脚本尝试从某个目录中的所有文件构建一个 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

观察到 ExcludeFilesAExcludeFilesB 看起来相同,但生成的组 RemainingFilesARemainingFilesB代码>不同。

最终,我想使用与生成 ExcludeFilesB 相同的方式生成的模式来获取列表 RemainingFilesA 。你能建议一种方法吗,还是我必须完全重新考虑我的方法?

I have a script that attempts to construct an ItemGroup out of all files in a certain directory while excluding files with certain names (regardless of extension).

The list of files to be excluded initially contains file extensions, and I am using Community Tasks' RegexReplace to replace the extensions with an asterisk. I then use this list in the item's Exclude attribute. For some reason the files do not get excluded properly, even though the list appears to be correct.

To try and find the cause I created a test script (below) which has two tasks: first one initialises two properties with the list of file patterns in two different ways. The second task prints both properties and the files resulting from using both these properties in the Exclude attribute.

The properties' values appear to be identical, however the resulting groups are different. How is this possible?

<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="
**AllFiles**
@(AllFiles, '
')" />
    <Message Text="
**PatternedFilenames**
@(PatternedFilenames, '
')" />
    <Message Text="
**RemainingFilesA**
@(RemainingFilesA, '
')" />
    <Message Text="
**RemainingFilesB**
@(RemainingFilesB, '
')" />
  </Target>

</Project>

Output (reformatted somewhat for clarity):

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

Observe that both ExcludeFilesA and ExcludeFilesB look identical, but the resulting groups RemainingFilesA and RemainingFilesB differ.

Ultimately I want to obtain the list RemainingFilesA using the pattern generated the same way ExcludeFilesB is generated. Can you suggest a way, or do I have to completely rethink my approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

人间☆小暴躁 2024-08-23 06:07:16

当自定义任务抛出异常时,意外地揭示了真正的原因。

ExcludeFilesA 的实际值是 TestDir\SampleProj.*;TestDir\SampleLib1.* 正如人们所期望的那样。但是 ExcludeFilesB 的实际值是 TestDir\SampleProj.%2a;TestDir\SampleLib1.%2a

据推测,Message 在使用字符串之前会对其进行转义,但 IncludeExclude 不会。这可以解释为什么字符串看起来相同但行为不同。

顺便说一句,执行顺序似乎与此无关,并且我非常确定(经过大量实验)所有内容都按照该脚本中出现的顺序准确执行和评估。

The true cause of this was revealed accidentally when a custom task threw an exception.

The actual value of ExcludeFilesA is TestDir\SampleProj.*;TestDir\SampleLib1.* like one might expect. However the actual value of ExcludeFilesB is TestDir\SampleProj.%2a;TestDir\SampleLib1.%2a.

Presumably Message unescapes the string before using it, but Include and Exclude do not. That would explain why the strings look the same but behave differently.

Incidentally, the execution order doesn't seem to have anything to do with this, and I'm pretty sure (following extensive experimentation) that everything gets executed and evaluated exactly in the order in which it appears in this script.

笑,眼淚并存 2024-08-23 06:07:16

需要在目标执行之前评估 ItemGroup,并且 PatternedFilenames ItemGroup 是在其目标容器内动态创建的。
您可以使用 CreateItem 任务解决此问题,这将确保 < code>PatternedFilenames 整个执行过程中的范围:

<RegexReplace Input="@(OriginalFilenames)" Expression="\.\w+$" Replacement=".*">
  <Output TaskParameter="Output" ItemName="PatternedFilenames_tmp"/>
</RegexReplace>
<CreateItem Include="@(PatternedFilenames_tmp)">
  <Output TaskParameter="Include" ItemName="PatternedFilenames"/>
</CreateItem>

ItemGroups need to be evaluated before targets execution, and the PatternedFilenames ItemGroup is being created on the fly within its target container.
You could workaround this using the CreateItem task, which will ensure the PatternedFilenames scope throughout the execution:

<RegexReplace Input="@(OriginalFilenames)" Expression="\.\w+$" Replacement=".*">
  <Output TaskParameter="Output" ItemName="PatternedFilenames_tmp"/>
</RegexReplace>
<CreateItem Include="@(PatternedFilenames_tmp)">
  <Output TaskParameter="Include" ItemName="PatternedFilenames"/>
</CreateItem>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文