It is a very common use case to copy files under one directory to another but keep the structure of the files copied.
You can do with some external command such as xcopy and wrap it in a task in MSBuild, however, MSBulid does porvide some built-in task for copying and moving files. See theMSBuild Built-in tasks here
As discussed in this post: How to : recursively Copy Files Using the <Copy> Task.
We know we can use something like this:
<ItemGroup>
<Compile Include=".\**\*.cs" />
</ItemGroup>
<Copy SourceFiles="@(Compile)" DestinationFolder="c:\foocopy\%(RecursiveDir)"></Copy>
This is a new way of using the MSBuild task/target.. the syntax above basically instruct to expand and execute the command once for each of the elements in the Compile item group. This is part of advanced MSBuild concept, which is called MSBuild Batching.. In this special case, it is using the Task Batching....
A more traditional way is like this:
<ItemGroup>
<SourceFiles Include="$(SOURCECODE_ROOT)$(FolderName)\core\mds\WinRel\**\*.dll" />
<SourceFiles Include="$(SOURCECODE_ROOT)$(FolderName)\core\WinRel\**\*.dll" />
<SourceFiles Include="$(SOURCECODE_ROOT)$(FolderName)\core\WinRel\**\*.exe" />
<SourceFiles Include="$(SOURCECODE_ROOT)$(FolderName)\core\WinRel\**\*.tlb" />
<SourceFiles Include="$(SOURCECODE_ROOT)$(FolderName)\core\WinRel\**\*.ocx" />
</ItemGroup>
<Copy SourceFiles="@(SourceFiles)" DestinationFiles="@(SourceFiles->'$(EXECUTABLES_ROOT)$(FolderName)\$(BuildLevel)\%(RecursiveDir)%(Filename)%(Extension)')" />
The key to the solution is the special use of the MSBuild Items, the % notation start something which is called the Metadata of the Item, and the syntax of
@(SourceFiles->'$(EXECUTABLES_ROOT)$(FolderName)\$(BuildLevel)\%(RecursiveDir)%(Filename)%(Extension)')
I happen to do some task of moving files from one directory to another, please see my code below.
<Target Name="CopyDebugOutput" Condition="'$(Configuration)'=='Debug'">
<PropertyGroup>
<DestinationFolder>$(OutDir)..\..\.debug\$(FrameworkSpecializedFolder)</DestinationFolder>
</PropertyGroup>
<ItemGroup>
<SourceFiles Include="$(OutDir)**\*" />
</ItemGroup>
<Message Text="DestinationFolder = $(DestinationFolder)" />
<Message Condition="'$(CoronaVerbose)'!='False' And !Exists($(DestinationFolder))" Text="Making $(DestinationFolder)" />
<MakeDir Condition="!Exists($(DestinationFolder))" Directories="$(DestinationFolder)" />
<Message Condition="'$(CoronaVerbose)' != 'False'" Text="Moving from $(OutDir) to $(DestinationFolder)" />
<Move SourceFiles="@(SourceFiles)" DestinationFiles="@(SourceFiles->'$(DestinationFolder)\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>