Skip to content

Instantly share code, notes, and snippets.

@psulek
Last active September 26, 2024 07:33
Show Gist options
  • Save psulek/f313f0e6e7a2c8d07cc105d39130cf62 to your computer and use it in GitHub Desktop.
Save psulek/f313f0e6e7a2c8d07cc105d39130cf62 to your computer and use it in GitHub Desktop.
MSBuild Task CopyIfNewer
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="CopyIfNewer" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildBinPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<SourceFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true"/>
<TargetFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true"/>
</ParameterGroup>
<Task>
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="Microsoft.Build.Framework"/>
<Using Namespace="Microsoft.Build.Utilities"/>
<Code Type="Fragment" Language="cs">
<![CDATA[
Success = SourceFiles.Length == TargetFiles.Length;
if (!Success)
{
Log.LogError("SourceFiles and TargetFiles must have the same number of elements.");
}
else
{
for (int i = 0; i < SourceFiles.Length; i++)
{
string sourceFile = SourceFiles[i].ItemSpec;
string targetFile = TargetFiles[i].ItemSpec;
if (!File.Exists(sourceFile))
{
Log.LogMessage(MessageImportance.High, $"Source file does not exist: {sourceFile}");
continue;
}
bool shouldCopy = !File.Exists(targetFile);
bool sameModified = false;
if (!shouldCopy)
{
var sourceLastModified = File.GetLastWriteTime(sourceFile);
var targetLastModified = File.GetLastWriteTime(targetFile);
shouldCopy = sourceLastModified > targetLastModified;
sameModified = sourceLastModified == targetLastModified;
}
if (shouldCopy)
{
try
{
File.Copy(sourceFile, targetFile, true);
Log.LogMessage(MessageImportance.High, $"Copied file '{sourceFile}' -> '{targetFile}' succeed.");
}
catch (Exception ex)
{
Log.LogError($"Error copying '{sourceFile}' to '{targetFile}': {ex.Message}");
Success = false;
break;
}
} else {
string reason = sameModified ? "same modified time" : "target is newer";
Log.LogMessage(MessageImportance.High, $"Skipping copy file '{sourceFile}' -> '{targetFile}' ({reason})");
}
}
}
]]>
</Code>
</Task>
</UsingTask>
<Target Name="CopyFiles">
<ItemGroup>
<SourceFiles Include="path\to\source\file1.txt;path\to\source\file2.txt" />
<TargetFiles Include="path\to\target\file1.txt;path\to\target\file2.txt" />
</ItemGroup>
<CopyIfNewer SourceFiles="@(SourceFiles)" TargetFiles="@(TargetFiles)" />
</Target>
</Project>
@psulek
Copy link
Author

psulek commented Sep 26, 2024

Sample msbuild targets file contains CopyIfNewer custom task to copy source files into target files but only if:

  • destionation file does not exist
  • or source file LastWriteTime is newer than LastWriteTime of existing target file
  • value of LastWriteTime is acquired by c# File.GetLastWriteTime()

NOTE: This is not possible with buildin msbuild Copy task

@psulek
Copy link
Author

psulek commented Sep 26, 2024

Same can be done by complicated way using msbuild only stuff, example:

<ItemGroup>
    <AppSettingsFilesToCopyWithTime Include="@(PackageWebModuleAppSettingsFiles->'$(TargetDir)\%(RecursiveDir)$(PackageWebModuleName).%(Filename)%(Extension)')">
        <OriginalIdentity>%(RootDir)%(Directory)%(Filename)%(Extension)</OriginalIdentity>
        <SourceTime>$([System.DateTime]::Parse('%(ModifiedTime)').Ticks)</SourceTime>
        <CanOverwrite></CanOverwrite>
    </AppSettingsFilesToCopyWithTime>

    <AppSettingsFilesToCopy_ Include="@(AppSettingsFilesToCopyWithTime)">
        <CanOverwrite Condition="!Exists('%(Identity)') Or '%(SourceTime)' > $([System.DateTime]::Parse('%(ModifiedTime)').Ticks)">true</CanOverwrite>
    </AppSettingsFilesToCopy_>

    <AppSettingsFilesToCopy Include="@(AppSettingsFilesToCopy_->'%(OriginalIdentity)')" Condition="'%(CanOverwrite)' == 'true'" />
</ItemGroup>

<Copy SourceFiles="@(AppSettingsFilesToCopy)"
      DestinationFiles="@(AppSettingsFilesToCopy->'$(TargetDir)\%(RecursiveDir)\$(PackageWebModuleName).%(Filename)%(Extension)')"
      OverwriteReadOnlyFiles="true" />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment