Veli Gebrev
Veli Gebrev

Reputation: 2521

MSBuild CopyTask: Copying the same file to multiple locations

Is there a way to get the CopyTask to copy the same file to multiple locations?

eg. I've generated an AssemblyInfo.cs file and want to copy it across to all my projects before building.

Upvotes: 1

Views: 2746

Answers (3)

Apeiron
Apeiron

Reputation: 704

I had a need to copy the contents of a directory to multiple locations, this is what I came up with that works. So I am posting it here ins case anyone else is in similar need and comes across this question like I did.

<!-- Create a list of the objects in PublishURL so it will copy to multiple directories -->
<ItemGroup>
    <PublishUrls Include="$(PublishUrl)"/>
</ItemGroup>
<PropertyGroup>
   <Files>$(OutputPath)\**\*</Files>
</PropertyGroup>

<!-- CopyNewFiles will copy all the files in $(OutputPath) to all the directories in the
     in $(PublishUrl). $(PublishUrl) can be a single directory, or a list of directories
     separated by a semicolon     -->
<Target Name ="CopyNewFiles">

    <!-- Get list of all files in the output directory; Cross product this with all 
    the output directories. -->
    <CreateItem Include ="$(Files)"
        AdditionalMetadata="RootDirectory=%(PublishUrls.FullPath)">
      <Output ItemName ="OutputFiles" TaskParameter ="Include"/>
    </CreateItem>

    <Message Text="'@(OutputFiles)' -> '%(RootDirectory)\%(RecursiveDir)'"/>

    <Copy  SourceFiles="@(OutputFiles)"
           DestinationFolder ="%(RootDirectory)\%(RecursiveDir)"/>
</Target>

If you want to copy AssemblyInfo.cs to Folders A and B you would set the property Files="AssemblyInfo.cs" and PublishUrls="A;B"

What makes this work is the extra metadata in the CreateItem task AdditionalMetadata="RootDirectory=%(PublishUrls.FullPath)" so for each files found in File it creates 1 entry for each item found in PublishUrls. In your case of a single file the equivelent in writing out the xml would be:

<ItemGroup>
    <OutputFiles Include="AssemblyInfo.cs">
        <RootDirectory>A</RootDirectory>
    </OutputFiles>
    <OutputFiles Include="AssemblyInfo.cs">
        <RootDirectory>B</RootDirectory>
    </OutputFiles>
</ItemGroup>

Now if you copied the contents of a folder that had files 1.txt and 2.txt copied to A and B the equivalent xml would be:

<ItemGroup>
    <OutputFiles Include="1.txt">
        <RootDirectory>A</RootDirectory>
    </OutputFiles>
    <OutputFiles Include="2.txt">
        <RootDirectory>A</RootDirectory>
    </OutputFiles>
    <OutputFiles Include="1.txt">
        <RootDirectory>B</RootDirectory>
    </OutputFiles>
    <OutputFiles Include="2.txt">
        <RootDirectory>B</RootDirectory>
    </OutputFiles>
</ItemGroup>

Upvotes: 1

Gerrie Schenck
Gerrie Schenck

Reputation: 22368

Check out the RoboCopy build task which is part of the Community Build Tasks library which you can find here. RoboCopy can copy one source file to multiple destinations.

On a side note: why don't you use one AssemblyInfo file on solution level and link to that in your projects if you need the same information in every project? Check out my accepted answer on this question: Automatic assembly version number management in VS2008

Upvotes: 2

Veli Gebrev
Veli Gebrev

Reputation: 2521

Right, well maybe I should attempt to do the things I want to do before asking for help :)

    <ItemGroup>
        <AssemblyInfoSource 
            Include="AssemblyInfo.cs;AssemblyInfo.cs" />
        <AssemblyInfoDestination
            Include="$(Destination1)\AssemblyInfo.cs;$(Destination2)\AssemblyInfo.cs" />
    </ItemGroup>

    <Copy SourceFiles="@(AssemblyInfoSource)" DestinationFiles="@(AssemblyInfoDestination)" />

Upvotes: 2

Related Questions