aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-10-04 10:36:39 -0700
committerRob Mensching <rob@firegiant.com>2022-10-04 14:23:47 -0700
commitbfec456aae2ad7abcec0f8c4c8b2f44308e48961 (patch)
tree6368675e8bf06d8bb04d16ea81bf4125c86c5e3e
parentd76532b128e4e187fd9ac5e1d018638dcf8876ec (diff)
downloadwix-bfec456aae2ad7abcec0f8c4c8b2f44308e48961.tar.gz
wix-bfec456aae2ad7abcec0f8c4c8b2f44308e48961.tar.bz2
wix-bfec456aae2ad7abcec0f8c4c8b2f44308e48961.zip
Add retries when trying to update bundle resources
Fixes 6882 and 6902
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs19
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs12
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs12
-rw-r--r--src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs2
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs37
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs2
6 files changed, 51 insertions, 33 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
index d633c823..26184462 100644
--- a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
+++ b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
@@ -2,6 +2,8 @@
2 2
3namespace WixToolset.Extensibility.Services 3namespace WixToolset.Extensibility.Services
4{ 4{
5 using System;
6
5 /// <summary> 7 /// <summary>
6 /// Abstracts basic file system operations. 8 /// Abstracts basic file system operations.
7 /// </summary> 9 /// </summary>
@@ -16,10 +18,27 @@ namespace WixToolset.Extensibility.Services
16 void CopyFile(string source, string destination, bool allowHardlink); 18 void CopyFile(string source, string destination, bool allowHardlink);
17 19
18 /// <summary> 20 /// <summary>
21 /// Deletes a file.
22 /// </summary>
23 /// <param name="source">The file to delete.</param>
24 /// <param name="throwOnError">Indicates the file must be deleted. Default is a best effort delete.</param>
25 /// <param name="maxRetries">Maximum retry attempts. Default is 4.</param>
26 void DeleteFile(string source, bool throwOnError = false, int maxRetries = 4);
27
28 /// <summary>
19 /// Moves a file. 29 /// Moves a file.
20 /// </summary> 30 /// </summary>
21 /// <param name="source">The file to move.</param> 31 /// <param name="source">The file to move.</param>
22 /// <param name="destination">The destination file.</param> 32 /// <param name="destination">The destination file.</param>
23 void MoveFile(string source, string destination); 33 void MoveFile(string source, string destination);
34
35 /// <summary>
36 /// Executes an action and retries on any exception a few times with short pause
37 /// between each attempt. Primarily intended for use with file system operations
38 /// that might get interrupted by external systems (usually anti-virus).
39 /// </summary>
40 /// <param name="action">Action to execute.</param>
41 /// <param name="maxRetries">Maximum retry attempts. Default is 4.</param>
42 void ExecuteWithRetries(Action action, int maxRetries = 4);
24 } 43 }
25} 44}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index aa4730fb..fdc3dfe3 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -349,17 +349,11 @@ namespace WixToolset.Core.Burn.Bundles
349 349
350 try 350 try
351 { 351 {
352 resources.Save(bundleTempPath); 352 this.FileSystem.ExecuteWithRetries(() => resources.Save(bundleTempPath));
353 } 353 }
354 catch (IOException) 354 catch (IOException e)
355 { 355 {
356 // If there was no icon or splash screen then this is unexpected so rethrow. 356 this.Messaging.Write(BurnBackendErrors.FailedToUpdateBundleResources(bundleInfo.SourceLineNumbers, bundleInfo.IconSourceFile?.Path, bundleInfo.SplashScreenSourceFile?.Path, e.Message));
357 if (bundleInfo.IconSourceFile == null && bundleInfo.SplashScreenSourceFile == null)
358 {
359 throw;
360 }
361
362 this.Messaging.Write(BurnBackendErrors.FailedToAddIconOrSplashScreenToBundle(bundleInfo.SourceLineNumbers, bundleInfo.IconSourceFile?.Path, bundleInfo.SplashScreenSourceFile?.Path));
363 } 357 }
364 } 358 }
365 359
diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
index 04290667..ee6fc0f8 100644
--- a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
@@ -42,7 +42,7 @@ namespace WixToolset.Core.Burn
42 return Message(sourceLineNumbers, Ids.ExternalPayloadCollision2, "The location of the symbol related to the previous error."); 42 return Message(sourceLineNumbers, Ids.ExternalPayloadCollision2, "The location of the symbol related to the previous error.");
43 } 43 }
44 44
45 public static Message FailedToAddIconOrSplashScreenToBundle(SourceLineNumber sourceLineNumbers, string iconPath, string splashScreenPath) 45 public static Message FailedToUpdateBundleResources(SourceLineNumber sourceLineNumbers, string iconPath, string splashScreenPath, string detail)
46 { 46 {
47 var additionalDetail = String.Empty; 47 var additionalDetail = String.Empty;
48 48
@@ -51,18 +51,18 @@ namespace WixToolset.Core.Burn
51 } 51 }
52 else if (String.IsNullOrEmpty(iconPath)) 52 else if (String.IsNullOrEmpty(iconPath))
53 { 53 {
54 additionalDetail = $" Ensure the splash screen file is a bitmap file at '{splashScreenPath}'"; 54 additionalDetail = $" Ensure the splash screen file is a bitmap file at '{splashScreenPath}'.";
55 } 55 }
56 else if (String.IsNullOrEmpty(splashScreenPath)) 56 else if (String.IsNullOrEmpty(splashScreenPath))
57 { 57 {
58 additionalDetail = $" Ensure the bundle icon file is an icon file at '{iconPath}'"; 58 additionalDetail = $" Ensure the bundle icon file is an icon file at '{iconPath}'.";
59 } 59 }
60 else 60 else
61 { 61 {
62 additionalDetail = $" Ensure the bundle icon file is an icon file at '{iconPath}' and the splash screen file is a bitmap file at '{splashScreenPath}'"; 62 additionalDetail = $" Ensure the bundle icon file is an icon file at '{iconPath}' and the splash screen file is a bitmap file at '{splashScreenPath}'.";
63 } 63 }
64 64
65 return Message(sourceLineNumbers, Ids.FailedToAddIconOrSplashScreenToBundle, "Failed to add resources to the bundle.{0}", additionalDetail); 65 return Message(sourceLineNumbers, Ids.FailedToUpdateBundleResources, "Failed to update resources in the bundle.{0} Detail: {1}", additionalDetail, detail);
66 } 66 }
67 67
68 public static Message PackageCachePayloadCollision(SourceLineNumber sourceLineNumbers, string payloadId, string payloadName, string packageId) 68 public static Message PackageCachePayloadCollision(SourceLineNumber sourceLineNumbers, string payloadId, string payloadName, string packageId)
@@ -113,7 +113,7 @@ namespace WixToolset.Core.Burn
113 TooManyAttachedContainers = 8008, 113 TooManyAttachedContainers = 8008,
114 IncompatibleWixBurnSection = 8009, 114 IncompatibleWixBurnSection = 8009,
115 UnsupportedRemotePackagePayload = 8010, 115 UnsupportedRemotePackagePayload = 8010,
116 FailedToAddIconOrSplashScreenToBundle = 8011, 116 FailedToUpdateBundleResources = 8011,
117 InvalidBundleManifest = 8012, 117 InvalidBundleManifest = 8012,
118 BundleMultipleProviders = 8013, 118 BundleMultipleProviders = 8013,
119 } // last available is 8499. 8500 is BurnBackendWarnings. 119 } // last available is 8499. 8500 is BurnBackendWarnings.
diff --git a/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs b/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
index 87c2590f..4a0329bc 100644
--- a/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
+++ b/src/wix/WixToolset.Core/Bind/TransferFilesCommand.cs
@@ -204,7 +204,7 @@ namespace WixToolset.Core.Bind
204 foreach (var file in files) 204 foreach (var file in files)
205 { 205 {
206 var fileInfo = new FileInfo(file); 206 var fileInfo = new FileInfo(file);
207 ExtensibilityServices.FileSystem.ActionWithRetries(() => fileInfo.SetAccessControl(aclReset)); 207 this.FileSystem.ExecuteWithRetries(() => fileInfo.SetAccessControl(aclReset));
208 } 208 }
209 } 209 }
210 } 210 }
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
index 8bda4966..4269cabe 100644
--- a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
+++ b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
@@ -12,13 +12,13 @@ namespace WixToolset.Core.ExtensibilityServices
12 { 12 {
13 public void CopyFile(string source, string destination, bool allowHardlink) 13 public void CopyFile(string source, string destination, bool allowHardlink)
14 { 14 {
15 EnsureDirectoryWithoutFile(destination); 15 this.EnsureDirectoryWithoutFile(destination);
16 16
17 var hardlinked = false; 17 var hardlinked = false;
18 18
19 if (allowHardlink) 19 if (allowHardlink)
20 { 20 {
21 ActionWithRetries(() => hardlinked = CreateHardLink(destination, source, IntPtr.Zero)); 21 this.ExecuteWithRetries(() => hardlinked = CreateHardLink(destination, source, IntPtr.Zero));
22 } 22 }
23 23
24 if (!hardlinked) 24 if (!hardlinked)
@@ -27,25 +27,30 @@ namespace WixToolset.Core.ExtensibilityServices
27 var er = Marshal.GetLastWin32Error(); 27 var er = Marshal.GetLastWin32Error();
28#endif 28#endif
29 29
30 ActionWithRetries(() => File.Copy(source, destination, overwrite: true)); 30 this.ExecuteWithRetries(() => File.Copy(source, destination, overwrite: true));
31 }
32 }
33
34 public void DeleteFile(string source, bool throwOnError = false, int maxRetries = 4)
35 {
36 try
37 {
38 this.ExecuteWithRetries(() => File.Delete(source), maxRetries);
39 }
40 catch when (!throwOnError)
41 {
42 // Do nothing on best-effort deletes.
31 } 43 }
32 } 44 }
33 45
34 public void MoveFile(string source, string destination) 46 public void MoveFile(string source, string destination)
35 { 47 {
36 EnsureDirectoryWithoutFile(destination); 48 this.EnsureDirectoryWithoutFile(destination);
37 49
38 ActionWithRetries(() => File.Move(source, destination)); 50 this.ExecuteWithRetries(() => File.Move(source, destination));
39 } 51 }
40 52
41 /// <summary> 53 public void ExecuteWithRetries(Action action, int maxRetries = 4)
42 /// Executes an action and retries on any exception up to a few times. Primarily
43 /// intended for use with file system operations that might get interrupted by
44 /// external systems (usually anti-virus).
45 /// </summary>
46 /// <param name="action">Action to execute.</param>
47 /// <param name="maxRetries">Maximum retry attempts.</param>
48 internal static void ActionWithRetries(Action action, int maxRetries = 3)
49 { 54 {
50 for (var attempt = 1; attempt <= maxRetries; ++attempt) 55 for (var attempt = 1; attempt <= maxRetries; ++attempt)
51 { 56 {
@@ -61,16 +66,16 @@ namespace WixToolset.Core.ExtensibilityServices
61 } 66 }
62 } 67 }
63 68
64 private static void EnsureDirectoryWithoutFile(string path) 69 private void EnsureDirectoryWithoutFile(string path)
65 { 70 {
66 var directory = Path.GetDirectoryName(path); 71 var directory = Path.GetDirectoryName(path);
67 72
68 if (!String.IsNullOrEmpty(directory)) 73 if (!String.IsNullOrEmpty(directory))
69 { 74 {
70 ActionWithRetries(() => Directory.CreateDirectory(directory)); 75 this.ExecuteWithRetries(() => Directory.CreateDirectory(directory));
71 } 76 }
72 77
73 ActionWithRetries(() => File.Delete(path)); 78 this.ExecuteWithRetries(() => File.Delete(path));
74 } 79 }
75 80
76 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 81 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
index ad31fc6d..cbfe6f85 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
@@ -428,7 +428,7 @@ namespace WixToolsetTest.CoreIntegration
428 var message = result.Messages.Where(m => m.Level == MessageLevel.Error).Select(m => m.ToString().Replace(folder, "<testdata>")).ToArray(); 428 var message = result.Messages.Where(m => m.Level == MessageLevel.Error).Select(m => m.ToString().Replace(folder, "<testdata>")).ToArray();
429 WixAssert.CompareLineByLine(new[] 429 WixAssert.CompareLineByLine(new[]
430 { 430 {
431 @"Failed to add resources to the bundle. Ensure the bundle icon file is an icon file at '<testdata>\.Data\burn.exe'" 431 @"Failed to update resources in the bundle. Ensure the bundle icon file is an icon file at '<testdata>\.Data\burn.exe'. Detail: Failed to save resource. Error: 87"
432 }, message); 432 }, message);
433 } 433 }
434 } 434 }