diff options
author | Rob Mensching <rob@firegiant.com> | 2022-07-07 17:01:55 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-07-07 17:55:44 -0700 |
commit | 3dac27d54c104796b85b6582d6f878a553f335fa (patch) | |
tree | 012154396d1408966c75419e0cddcf1bc681d0d4 | |
parent | 76709e28e052c0b9708495153ddfd5303dc6623f (diff) | |
download | wix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.gz wix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.bz2 wix-3dac27d54c104796b85b6582d6f878a553f335fa.zip |
Add retries to several file system operations
Closes #6815
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 @@ | |||
3 | namespace WixToolsetTest.CoreIntegration | 3 | namespace 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 | } |