| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { |
| 5 | using System; |
| 6 | using System.Collections.Generic; |
| 7 | using System.IO; |
| 8 | using System.Linq; |
| 9 | using WixToolset.Core.WindowsInstaller.Unbind; |
| 10 | using WixToolset.Data; |
| 11 | using WixToolset.Data.Symbols; |
| 12 | using WixToolset.Data.WindowsInstaller; |
| 13 | using WixToolset.Extensibility; |
| 14 | using WixToolset.Extensibility.Data; |
| 15 | using WixToolset.Extensibility.Services; |
| 16 | |
| 17 | internal class CreatePatchTransformsCommand |
| 18 | { |
| 19 | public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection<IResolverExtension> resolverExtensions, IReadOnlyCollection<IWindowsInstallerBackendBinderExtension> backendExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection<IBindPath> bindPaths) |
| 20 | { |
| 21 | this.Messaging = messaging; |
| 22 | this.BackendHelper = backendHelper; |
| 23 | this.FileSystem = fileSystem; |
| 24 | this.PathResolver = pathResolver; |
| 25 | this.FileResolver = fileResolver; |
| 26 | this.ResolverExtensions = resolverExtensions; |
| 27 | this.BackendExtensions = backendExtensions; |
| 28 | this.Intermediate = intermediate; |
| 29 | this.IntermediateFolder = intermediateFolder; |
| 30 | this.BindPaths = bindPaths; |
| 31 | } |
| 32 | |
| 33 | private IMessaging Messaging { get; } |
| 34 | |
| 35 | private IBackendHelper BackendHelper { get; } |
| 36 | |
| 37 | private IFileSystem FileSystem { get; } |
| 38 | |
| 39 | private IPathResolver PathResolver { get; } |
| 40 | |
| 41 | private IFileResolver FileResolver { get; } |
| 42 | |
| 43 | private IReadOnlyCollection<IResolverExtension> ResolverExtensions { get; } |
| 44 | |
| 45 | private IReadOnlyCollection<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } |
| 46 | |
| 47 | private Intermediate Intermediate { get; } |
| 48 | |
| 49 | private string IntermediateFolder { get; } |
| 50 | |
| 51 | private IReadOnlyCollection<IBindPath> BindPaths { get; } |
| 52 | |
| 53 | public PatchFilterMap PatchFilterMap { get; private set; } |
| 54 | |
| 55 | public IEnumerable<PatchTransform> PatchTransforms { get; private set; } |
| 56 | |
| 57 | public IEnumerable<PatchTransform> Execute() |
| 58 | { |
| 59 | var patchFilterMap = new PatchFilterMap(); |
| 60 | var patchTransforms = new List<PatchTransform>(); |
| 61 | |
| 62 | var symbols = this.Intermediate.Sections.SelectMany(s => s.Symbols); |
| 63 | |
| 64 | var patchBaselineSymbols = symbols.OfType<WixPatchBaselineSymbol>(); |
| 65 | |
| 66 | var patchRefSymbols = symbols.OfType<WixPatchRefSymbol>().ToList(); |
| 67 | |
| 68 | foreach (var symbol in patchBaselineSymbols) |
| 69 | { |
| 70 | var targetData = this.GetWindowsInstallerData(symbol.BaselineFile.Path, BindStage.Target); |
| 71 | var updatedData = this.GetWindowsInstallerData(symbol.UpdateFile.Path, BindStage.Updated); |
| 72 | |
| 73 | if (patchRefSymbols.Count > 0) |
| 74 | { |
| 75 | var targetCommand = new GeneratePatchFilterIdsCommand(this.BackendExtensions, targetData, "target:"); |
| 76 | targetCommand.Execute(); |
| 77 | |
| 78 | patchFilterMap.AddTargetRowFilterIds(targetCommand.RowToFilterId); |
| 79 | |
| 80 | var updatedCommand = new GeneratePatchFilterIdsCommand(this.BackendExtensions, updatedData, "updated:"); |
| 81 | updatedCommand.Execute(); |
| 82 | |
| 83 | patchFilterMap.AddUpdatedRowFilterIds(updatedCommand.RowToFilterId); |
| 84 | } |
| 85 | |
| 86 | var command = new GenerateTransformCommand(this.Messaging, targetData, updatedData, patchFilterMap, preserveUnchangedRows: true, showPedanticMessages: false); |
| 87 | var transform = command.Execute(); |
| 88 | |
| 89 | patchTransforms.Add(new PatchTransform(symbol.Id.Id, transform)); |
| 90 | } |
| 91 | |
| 92 | this.PatchFilterMap = patchFilterMap; |
| 93 | this.PatchTransforms = patchTransforms; |
| 94 | |
| 95 | return this.PatchTransforms; |
| 96 | } |
| 97 | |
| 98 | private WindowsInstallerData GetWindowsInstallerData(string path, BindStage stage) |
| 99 | { |
| 100 | if (DataLoader.TryLoadWindowsInstallerData(path, true, out var data)) |
| 101 | { |
| 102 | // Re-resolve file paths only when loading from .wixpdb. |
| 103 | this.ReResolveWindowsInstallerData(data, stage); |
| 104 | } |
| 105 | else |
| 106 | { |
| 107 | var stageFolder = $"_{stage.ToString().ToLowerInvariant()}_msi"; |
| 108 | var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder); |
| 109 | var extractFilesFolder = Path.Combine(exportBasePath, "File"); |
| 110 | |
| 111 | var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.PathResolver, path, null, OutputType.Package, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); |
| 112 | data = command.Execute(); |
| 113 | } |
| 114 | |
| 115 | return data; |
| 116 | } |
| 117 | |
| 118 | private void ReResolveWindowsInstallerData(WindowsInstallerData data, BindStage stage) |
| 119 | { |
| 120 | var bindPaths = this.BindPaths.Where(b => b.Stage == stage).ToList(); |
| 121 | |
| 122 | if (bindPaths.Count == 0) |
| 123 | { |
| 124 | return; |
| 125 | } |
| 126 | |
| 127 | foreach (var table in data.Tables) |
| 128 | { |
| 129 | foreach (var row in table.Rows) |
| 130 | { |
| 131 | foreach (var field in row.Fields.Where(f => f.Column.Type == ColumnType.Object)) |
| 132 | { |
| 133 | if (field.PreviousData != null) |
| 134 | { |
| 135 | try |
| 136 | { |
| 137 | var originalPath = field.AsString(); |
| 138 | |
| 139 | var resolvedPath = this.FileResolver.ResolveFile(field.PreviousData, this.ResolverExtensions, bindPaths, stage, row.SourceLineNumbers, null); |
| 140 | |
| 141 | if (!String.Equals(originalPath, resolvedPath, StringComparison.OrdinalIgnoreCase)) |
| 142 | { |
| 143 | field.Data = resolvedPath; |
| 144 | } |
| 145 | } |
| 146 | catch (WixException e) |
| 147 | { |
| 148 | this.Messaging.Write(e.Error); |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | } |