diff options
| author | Rob Mensching <rob@firegiant.com> | 2021-01-06 15:15:35 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2021-01-06 15:24:56 -0800 |
| commit | 093e1dd144b260b58a0ae46d722d1dbc4019d9d5 (patch) | |
| tree | 6fd63241e2b196b040d26b3b1749d4c3ecbc531c /src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs | |
| parent | bf30546113c5f1ffc4cf917b560c5a0451eda37d (diff) | |
| download | wix-093e1dd144b260b58a0ae46d722d1dbc4019d9d5.tar.gz wix-093e1dd144b260b58a0ae46d722d1dbc4019d9d5.tar.bz2 wix-093e1dd144b260b58a0ae46d722d1dbc4019d9d5.zip | |
Implement improved file sequence optimization
First ensures files are grouped by DiskId. Then files are sequenced by
target directory order to optimize MSI installation behavior. Finally,
files are alphabetized in the directory. Additional optimizations could
be considered in the future from here.
Fixes wixtoolset/issues#4409
Fixes wixtoolset/issues#4708
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs')
| -rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs | 92 |
1 files changed, 85 insertions, 7 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs index 6943d345..e96dfd91 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/OptimizeFileFacadesOrderCommand.cs | |||
| @@ -4,34 +4,112 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Linq; | ||
| 7 | using WixToolset.Core.Bind; | 8 | using WixToolset.Core.Bind; |
| 9 | using WixToolset.Data; | ||
| 10 | using WixToolset.Data.Symbols; | ||
| 11 | using WixToolset.Extensibility.Data; | ||
| 12 | using WixToolset.Extensibility.Services; | ||
| 8 | 13 | ||
| 9 | internal class OptimizeFileFacadesOrderCommand | 14 | internal class OptimizeFileFacadesOrderCommand |
| 10 | { | 15 | { |
| 11 | public OptimizeFileFacadesOrderCommand(List<FileFacade> fileFacades) | 16 | public OptimizeFileFacadesOrderCommand(IBackendHelper helper, IPathResolver pathResolver, IntermediateSection section, Platform platform, List<FileFacade> fileFacades) |
| 12 | { | 17 | { |
| 18 | this.BackendHelper = helper; | ||
| 19 | this.PathResolver = pathResolver; | ||
| 20 | this.Section = section; | ||
| 21 | this.Platform = platform; | ||
| 13 | this.FileFacades = fileFacades; | 22 | this.FileFacades = fileFacades; |
| 14 | } | 23 | } |
| 15 | 24 | ||
| 16 | public List<FileFacade> FileFacades { get; private set; } | 25 | public List<FileFacade> FileFacades { get; private set; } |
| 17 | 26 | ||
| 27 | private IBackendHelper BackendHelper { get; } | ||
| 28 | |||
| 29 | private IPathResolver PathResolver { get; } | ||
| 30 | |||
| 31 | private IntermediateSection Section { get; } | ||
| 32 | |||
| 33 | private Platform Platform { get; } | ||
| 34 | |||
| 18 | public List<FileFacade> Execute() | 35 | public List<FileFacade> Execute() |
| 19 | { | 36 | { |
| 20 | this.FileFacades.Sort(FileFacadeOptimizer.Instance); | 37 | var canonicalComponentTargetPaths = this.ComponentTargetPaths(); |
| 38 | |||
| 39 | this.FileFacades.Sort(new FileFacadeOptimizer(canonicalComponentTargetPaths)); | ||
| 21 | 40 | ||
| 22 | return this.FileFacades; | 41 | return this.FileFacades; |
| 23 | } | 42 | } |
| 24 | 43 | ||
| 44 | private Dictionary<string, string> ComponentTargetPaths() | ||
| 45 | { | ||
| 46 | var directories = this.ResolveDirectories(); | ||
| 47 | |||
| 48 | var canonicalPathsByDirectoryId = new Dictionary<string, string>(); | ||
| 49 | foreach (var component in this.Section.Symbols.OfType<ComponentSymbol>()) | ||
| 50 | { | ||
| 51 | var directoryPath = this.PathResolver.GetCanonicalDirectoryPath(directories, null, component.DirectoryRef, this.Platform); | ||
| 52 | canonicalPathsByDirectoryId.Add(component.Id.Id, directoryPath); | ||
| 53 | } | ||
| 54 | |||
| 55 | return canonicalPathsByDirectoryId; | ||
| 56 | } | ||
| 57 | |||
| 58 | private Dictionary<string, IResolvedDirectory> ResolveDirectories() | ||
| 59 | { | ||
| 60 | var targetPathsByDirectoryId = new Dictionary<string, IResolvedDirectory>(); | ||
| 61 | |||
| 62 | // Get the target paths for all directories. | ||
| 63 | foreach (var directory in this.Section.Symbols.OfType<DirectorySymbol>()) | ||
| 64 | { | ||
| 65 | var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directory.ParentDirectoryRef, directory.Name); | ||
| 66 | targetPathsByDirectoryId.Add(directory.Id.Id, resolvedDirectory); | ||
| 67 | } | ||
| 68 | |||
| 69 | return targetPathsByDirectoryId; | ||
| 70 | } | ||
| 71 | |||
| 25 | private class FileFacadeOptimizer : IComparer<FileFacade> | 72 | private class FileFacadeOptimizer : IComparer<FileFacade> |
| 26 | { | 73 | { |
| 27 | public static readonly FileFacadeOptimizer Instance = new FileFacadeOptimizer(); | 74 | public FileFacadeOptimizer(Dictionary<string, string> componentTargetPaths) |
| 75 | { | ||
| 76 | this.ComponentTargetPaths = componentTargetPaths; | ||
| 77 | } | ||
| 78 | |||
| 79 | private Dictionary<string, string> ComponentTargetPaths { get; } | ||
| 28 | 80 | ||
| 29 | public int Compare(FileFacade x, FileFacade y) | 81 | public int Compare(FileFacade x, FileFacade y) |
| 30 | { | 82 | { |
| 31 | // TODO: Sort these facades even smarter by directory path and component id | 83 | // First group files by DiskId. |
| 32 | // and maybe file size or file extension and other creative ideas to | 84 | var compare = x.DiskId.CompareTo(y.DiskId); |
| 33 | // get optimal install speed out of MSI. | 85 | |
| 34 | return String.Compare(x.ComponentRef, y.ComponentRef, StringComparison.Ordinal); | 86 | if (compare != 0) |
| 87 | { | ||
| 88 | return compare; | ||
| 89 | } | ||
| 90 | |||
| 91 | // Next try to group files by target install directory. | ||
| 92 | if (this.ComponentTargetPaths.TryGetValue(x.ComponentRef, out var canonicalX) && | ||
| 93 | this.ComponentTargetPaths.TryGetValue(y.ComponentRef, out var canonicalY)) | ||
| 94 | { | ||
| 95 | compare = String.Compare(canonicalX, canonicalY, StringComparison.Ordinal); | ||
| 96 | |||
| 97 | if (compare != 0) | ||
| 98 | { | ||
| 99 | return compare; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | // TODO: Consider sorting these facades even smarter by file size or file extension | ||
| 104 | // or other creative ideas to get optimal install speed out of MSI. | ||
| 105 | compare = String.Compare(x.FileName, y.FileName, StringComparison.Ordinal); | ||
| 106 | |||
| 107 | if (compare != 0) | ||
| 108 | { | ||
| 109 | return compare; | ||
| 110 | } | ||
| 111 | |||
| 112 | return String.Compare(x.Id, y.Id, StringComparison.Ordinal); | ||
| 35 | } | 113 | } |
| 36 | } | 114 | } |
| 37 | } | 115 | } |
