Как использовать задачу копирования MSBuild для копирования в несколько папок назначения? - PullRequest
14 голосов
/ 11 сентября 2010

Я пытаюсь рекурсивно скопировать папку в несколько папок назначения, используя задачу копирования MSBuild. Я видел следующий вопрос, который дал мне хорошее начало, но я, должно быть, что-то упустил:

Создать копию в нескольких местах на основе списка параметров назначения?

Ниже приведен фрагмент моего файла сборки:

<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>

Когда я запускаю это, задача «Сообщение», как я и ожидал, выплевывает 2 строки:

\\server1\path
\\server2\path

Проблема в том, что задача «Копировать» запускается только один раз и копирует файлы в корень текущего жесткого диска, а не в указанные сетевые пути:

Копирует в C:\file1.txt вместо \\server1\path\file1.txt

Я довольно новичок в MSBuild, поэтому я чувствую, что мне здесь чего-то не хватает.

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

24 голосов
/ 11 сентября 2010

То, с чем вы здесь работаете, называется группировкой.Я довольно много писал о пакетировании.Вы можете найти мои блоги, перечисленные в 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.
4 голосов
/ 12 декабря 2012

Самым важным недостающим элементом головоломки является атрибут 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>
...