0

I am writing an MSBuild target/xml configuration file.

Let's say I have two folders in my project source folder which I want to copy over. I want to copy over just these folders (and their contents), and ignore the other folders.

The specification below is copying the correct files, however they are all just dumped into one folder. The two top level folders (bin and obj) are lost, and their contents are just combined into one folder.

I want to basically copy the two folders into the new top level folder, exactly as they and their contents are.

Script

<Target Name="Package" DependsOnTargets="Build">
    <Message Text="Copying."></Message>
    <ItemGroup>
        <PackagedFiles Include="$(PipelineFolder)MyFolder\bin\**;
                                $(PipelineFolder)MyFolder\obj\**" />
    </ItemGroup>

    <MakeDir Directories="$(PipelineFolder)CopiedFolder" />
    <Copy SourceFiles="@(PackagedFiles)" DestinationFolder="$(PipelineFolder)CopiedFolder\%(RecursiveDir)%(Filename)%(Extension)" />

</Target>

What is the easiest way to copy just the top level folders, I guess the wildcard in the PackagedFiles are pointing to everything within these folders. I can't do this from the folder above the working directory as then I will copy all the other folders (not just the ones I want, bin and obj).

Thanks.

dahui
  • 2,128
  • 2
  • 21
  • 40

1 Answers1

1

You can use batching for that. The first target defines the folders to include and the second target first defines those folders with the use DependsOnTargets attribute. The Outputs attribute tells MSBuild to use batching for that target.

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

<Target Name="DefinePackageFolders">
    <ItemGroup>
        <PackageFolders Include="bin"/>
        <PackageFolders Include="obj"/>
    </ItemGroup>
</Target>

<Target Name="CopyPackageFolders" DependsOnTargets="DefinePackageFolders" Outputs="%(PackageFolders.Identity)">
    <Message Text="Copying %(PackageFolders.Identity)" />
    <PropertyGroup>
        <TargetSubFolder>%(PackageFolders.Identity)</TargetSubFolder>
    </PropertyGroup>
    <ItemGroup>
        <Files Include="$(PipelineFolder)MyFolder\%(PackageFolders.Identity)\**" />
    </ItemGroup>
    <MakeDir Directories="$(PipelineFolder)CopiedFolder\%(PackageFolders.Identity)" />
    <Copy SourceFiles="@(Files)" DestinationFiles="$(PipelineFolder)CopiedFolder\$(TargetSubFolder)\%(RecursiveDir)%(Filename)%(Extension)" />
</Target>

</Project>

Note: You have to wrap %(PackageFolders.Identity) into a separate property because you can not use two different itemgroups in the DestinationFiles attribute.

Lets say you have the following input data:

C:\PipelineFolder\MyFolder\bin\bin.txt
C:\PipelineFolder\MyFolder\obj\obj.txt

Let's assume the provided script is located in C:\test.proj Then you would call the script with a given PipelineFolder property.

C:\> msbuild test.proj /p:PipelineFolder=C:\PipelineFolder\

The resulting file set would be:

C:\PipelineFolder\CopiedFolder\bin\bin.txt
C:\PipelineFolder\CopiedFolder\obj\obj.txt
evilpurple
  • 41
  • 1
  • 7
  • Thanks for the answer! – dahui Feb 11 '16 at 15:04
  • Sorry I only just got round to doing this, I had to put the project on hold. This isn't quite working as it's still just dumping all the matching files in one directory, so I've unmarked it as the answer for now. Is it important that I have the "DefinePackageFolders" and "CopyPackageFolders" in seperate targets? – dahui Feb 16 '16 at 14:29
  • It's a bit closer to working, but it's still putting all the files in one of my nested folders (the second "obj" folder) – dahui Feb 16 '16 at 14:32
  • @dahui The separation of targets is by design. One target defines the folders to analyze, the other target will be called for each defined folder. – evilpurple Feb 17 '16 at 12:47
  • @dahui I added an example set of input and output data. Maybe you clould clarify what is exactly wrong with that output. – evilpurple Feb 17 '16 at 12:58