【问题标题】:Copy Item's Metadata for batching复制项目的元数据以进行批处理
【发布时间】:2011-11-30 15:46:45
【问题描述】:

这是this question的扩展。
假设我正在从其他项目的元数据创建项目:

<ItemGroup>
    <A>
        <files>f1;f2;f3;...</files>
        <x>...</x>
        <y>...</y>
        <z>...</z>
        ...
    </A>
    <B Include="%(A.files)">
        <x>%(A.x)</x>
        <y>%(A.y)</y>
        <z>%(A.z)</z>
        ...
    </B>
</ItemGroup>

%(A.files) 是由; 分隔的文件列表,因此对于每个 A 项,我会创建许多 B 项(每个文件一个)。
但是,当我处理 B 项目时,我经常需要原始的 A 项目元数据。在此示例中,我将每个元数据手动从 A 复制到 B

有没有一种方法可以将 A 的所有元数据复制到 B 而无需明确指定每个元数据?

【问题讨论】:

    标签: msbuild


    【解决方案1】:

    据我所知,MSBuild 仅在输入和输出项列表之间存在一对一映射时复制元数据。在您的情况下,您从一个项目开始并扩展到多个项目。为了解决这个问题,我建议使用项目批处理:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <Project
        xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
        ToolsVersion="4.0"
        DefaultTargets="Print">
    
        <ItemGroup>
            <A Include="A1">
                <files>test.htm;test_sol1.htm</files>
                <x>a1</x>
                <y>b1</y>
                <z>c1</z>
            </A>
            <A Include="A2">
                <files>test.proj</files>
                <x>a2</x>
                <y>b2</y>
                <z>c2</z>
            </A>
        </ItemGroup>
    
        <Target Name="ExpandA">
            <ItemGroup>
                <ExpandedA Include="%(A.files)">
                    <Original>%(Identity)</Original>
                </ExpandedA>
            </ItemGroup>
        </Target>
    
        <Target
            Name="CopyMetadata"
            Outputs="%(ExpandedA.Identity)"
            DependsOnTargets="ExpandA">
    
            <PropertyGroup>
                <ExpandedAIdentity>%(ExpandedA.Identity)</ExpandedAIdentity>
                <ExpandedAOriginal>%(ExpandedA.Original)</ExpandedAOriginal>
            </PropertyGroup>
            <ItemGroup>
                <ExpandedAMetadata Include="@(A)" Condition=" '%(Identity)' == '$(ExpandedAOriginal)' ">
                    <Expanded>$(ExpandedAIdentity)</Expanded>
                </ExpandedAMetadata>
            </ItemGroup>
        </Target>
    
        <Target Name="Print" DependsOnTargets="CopyMetadata">
            <ItemGroup>
                <B Include="@(ExpandedAMetadata->'%(Expanded)')" />
            </ItemGroup>
    
            <!--Use commas to illustrate that "files" has been expanded-->
            <Message Text="A: %(A.files)" />
            <Message Text="ExpandedA: @(ExpandedA, ',')" />
            <Message Text="ExpandedAMetadata: @(ExpandedAMetadata, ',')" />
            <Message Text="B: @(B->'%(Identity) x:%(x) y:%(y) z:%(z)', ',')" />
        </Target>
    </Project>
    

    以及“打印”目标的输出:

    Print:
      A: test.htm;test_sol1.htm
      A: test.proj
      ExpandedA: test.htm,test_sol1.htm,test.proj
      ExpandedAMetadata: A1,A1,A2
      B: test.htm x:a1 y:b1 z:c1,test_sol1.htm x:a1 y:b1 z:c1,test.proj x:a2 y:b2 z:c2
    

    ExpandedA 类似于您原始问题中的B;它是A 的扩展版本,但没有任何元数据。然后我为ExpandedA 中的每个项目运行一次CopyMetadata 目标(感谢项目批处理)。每次运行时,原始的 A 项目及其所有元数据都会复制到 ExpandedAMetadata 项目组。 Original 元数据用于确保正确的A 项目与每个文件相关联。最后,在Print 目标中,B 是使用项目转换构造的,因此来自ExpandedAMetadata 的所有元数据也会被复制。

    【讨论】:

    • 不错的解决方案,我喜欢您使用批处理的方式。但是每次我需要复制元数据时,我都需要编写一个额外的“CopyMetadata”目标来进行批处理。我发现自己经常复制元数据,我希望有一些“CopyMetadata”任务或类似的东西可以让我做到这一点,而无需为每个复制操作编写另一个目标。 (也许是“复制元数据”内联任务...)
    【解决方案2】:

    有点晚了,但我更喜欢这个解决方案:

    <B Include="@(A->Metadata('files'))" />
    

    完整示例:

    <?xml version="1.0" encoding="utf-8"?>
    <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    
        <ItemGroup>
            <A Include="A1">
                <files>a1_file1.htm;a1_file2.htm</files>
                <x>a1</x>
                <y>b1</y>
                <z>c1</z>
            </A>
            <A Include="A2">
                <files>a2_file.proj</files>
                <x>a2</x>
                <y>b2</y>
                <z>c2</z>
            </A>
            <B Include="@(A->Metadata('files'))" />
        </ItemGroup>
    
        <Target Name="Build">
            <Message Text="A: @(A->'%(Identity) x:%(x) y:%(y) z:%(z) files:%(files)', '&#x0D;&#x0A;   ')" />
            <Message Text="B: @(B->'%(Identity) x:%(x) y:%(y) z:%(z) files:%(files)', '&#x0D;&#x0A;   ')" />
        </Target>
    
    </Project>
    

    输出:

    Build:
      A: A1 x:a1 y:b1 z:c1 files:a1_file1.htm;a1_file2.htm
         A2 x:a2 y:b2 z:c2 files:a2_file.proj
      B: a1_file1.htm x:a1 y:b1 z:c1 files:a1_file1.htm;a1_file2.htm
         a1_file2.htm x:a1 y:b1 z:c1 files:a1_file1.htm;a1_file2.htm
         a2_file.proj x:a2 y:b2 z:c2 files:a2_file.proj
    

    【讨论】:

      【解决方案3】:

      我不确定我是否得到了你需要的东西,但是你可以像answer for related question 一样将所有 A 元数据复制到 B:

      <B Include="@(A)">
      

      它应该将所有元数据从 A 复制到 B。

      【讨论】:

      • 没有。我需要为文件元数据中的每个文件创建一个 B 项,而不是您建议的每个 A 项。
      【解决方案4】:

      这是对原始问题的直接回答:

        <!-- Copy all metadata from an input item to an output item -->
        <UsingTask
          TaskName="CopyMetadataTask"
          TaskFactory="CodeTaskFactory"
          AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
          <ParameterGroup>
            <InputItem ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
            <OutputItem ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" />
          </ParameterGroup>
          <Task>
            <Using Namespace="System"/>
            <Code Type="Fragment" Language="cs">
              <![CDATA[
                InputItem.CopyMetadataTo(OutputItem);
              ]]>
            </Code>
          </Task>
        </UsingTask>
      

      您可以使用此任务以原始方式在项目之间复制元数据,并且可以轻松地将其调整为输入/输出上的多个项目,尽管这样做会引发问题,即必须匹配输入和输出1/1,或做出其他妥协,尽管它可以很容易地适应确切的情况,因为您可以修改 C# 代码,但这是必要的。例如:

        <UsingTask
          TaskName="CopyMetadataTask"
          TaskFactory="CodeTaskFactory"
          AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
          <ParameterGroup>
            <InputItem ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <OutputItem ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
          </ParameterGroup>
          <Task>
            <Using Namespace="System"/>
            <Code Type="Fragment" Language="cs">
              <![CDATA[
                for (int i = 0; i < InputItem.Length; ++i)
                {
                  InputItem[i].CopyMetadataTo(OutputItem[i]);
                }
              ]]>
            </Code>
          </Task>
        </UsingTask>
      

      然后可以调用该任务直接复制元数据,例如(使用第二个版本):

      <ItemGroup>
        <ItemListA Include="a;b;c" Metadata="MetadataValue" />
        <ItemListB Include="d;e;f" OtherMetadata="OtherValue" />
      </ItemGroup>
      <CopyMetadataTask InputItem="@(ItemListA)" OutputItem="@(ItemListB)" />
      

      【讨论】:

        猜你喜欢
        • 2016-08-11
        • 2023-03-25
        • 1970-01-01
        • 2013-01-29
        • 1970-01-01
        • 2012-10-30
        • 2018-08-17
        • 2015-12-22
        • 1970-01-01
        相关资源
        最近更新 更多