Recursively copied folders in MSBuild are incorrectly copied into one folder

272 views Asked by At

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.

1

There are 1 answers

5
evilpurple On

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