aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-07-07 17:01:55 -0700
committerRob Mensching <rob@firegiant.com>2022-07-07 17:55:44 -0700
commit3dac27d54c104796b85b6582d6f878a553f335fa (patch)
tree012154396d1408966c75419e0cddcf1bc681d0d4
parent76709e28e052c0b9708495153ddfd5303dc6623f (diff)
downloadwix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.gz
wix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.bz2
wix-3dac27d54c104796b85b6582d6f878a553f335fa.zip
Add retries to several file system operations
Closes #6815
-rw-r--r--src/wix/WixToolset.Core.Native/FileSystem.cs44
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs8
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/ModuleFixture.cs63
3 files changed, 106 insertions, 9 deletions
diff --git a/src/wix/WixToolset.Core.Native/FileSystem.cs b/src/wix/WixToolset.Core.Native/FileSystem.cs
index 3dc336d6..3c59c90c 100644
--- a/src/wix/WixToolset.Core.Native/FileSystem.cs
+++ b/src/wix/WixToolset.Core.Native/FileSystem.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Native
7 using System.IO; 7 using System.IO;
8 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices;
9 using System.Security.AccessControl; 9 using System.Security.AccessControl;
10 using System.Threading;
10 11
11 /// <summary> 12 /// <summary>
12 /// File system helpers. 13 /// File system helpers.
@@ -23,13 +24,20 @@ namespace WixToolset.Core.Native
23 { 24 {
24 EnsureDirectoryWithoutFile(destination); 25 EnsureDirectoryWithoutFile(destination);
25 26
26 if (!allowHardlink || !CreateHardLink(destination, source, IntPtr.Zero)) 27 var hardlinked = false;
28
29 if (allowHardlink)
30 {
31 ActionWithRetries(() => hardlinked = CreateHardLink(destination, source, IntPtr.Zero));
32 }
33
34 if (!hardlinked)
27 { 35 {
28#if DEBUG 36#if DEBUG
29 var er = Marshal.GetLastWin32Error(); 37 var er = Marshal.GetLastWin32Error();
30#endif 38#endif
31 39
32 File.Copy(source, destination, overwrite: true); 40 ActionWithRetries(() => File.Copy(source, destination, overwrite: true));
33 } 41 }
34 } 42 }
35 43
@@ -42,7 +50,7 @@ namespace WixToolset.Core.Native
42 { 50 {
43 EnsureDirectoryWithoutFile(destination); 51 EnsureDirectoryWithoutFile(destination);
44 52
45 File.Move(source, destination); 53 ActionWithRetries(() => File.Move(source, destination));
46 } 54 }
47 55
48 /// <summary> 56 /// <summary>
@@ -56,7 +64,31 @@ namespace WixToolset.Core.Native
56 64
57 foreach (var file in files) 65 foreach (var file in files)
58 { 66 {
59 new FileInfo(file).SetAccessControl(aclReset); 67 var fileInfo = new FileInfo(file);
68 ActionWithRetries(() => fileInfo.SetAccessControl(aclReset));
69 }
70 }
71
72 /// <summary>
73 /// Executes an action and retries on any exception up to a few times. Primarily
74 /// intended for use with file system operations that might get interrupted by
75 /// external systems (usually anti-virus).
76 /// </summary>
77 /// <param name="action">Action to execute.</param>
78 /// <param name="maxRetries">Maximum retry attempts.</param>
79 public static void ActionWithRetries(Action action, int maxRetries = 3)
80 {
81 for (var attempt = 1; attempt <= maxRetries; ++attempt)
82 {
83 try
84 {
85 action();
86 break;
87 }
88 catch when (attempt < maxRetries)
89 {
90 Thread.Sleep(250);
91 }
60 } 92 }
61 } 93 }
62 94
@@ -66,10 +98,10 @@ namespace WixToolset.Core.Native
66 98
67 if (!String.IsNullOrEmpty(directory)) 99 if (!String.IsNullOrEmpty(directory))
68 { 100 {
69 Directory.CreateDirectory(directory); 101 ActionWithRetries(() => Directory.CreateDirectory(directory));
70 } 102 }
71 103
72 File.Delete(path); 104 ActionWithRetries(() => File.Delete(path));
73 } 105 }
74 106
75 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 107 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
index 96b7866c..7af0ca19 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -9,6 +9,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9 using System.Linq; 9 using System.Linq;
10 using System.Runtime.InteropServices; 10 using System.Runtime.InteropServices;
11 using System.Text; 11 using System.Text;
12 using System.Threading;
13 using WixToolset.Core.Native;
12 using WixToolset.Core.Native.Msi; 14 using WixToolset.Core.Native.Msi;
13 using WixToolset.Core.Native.Msm; 15 using WixToolset.Core.Native.Msm;
14 using WixToolset.Data; 16 using WixToolset.Data;
@@ -69,10 +71,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
69 { 71 {
70 merge = MsmInterop.GetMsmMerge(); 72 merge = MsmInterop.GetMsmMerge();
71 73
72 merge.OpenLog(logPath); 74 FileSystem.ActionWithRetries(() => merge.OpenLog(logPath));
73 logOpen = true; 75 logOpen = true;
74 76
75 merge.OpenDatabase(this.OutputPath); 77 FileSystem.ActionWithRetries(() => merge.OpenDatabase(this.OutputPath));
76 databaseOpen = true; 78 databaseOpen = true;
77 79
78 var featureModulesByMergeId = this.Section.Symbols.OfType<WixFeatureModulesSymbol>().GroupBy(t => t.WixMergeRef).ToDictionary(g => g.Key); 80 var featureModulesByMergeId = this.Section.Symbols.OfType<WixFeatureModulesSymbol>().GroupBy(t => t.WixMergeRef).ToDictionary(g => g.Key);
@@ -97,7 +99,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
97 } 99 }
98 100
99 this.Messaging.Write(VerboseMessages.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage)); 101 this.Messaging.Write(VerboseMessages.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage));
100 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); 102 FileSystem.ActionWithRetries(() => merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage));
101 moduleOpen = true; 103 moduleOpen = true;
102 104
103 trackedFiles.Add(this.BackendHelper.TrackFile(wixMergeRow.SourceFile, TrackedFileType.Input, wixMergeRow.SourceLineNumbers)); 105 trackedFiles.Add(this.BackendHelper.TrackFile(wixMergeRow.SourceFile, TrackedFileType.Input, wixMergeRow.SourceLineNumbers));
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ModuleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ModuleFixture.cs
index af200c0d..89ad7bc7 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/ModuleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/ModuleFixture.cs
@@ -3,8 +3,12 @@
3namespace WixToolsetTest.CoreIntegration 3namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System.IO; 5 using System.IO;
6 using System.Linq;
6 using WixBuildTools.TestSupport; 7 using WixBuildTools.TestSupport;
7 using WixToolset.Core.TestPackage; 8 using WixToolset.Core.TestPackage;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using WixToolset.Data.WindowsInstaller;
8 using Xunit; 12 using Xunit;
9 13
10 public class ModuleFixture 14 public class ModuleFixture
@@ -42,5 +46,64 @@ namespace WixToolsetTest.CoreIntegration
42 }, rows); 46 }, rows);
43 } 47 }
44 } 48 }
49
50 [Fact]
51 public void CanMergeModuleAndValidate()
52 {
53 var msmFolder = TestData.Get(@"TestData\SimpleModule");
54 var folder = TestData.Get(@"TestData\SimpleMerge");
55
56 using (var fs = new DisposableFileSystem())
57 {
58 var intermediateFolder = fs.GetFolder();
59 var msiPath = Path.Combine(intermediateFolder, @"bin\test.msi");
60 var cabPath = Path.Combine(intermediateFolder, @"bin\cab1.cab");
61
62 var msmResult = WixRunner.Execute(new[]
63 {
64 "build",
65 Path.Combine(msmFolder, "Module.wxs"),
66 "-loc", Path.Combine(msmFolder, "Module.en-us.wxl"),
67 "-bindpath", Path.Combine(msmFolder, "data"),
68 "-intermediateFolder", intermediateFolder,
69 "-o", Path.Combine(intermediateFolder, "bin", "test", "test.msm")
70 });
71
72 msmResult.AssertSuccess();
73
74 var result = WixRunner.Execute(new[]
75 {
76 "build",
77 Path.Combine(folder, "Package.wxs"),
78 "-loc", Path.Combine(folder, "Package.en-us.wxl"),
79 "-bindpath", Path.Combine(intermediateFolder, "bin", "test"),
80 "-intermediateFolder", intermediateFolder,
81 "-o", msiPath
82 });
83
84 result.AssertSuccess();
85
86 var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
87 var section = intermediate.Sections.Single();
88 Assert.Empty(section.Symbols.OfType<FileSymbol>());
89
90 var data = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"));
91 Assert.Empty(data.Tables["File"].Rows);
92
93 var results = Query.QueryDatabase(msiPath, new[] { "File" });
94 WixAssert.CompareLineByLine(new[]
95 {
96 "File:File1.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tModuleComponent1.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tfile1.txt\t17\t\t\t512\t1",
97 "File:File2.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tModuleComponent2.243FB739_4D05_472F_9CFB_EF6B1017B6DE\tfile2.txt\t17\t\t\t512\t2",
98 }, results);
99
100 var files = Query.GetCabinetFiles(cabPath);
101 WixAssert.CompareLineByLine(new[]
102 {
103 "File1.243FB739_4D05_472F_9CFB_EF6B1017B6DE",
104 "File2.243FB739_4D05_472F_9CFB_EF6B1017B6DE"
105 }, files.Select(f => f.Name).ToArray());
106 }
107 }
45 } 108 }
46} 109}