Last active
September 26, 2024 07:33
-
-
Save psulek/f313f0e6e7a2c8d07cc105d39130cf62 to your computer and use it in GitHub Desktop.
MSBuild Task CopyIfNewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
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
Sample msbuild targets file contains
CopyIfNewer
custom task to copy source files into target files but only if:LastWriteTime
is newer thanLastWriteTime
of existing target fileLastWriteTime
is acquired by c#File.GetLastWriteTime()