MSBuild的ItemGroup包含/排除模式问题

12

问题:根据传递的排除属性值,ItemGroups数组未正确构建。

如果运行此脚本,它会创建一些示例文件,然后尝试基于包含/排除属性创建一个名为TheFiles的数组,问题是当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.
开始构建 2022/3/26 下午2:30:42。 项目“C:\SVN\TrunkDeployment\TestMsBuild\Test.build.xml”在节点 1 上(默认目标)。 DynamicExcludeList: FinalExcludes: .\AFolder\**\*.cs TheFiles: .\AFolder\test.cs;.\AFolder\test.txt HardcodedExcludeList: FinalExcludes: .\AFolder\**\*.cs TheFilesWithHardcodedExcludes: .\AFolder\test.txt 已完成项目“C:\SVN\TrunkDeployment\TestMsBuild\Test.build.xml”(默认目标)的生成。
生成成功。 0 个警告 0 个错误
用时 00:00:00.06

编辑

我已更新上述脚本以使用CreateItem,但当要排除的项目列表包含多个路径时仍存在问题(即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>
2个回答

21

好的,我尝试了一下,我认为问题出在你使用一个代表多个值的标量值属性上。我建议批处理和转换(参见http://scottlaw.knot.org/blog/?p=402http://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>

1
感谢修正,这只是在 MSBuild 中出现的一些麻烦事情之一(在我看来),随着你想要加入更多排除项,它变得越来越复杂(例如:某些目录中的某些文件,但不包括其他文件等),也许应该寻找另一个解决方案。 - Keith

3
在目标 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>

理论上,ItemGroupCreateItem是等效的,但我见过这样的情况(动态情况),在这种情况下必须使用CreateItem

1
事实上,当解析您的脚本并在运行每个目标之前,属性和ItemGroup都会被评估。这就是为什么您的ItemGroup没有填充您所想要的内容,而是填充了msbuild脚本执行之前存在的内容。您可以在此处阅读更多信息:http://blogs.msdn.com/b/msbuild/archive/2006/01/03/508629.aspx和http://www.sedodream.com/PermaLink,guid,dd6cb1db-c0e4-47f7-ad84-6e59ff6b03d0.aspx。避免此问题的好方法是在目标外部使用ItemGroup(用于静态项),并在内部创建项目(用于动态项)。 - Benjamin Baumann
谢谢解释。但是如果你查看CreateItem的文档(http://msdn.microsoft.com/en-us/library/s2y3e43x.aspx),你会发现该任务已被弃用,因此你可以认为在目标内部的ItemGroup将像CreateItem一样动态评估。 - Julien Hoarau
感谢您的反馈,我仍然存在一个问题(稍后发布),根据这个:https://dev59.com/dnNA5IYBdhLWcg3wfeAm,在3.5中CreateItem已经过时了。 - Keith
它在我的初始示例中解决了问题,该示例为简化而削减。我开始实施您的修复程序,但是一旦将更多路径添加到CommonFileExclusion的值中,就会遇到相同的问题。如果您仍然有任何想法,那将是太棒了(我已更新我的帖子以反映上述问题)。感谢您的帮助。 - Keith
我尝试使用CreateProperty而不是propertyGroup,但它没有起作用。 - Julien Hoarau
显示剩余4条评论

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