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 /src | |
| parent | 76709e28e052c0b9708495153ddfd5303dc6623f (diff) | |
| download | wix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.gz wix-3dac27d54c104796b85b6582d6f878a553f335fa.tar.bz2 wix-3dac27d54c104796b85b6582d6f878a553f335fa.zip | |
Add retries to several file system operations
Closes #6815
Diffstat (limited to '')
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 | } |
