如何使用MSBuild复制任务复制到多个目标文件夹?

时间:2010-09-11 03:10:25

标签: msbuild msbuild-task

我正在尝试使用MSBuild的复制任务递归地将文件夹复制到多个目标文件夹。我已经看到以下问题给了我一个良好的开端,但我必须遗漏一些东西:

Msbuild copy to several locations based on list of destination parameter?

我的构建文件中的代码段如下:

<ItemGroup>
    <DeployPath Include="\\server1\path" />
    <DeployPath Include="\\server2\path" />
</Item Group>

<Target Name="Deploy">
    <Message Text="%(DeployPath.Identity)" />
    <Copy SourceFiles="@(ItemsToCopy)" DestinationFolder="%(DeployPath.Identity)\%(RecursiveDir)" />
</Target>

当我运行它时,正如我所料,“消息”任务会吐出两行:

\\server1\path
\\server2\path

问题是,“复制”任务似乎只运行一次,并将文件复制到当前硬盘的根目录而不是指定的网络路径:

复制到C:\file1.txt而不是\\server1\path\file1.txt

我是MSBuild的新手,所以我觉得我在这里缺少一些非常基本的东西。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:24)

您在这里处理的内容称为批处理。我在博客上写了很多关于批处理的文章。您可以在http://sedotech.com/Resources#Batching找到我的博客。批处理是一种在MSBuild中实际执行循环的方法。您可以使用公共元数据值将组拆分为值。元数据可以是Identity,FullPath,Filename等值。您甚至可以创建自己的元数据。在任何情况下,当您批量处理多个值时,它们将彼此独立地进行批处理。看看我创建的示例。执行目标的结果显示在脚本之后。

<Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <ItemsToCopy Include="src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt"/>
  </ItemGroup>

  <ItemGroup>
    <DeployPath Include="C:\temp\path01\" />
    <DeployPath Include="C:\temp\path02\" />
  </ItemGroup>

  <!--
    Target batching is happening here because there is a 
    %() expression inside the Outputs attribute. So that 
    means that this target will be repeated once per
    uinque batch of %(DeployPath.Identity). Identity is
    the value that is passed in the Incude= attribute.
    Since we know there are two values we know that
    this target will be executed twice, and on each 
    pass the DeployPath item will only look to contain
    a single value. If there were duplicates then the list
    could contain more than 1 value.
  -->
  <Target Name="Demo" Outputs="%(DeployPath.Identity)">
    <Message Text="DeployPath.Identity: %(DeployPath.Identity)" />

    <Message Text="======================================" Importance="high"/>
    <Message Text="ItemsToCopy1: @(ItemsToCopy)|| DeployPath.Identity: %(DeployPath.Identity)" />
    <Message Text="======================================" Importance="high"/>
    <!--
      In the next emample you are batching on both the DeployPath item list as well as
      the ItemsToCopy item. When two batched items are in the same expression they are
      matched individually, so you ge a value for DeployPath metadata but not ItemsToCopy
      metadata. That is why your copy only copied to one location.
    -->
    <Message Text="ItemsToCopy2: @(ItemsToCopy)|| DeployPath.Identity-RecursiveDir: %(DeployPath.Identity)\%(RecursiveDir)" />
    <Message Text="======================================" Importance="high"/>
    <!-- 
      In this example I create a property and assign it the value of 
      %(DeployPath.Identity). We know there will only be one such
      value. Because there should only be one value with Identity 
      when this target is executed so it is safe to 
      convert item to property

      Because we are not batching on both items we will get the values for both vaules
      to be correct becuase the target is repeated for the other
      DeployPath values.
    -->
    <PropertyGroup>
      <_DeployPathIdentity>%(DeployPath.Identity)</_DeployPathIdentity>
    </PropertyGroup>
    <Message Text="ItemsToCopy3: @(ItemsToCopy)|| _DeployPathIdentity-RecursiveDir: $(_DeployPathIdentity)\%(RecursiveDir)" />

    <!-- 
      I've always preferred to use DestinationFiles so my sample
      below uses that. But you could change the target to use
      DestinationFolder instead.
    -->
    <Copy SourceFiles="@(ItemsToCopy)"
          DestinationFiles="@(ItemsToCopy->'$(_DeployPathIdentity)%(RecursiveDir)%(Filename)%(Extension)')" />
  </Target>

</Project>

<强>输出

Build started 9/10/2010 9:31:28 PM.
Project "I:\Development\My Code\Community\MSBuild\CopyFiles01.proj" on node 1 (default targets).
Demo:
  DeployPath.Identity: C:\temp\path01\
  ======================================
  ItemsToCopy1: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| DeployPath.I
  dentity: C:\temp\path01\
  ======================================
  ItemsToCopy2: || DeployPath.Identity-RecursiveDir: C:\temp\path01\\
  ItemsToCopy2: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| DeployPath.I
  dentity-RecursiveDir: \
  ======================================
  ItemsToCopy3: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| _DeployPathI
  dentity-RecursiveDir: C:\temp\path01\\
  Creating directory "C:\temp\path01".
  Copying file from "src\0001.txt" to "C:\temp\path01\0001.txt".
  Copying file from "src\0002.txt" to "C:\temp\path01\0002.txt".
  Copying file from "src\sub\sub-0001.txt" to "C:\temp\path01\sub-0001.txt".
  Copying file from "src\sub\sub-0002.txt" to "C:\temp\path01\sub-0002.txt".
Demo:
  DeployPath.Identity: C:\temp\path02\
  ======================================
  ItemsToCopy1: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| DeployPath.I
  dentity: C:\temp\path02\
  ======================================
  ItemsToCopy2: || DeployPath.Identity-RecursiveDir: C:\temp\path02\\
  ItemsToCopy2: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| DeployPath.I
  dentity-RecursiveDir: \
  ======================================
  ItemsToCopy3: src\0001.txt;src\0002.txt;src\sub\sub-0001.txt;src\sub\sub-0002.txt|| _DeployPathI
  dentity-RecursiveDir: C:\temp\path02\\
  Creating directory "C:\temp\path02".
  Copying file from "src\0001.txt" to "C:\temp\path02\0001.txt".
  Copying file from "src\0002.txt" to "C:\temp\path02\0002.txt".
  Copying file from "src\sub\sub-0001.txt" to "C:\temp\path02\sub-0001.txt".
  Copying file from "src\sub\sub-0002.txt" to "C:\temp\path02\sub-0002.txt".
Done Building Project "I:\Development\My Code\Community\MSBuild\CopyFiles01.proj" (default targets
).


Build succeeded.

答案 1 :(得分:4)

拼图中最重要的缺失部分似乎是Outputs元素上的Target属性,没有它,您将始终只为整个列表中的一个项目执行目标。另一件是你需要在路上定义的新属性。

您的问题的解决方案可能如下所示:

<ItemGroup>
    <DeployPath Include="\\server1\path" />
    <DeployPath Include="\\server2\path" />
</ItemGroup>

<Target Name="Deploy" Outputs="%(DeployPath.Identity)">
    <PropertyGroup>
        <Destination>%(DeployPath.Identity)</Destination>
    </PropertyGroup>
    <Message Text="Processing: '$(Destination)" />
    <Copy SourceFiles="@(ItemsToCopy)"
          DestinationFolder="%(DeployPath.Identity)\%(RecursiveDir)" />
</Target>