I wonder how to synchronize two folders including subfolders using MSBuild.
What I like to do is
a) to copy all files from the source folder to the dest folder that are newer or don't exist in the dest folder
and
b) to remove all files from the dest folder that don't exist (anymore) in the source folder
a) is pretty easy using the <Copy>
task but how can I accomplish b) ?
This is my build file so far:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0"
DefaultTargets="Backup">
<PropertyGroup>
<SourceFolder>C:\source</SourceFolder>
<DestFolder>C:\dest</DestFolder>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$(SourceFolder)\**" />
</ItemGroup>
<Target Name="Backup">
<!-- copy all files from the source folder to the dest folder
that are newer or don't exist in the dest folder -->
<Copy
SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy->'$(DestFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="True" />
<!-- TODO: remove all files from the dest folder
that don't exist in the source folder -->
</Target>
</Project>
You can do it with the GetDistinctItems
task from MSBuild Extension pack.
The basic idea is to get the distinct items between files from the source and the destination folder.
<Project ToolsVersion="3.5" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks" />
<PropertyGroup>
<SourceFolder>C:\source</SourceFolder>
<DestFolder>C:\dest</DestFolder>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$(SourceFolder)\**" />
</ItemGroup>
<Target Name="Backup">
<!-- copy all files from the source folder to the dest folder
that are newer or don't exist in the dest folder -->
<Copy SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy->'$(DestFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="True" />
<!-- Remove all files from the dest folder
that don't exist in the source folder -->
<ItemGroup>
<DestFiles Include="$(DestFolder)\**"/>
</ItemGroup>
<!-- Cannot compare FilesToCopy with DestFiles directly,
root folders are different-->
<ItemGroup>
<SrcFilesLeave Include="%(FilesToCopy.RecursiveDir)%(FilesToCopy.Filename)%(FilesToCopy.Extension)"/>
<DestFilesLeave Include="%(DestFiles.RecursiveDir)%(DestFiles.Filename)%(DestFiles.Extension)"/>
</ItemGroup>
<MSBuild.ExtensionPack.Framework.MsBuildHelper TaskAction="GetDistinctItems"
InputItems1="@(SrcFilesLeave)"
InputItems2="@(DestFilesLeave)">
<Output TaskParameter="OutputItems" ItemName="Distinct"/>
</MSBuild.ExtensionPack.Framework.MsBuildHelper>
<Message Text="Distinct %(Distinct.Identity)"/>
<Delete Files="$(DestFolder)\%(Distinct.Identity)" />
</Target>
</Project>
In case someone don't want or can't install the expansion pack
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0"
DefaultTargets="Backup">
<PropertyGroup>
<SourceFolder>C:\source</SourceFolder>
<DestFolder>C:\dest</DestFolder>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$(SourceFolder)\**" />
</ItemGroup>
<Target Name="Backup">
<!-- copy all files from the source folder to the dest folder
that are newer or don't exist in the dest folder -->
<Copy
SourceFiles="@(FilesToCopy)"
DestinationFiles="@(FilesToCopy->'$(DestFolder)\%(RecursiveDir)%Filename)%(Extension)')"
SkipUnchangedFiles="True">
<Output TaskParameter="CopiedFiles" ItemName="Copied"/>
</Copy>
<ItemGroup>
<OutdatedFiles Include="$(DestFolder)\**" Exclude="@(Copied)"/>
</ItemGroup>
<Delete Files="@(OutdatedFiles)"/>
</Target>
</Project>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With