.net – Why doesn’t MSBuild copy as I would expect

msbuildnet

I'm needing to script my build. I'm using MSBUILD because of it's integration with VS.net. I am trying to copy some files from the build environment to the deployment folder. I'm using the copy task of MSBuild. But instead of copying the directory tree as I would expect. it copies all the contents into a single folder. I repeat all the files from the directory tree end up in one folder. I need it to copy the tree of folders and directories into the destination folder. Is there something I'm missing?

Here is the relavant parts of my build script:

<PropertyGroup>
    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
    <Source>outputfolder</Source>
    <DestEnv>x</DestEnv>
    <DeployPath>\\networkpath\$(DestEnv)</DeployPath>
</PropertyGroup>
<ItemGroup>
    <TargetDir Include="$(DeployPath)\**\*" Exclude="**\web.config"></TargetDir>
    <SourceDir Include="$(Source)\**\*" />
</ItemGroup>    
<Target Name="Clean" >
    <!-- clean detail ... -->
</Target>
<Target Name="migrate" DependsOnTargets="Clean">
    <Copy DestinationFolder="$(DeployPath)" SourceFiles="@(SourceDir)" />
</Target>

Best Answer

Just a gem we found as we were debugging MSBuild issues around copying:

http://blog.scrappydog.com/2008/06/subtle-msbuild-bug-feature.html

ItemGroups are parsed before Targets, so any Targets that create new files (e.g. compiles!) won't be picked up when an ItemGroup is referenced further along the script.

Eric Bowen also describes a work-around for this "feature", the CreateItem task:

<Target Name="Copy" >
    <CreateItem Include="..\Source\**\bin\**\*.exe"
        Exclude="..\Source\**\bin\**\*.vshost.exe">
        <Output TaskParameter="Include" ItemName="CompileOutput" />
    </CreateItem>
    <Copy SourceFiles="@(CompileOutput)" 
        DestinationFolder="$(OutputDirectory)"></Copy>
</Target>

Many kudos to him!