diff options
56 files changed, 1508 insertions, 1676 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs index 40378a2e..77ce73aa 100644 --- a/src/api/wix/WixToolset.Data/ErrorMessages.cs +++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs | |||
| @@ -651,7 +651,8 @@ namespace WixToolset.Data | |||
| 651 | public static Message FileNotFound(SourceLineNumber sourceLineNumbers, string file, string fileType, IEnumerable<string> checkedPaths) | 651 | public static Message FileNotFound(SourceLineNumber sourceLineNumbers, string file, string fileType, IEnumerable<string> checkedPaths) |
| 652 | { | 652 | { |
| 653 | var combinedCheckedPaths = String.Join(", ", checkedPaths); | 653 | var combinedCheckedPaths = String.Join(", ", checkedPaths); |
| 654 | return Message(sourceLineNumbers, Ids.FileNotFound, "The system cannot find the file '{0}' with type '{1}'. The following paths were checked: {2}", file, fileType, combinedCheckedPaths); | 654 | var withType = String.IsNullOrEmpty(fileType) ? String.Empty : $" with type '{fileType}'"; |
| 655 | return Message(sourceLineNumbers, Ids.FileNotFound, "The system cannot find the file '{0}'{1}. The following paths were checked: {2}", file, withType, combinedCheckedPaths); | ||
| 655 | } | 656 | } |
| 656 | 657 | ||
| 657 | public static Message FileOrDirectoryPathRequired(string parameter) | 658 | public static Message FileOrDirectoryPathRequired(string parameter) |
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixPatchBaselineSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixPatchBaselineSymbol.cs index d7295424..cbf51e2e 100644 --- a/src/api/wix/WixToolset.Data/Symbols/WixPatchBaselineSymbol.cs +++ b/src/api/wix/WixToolset.Data/Symbols/WixPatchBaselineSymbol.cs | |||
| @@ -14,7 +14,6 @@ namespace WixToolset.Data | |||
| 14 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.ValidationFlags), IntermediateFieldType.Number), | 14 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.ValidationFlags), IntermediateFieldType.Number), |
| 15 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.BaselineFile), IntermediateFieldType.Path), | 15 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.BaselineFile), IntermediateFieldType.Path), |
| 16 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.UpdateFile), IntermediateFieldType.Path), | 16 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.UpdateFile), IntermediateFieldType.Path), |
| 17 | new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.TransformFile), IntermediateFieldType.Path), | ||
| 18 | }, | 17 | }, |
| 19 | typeof(WixPatchBaselineSymbol)); | 18 | typeof(WixPatchBaselineSymbol)); |
| 20 | } | 19 | } |
| @@ -28,7 +27,6 @@ namespace WixToolset.Data.Symbols | |||
| 28 | ValidationFlags, | 27 | ValidationFlags, |
| 29 | BaselineFile, | 28 | BaselineFile, |
| 30 | UpdateFile, | 29 | UpdateFile, |
| 31 | TransformFile, | ||
| 32 | } | 30 | } |
| 33 | 31 | ||
| 34 | public class WixPatchBaselineSymbol : IntermediateSymbol | 32 | public class WixPatchBaselineSymbol : IntermediateSymbol |
| @@ -66,11 +64,5 @@ namespace WixToolset.Data.Symbols | |||
| 66 | get => this.Fields[(int)WixPatchBaselineSymbolFields.UpdateFile].AsPath(); | 64 | get => this.Fields[(int)WixPatchBaselineSymbolFields.UpdateFile].AsPath(); |
| 67 | set => this.Set((int)WixPatchBaselineSymbolFields.UpdateFile, value); | 65 | set => this.Set((int)WixPatchBaselineSymbolFields.UpdateFile, value); |
| 68 | } | 66 | } |
| 69 | |||
| 70 | public IntermediateFieldPathValue TransformFile | ||
| 71 | { | ||
| 72 | get => this.Fields[(int)WixPatchBaselineSymbolFields.TransformFile].AsPath(); | ||
| 73 | set => this.Set((int)WixPatchBaselineSymbolFields.TransformFile, value); | ||
| 74 | } | ||
| 75 | } | 67 | } |
| 76 | } | 68 | } |
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IBindContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IBindContext.cs index 671da292..9d663c65 100644 --- a/src/api/wix/WixToolset.Extensibility/Data/IBindContext.cs +++ b/src/api/wix/WixToolset.Extensibility/Data/IBindContext.cs | |||
| @@ -18,6 +18,11 @@ namespace WixToolset.Extensibility.Data | |||
| 18 | IServiceProvider ServiceProvider { get; } | 18 | IServiceProvider ServiceProvider { get; } |
| 19 | 19 | ||
| 20 | /// <summary> | 20 | /// <summary> |
| 21 | /// Bind paths used during resolution. | ||
| 22 | /// </summary> | ||
| 23 | IReadOnlyCollection<IBindPath> BindPaths { get; set; } | ||
| 24 | |||
| 25 | /// <summary> | ||
| 21 | /// Counnt of threads to use in cabbing. | 26 | /// Counnt of threads to use in cabbing. |
| 22 | /// </summary> | 27 | /// </summary> |
| 23 | int CabbingThreadCount { get; set; } | 28 | int CabbingThreadCount { get; set; } |
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IFileFacade.cs b/src/api/wix/WixToolset.Extensibility/Data/IFileFacade.cs index fea00d4e..8a9e3fee 100644 --- a/src/api/wix/WixToolset.Extensibility/Data/IFileFacade.cs +++ b/src/api/wix/WixToolset.Extensibility/Data/IFileFacade.cs | |||
| @@ -5,34 +5,13 @@ namespace WixToolset.Extensibility.Data | |||
| 5 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
| 6 | using WixToolset.Data; | 6 | using WixToolset.Data; |
| 7 | using WixToolset.Data.Symbols; | 7 | using WixToolset.Data.Symbols; |
| 8 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 9 | 8 | ||
| 10 | /// <summary> | 9 | /// <summary> |
| 11 | /// Interface that provides a common facade over <c>FileSymbol</c> and <c>FileRow</c>. | 10 | /// Interface that provides a common facade over file information. |
| 12 | /// </summary> | 11 | /// </summary> |
| 13 | public interface IFileFacade | 12 | public interface IFileFacade |
| 14 | { | 13 | { |
| 15 | /// <summary> | 14 | /// <summary> |
| 16 | /// Reference to assembly application for this file. | ||
| 17 | /// </summary> | ||
| 18 | string AssemblyApplicationFileRef { get; } | ||
| 19 | |||
| 20 | /// <summary> | ||
| 21 | /// Reference to assembly manifest for this file. | ||
| 22 | /// </summary> | ||
| 23 | string AssemblyManifestFileRef { get; } | ||
| 24 | |||
| 25 | /// <summary> | ||
| 26 | /// List of assembly name values in the file. | ||
| 27 | /// </summary> | ||
| 28 | List<MsiAssemblyNameSymbol> AssemblyNames { get; set; } | ||
| 29 | |||
| 30 | /// <summary> | ||
| 31 | /// Optionally indicates what sort of assembly the file is. | ||
| 32 | /// </summary> | ||
| 33 | AssemblyType? AssemblyType { get; } | ||
| 34 | |||
| 35 | /// <summary> | ||
| 36 | /// Component containing the file. | 15 | /// Component containing the file. |
| 37 | /// </summary> | 16 | /// </summary> |
| 38 | string ComponentRef { get; } | 17 | string ComponentRef { get; } |
| @@ -58,21 +37,6 @@ namespace WixToolset.Extensibility.Data | |||
| 58 | int FileSize { get; set; } | 37 | int FileSize { get; set; } |
| 59 | 38 | ||
| 60 | /// <summary> | 39 | /// <summary> |
| 61 | /// Indicates whether the file came from a merge module. | ||
| 62 | /// </summary> | ||
| 63 | bool FromModule { get; } | ||
| 64 | |||
| 65 | /// <summary> | ||
| 66 | /// Indicates whether the file came from a transform. | ||
| 67 | /// </summary> | ||
| 68 | bool FromTransform { get; } | ||
| 69 | |||
| 70 | /// <summary> | ||
| 71 | /// Hash symbol of the file. | ||
| 72 | /// </summary> | ||
| 73 | MsiFileHashSymbol Hash { get; set; } | ||
| 74 | |||
| 75 | /// <summary> | ||
| 76 | /// Underlying identifier of the file. | 40 | /// Underlying identifier of the file. |
| 77 | /// </summary> | 41 | /// </summary> |
| 78 | Identifier Identifier { get; } | 42 | Identifier Identifier { get; } |
| @@ -118,9 +82,13 @@ namespace WixToolset.Extensibility.Data | |||
| 118 | string Version { get; set; } | 82 | string Version { get; set; } |
| 119 | 83 | ||
| 120 | /// <summary> | 84 | /// <summary> |
| 121 | /// Gets the underlying <c>FileRow</c> if one is present. | 85 | /// Calculated hash of the file. |
| 86 | /// </summary> | ||
| 87 | MsiFileHashSymbol MsiFileHashSymbol { get; set; } | ||
| 88 | |||
| 89 | /// <summary> | ||
| 90 | /// Assembly names found in the file. | ||
| 122 | /// </summary> | 91 | /// </summary> |
| 123 | /// <returns><c>FileRow</c> if one is present, otherwise throws.</returns> | 92 | ICollection<MsiAssemblyNameSymbol> AssemblyNameSymbols { get; } |
| 124 | FileRow GetFileRow(); | ||
| 125 | } | 93 | } |
| 126 | } | 94 | } |
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs index 27d30a5a..7b974942 100644 --- a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs +++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs | |||
| @@ -63,11 +63,6 @@ namespace WixToolset.Extensibility.Data | |||
| 63 | string IntermediateFolder { get; set; } | 63 | string IntermediateFolder { get; set; } |
| 64 | 64 | ||
| 65 | /// <summary> | 65 | /// <summary> |
| 66 | /// Gets or sets whether the decompiler admin image. | ||
| 67 | /// </summary> | ||
| 68 | bool IsAdminImage { get; set; } | ||
| 69 | |||
| 70 | /// <summary> | ||
| 71 | /// Gets or sets where to output the result. | 66 | /// Gets or sets where to output the result. |
| 72 | /// </summary> | 67 | /// </summary> |
| 73 | string OutputPath { get; set; } | 68 | string OutputPath { get; set; } |
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs b/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs index eff42b99..8bb1b2d6 100644 --- a/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs +++ b/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs | |||
| @@ -5,8 +5,6 @@ namespace WixToolset.Extensibility.Services | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using WixToolset.Data; | 7 | using WixToolset.Data; |
| 8 | using WixToolset.Data.Symbols; | ||
| 9 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 10 | using WixToolset.Extensibility.Data; | 8 | using WixToolset.Extensibility.Data; |
| 11 | 9 | ||
| 12 | /// <summary> | 10 | /// <summary> |
| @@ -15,28 +13,6 @@ namespace WixToolset.Extensibility.Services | |||
| 15 | public interface IBackendHelper : ILayoutServices | 13 | public interface IBackendHelper : ILayoutServices |
| 16 | { | 14 | { |
| 17 | /// <summary> | 15 | /// <summary> |
| 18 | /// Creates a file facade from a <c>FileSymbol</c> and possible <c>AssemblySymbol</c>. | ||
| 19 | /// </summary> | ||
| 20 | /// <param name="file"><c>FileSymbol</c> backing the facade.</param> | ||
| 21 | /// <param name="assembly"><c>AssemblySymbol</c> backing the facade.</param> | ||
| 22 | /// <returns></returns> | ||
| 23 | IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly); | ||
| 24 | |||
| 25 | /// <summary> | ||
| 26 | /// Creates a file facade from a File row. | ||
| 27 | /// </summary> | ||
| 28 | /// <param name="fileRow"><c>FileRow</c> </param> | ||
| 29 | /// <returns>New <c>IFileFacade</c>.</returns> | ||
| 30 | IFileFacade CreateFileFacade(FileRow fileRow); | ||
| 31 | |||
| 32 | /// <summary> | ||
| 33 | /// Creates a file facade from a Merge Module's File symbol. | ||
| 34 | /// </summary> | ||
| 35 | /// <param name="fileSymbol"><c>FileSymbol</c> created from a Merge Module.</param> | ||
| 36 | /// <returns>New <c>IFileFacade</c>.</returns> | ||
| 37 | IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol); | ||
| 38 | |||
| 39 | /// <summary> | ||
| 40 | /// Creates a MSI compatible GUID. | 16 | /// Creates a MSI compatible GUID. |
| 41 | /// </summary> | 17 | /// </summary> |
| 42 | /// <returns>Creates an uppercase GUID with braces.</returns> | 18 | /// <returns>Creates an uppercase GUID with braces.</returns> |
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IFileResolver.cs b/src/api/wix/WixToolset.Extensibility/Services/IFileResolver.cs new file mode 100644 index 00000000..2804cc28 --- /dev/null +++ b/src/api/wix/WixToolset.Extensibility/Services/IFileResolver.cs | |||
| @@ -0,0 +1,38 @@ | |||
| 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.Extensibility.Services | ||
| 4 | { | ||
| 5 | using System.Collections.Generic; | ||
| 6 | using WixToolset.Data; | ||
| 7 | using WixToolset.Extensibility.Data; | ||
| 8 | |||
| 9 | /// <summary> | ||
| 10 | /// Interface to resolve file paths using extensions and bind paths. | ||
| 11 | /// </summary> | ||
| 12 | public interface IFileResolver | ||
| 13 | { | ||
| 14 | /// <summary> | ||
| 15 | /// Resolves the source path of a file using binder extensions. | ||
| 16 | /// </summary> | ||
| 17 | /// <param name="source">Original source value.</param> | ||
| 18 | /// <param name="librarianExtensions">Extensions used to resolve the file path.</param> | ||
| 19 | /// <param name="bindPaths">Collection of bind paths for the binding stage.</param> | ||
| 20 | /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> | ||
| 21 | /// <param name="symbolDefinition">Optional type of source file being resolved.</param> | ||
| 22 | /// <returns>Should return a valid path for the stream to be imported.</returns> | ||
| 23 | string ResolveFile(string source, IEnumerable<ILibrarianExtension> librarianExtensions, IEnumerable<IBindPath> bindPaths, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition); | ||
| 24 | |||
| 25 | /// <summary> | ||
| 26 | /// Resolves the source path of a file using binder extensions. | ||
| 27 | /// </summary> | ||
| 28 | /// <param name="source">Original source value.</param> | ||
| 29 | /// <param name="resolverExtensions">Extensions used to resolve the file path.</param> | ||
| 30 | /// <param name="bindPaths">Collection of bind paths for the binding stage.</param> | ||
| 31 | /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> | ||
| 32 | /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> | ||
| 33 | /// <param name="symbolDefinition">Optional type of source file being resolved.</param> | ||
| 34 | /// <param name="alreadyCheckedPaths">Optional collection of paths already checked.</param> | ||
| 35 | /// <returns>Should return a valid path for the stream to be imported.</returns> | ||
| 36 | string ResolveFile(string source, IEnumerable<IResolverExtension> resolverExtensions, IEnumerable<IBindPath> bindPaths, BindStage bindStage, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, IEnumerable<string> alreadyCheckedPaths = null); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerBackendHelper.cs b/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerBackendHelper.cs index 81325131..2216e957 100644 --- a/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerBackendHelper.cs +++ b/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerBackendHelper.cs | |||
| @@ -3,7 +3,10 @@ | |||
| 3 | namespace WixToolset.Extensibility.Services | 3 | namespace WixToolset.Extensibility.Services |
| 4 | { | 4 | { |
| 5 | using WixToolset.Data; | 5 | using WixToolset.Data; |
| 6 | using WixToolset.Data.Symbols; | ||
| 6 | using WixToolset.Data.WindowsInstaller; | 7 | using WixToolset.Data.WindowsInstaller; |
| 8 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 9 | using WixToolset.Extensibility.Data; | ||
| 7 | 10 | ||
| 8 | /// <summary> | 11 | /// <summary> |
| 9 | /// Interface provided to help Windows Installer backend extensions. | 12 | /// Interface provided to help Windows Installer backend extensions. |
| @@ -11,6 +14,20 @@ namespace WixToolset.Extensibility.Services | |||
| 11 | public interface IWindowsInstallerBackendHelper : IBackendHelper | 14 | public interface IWindowsInstallerBackendHelper : IBackendHelper |
| 12 | { | 15 | { |
| 13 | /// <summary> | 16 | /// <summary> |
| 17 | /// Creates a file facade from a <c>FileSymbol</c>. | ||
| 18 | /// </summary> | ||
| 19 | /// <param name="file"><c>FileSymbol</c> backing the facade.</param> | ||
| 20 | /// <returns></returns> | ||
| 21 | IFileFacade CreateFileFacade(FileSymbol file); | ||
| 22 | |||
| 23 | /// <summary> | ||
| 24 | /// Creates a file facade from a File row. | ||
| 25 | /// </summary> | ||
| 26 | /// <param name="fileRow"><c>FileRow</c></param> | ||
| 27 | /// <returns>New <c>IFileFacade</c>.</returns> | ||
| 28 | IFileFacade CreateFileFacade(FileRow fileRow); | ||
| 29 | |||
| 30 | /// <summary> | ||
| 14 | /// Creates a <see cref="Row"/> in the specified table. | 31 | /// Creates a <see cref="Row"/> in the specified table. |
| 15 | /// </summary> | 32 | /// </summary> |
| 16 | /// <param name="section">Parent section.</param> | 33 | /// <param name="section">Parent section.</param> |
diff --git a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs index 93e7fc20..f2b3587d 100644 --- a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs +++ b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs | |||
| @@ -35,21 +35,6 @@ namespace WixToolset.Core.Burn.ExtensibilityServices | |||
| 35 | 35 | ||
| 36 | #region IBackendHelper interfaces | 36 | #region IBackendHelper interfaces |
| 37 | 37 | ||
| 38 | public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly) | ||
| 39 | { | ||
| 40 | return this.backendHelper.CreateFileFacade(file, assembly); | ||
| 41 | } | ||
| 42 | |||
| 43 | public IFileFacade CreateFileFacade(FileRow fileRow) | ||
| 44 | { | ||
| 45 | return this.backendHelper.CreateFileFacade(fileRow); | ||
| 46 | } | ||
| 47 | |||
| 48 | public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol) | ||
| 49 | { | ||
| 50 | return this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol); | ||
| 51 | } | ||
| 52 | |||
| 53 | public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) | 38 | public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) |
| 54 | { | 39 | { |
| 55 | return this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers); | 40 | return this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers); |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AddCreateFoldersCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AddCreateFoldersCommand.cs index c4fddb3e..44b66cea 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AddCreateFoldersCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AddCreateFoldersCommand.cs | |||
| @@ -35,4 +35,4 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 35 | } | 35 | } |
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | } \ No newline at end of file | 38 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs index 66078d8d..6d37fdc2 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs | |||
| @@ -145,8 +145,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 145 | var mediaSymbol = patchMediaByDiskId[baselineSymbol.DiskId]; | 145 | var mediaSymbol = patchMediaByDiskId[baselineSymbol.DiskId]; |
| 146 | 146 | ||
| 147 | // Ensure that files are sequenced after the last file in any transform. | 147 | // Ensure that files are sequenced after the last file in any transform. |
| 148 | var transformMediaTable = mainTransform.Transform.Tables["Media"]; | 148 | if (mainTransform.Transform.Tables.TryGetTable("Media", out var transformMediaTable)) |
| 149 | if (null != transformMediaTable && 0 < transformMediaTable.Rows.Count) | ||
| 150 | { | 149 | { |
| 151 | foreach (MediaRow transformMediaRow in transformMediaTable.Rows) | 150 | foreach (MediaRow transformMediaRow in transformMediaTable.Rows) |
| 152 | { | 151 | { |
| @@ -370,16 +369,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 370 | { | 369 | { |
| 371 | if (transform.TryGetTable(tableName, out var table)) | 370 | if (transform.TryGetTable(tableName, out var table)) |
| 372 | { | 371 | { |
| 373 | foreach (var row in table.Rows) | 372 | foreach (var row in table.Rows.Where(r => r.Operation == RowOperation.Add)) |
| 374 | { | 373 | { |
| 375 | if (row.Operation == RowOperation.Add) | 374 | success = false; |
| 376 | { | ||
| 377 | success = false; | ||
| 378 | 375 | ||
| 379 | var primaryKey = row.GetPrimaryKey('/') ?? String.Empty; | 376 | var primaryKey = row.GetPrimaryKey('/') ?? String.Empty; |
| 380 | 377 | ||
| 381 | this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey)); | 378 | this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey)); |
| 382 | } | ||
| 383 | } | 379 | } |
| 384 | } | 380 | } |
| 385 | } | 381 | } |
| @@ -925,7 +921,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 925 | for (var i = 0; i < propertyTable.Rows.Count; ++i) | 921 | for (var i = 0; i < propertyTable.Rows.Count; ++i) |
| 926 | { | 922 | { |
| 927 | var propertyRow = propertyTable.Rows[i]; | 923 | var propertyRow = propertyTable.Rows[i]; |
| 928 | var property = (string)propertyRow[0]; | 924 | var property = propertyRow.FieldAsString(0); |
| 929 | 925 | ||
| 930 | if ("ProductCode" == property) | 926 | if ("ProductCode" == property) |
| 931 | { | 927 | { |
| @@ -1173,10 +1169,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1173 | { | 1169 | { |
| 1174 | var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); | 1170 | var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); |
| 1175 | 1171 | ||
| 1176 | foreach (FileRow mainFileRow in mainFileTable.Rows) | 1172 | foreach (var mainFileRow in mainFileTable.Rows.Cast<FileRow>()) |
| 1177 | { | 1173 | { |
| 1178 | // Set File.Sequence to non null to satisfy transform bind. | 1174 | // Set File.Sequence to non null to satisfy transform bind and suppress any |
| 1175 | // change to File.Sequence to avoid bloat. | ||
| 1179 | mainFileRow.Sequence = 1; | 1176 | mainFileRow.Sequence = 1; |
| 1177 | mainFileRow.Fields[7].Modified = false; | ||
| 1178 | |||
| 1179 | // Override authored media to the media provided in the patch. | ||
| 1180 | mainFileRow.DiskId = mediaSymbol.DiskId; | ||
| 1180 | 1181 | ||
| 1181 | // Delete's don't need rows in the paired transform. | 1182 | // Delete's don't need rows in the paired transform. |
| 1182 | if (mainFileRow.Operation == RowOperation.Delete) | 1183 | if (mainFileRow.Operation == RowOperation.Delete) |
| @@ -1188,13 +1189,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1188 | pairedFileRow.Operation = RowOperation.Modify; | 1189 | pairedFileRow.Operation = RowOperation.Modify; |
| 1189 | mainFileRow.CopyTo(pairedFileRow); | 1190 | mainFileRow.CopyTo(pairedFileRow); |
| 1190 | 1191 | ||
| 1191 | // Override authored media for patch bind. | 1192 | // Force modified File rows to appear in the transform. |
| 1192 | mainFileRow.DiskId = mediaSymbol.DiskId; | ||
| 1193 | |||
| 1194 | // Suppress any change to File.Sequence to avoid bloat. | ||
| 1195 | mainFileRow.Fields[7].Modified = false; | ||
| 1196 | |||
| 1197 | // Force File row to appear in the transform. | ||
| 1198 | switch (mainFileRow.Operation) | 1193 | switch (mainFileRow.Operation) |
| 1199 | { | 1194 | { |
| 1200 | case RowOperation.Modify: | 1195 | case RowOperation.Modify: |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index b849aea5..41559f8b 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
| @@ -21,7 +21,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 21 | // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. | 21 | // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. |
| 22 | internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); | 22 | internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); |
| 23 | 23 | ||
| 24 | public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages = null) | 24 | public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> patchSubStorages = null) |
| 25 | { | 25 | { |
| 26 | this.ServiceProvider = context.ServiceProvider; | 26 | this.ServiceProvider = context.ServiceProvider; |
| 27 | 27 | ||
| @@ -47,7 +47,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 47 | this.ResolvedLcid = context.ResolvedLcid; | 47 | this.ResolvedLcid = context.ResolvedLcid; |
| 48 | this.SuppressLayout = context.SuppressLayout; | 48 | this.SuppressLayout = context.SuppressLayout; |
| 49 | 49 | ||
| 50 | this.SubStorages = subStorages; | 50 | this.PatchSubStorages = patchSubStorages; |
| 51 | 51 | ||
| 52 | this.BackendExtensions = backendExtension; | 52 | this.BackendExtensions = backendExtension; |
| 53 | } | 53 | } |
| @@ -76,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 76 | 76 | ||
| 77 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } | 77 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } |
| 78 | 78 | ||
| 79 | private IEnumerable<SubStorage> SubStorages { get; } | 79 | private IEnumerable<SubStorage> PatchSubStorages { get; } |
| 80 | 80 | ||
| 81 | private Intermediate Intermediate { get; } | 81 | private Intermediate Intermediate { get; } |
| 82 | 82 | ||
| @@ -180,35 +180,42 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 180 | command.Execute(); | 180 | command.Execute(); |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | #if TODO_PATCHING | 183 | // Add missing CreateFolder symbols to null-keypath components. |
| 184 | ////if (OutputType.Patch == this.Output.Type) | 184 | { |
| 185 | ////{ | 185 | var command = new AddCreateFoldersCommand(section); |
| 186 | //// foreach (SubStorage substorage in this.Output.SubStorages) | 186 | command.Execute(); |
| 187 | //// { | 187 | } |
| 188 | //// Output transform = substorage.Data; | ||
| 189 | |||
| 190 | //// ResolveFieldsCommand command = new ResolveFieldsCommand(); | ||
| 191 | //// command.Tables = transform.Tables; | ||
| 192 | //// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | ||
| 193 | //// command.FileManagerCore = this.FileManagerCore; | ||
| 194 | //// command.FileManagers = this.FileManagers; | ||
| 195 | //// command.SupportDelayedResolution = false; | ||
| 196 | //// command.TempFilesLocation = this.TempFilesLocation; | ||
| 197 | //// command.WixVariableResolver = this.WixVariableResolver; | ||
| 198 | //// command.Execute(); | ||
| 199 | |||
| 200 | //// this.MergeUnrealTables(transform.Tables); | ||
| 201 | //// } | ||
| 202 | ////} | ||
| 203 | #endif | ||
| 204 | 188 | ||
| 205 | if (this.Messaging.EncounteredError) | 189 | if (this.Messaging.EncounteredError) |
| 206 | { | 190 | { |
| 207 | return null; | 191 | return null; |
| 208 | } | 192 | } |
| 209 | 193 | ||
| 210 | this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound); | 194 | // Process dependency references. |
| 211 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); | 195 | if (SectionType.Product == section.Type || SectionType.Module == section.Type) |
| 196 | { | ||
| 197 | var dependencyRefs = section.Symbols.OfType<WixDependencyRefSymbol>().ToList(); | ||
| 198 | |||
| 199 | if (dependencyRefs.Any()) | ||
| 200 | { | ||
| 201 | var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs); | ||
| 202 | command.Execute(); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | // Process SoftwareTags in MSI packages. | ||
| 207 | if (SectionType.Product == section.Type) | ||
| 208 | { | ||
| 209 | var softwareTags = section.Symbols.OfType<WixPackageTagSymbol>().ToList(); | ||
| 210 | |||
| 211 | if (softwareTags.Any()) | ||
| 212 | { | ||
| 213 | var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, softwareTags, this.IntermediateFolder); | ||
| 214 | command.Execute(); | ||
| 215 | |||
| 216 | trackedFiles.AddRange(command.TrackedFiles); | ||
| 217 | } | ||
| 218 | } | ||
| 212 | 219 | ||
| 213 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). | 220 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). |
| 214 | { | 221 | { |
| @@ -217,21 +224,33 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 217 | trackedFiles.AddRange(extractedFiles); | 224 | trackedFiles.AddRange(extractedFiles); |
| 218 | } | 225 | } |
| 219 | 226 | ||
| 227 | // Update symbols that reference text files on disk. Some of those files may have come from .wixlibs and WixExtensions | ||
| 228 | // extracted above. | ||
| 229 | { | ||
| 230 | var command = new UpdateFromTextFilesCommand(this.Messaging, section); | ||
| 231 | command.Execute(); | ||
| 232 | } | ||
| 233 | |||
| 234 | this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound); | ||
| 235 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); | ||
| 236 | |||
| 220 | // This must occur after all variables and source paths have been resolved. | 237 | // This must occur after all variables and source paths have been resolved. |
| 221 | List<IFileFacade> fileFacades; | 238 | List<IFileFacade> allFileFacades; |
| 222 | if (SectionType.Patch == section.Type) | 239 | List<IFileFacade> fileFacadesFromIntermediate; |
| 240 | List<IFileFacade> fileFacadesFromModule = null; | ||
| 241 | if (section.Type == SectionType.Patch) | ||
| 223 | { | 242 | { |
| 224 | var command = new GetFileFacadesFromTransforms(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, this.SubStorages); | 243 | var command = new GetFileFacadesFromTransforms(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, this.PatchSubStorages); |
| 225 | command.Execute(); | 244 | command.Execute(); |
| 226 | 245 | ||
| 227 | fileFacades = command.FileFacades; | 246 | allFileFacades = fileFacadesFromIntermediate = command.FileFacades; |
| 228 | } | 247 | } |
| 229 | else | 248 | else |
| 230 | { | 249 | { |
| 231 | var command = new GetFileFacadesCommand(section, this.WindowsInstallerBackendHelper); | 250 | var command = new GetFileFacadesCommand(section, this.WindowsInstallerBackendHelper); |
| 232 | command.Execute(); | 251 | command.Execute(); |
| 233 | 252 | ||
| 234 | fileFacades = command.FileFacades; | 253 | allFileFacades = fileFacadesFromIntermediate = command.FileFacades; |
| 235 | } | 254 | } |
| 236 | 255 | ||
| 237 | // Retrieve file information from merge modules. | 256 | // Retrieve file information from merge modules. |
| @@ -243,10 +262,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 243 | { | 262 | { |
| 244 | containsMergeModules = true; | 263 | containsMergeModules = true; |
| 245 | 264 | ||
| 246 | var command = new ExtractMergeModuleFilesCommand(this.Messaging, this.WindowsInstallerBackendHelper, wixMergeSymbols, fileFacades, installerVersion, this.IntermediateFolder, this.SuppressLayout); | 265 | var command = new ExtractMergeModuleFilesCommand(this.Messaging, this.WindowsInstallerBackendHelper, wixMergeSymbols, fileFacadesFromIntermediate, installerVersion, this.IntermediateFolder, this.SuppressLayout); |
| 247 | command.Execute(); | 266 | command.Execute(); |
| 248 | 267 | ||
| 249 | fileFacades.AddRange(command.MergeModulesFileFacades); | 268 | fileFacadesFromModule = new List<IFileFacade>(command.MergeModulesFileFacades); |
| 269 | allFileFacades.AddRange(fileFacadesFromModule); | ||
| 250 | trackedFiles.AddRange(command.TrackedFiles); | 270 | trackedFiles.AddRange(command.TrackedFiles); |
| 251 | } | 271 | } |
| 252 | } | 272 | } |
| @@ -257,23 +277,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 257 | return null; | 277 | return null; |
| 258 | } | 278 | } |
| 259 | 279 | ||
| 260 | // Process SoftwareTags in MSI packages. | ||
| 261 | if (SectionType.Product == section.Type) | ||
| 262 | { | ||
| 263 | var softwareTags = section.Symbols.OfType<WixPackageTagSymbol>().ToList(); | ||
| 264 | |||
| 265 | if (softwareTags.Any()) | ||
| 266 | { | ||
| 267 | var command = new ProcessPackageSoftwareTagsCommand(section, this.WindowsInstallerBackendHelper, softwareTags, this.IntermediateFolder); | ||
| 268 | command.Execute(); | ||
| 269 | |||
| 270 | trackedFiles.AddRange(command.TrackedFiles); | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | // Gather information about files that do not come from merge modules. | 280 | // Gather information about files that do not come from merge modules. |
| 275 | { | 281 | { |
| 276 | var command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, fileFacades.Where(f => !f.FromModule), variableCache, overwriteHash: true); | 282 | var command = new UpdateFileFacadesCommand(this.Messaging, section, allFileFacades, fileFacadesFromIntermediate, variableCache, overwriteHash: true); |
| 277 | command.Execute(); | 283 | command.Execute(); |
| 278 | } | 284 | } |
| 279 | 285 | ||
| @@ -289,18 +295,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 289 | this.WindowsInstallerBackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache); | 295 | this.WindowsInstallerBackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache); |
| 290 | } | 296 | } |
| 291 | 297 | ||
| 292 | // Update symbols that reference text files on disk. | ||
| 293 | { | ||
| 294 | var command = new UpdateFromTextFilesCommand(this.Messaging, section); | ||
| 295 | command.Execute(); | ||
| 296 | } | ||
| 297 | |||
| 298 | // Add missing CreateFolder symbols to null-keypath components. | ||
| 299 | { | ||
| 300 | var command = new AddCreateFoldersCommand(section); | ||
| 301 | command.Execute(); | ||
| 302 | } | ||
| 303 | |||
| 304 | // Now that delayed fields are processed, fixup the package version (if needed) and validate it | 298 | // Now that delayed fields are processed, fixup the package version (if needed) and validate it |
| 305 | // which will short circuit duplicate errors later if the ProductVersion is invalid. | 299 | // which will short circuit duplicate errors later if the ProductVersion is invalid. |
| 306 | if (SectionType.Product == section.Type) | 300 | if (SectionType.Product == section.Type) |
| @@ -308,18 +302,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 308 | this.ProcessProductVersion(packageSymbol, section, validate: true); | 302 | this.ProcessProductVersion(packageSymbol, section, validate: true); |
| 309 | } | 303 | } |
| 310 | 304 | ||
| 311 | // Process dependency references. | ||
| 312 | if (SectionType.Product == section.Type || SectionType.Module == section.Type) | ||
| 313 | { | ||
| 314 | var dependencyRefs = section.Symbols.OfType<WixDependencyRefSymbol>().ToList(); | ||
| 315 | |||
| 316 | if (dependencyRefs.Any()) | ||
| 317 | { | ||
| 318 | var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs); | ||
| 319 | command.Execute(); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | // If there are any backend extensions, give them the opportunity to process | 305 | // If there are any backend extensions, give them the opportunity to process |
| 324 | // the section now that the fields have all be resolved. | 306 | // the section now that the fields have all be resolved. |
| 325 | // | 307 | // |
| @@ -339,9 +321,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 339 | 321 | ||
| 340 | if (reresolvedFiles.Any()) | 322 | if (reresolvedFiles.Any()) |
| 341 | { | 323 | { |
| 342 | var updatedFacades = reresolvedFiles.Select(f => fileFacades.First(ff => ff.Id == f.Id?.Id)); | 324 | var updatedFacades = reresolvedFiles.Select(f => allFileFacades.First(ff => ff.Id == f.Id?.Id)); |
| 343 | 325 | ||
| 344 | var command = new UpdateFileFacadesCommand(this.Messaging, section, fileFacades, updatedFacades, variableCache, overwriteHash: false); | 326 | var command = new UpdateFileFacadesCommand(this.Messaging, section, allFileFacades, updatedFacades, variableCache, overwriteHash: false); |
| 345 | command.Execute(); | 327 | command.Execute(); |
| 346 | } | 328 | } |
| 347 | } | 329 | } |
| @@ -363,28 +345,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 363 | } | 345 | } |
| 364 | } | 346 | } |
| 365 | 347 | ||
| 366 | // Set generated component guids and validate all guids. | ||
| 367 | { | ||
| 368 | var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform); | ||
| 369 | command.Execute(); | ||
| 370 | } | ||
| 371 | |||
| 372 | // Assign files to media and update file sequences. | 348 | // Assign files to media and update file sequences. |
| 373 | Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia; | 349 | Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia; |
| 374 | IEnumerable<IFileFacade> uncompressedFiles; | 350 | IEnumerable<IFileFacade> uncompressedFiles; |
| 375 | { | 351 | { |
| 376 | var order = new OptimizeFileFacadesOrderCommand(this.WindowsInstallerBackendHelper, this.PathResolver, section, platform, fileFacades); | 352 | var order = new OptimizeFileFacadesOrderCommand(this.WindowsInstallerBackendHelper, this.PathResolver, section, platform, allFileFacades); |
| 377 | order.Execute(); | 353 | order.Execute(); |
| 378 | 354 | ||
| 379 | fileFacades = order.FileFacades; | 355 | allFileFacades = order.FileFacades; |
| 380 | 356 | ||
| 381 | var assign = new AssignMediaCommand(section, this.Messaging, fileFacades, compressed); | 357 | var assign = new AssignMediaCommand(section, this.Messaging, allFileFacades, compressed); |
| 382 | assign.Execute(); | 358 | assign.Execute(); |
| 383 | 359 | ||
| 384 | filesByCabinetMedia = assign.FileFacadesByCabinetMedia; | 360 | filesByCabinetMedia = assign.FileFacadesByCabinetMedia; |
| 385 | uncompressedFiles = assign.UncompressedFileFacades; | 361 | uncompressedFiles = assign.UncompressedFileFacades; |
| 386 | 362 | ||
| 387 | var update = new UpdateMediaSequencesCommand(section, fileFacades); | 363 | var update = new UpdateMediaSequencesCommand(section, allFileFacades); |
| 388 | update.Execute(); | 364 | update.Execute(); |
| 389 | } | 365 | } |
| 390 | 366 | ||
| @@ -394,6 +370,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 394 | return null; | 370 | return null; |
| 395 | } | 371 | } |
| 396 | 372 | ||
| 373 | // Copy updated file facade data back into the transforms or symbols as appropriate. | ||
| 374 | if (section.Type == SectionType.Patch) | ||
| 375 | { | ||
| 376 | var command = new UpdateTransformsWithFileFacades(this.Messaging, section, this.PatchSubStorages, tableDefinitions, allFileFacades); | ||
| 377 | command.Execute(); | ||
| 378 | } | ||
| 379 | else | ||
| 380 | { | ||
| 381 | var command = new UpdateSymbolsWithFileFacadesCommand(section, allFileFacades); | ||
| 382 | command.Execute(); | ||
| 383 | } | ||
| 384 | |||
| 385 | // Set generated component guids and validate all guids. | ||
| 386 | { | ||
| 387 | var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform); | ||
| 388 | command.Execute(); | ||
| 389 | } | ||
| 390 | |||
| 397 | // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better. | 391 | // Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better. |
| 398 | WindowsInstallerData data; | 392 | WindowsInstallerData data; |
| 399 | { | 393 | { |
| @@ -412,9 +406,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 412 | var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions); | 406 | var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions); |
| 413 | suppressedTableNames = unsuppress.Execute(); | 407 | suppressedTableNames = unsuppress.Execute(); |
| 414 | } | 408 | } |
| 409 | else if (data.Type == OutputType.Product) // we can create instance transforms since Component Guids and Outputs are created. | ||
| 410 | { | ||
| 411 | var command = new CreateInstanceTransformsCommand(section, data, tableDefinitions, this.WindowsInstallerBackendHelper); | ||
| 412 | command.Execute(); | ||
| 413 | |||
| 414 | foreach (var storage in command.SubStorages) | ||
| 415 | { | ||
| 416 | data.SubStorages.Add(storage); | ||
| 417 | } | ||
| 418 | } | ||
| 415 | else if (data.Type == OutputType.Patch) | 419 | else if (data.Type == OutputType.Patch) |
| 416 | { | 420 | { |
| 417 | foreach (var storage in this.SubStorages) | 421 | foreach (var storage in this.PatchSubStorages) |
| 418 | { | 422 | { |
| 419 | data.SubStorages.Add(storage); | 423 | data.SubStorages.Add(storage); |
| 420 | } | 424 | } |
| @@ -426,13 +430,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 426 | return null; | 430 | return null; |
| 427 | } | 431 | } |
| 428 | 432 | ||
| 429 | // Ensure the intermediate folder is created since delta patches will be | 433 | if (section.Type == SectionType.Patch && this.DeltaBinaryPatch) |
| 430 | // created there. | ||
| 431 | Directory.CreateDirectory(this.IntermediateFolder); | ||
| 432 | |||
| 433 | if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) | ||
| 434 | { | 434 | { |
| 435 | var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Symbols.OfType<WixPatchSymbol>().FirstOrDefault()); | 435 | var command = new CreateDeltaPatchesCommand(allFileFacades, this.IntermediateFolder, section.Symbols.OfType<WixPatchSymbol>().FirstOrDefault()); |
| 436 | command.Execute(); | 436 | command.Execute(); |
| 437 | } | 437 | } |
| 438 | 438 | ||
| @@ -454,19 +454,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 454 | return null; | 454 | return null; |
| 455 | } | 455 | } |
| 456 | 456 | ||
| 457 | // We can create instance transforms since Component Guids and Outputs are created. | ||
| 458 | if (data.Type == OutputType.Product) | ||
| 459 | { | ||
| 460 | var command = new CreateInstanceTransformsCommand(section, data, tableDefinitions, this.WindowsInstallerBackendHelper); | ||
| 461 | command.Execute(); | ||
| 462 | } | ||
| 463 | else if (data.Type == OutputType.Patch) | ||
| 464 | { | ||
| 465 | // Copy output data back into the transforms. | ||
| 466 | var command = new UpdateTransformsWithFileFacades(this.Messaging, data, this.SubStorages, tableDefinitions, fileFacades); | ||
| 467 | command.Execute(); | ||
| 468 | } | ||
| 469 | |||
| 470 | // Generate database file. | 457 | // Generate database file. |
| 471 | { | 458 | { |
| 472 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); | 459 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); |
| @@ -491,7 +478,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 491 | { | 478 | { |
| 492 | this.Messaging.Write(VerboseMessages.MergingModules()); | 479 | this.Messaging.Write(VerboseMessages.MergingModules()); |
| 493 | 480 | ||
| 494 | var command = new MergeModulesCommand(this.Messaging, this.WindowsInstallerBackendHelper, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder); | 481 | var command = new MergeModulesCommand(this.Messaging, this.WindowsInstallerBackendHelper, fileFacadesFromModule, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder); |
| 495 | command.Execute(); | 482 | command.Execute(); |
| 496 | 483 | ||
| 497 | trackedFiles.AddRange(command.TrackedFiles); | 484 | trackedFiles.AddRange(command.TrackedFiles); |
| @@ -518,7 +505,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 518 | var fi = new FileInfo(this.OutputPath); | 505 | var fi = new FileInfo(this.OutputPath); |
| 519 | if (fi.Length > Int32.MaxValue) | 506 | if (fi.Length > Int32.MaxValue) |
| 520 | { | 507 | { |
| 521 | this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(null, this.OutputPath, "MSI")); | 508 | this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(null, this.OutputPath, data.Type.ToString())); |
| 522 | } | 509 | } |
| 523 | } | 510 | } |
| 524 | catch | 511 | catch |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs index 9acbe475..93aad0a6 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | |||
| @@ -169,18 +169,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 169 | } | 169 | } |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | // create the cabinet file | 172 | // Calculate the files to be compressed into the cabinet. |
| 173 | var cabinetPath = Path.GetFullPath(cabinetWorkItem.CabinetFile); | 173 | var compressFiles = new List<CabinetCompressFile>(); |
| 174 | |||
| 175 | foreach (var facade in cabinetWorkItem.FileFacades.OrderBy(f => f.Sequence)) | ||
| 176 | { | ||
| 177 | var modularizedId = facade.Id + cabinetWorkItem.ModularizationSuffix; | ||
| 174 | 178 | ||
| 175 | var files = cabinetWorkItem.FileFacades | 179 | var compressFile = cabinetWorkItem.HashesByFileId.TryGetValue(facade.Id, out var hash) ? |
| 176 | .OrderBy(f => f.Sequence) | 180 | new CabinetCompressFile(facade.SourcePath, modularizedId, hash.HashPart1, hash.HashPart2, hash.HashPart3, hash.HashPart4) : |
| 177 | .Select(facade => facade.Hash == null ? | 181 | new CabinetCompressFile(facade.SourcePath, modularizedId); |
| 178 | new CabinetCompressFile(facade.SourcePath, facade.Id + cabinetWorkItem.ModularizationSuffix) : | ||
| 179 | new CabinetCompressFile(facade.SourcePath, facade.Id + cabinetWorkItem.ModularizationSuffix, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4)) | ||
| 180 | .ToList(); | ||
| 181 | 182 | ||
| 183 | compressFiles.Add(compressFile); | ||
| 184 | } | ||
| 185 | |||
| 186 | // create the cabinet file | ||
| 187 | var cabinetPath = Path.GetFullPath(cabinetWorkItem.CabinetFile); | ||
| 182 | var cab = new Cabinet(cabinetPath); | 188 | var cab = new Cabinet(cabinetPath); |
| 183 | var created = cab.Compress(files, cabinetWorkItem.CompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold); | 189 | var created = cab.Compress(compressFiles, cabinetWorkItem.CompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold); |
| 184 | 190 | ||
| 185 | // Best effort check to see if the cabinet is too large for the Windows Installer. | 191 | // Best effort check to see if the cabinet is too large for the Windows Installer. |
| 186 | try | 192 | try |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs index 12332c80..d9bc7a7e 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs | |||
| @@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 4 | { | 4 | { |
| 5 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
| 6 | using WixToolset.Data; | 6 | using WixToolset.Data; |
| 7 | using WixToolset.Data.Symbols; | ||
| 7 | using WixToolset.Extensibility.Data; | 8 | using WixToolset.Extensibility.Data; |
| 8 | 9 | ||
| 9 | /// <summary> | 10 | /// <summary> |
| @@ -17,11 +18,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 17 | /// <param name="sourceLineNumber">Source line number that requires the cabinet creation.</param> | 18 | /// <param name="sourceLineNumber">Source line number that requires the cabinet creation.</param> |
| 18 | /// <param name="diskId"></param> | 19 | /// <param name="diskId"></param> |
| 19 | /// <param name="fileFacades">The collection of files in this cabinet.</param> | 20 | /// <param name="fileFacades">The collection of files in this cabinet.</param> |
| 21 | /// <param name="hashesByFileId">The hashes for unversioned files.</param> | ||
| 20 | /// <param name="cabinetFile">The cabinet file.</param> | 22 | /// <param name="cabinetFile">The cabinet file.</param> |
| 21 | /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> | 23 | /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> |
| 22 | /// <param name="compressionLevel">The compression level of the cabinet.</param> | 24 | /// <param name="compressionLevel">The compression level of the cabinet.</param> |
| 23 | /// <param name="modularizationSuffix">Modularization suffix used when building a Merge Module.</param> | 25 | /// <param name="modularizationSuffix">Modularization suffix used when building a Merge Module.</param> |
| 24 | public CabinetWorkItem(SourceLineNumber sourceLineNumber, int diskId, string cabinetFile, IEnumerable<IFileFacade> fileFacades, int maxThreshold, CompressionLevel compressionLevel, string modularizationSuffix) | 26 | public CabinetWorkItem(SourceLineNumber sourceLineNumber, int diskId, string cabinetFile, IEnumerable<IFileFacade> fileFacades, Dictionary<string, MsiFileHashSymbol> hashesByFileId, int maxThreshold, CompressionLevel compressionLevel, string modularizationSuffix) |
| 25 | { | 27 | { |
| 26 | this.SourceLineNumber = sourceLineNumber; | 28 | this.SourceLineNumber = sourceLineNumber; |
| 27 | this.DiskId = diskId; | 29 | this.DiskId = diskId; |
| @@ -29,6 +31,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 29 | this.CompressionLevel = compressionLevel; | 31 | this.CompressionLevel = compressionLevel; |
| 30 | this.ModularizationSuffix = modularizationSuffix; | 32 | this.ModularizationSuffix = modularizationSuffix; |
| 31 | this.FileFacades = fileFacades; | 33 | this.FileFacades = fileFacades; |
| 34 | this.HashesByFileId = hashesByFileId; | ||
| 32 | this.MaxThreshold = maxThreshold; | 35 | this.MaxThreshold = maxThreshold; |
| 33 | } | 36 | } |
| 34 | 37 | ||
| @@ -66,6 +69,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 66 | public IEnumerable<IFileFacade> FileFacades { get; } | 69 | public IEnumerable<IFileFacade> FileFacades { get; } |
| 67 | 70 | ||
| 68 | /// <summary> | 71 | /// <summary> |
| 72 | /// The hashes for unversioned files. | ||
| 73 | /// </summary> | ||
| 74 | public Dictionary<string, MsiFileHashSymbol> HashesByFileId { get; } | ||
| 75 | |||
| 76 | /// <summary> | ||
| 69 | /// Gets the max threshold. | 77 | /// Gets the max threshold. |
| 70 | /// </summary> | 78 | /// </summary> |
| 71 | /// <value>The maximum threshold for a folder in a cabinet.</value> | 79 | /// <value>The maximum threshold for a folder in a cabinet.</value> |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index 6ed107d5..1ed2ba79 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | |||
| @@ -95,6 +95,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 95 | 95 | ||
| 96 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, maximumCabinetSizeForLargeFileSplitting, maximumUncompressedMediaSize); | 96 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, maximumCabinetSizeForLargeFileSplitting, maximumUncompressedMediaSize); |
| 97 | 97 | ||
| 98 | var hashesByFileId = this.Section.Symbols.OfType<MsiFileHashSymbol>().ToDictionary(s => s.Id.Id); | ||
| 99 | |||
| 98 | foreach (var entry in this.FileFacadesByCabinet) | 100 | foreach (var entry in this.FileFacadesByCabinet) |
| 99 | { | 101 | { |
| 100 | var mediaSymbol = entry.Key; | 102 | var mediaSymbol = entry.Key; |
| @@ -102,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 102 | var compressionLevel = mediaSymbol.CompressionLevel ?? this.DefaultCompressionLevel ?? CompressionLevel.Medium; | 104 | var compressionLevel = mediaSymbol.CompressionLevel ?? this.DefaultCompressionLevel ?? CompressionLevel.Medium; |
| 103 | var cabinetDir = this.ResolveMedia(mediaSymbol, mediaSymbol.Layout, this.LayoutDirectory); | 105 | var cabinetDir = this.ResolveMedia(mediaSymbol, mediaSymbol.Layout, this.LayoutDirectory); |
| 104 | 106 | ||
| 105 | var cabinetWorkItem = this.CreateCabinetWorkItem(this.Data, cabinetDir, mediaSymbol, compressionLevel, files); | 107 | var cabinetWorkItem = this.CreateCabinetWorkItem(this.Data, cabinetDir, mediaSymbol, compressionLevel, files, hashesByFileId); |
| 106 | if (null != cabinetWorkItem) | 108 | if (null != cabinetWorkItem) |
| 107 | { | 109 | { |
| 108 | cabinetBuilder.Enqueue(cabinetWorkItem); | 110 | cabinetBuilder.Enqueue(cabinetWorkItem); |
| @@ -140,16 +142,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 140 | return cabbingThreadCount; | 142 | return cabbingThreadCount; |
| 141 | } | 143 | } |
| 142 | 144 | ||
| 143 | /// <summary> | 145 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades, Dictionary<string, MsiFileHashSymbol> hashesByFileId) |
| 144 | /// Creates a work item to create a cabinet. | ||
| 145 | /// </summary> | ||
| 146 | /// <param name="data">Windows Installer data for the current database.</param> | ||
| 147 | /// <param name="cabinetDir">Directory to create cabinet in.</param> | ||
| 148 | /// <param name="mediaSymbol">Media symbol containing information about the cabinet.</param> | ||
| 149 | /// <param name="compressionLevel">Desired compression level.</param> | ||
| 150 | /// <param name="fileFacades">Collection of files in this cabinet.</param> | ||
| 151 | /// <returns>created CabinetWorkItem object</returns> | ||
| 152 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades) | ||
| 153 | { | 146 | { |
| 154 | CabinetWorkItem cabinetWorkItem = null; | 147 | CabinetWorkItem cabinetWorkItem = null; |
| 155 | 148 | ||
| @@ -171,7 +164,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 171 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) | 164 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) |
| 172 | { | 165 | { |
| 173 | // Default to the threshold for best smartcabbing (makes smallest cabinet). | 166 | // Default to the threshold for best smartcabbing (makes smallest cabinet). |
| 174 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, mediaSymbol.DiskId, resolvedCabinet.Path, fileFacades, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); | 167 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, mediaSymbol.DiskId, resolvedCabinet.Path, fileFacades, hashesByFileId, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); |
| 175 | } | 168 | } |
| 176 | else // reuse the cabinet from the cabinet cache. | 169 | else // reuse the cabinet from the cabinet cache. |
| 177 | { | 170 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs index d0e25571..1d480250 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs | |||
| @@ -30,8 +30,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 30 | 30 | ||
| 31 | private IBackendHelper BackendHelper { get; } | 31 | private IBackendHelper BackendHelper { get; } |
| 32 | 32 | ||
| 33 | public void Execute() | 33 | public IReadOnlyCollection<SubStorage> SubStorages { get; private set; } |
| 34 | |||
| 35 | public IReadOnlyCollection<SubStorage> Execute() | ||
| 34 | { | 36 | { |
| 37 | var subStorages = new List<SubStorage>(); | ||
| 38 | |||
| 35 | // Create and add substorages for instance transforms. | 39 | // Create and add substorages for instance transforms. |
| 36 | var wixInstanceTransformsSymbols = this.Section.Symbols.OfType<WixInstanceTransformsSymbol>(); | 40 | var wixInstanceTransformsSymbols = this.Section.Symbols.OfType<WixInstanceTransformsSymbol>(); |
| 37 | 41 | ||
| @@ -252,9 +256,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 252 | summaryRow[1] = "4"; | 256 | summaryRow[1] = "4"; |
| 253 | } | 257 | } |
| 254 | 258 | ||
| 255 | this.Output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); | 259 | subStorages.Add(new SubStorage(instanceId, instanceTransform)); |
| 256 | } | 260 | } |
| 257 | } | 261 | } |
| 262 | |||
| 263 | return this.SubStorages = subStorages; | ||
| 258 | } | 264 | } |
| 259 | } | 265 | } |
| 260 | } | 266 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs index 5c993f63..6d5bff69 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs | |||
| @@ -6,31 +6,44 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 7 | using System.IO; |
| 8 | using System.Linq; | 8 | using System.Linq; |
| 9 | using WixToolset.Core.Native.Msi; | ||
| 10 | using WixToolset.Core.WindowsInstaller.Unbind; | 9 | using WixToolset.Core.WindowsInstaller.Unbind; |
| 11 | using WixToolset.Data; | 10 | using WixToolset.Data; |
| 12 | using WixToolset.Data.Symbols; | 11 | using WixToolset.Data.Symbols; |
| 13 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
| 13 | using WixToolset.Extensibility; | ||
| 14 | using WixToolset.Extensibility.Data; | ||
| 14 | using WixToolset.Extensibility.Services; | 15 | using WixToolset.Extensibility.Services; |
| 15 | 16 | ||
| 16 | internal class CreatePatchTransformsCommand | 17 | internal class CreatePatchTransformsCommand |
| 17 | { | 18 | { |
| 18 | public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, Intermediate intermediate, string intermediateFolder) | 19 | public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection<IResolverExtension> resolverExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection<IBindPath> bindPaths) |
| 19 | { | 20 | { |
| 20 | this.Messaging = messaging; | 21 | this.Messaging = messaging; |
| 21 | this.BackendHelper = backendHelper; | 22 | this.BackendHelper = backendHelper; |
| 23 | this.PathResolver = pathResolver; | ||
| 24 | this.FileResolver = fileResolver; | ||
| 25 | this.ResolverExtensions = resolverExtensions; | ||
| 22 | this.Intermediate = intermediate; | 26 | this.Intermediate = intermediate; |
| 23 | this.IntermediateFolder = intermediateFolder; | 27 | this.IntermediateFolder = intermediateFolder; |
| 28 | this.BindPaths = bindPaths; | ||
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | private IMessaging Messaging { get; } | 31 | private IMessaging Messaging { get; } |
| 27 | 32 | ||
| 28 | private IBackendHelper BackendHelper { get; } | 33 | private IBackendHelper BackendHelper { get; } |
| 29 | 34 | ||
| 35 | private IPathResolver PathResolver { get; } | ||
| 36 | |||
| 37 | private IFileResolver FileResolver { get; } | ||
| 38 | |||
| 39 | private IReadOnlyCollection<IResolverExtension> ResolverExtensions { get; } | ||
| 40 | |||
| 30 | private Intermediate Intermediate { get; } | 41 | private Intermediate Intermediate { get; } |
| 31 | 42 | ||
| 32 | private string IntermediateFolder { get; } | 43 | private string IntermediateFolder { get; } |
| 33 | 44 | ||
| 45 | private IReadOnlyCollection<IBindPath> BindPaths { get; } | ||
| 46 | |||
| 34 | public IEnumerable<PatchTransform> PatchTransforms { get; private set; } | 47 | public IEnumerable<PatchTransform> PatchTransforms { get; private set; } |
| 35 | 48 | ||
| 36 | public IEnumerable<PatchTransform> Execute() | 49 | public IEnumerable<PatchTransform> Execute() |
| @@ -41,23 +54,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 41 | 54 | ||
| 42 | foreach (var symbol in symbols) | 55 | foreach (var symbol in symbols) |
| 43 | { | 56 | { |
| 44 | WindowsInstallerData transform; | 57 | var targetData = this.GetWindowsInstallerData(symbol.BaselineFile.Path, BindStage.Target); |
| 45 | 58 | var updatedData = this.GetWindowsInstallerData(symbol.UpdateFile.Path, BindStage.Updated); | |
| 46 | if (symbol.TransformFile is null) | ||
| 47 | { | ||
| 48 | var baselineData = this.GetData(symbol.BaselineFile.Path); | ||
| 49 | var updateData = this.GetData(symbol.UpdateFile.Path); | ||
| 50 | 59 | ||
| 51 | var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, preserveUnchangedRows: true, showPedanticMessages: false); | 60 | var command = new GenerateTransformCommand(this.Messaging, targetData, updatedData, preserveUnchangedRows: true, showPedanticMessages: false); |
| 52 | transform = command.Execute(); | 61 | var transform = command.Execute(); |
| 53 | } | ||
| 54 | else | ||
| 55 | { | ||
| 56 | var exportBasePath = Path.Combine(this.IntermediateFolder, "_trans"); // TODO: come up with a better path. | ||
| 57 | |||
| 58 | var command = new UnbindTransformCommand(this.Messaging, this.BackendHelper, symbol.TransformFile.Path, exportBasePath, this.IntermediateFolder); | ||
| 59 | transform = command.Execute(); | ||
| 60 | } | ||
| 61 | 62 | ||
| 62 | patchTransforms.Add(new PatchTransform(symbol.Id.Id, transform)); | 63 | patchTransforms.Add(new PatchTransform(symbol.Id.Id, transform)); |
| 63 | } | 64 | } |
| @@ -67,26 +68,61 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 67 | return this.PatchTransforms; | 68 | return this.PatchTransforms; |
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | private WindowsInstallerData GetData(string path) | 71 | private WindowsInstallerData GetWindowsInstallerData(string path, BindStage stage) |
| 71 | { | 72 | { |
| 72 | var ext = Path.GetExtension(path); | 73 | if (DataLoader.TryLoadWindowsInstallerData(path, true, out var data)) |
| 73 | |||
| 74 | if (".msi".Equals(ext, StringComparison.OrdinalIgnoreCase)) | ||
| 75 | { | 74 | { |
| 76 | using (var database = new Database(path, OpenDatabase.ReadOnly)) | 75 | // Re-resolve file paths only when loading from .wixpdb. |
| 77 | { | 76 | this.ReResolveWindowsInstallerData(data, stage); |
| 78 | var exportBasePath = Path.Combine(this.IntermediateFolder, "_msi"); // TODO: come up with a better path. | 77 | } |
| 78 | else | ||
| 79 | { | ||
| 80 | var stageFolder = $"_{stage.ToString().ToLowerInvariant()}_msi"; | ||
| 81 | var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder); | ||
| 82 | var extractFilesFolder = Path.Combine(exportBasePath, "File"); | ||
| 83 | |||
| 84 | var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, OutputType.Product, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); | ||
| 85 | data = command.Execute(); | ||
| 86 | } | ||
| 79 | 87 | ||
| 80 | var isAdminImage = false; // TODO: need a better way to set this | 88 | return data; |
| 89 | } | ||
| 81 | 90 | ||
| 82 | var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, path, OutputType.Product, exportBasePath, this.IntermediateFolder, isAdminImage, suppressDemodularization: true, skipSummaryInfo: true); | 91 | private void ReResolveWindowsInstallerData(WindowsInstallerData data, BindStage stage) |
| 83 | return command.Execute(); | 92 | { |
| 84 | } | 93 | var bindPaths = this.BindPaths.Where(b => b.Stage == stage).ToList(); |
| 94 | |||
| 95 | if (bindPaths.Count == 0) | ||
| 96 | { | ||
| 97 | return; | ||
| 85 | } | 98 | } |
| 86 | else // assume .wixpdb (or .wixout) | 99 | |
| 100 | foreach (var table in data.Tables) | ||
| 87 | { | 101 | { |
| 88 | var data = WindowsInstallerData.Load(path, true); | 102 | foreach (var row in table.Rows) |
| 89 | return data; | 103 | { |
| 104 | foreach (var field in row.Fields.Where(f => f.Column.Type == ColumnType.Object)) | ||
| 105 | { | ||
| 106 | if (field.PreviousData != null) | ||
| 107 | { | ||
| 108 | try | ||
| 109 | { | ||
| 110 | var originalPath = field.AsString(); | ||
| 111 | |||
| 112 | var resolvedPath = this.FileResolver.ResolveFile(field.PreviousData, this.ResolverExtensions, bindPaths, stage, row.SourceLineNumbers, null); | ||
| 113 | |||
| 114 | if (!String.Equals(originalPath, resolvedPath, StringComparison.OrdinalIgnoreCase)) | ||
| 115 | { | ||
| 116 | field.Data = resolvedPath; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | catch (WixException e) | ||
| 120 | { | ||
| 121 | this.Messaging.Write(e.Error); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 90 | } | 126 | } |
| 91 | } | 127 | } |
| 92 | } | 128 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs index 60317cd9..4d99ff40 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs | |||
| @@ -662,10 +662,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 662 | row.FileSize = symbol.FileSize; | 662 | row.FileSize = symbol.FileSize; |
| 663 | row.Version = symbol.Version; | 663 | row.Version = symbol.Version; |
| 664 | row.Language = symbol.Language; | 664 | row.Language = symbol.Language; |
| 665 | row.DiskId = symbol.DiskId ?? 1; // TODO: is 1 the correct thing to default here | ||
| 666 | row.Sequence = symbol.Sequence; | 665 | row.Sequence = symbol.Sequence; |
| 666 | row.DiskId = symbol.DiskId ?? throw new InvalidDataException("FileSymbol.DiskId should have been initialized before creating WindowsInstallerData from IntermediateRepresentation."); | ||
| 667 | row.Source = symbol.Source.Path; | 667 | row.Source = symbol.Source.Path; |
| 668 | 668 | ||
| 669 | var previousSourceField = symbol.Fields[(int)FileSymbolFields.Source]?.PreviousValue; | ||
| 670 | row.PreviousSource = previousSourceField?.AsPath().Path; | ||
| 671 | |||
| 669 | var attributes = (symbol.Attributes & FileSymbolAttributes.Checksum) == FileSymbolAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; | 672 | var attributes = (symbol.Attributes & FileSymbolAttributes.Checksum) == FileSymbolAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; |
| 670 | attributes |= (symbol.Attributes & FileSymbolAttributes.Compressed) == FileSymbolAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; | 673 | attributes |= (symbol.Attributes & FileSymbolAttributes.Compressed) == FileSymbolAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; |
| 671 | attributes |= (symbol.Attributes & FileSymbolAttributes.Uncompressed) == FileSymbolAttributes.Uncompressed ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0; | 674 | attributes |= (symbol.Attributes & FileSymbolAttributes.Uncompressed) == FileSymbolAttributes.Uncompressed ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0; |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/DataLoader.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/DataLoader.cs new file mode 100644 index 00000000..6214bbdb --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/DataLoader.cs | |||
| @@ -0,0 +1,49 @@ | |||
| 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.IO; | ||
| 7 | using WixToolset.Data.WindowsInstaller; | ||
| 8 | |||
| 9 | internal class DataLoader | ||
| 10 | { | ||
| 11 | public static bool TryLoadWindowsInstallerData(string path, out WindowsInstallerData data) | ||
| 12 | { | ||
| 13 | return TryLoadWindowsInstallerData(path, false, out data); | ||
| 14 | } | ||
| 15 | |||
| 16 | public static bool TryLoadWindowsInstallerData(string path, bool suppressVersionCheck, out WindowsInstallerData data) | ||
| 17 | { | ||
| 18 | data = null; | ||
| 19 | |||
| 20 | var extension = Path.GetExtension(path); | ||
| 21 | |||
| 22 | // If the path is _not_ obviously a Windows Installer database, let's try opening it as | ||
| 23 | // our own data file format. | ||
| 24 | if (!extension.Equals(".msi", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".msm", StringComparison.OrdinalIgnoreCase)) | ||
| 25 | { | ||
| 26 | (data, _) = LoadWindowsInstallerDataSafely(path, suppressVersionCheck); | ||
| 27 | } | ||
| 28 | |||
| 29 | return data != null; | ||
| 30 | } | ||
| 31 | |||
| 32 | public static (WindowsInstallerData, Exception) LoadWindowsInstallerDataSafely(string path, bool suppressVersionCheck = false) | ||
| 33 | { | ||
| 34 | WindowsInstallerData data = null; | ||
| 35 | Exception exception = null; | ||
| 36 | |||
| 37 | try | ||
| 38 | { | ||
| 39 | data = WindowsInstallerData.Load(path, suppressVersionCheck); | ||
| 40 | } | ||
| 41 | catch (Exception e) | ||
| 42 | { | ||
| 43 | exception = e; | ||
| 44 | } | ||
| 45 | |||
| 46 | return (data, exception); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs index 06168727..94ed0afc 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs | |||
| @@ -22,12 +22,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 22 | /// </summary> | 22 | /// </summary> |
| 23 | internal class ExtractMergeModuleFilesCommand | 23 | internal class ExtractMergeModuleFilesCommand |
| 24 | { | 24 | { |
| 25 | public ExtractMergeModuleFilesCommand(IMessaging messaging, IWindowsInstallerBackendHelper backendHelper, IEnumerable<WixMergeSymbol> wixMergeSymbols, IEnumerable<IFileFacade> fileFacades, int installerVersion, string intermediateFolder, bool suppressLayout) | 25 | public ExtractMergeModuleFilesCommand(IMessaging messaging, IWindowsInstallerBackendHelper backendHelper, IEnumerable<WixMergeSymbol> wixMergeSymbols, IEnumerable<IFileFacade> fileFacadesFromIntermediate, int installerVersion, string intermediateFolder, bool suppressLayout) |
| 26 | { | 26 | { |
| 27 | this.Messaging = messaging; | 27 | this.Messaging = messaging; |
| 28 | this.BackendHelper = backendHelper; | 28 | this.BackendHelper = backendHelper; |
| 29 | this.WixMergeSymbols = wixMergeSymbols; | 29 | this.WixMergeSymbols = wixMergeSymbols; |
| 30 | this.FileFacades = fileFacades; | 30 | this.FileFacadesFromIntermediate = fileFacadesFromIntermediate; |
| 31 | this.OutputInstallerVersion = installerVersion; | 31 | this.OutputInstallerVersion = installerVersion; |
| 32 | this.IntermediateFolder = intermediateFolder; | 32 | this.IntermediateFolder = intermediateFolder; |
| 33 | this.SuppressLayout = suppressLayout; | 33 | this.SuppressLayout = suppressLayout; |
| @@ -39,7 +39,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 39 | 39 | ||
| 40 | private IEnumerable<WixMergeSymbol> WixMergeSymbols { get; } | 40 | private IEnumerable<WixMergeSymbol> WixMergeSymbols { get; } |
| 41 | 41 | ||
| 42 | private IEnumerable<IFileFacade> FileFacades { get; } | 42 | private IEnumerable<IFileFacade> FileFacadesFromIntermediate { get; } |
| 43 | 43 | ||
| 44 | private int OutputInstallerVersion { get; } | 44 | private int OutputInstallerVersion { get; } |
| 45 | 45 | ||
| @@ -65,7 +65,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 65 | // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let | 65 | // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let |
| 66 | // this case be slightly more expensive because the cost of maintaining an indexed file row collection | 66 | // this case be slightly more expensive because the cost of maintaining an indexed file row collection |
| 67 | // is a lot more costly for the common cases. | 67 | // is a lot more costly for the common cases. |
| 68 | var indexedFileFacades = this.FileFacades.ToDictionary(f => f.Id, StringComparer.Ordinal); | 68 | var indexedFileFacades = this.FileFacadesFromIntermediate.ToDictionary(f => f.Id, StringComparer.Ordinal); |
| 69 | 69 | ||
| 70 | foreach (var wixMergeRow in this.WixMergeSymbols) | 70 | foreach (var wixMergeRow in this.WixMergeSymbols) |
| 71 | { | 71 | { |
| @@ -115,7 +115,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 115 | fileSymbol.DiskId = wixMergeRow.DiskId; | 115 | fileSymbol.DiskId = wixMergeRow.DiskId; |
| 116 | fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) }; | 116 | fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) }; |
| 117 | 117 | ||
| 118 | var mergeModuleFileFacade = this.BackendHelper.CreateFileFacadeFromMergeModule(fileSymbol); | 118 | var mergeModuleFileFacade = this.BackendHelper.CreateFileFacade(fileSymbol); |
| 119 | 119 | ||
| 120 | // If case-sensitive collision with another merge module or a user-authored file identifier. | 120 | // If case-sensitive collision with another merge module or a user-authored file identifier. |
| 121 | if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade)) | 121 | if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade)) |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs index 575065bb..92a0e11f 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs | |||
| @@ -16,7 +16,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 16 | /// </summary> | 16 | /// </summary> |
| 17 | internal class GenerateTransformCommand | 17 | internal class GenerateTransformCommand |
| 18 | { | 18 | { |
| 19 | private const char sectionDelimiter = '/'; | 19 | private const char SectionDelimiter = '/'; |
| 20 | private readonly IMessaging messaging; | 20 | private readonly IMessaging messaging; |
| 21 | private SummaryInformationStreams transformSummaryInfo; | 21 | private SummaryInformationStreams transformSummaryInfo; |
| 22 | 22 | ||
| @@ -38,22 +38,10 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 38 | 38 | ||
| 39 | private TransformFlags ValidationFlags { get; } | 39 | private TransformFlags ValidationFlags { get; } |
| 40 | 40 | ||
| 41 | /// <summary> | ||
| 42 | /// Gets or sets the option to show pedantic messages. | ||
| 43 | /// </summary> | ||
| 44 | /// <value>The option to show pedantic messages.</value> | ||
| 45 | private bool ShowPedanticMessages { get; } | 41 | private bool ShowPedanticMessages { get; } |
| 46 | 42 | ||
| 47 | /// <summary> | ||
| 48 | /// Gets or sets the option to suppress keeping special rows. | ||
| 49 | /// </summary> | ||
| 50 | /// <value>The option to suppress keeping special rows.</value> | ||
| 51 | private bool SuppressKeepingSpecialRows { get; } | 43 | private bool SuppressKeepingSpecialRows { get; } |
| 52 | 44 | ||
| 53 | /// <summary> | ||
| 54 | /// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output. | ||
| 55 | /// </summary> | ||
| 56 | /// <value>The option to keep all rows including unchanged rows.</value> | ||
| 57 | private bool PreserveUnchangedRows { get; } | 45 | private bool PreserveUnchangedRows { get; } |
| 58 | 46 | ||
| 59 | public WindowsInstallerData Transform { get; private set; } | 47 | public WindowsInstallerData Transform { get; private set; } |
| @@ -124,7 +112,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 124 | foreach (var updatedRow in updatedTable.Rows) | 112 | foreach (var updatedRow in updatedTable.Rows) |
| 125 | { | 113 | { |
| 126 | updatedRow.Operation = RowOperation.Add; | 114 | updatedRow.Operation = RowOperation.Add; |
| 127 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 115 | updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; |
| 128 | addedTable.Rows.Add(updatedRow); | 116 | addedTable.Rows.Add(updatedRow); |
| 129 | } | 117 | } |
| 130 | } | 118 | } |
| @@ -152,27 +140,8 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 152 | 140 | ||
| 153 | if (null != primaryKey) | 141 | if (null != primaryKey) |
| 154 | { | 142 | { |
| 155 | if (index.TryGetValue(primaryKey, out var collisionRow)) | 143 | if (index.ContainsKey(primaryKey)) |
| 156 | { | 144 | { |
| 157 | #if TODO_PATCH // This case doesn't seem like it can happen any longer. | ||
| 158 | // Overriding WixActionRows have a primary key defined and take precedence in the index. | ||
| 159 | if (row is WixActionRow actionRow) | ||
| 160 | { | ||
| 161 | // If the current row is not overridable, see if the indexed row is. | ||
| 162 | if (!actionRow.Overridable) | ||
| 163 | { | ||
| 164 | if (collisionRow is WixActionRow indexedRow && indexedRow.Overridable) | ||
| 165 | { | ||
| 166 | // The indexed key is overridable and should be replaced. | ||
| 167 | index[primaryKey] = actionRow; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | // If we got this far, the row does not need to be indexed. | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | #endif | ||
| 175 | |||
| 176 | if (this.ShowPedanticMessages) | 145 | if (this.ShowPedanticMessages) |
| 177 | { | 146 | { |
| 178 | this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name)); | 147 | this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name)); |
| @@ -208,7 +177,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 208 | else if (null == updatedRow) | 177 | else if (null == updatedRow) |
| 209 | { | 178 | { |
| 210 | targetRow.Operation = RowOperation.Delete; | 179 | targetRow.Operation = RowOperation.Delete; |
| 211 | targetRow.SectionId += sectionDelimiter; | 180 | targetRow.SectionId += SectionDelimiter; |
| 212 | 181 | ||
| 213 | comparedRow = targetRow; | 182 | comparedRow = targetRow; |
| 214 | keepRow = true; | 183 | keepRow = true; |
| @@ -219,10 +188,10 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 219 | updatedRow.Operation = RowOperation.None; | 188 | updatedRow.Operation = RowOperation.None; |
| 220 | if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) | 189 | if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) |
| 221 | { | 190 | { |
| 222 | // ignore rows that shouldn't be in a transform | 191 | // Include only summary information rows that are allowed in a transform. |
| 223 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) | 192 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) |
| 224 | { | 193 | { |
| 225 | updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | 194 | updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; |
| 226 | comparedRow = updatedRow; | 195 | comparedRow = updatedRow; |
| 227 | keepRow = true; | 196 | keepRow = true; |
| 228 | } | 197 | } |
| @@ -273,14 +242,14 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 273 | updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex; | 242 | updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex; |
| 274 | updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; | 243 | updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; |
| 275 | 244 | ||
| 276 | // always keep a copy of the previous data even if they are identical | 245 | // Always keep a copy of the previous data even if they are identical. |
| 277 | // This makes diff.wixmst clean and easier to control patch logic | 246 | // This makes diff data clean and easier to control in patch logic. |
| 278 | updatedObjectField.PreviousData = (string)targetObjectField.Data; | 247 | updatedObjectField.PreviousData = (string)targetObjectField.Data; |
| 279 | 248 | ||
| 280 | // always remember the unresolved data for target build | 249 | // Always remember the unresolved data for target build. |
| 281 | updatedObjectField.UnresolvedPreviousData = targetObjectField.UnresolvedData; | 250 | updatedObjectField.UnresolvedPreviousData = targetObjectField.UnresolvedData; |
| 282 | 251 | ||
| 283 | // keep rows containing object fields so the files can be compared in the binder | 252 | // Keep rows containing object fields so the files can be compared later. |
| 284 | keepRow = !this.SuppressKeepingSpecialRows; | 253 | keepRow = !this.SuppressKeepingSpecialRows; |
| 285 | } | 254 | } |
| 286 | else | 255 | else |
| @@ -305,7 +274,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 305 | if (keepRow) | 274 | if (keepRow) |
| 306 | { | 275 | { |
| 307 | comparedRow = updatedRow; | 276 | comparedRow = updatedRow; |
| 308 | comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | 277 | comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; |
| 309 | } | 278 | } |
| 310 | } | 279 | } |
| 311 | } | 280 | } |
| @@ -333,9 +302,6 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 333 | } | 302 | } |
| 334 | else // possibly modified table. | 303 | else // possibly modified table. |
| 335 | { | 304 | { |
| 336 | var updatedPrimaryKeys = new Dictionary<string, Row>(); | ||
| 337 | var targetPrimaryKeys = new Dictionary<string, Row>(); | ||
| 338 | |||
| 339 | // compare the table definitions | 305 | // compare the table definitions |
| 340 | if (0 != targetTable.Definition.CompareTo(updatedTable.Definition)) | 306 | if (0 != targetTable.Definition.CompareTo(updatedTable.Definition)) |
| 341 | { | 307 | { |
| @@ -344,6 +310,9 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 344 | } | 310 | } |
| 345 | else | 311 | else |
| 346 | { | 312 | { |
| 313 | var updatedPrimaryKeys = new Dictionary<string, Row>(); | ||
| 314 | var targetPrimaryKeys = new Dictionary<string, Row>(); | ||
| 315 | |||
| 347 | this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys); | 316 | this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys); |
| 348 | 317 | ||
| 349 | // diff the target and updated rows | 318 | // diff the target and updated rows |
| @@ -371,7 +340,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 371 | var updatedRow = updatedPrimaryKeyEntry.Value; | 340 | var updatedRow = updatedPrimaryKeyEntry.Value; |
| 372 | 341 | ||
| 373 | updatedRow.Operation = RowOperation.Add; | 342 | updatedRow.Operation = RowOperation.Add; |
| 374 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 343 | updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; |
| 375 | rows.Add(updatedRow); | 344 | rows.Add(updatedRow); |
| 376 | } | 345 | } |
| 377 | } | 346 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs index 949d5e18..fa072293 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs | |||
| @@ -2,9 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { | 4 | { |
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
| 7 | using System.Globalization; | ||
| 8 | using System.Linq; | 6 | using System.Linq; |
| 9 | using WixToolset.Data; | 7 | using WixToolset.Data; |
| 10 | using WixToolset.Data.Symbols; | 8 | using WixToolset.Data.Symbols; |
| @@ -29,20 +27,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 29 | { | 27 | { |
| 30 | var facades = new List<IFileFacade>(); | 28 | var facades = new List<IFileFacade>(); |
| 31 | 29 | ||
| 32 | var assemblyFile = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id); | ||
| 33 | #if TODO_PATCHING_DELTA | 30 | #if TODO_PATCHING_DELTA |
| 34 | //var deltaPatchFiles = this.Section.Symbols.OfType<WixDeltaPatchFileSymbol>().ToDictionary(t => t.Id.Id); | 31 | //var deltaPatchFiles = this.Section.Symbols.OfType<WixDeltaPatchFileSymbol>().ToDictionary(t => t.Id.Id); |
| 35 | #endif | 32 | #endif |
| 36 | 33 | ||
| 37 | foreach (var file in this.Section.Symbols.OfType<FileSymbol>()) | 34 | foreach (var file in this.Section.Symbols.OfType<FileSymbol>()) |
| 38 | { | 35 | { |
| 39 | assemblyFile.TryGetValue(file.Id.Id, out var assembly); | ||
| 40 | |||
| 41 | #if TODO_PATCHING_DELTA | 36 | #if TODO_PATCHING_DELTA |
| 42 | //deltaPatchFiles.TryGetValue(file.Id.Id, out var deltaPatchFile); | 37 | //deltaPatchFiles.TryGetValue(file.Id.Id, out var deltaPatchFile); |
| 43 | // TODO: should we be passing along delta information to the file facade? Probably, right? | 38 | // TODO: should we be passing along delta information to the file facade? Probably, right? |
| 44 | #endif | 39 | #endif |
| 45 | var fileFacade = this.BackendHelper.CreateFileFacade(file, assembly); | 40 | var fileFacade = this.BackendHelper.CreateFileFacade(file); |
| 46 | 41 | ||
| 47 | facades.Add(fileFacade); | 42 | facades.Add(fileFacade); |
| 48 | } | 43 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs index 441038ec..7c71e238 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs | |||
| @@ -37,8 +37,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 37 | 37 | ||
| 38 | var patchMediaFileRows = new Dictionary<int, RowDictionary<FileRow>>(); | 38 | var patchMediaFileRows = new Dictionary<int, RowDictionary<FileRow>>(); |
| 39 | 39 | ||
| 40 | //var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | ||
| 41 | |||
| 42 | // Index paired transforms by name without their "#" prefix. | 40 | // Index paired transforms by name without their "#" prefix. |
| 43 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data); | 41 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data); |
| 44 | 42 | ||
| @@ -57,7 +55,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 57 | var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name]; | 55 | var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name]; |
| 58 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | 56 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); |
| 59 | 57 | ||
| 60 | foreach (FileRow mainFileRow in mainFileTable.Rows.Where(f => f.Operation != RowOperation.Delete)) | 58 | foreach (var mainFileRow in mainFileTable.Rows.Where(f => f.Operation != RowOperation.Delete).Cast<FileRow>()) |
| 61 | { | 59 | { |
| 62 | var mainFileId = mainFileRow.File; | 60 | var mainFileId = mainFileRow.File; |
| 63 | 61 | ||
| @@ -89,8 +87,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 89 | } | 87 | } |
| 90 | else | 88 | else |
| 91 | { | 89 | { |
| 92 | // TODO: should this entire condition be placed in the binder file manager? | 90 | if ( |
| 93 | if (/*(0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&*/ | 91 | #if TODO_PATCHING_DELTA |
| 92 | (0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && | ||
| 93 | #endif | ||
| 94 | !this.FileSystemManager.CompareFiles(objectField.PreviousData, objectField.Data.ToString())) | 94 | !this.FileSystemManager.CompareFiles(objectField.PreviousData, objectField.Data.ToString())) |
| 95 | { | 95 | { |
| 96 | // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. | 96 | // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. |
| @@ -133,11 +133,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 133 | patchMediaFileRows.Add(diskId, mediaFileRows); | 133 | patchMediaFileRows.Add(diskId, mediaFileRows); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | var patchFileRow = mediaFileRows.Get(mainFileId); | 136 | if (!mediaFileRows.TryGetValue(mainFileId, out var patchFileRow)) |
| 137 | |||
| 138 | if (null == patchFileRow) | ||
| 139 | { | 137 | { |
| 140 | //patchFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 141 | patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers); | 138 | patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers); |
| 142 | mainFileRow.CopyTo(patchFileRow); | 139 | mainFileRow.CopyTo(patchFileRow); |
| 143 | 140 | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index 7e6a7de0..5f21f496 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs | |||
| @@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 10 | using System.Runtime.InteropServices; | 10 | using System.Runtime.InteropServices; |
| 11 | using System.Text; | 11 | using System.Text; |
| 12 | using System.Threading; | 12 | using System.Threading; |
| 13 | using WixToolset.Core.Native; | ||
| 14 | using WixToolset.Core.Native.Msi; | 13 | using WixToolset.Core.Native.Msi; |
| 15 | using WixToolset.Core.Native.Msm; | 14 | using WixToolset.Core.Native.Msm; |
| 16 | using WixToolset.Data; | 15 | using WixToolset.Data; |
| @@ -24,11 +23,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 24 | /// </summary> | 23 | /// </summary> |
| 25 | internal class MergeModulesCommand | 24 | internal class MergeModulesCommand |
| 26 | { | 25 | { |
| 27 | public MergeModulesCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileFacade> fileFacades, IntermediateSection section, IEnumerable<string> suppressedTableNames, string outputPath, string intermediateFolder) | 26 | public MergeModulesCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileFacade> fileFacadesFromModule, IntermediateSection section, IEnumerable<string> suppressedTableNames, string outputPath, string intermediateFolder) |
| 28 | { | 27 | { |
| 29 | this.Messaging = messaging; | 28 | this.Messaging = messaging; |
| 30 | this.BackendHelper = backendHelper; | 29 | this.BackendHelper = backendHelper; |
| 31 | this.FileFacades = fileFacades; | 30 | this.FileFacadesFromModule = fileFacadesFromModule; |
| 32 | this.Section = section; | 31 | this.Section = section; |
| 33 | this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>(); | 32 | this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>(); |
| 34 | this.OutputPath = outputPath; | 33 | this.OutputPath = outputPath; |
| @@ -39,7 +38,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 39 | 38 | ||
| 40 | private IBackendHelper BackendHelper { get; } | 39 | private IBackendHelper BackendHelper { get; } |
| 41 | 40 | ||
| 42 | private IEnumerable<IFileFacade> FileFacades { get; } | 41 | private IEnumerable<IFileFacade> FileFacadesFromModule { get; } |
| 43 | 42 | ||
| 44 | private IntermediateSection Section { get; } | 43 | private IntermediateSection Section { get; } |
| 45 | 44 | ||
| @@ -280,13 +279,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 280 | this.Messaging.Write(VerboseMessages.ResequencingMergeModuleFiles()); | 279 | this.Messaging.Write(VerboseMessages.ResequencingMergeModuleFiles()); |
| 281 | using (var view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) | 280 | using (var view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) |
| 282 | { | 281 | { |
| 283 | foreach (var file in this.FileFacades) | 282 | foreach (var file in this.FileFacadesFromModule) |
| 284 | { | 283 | { |
| 285 | if (!file.FromModule) | ||
| 286 | { | ||
| 287 | continue; | ||
| 288 | } | ||
| 289 | |||
| 290 | using (var record = new Record(1)) | 284 | using (var record = new Record(1)) |
| 291 | { | 285 | { |
| 292 | record.SetString(1, file.Id); | 286 | record.SetString(1, file.Id); |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs index 217609be..1f09a267 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs | |||
| @@ -47,28 +47,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 47 | if ("ProductCode" == propertySymbol.Id.Id && "*".Equals(propertySymbol.Value, StringComparison.Ordinal)) | 47 | if ("ProductCode" == propertySymbol.Id.Id && "*".Equals(propertySymbol.Value, StringComparison.Ordinal)) |
| 48 | { | 48 | { |
| 49 | propertySymbol.Value = this.BackendHelper.CreateGuid(); | 49 | propertySymbol.Value = this.BackendHelper.CreateGuid(); |
| 50 | |||
| 51 | #if TODO_PATCHING // Is this still necessary? | ||
| 52 | // Update the target ProductCode in any instance transforms. | ||
| 53 | foreach (SubStorage subStorage in this.Output.SubStorages) | ||
| 54 | { | ||
| 55 | Output subStorageOutput = subStorage.Data; | ||
| 56 | if (OutputType.Transform != subStorageOutput.Type) | ||
| 57 | { | ||
| 58 | continue; | ||
| 59 | } | ||
| 60 | |||
| 61 | Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"]; | ||
| 62 | foreach (Row row in instanceSummaryInformationTable.Rows) | ||
| 63 | { | ||
| 64 | if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0)) | ||
| 65 | { | ||
| 66 | row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value); | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | #endif | ||
| 72 | } | 50 | } |
| 73 | else if ("ProductLanguage" == propertySymbol.Id.Id) | 51 | else if ("ProductLanguage" == propertySymbol.Id.Id) |
| 74 | { | 52 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 8043ffa8..f2f8e126 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
| @@ -19,11 +19,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 19 | /// </summary> | 19 | /// </summary> |
| 20 | internal class UpdateFileFacadesCommand | 20 | internal class UpdateFileFacadesCommand |
| 21 | { | 21 | { |
| 22 | public UpdateFileFacadesCommand(IMessaging messaging, IntermediateSection section, IEnumerable<IFileFacade> fileFacades, IEnumerable<IFileFacade> updateFileFacades, IDictionary<string, string> variableCache, bool overwriteHash) | 22 | public UpdateFileFacadesCommand(IMessaging messaging, IntermediateSection section, IEnumerable<IFileFacade> allFileFacades, IEnumerable<IFileFacade> updateFileFacades, IDictionary<string, string> variableCache, bool overwriteHash) |
| 23 | { | 23 | { |
| 24 | this.Messaging = messaging; | 24 | this.Messaging = messaging; |
| 25 | this.Section = section; | 25 | this.Section = section; |
| 26 | this.FileFacades = fileFacades; | 26 | this.AllFileFacades = allFileFacades; |
| 27 | this.UpdateFileFacades = updateFileFacades; | 27 | this.UpdateFileFacades = updateFileFacades; |
| 28 | this.VariableCache = variableCache; | 28 | this.VariableCache = variableCache; |
| 29 | this.OverwriteHash = overwriteHash; | 29 | this.OverwriteHash = overwriteHash; |
| @@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 33 | 33 | ||
| 34 | private IntermediateSection Section { get; } | 34 | private IntermediateSection Section { get; } |
| 35 | 35 | ||
| 36 | private IEnumerable<IFileFacade> FileFacades { get; } | 36 | private IEnumerable<IFileFacade> AllFileFacades { get; } |
| 37 | 37 | ||
| 38 | private IEnumerable<IFileFacade> UpdateFileFacades { get; } | 38 | private IEnumerable<IFileFacade> UpdateFileFacades { get; } |
| 39 | 39 | ||
| @@ -43,15 +43,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 43 | 43 | ||
| 44 | public void Execute() | 44 | public void Execute() |
| 45 | { | 45 | { |
| 46 | var assemblySymbols = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id); | ||
| 46 | var assemblyNameSymbols = this.Section.Symbols.OfType<MsiAssemblyNameSymbol>().ToDictionary(t => t.Id.Id); | 47 | var assemblyNameSymbols = this.Section.Symbols.OfType<MsiAssemblyNameSymbol>().ToDictionary(t => t.Id.Id); |
| 47 | 48 | ||
| 48 | foreach (var file in this.UpdateFileFacades.Where(f => f.SourcePath != null)) | 49 | foreach (var file in this.UpdateFileFacades.Where(f => f.SourcePath != null)) |
| 49 | { | 50 | { |
| 50 | this.UpdateFileFacade(file, assemblyNameSymbols); | 51 | this.UpdateFileFacade(file, assemblySymbols, assemblyNameSymbols); |
| 51 | } | 52 | } |
| 52 | } | 53 | } |
| 53 | 54 | ||
| 54 | private void UpdateFileFacade(IFileFacade facade, Dictionary<string, MsiAssemblyNameSymbol> assemblyNameSymbols) | 55 | private void UpdateFileFacade(IFileFacade facade, Dictionary<string, AssemblySymbol> assemblySymbols, Dictionary<string, MsiAssemblyNameSymbol> assemblyNameSymbols) |
| 55 | { | 56 | { |
| 56 | FileInfo fileInfo = null; | 57 | FileInfo fileInfo = null; |
| 57 | try | 58 | try |
| @@ -124,7 +125,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 124 | // | 125 | // |
| 125 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version | 126 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version |
| 126 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. | 127 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. |
| 127 | if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) | 128 | if (!this.AllFileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) |
| 128 | { | 129 | { |
| 129 | this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.SourceLineNumber, facade.Version, facade.Id)); | 130 | this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.SourceLineNumber, facade.Version, facade.Id)); |
| 130 | } | 131 | } |
| @@ -153,16 +154,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 153 | } | 154 | } |
| 154 | } | 155 | } |
| 155 | 156 | ||
| 156 | if (null == facade.Hash) | 157 | // Remember the hash symbol for use later. |
| 158 | facade.MsiFileHashSymbol = new MsiFileHashSymbol(facade.SourceLineNumber, facade.Identifier) | ||
| 157 | { | 159 | { |
| 158 | facade.Hash = this.Section.AddSymbol(new MsiFileHashSymbol(facade.SourceLineNumber, facade.Identifier)); | 160 | Options = 0, |
| 159 | } | 161 | HashPart1 = hash[0], |
| 160 | 162 | HashPart2 = hash[1], | |
| 161 | facade.Hash.Options = 0; | 163 | HashPart3 = hash[2], |
| 162 | facade.Hash.HashPart1 = hash[0]; | 164 | HashPart4 = hash[3], |
| 163 | facade.Hash.HashPart2 = hash[1]; | 165 | }; |
| 164 | facade.Hash.HashPart3 = hash[2]; | ||
| 165 | facade.Hash.HashPart4 = hash[3]; | ||
| 166 | } | 166 | } |
| 167 | } | 167 | } |
| 168 | else // update the file row with the version and language information. | 168 | else // update the file row with the version and language information. |
| @@ -173,7 +173,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 173 | { | 173 | { |
| 174 | facade.Version = version; | 174 | facade.Version = version; |
| 175 | } | 175 | } |
| 176 | else if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below. | 176 | else if (!this.AllFileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below. |
| 177 | { | 177 | { |
| 178 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching | 178 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching |
| 179 | // the version value). We didn't find it so, we will override the default version they provided with the actual | 179 | // the version value). We didn't find it so, we will override the default version they provided with the actual |
| @@ -204,108 +204,104 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 204 | this.VariableCache[$"filelanguage.{facade.Id}"] = facade.Language ?? String.Empty; | 204 | this.VariableCache[$"filelanguage.{facade.Id}"] = facade.Language ?? String.Empty; |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | // If this is a CLR assembly, load the assembly and get the assembly name information | 207 | // If there is an assembly for this file. |
| 208 | if (AssemblyType.DotNetAssembly == facade.AssemblyType) | 208 | if (assemblySymbols.TryGetValue(facade.Id, out var assemblySymbol)) |
| 209 | { | 209 | { |
| 210 | try | 210 | // If this is a CLR assembly, load the assembly and get the assembly name information |
| 211 | if (AssemblyType.DotNetAssembly == assemblySymbol.Type) | ||
| 211 | { | 212 | { |
| 212 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version); | 213 | try |
| 214 | { | ||
| 215 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version); | ||
| 213 | 216 | ||
| 214 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "name", assemblyName.Name); | 217 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name); |
| 215 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "culture", assemblyName.Culture); | 218 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "culture", assemblyName.Culture); |
| 216 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "version", assemblyName.Version); | 219 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "version", assemblyName.Version); |
| 217 | 220 | ||
| 218 | if (!String.IsNullOrEmpty(assemblyName.Architecture)) | 221 | if (!String.IsNullOrEmpty(assemblyName.Architecture)) |
| 219 | { | 222 | { |
| 220 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "processorArchitecture", assemblyName.Architecture); | 223 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "processorArchitecture", assemblyName.Architecture); |
| 221 | } | 224 | } |
| 222 | // TODO: WiX v3 seemed to do this but not clear it should actually be done. | 225 | // TODO: WiX v3 seemed to do this but not clear it should actually be done. |
| 223 | //else if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) | 226 | //else if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) |
| 224 | //{ | 227 | //{ |
| 225 | // this.SetMsiAssemblyName(assemblyNameSymbols, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); | 228 | // this.SetMsiAssemblyName(assemblyNameSymbols, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); |
| 226 | //} | 229 | //} |
| 227 | 230 | ||
| 228 | if (assemblyName.StrongNamedSigned) | 231 | if (assemblyName.StrongNamedSigned) |
| 229 | { | 232 | { |
| 230 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "publicKeyToken", assemblyName.PublicKeyToken); | 233 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "publicKeyToken", assemblyName.PublicKeyToken); |
| 231 | } | 234 | } |
| 232 | else if (facade.AssemblyApplicationFileRef == null) | 235 | else if (assemblySymbol.ApplicationFileRef == null) |
| 233 | { | 236 | { |
| 234 | throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef)); | 237 | throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef)); |
| 235 | } | 238 | } |
| 236 | 239 | ||
| 237 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) | 240 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) |
| 238 | { | 241 | { |
| 239 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "fileVersion", assemblyName.FileVersion); | 242 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "fileVersion", assemblyName.FileVersion); |
| 240 | } | 243 | } |
| 241 | 244 | ||
| 242 | // add the assembly name to the information cache | 245 | // add the assembly name to the information cache |
| 243 | if (null != this.VariableCache) | 246 | if (null != this.VariableCache) |
| 247 | { | ||
| 248 | this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName(); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | catch (WixException e) | ||
| 244 | { | 252 | { |
| 245 | this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName(); | 253 | this.Messaging.Write(e.Error); |
| 246 | } | 254 | } |
| 247 | } | 255 | } |
| 248 | catch (WixException e) | 256 | else if (AssemblyType.Win32Assembly == assemblySymbol.Type) |
| 249 | { | ||
| 250 | this.Messaging.Write(e.Error); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | else if (AssemblyType.Win32Assembly == facade.AssemblyType) | ||
| 254 | { | ||
| 255 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through | ||
| 256 | // all files like this. Even though this is a rare case it looks like we might be able to index the | ||
| 257 | // file earlier. | ||
| 258 | var fileManifest = this.FileFacades.FirstOrDefault(r => r.Id.Equals(facade.AssemblyManifestFileRef, StringComparison.Ordinal)); | ||
| 259 | if (null == fileManifest) | ||
| 260 | { | ||
| 261 | this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.SourceLineNumber, facade.Id, facade.AssemblyManifestFileRef)); | ||
| 262 | } | ||
| 263 | |||
| 264 | try | ||
| 265 | { | 257 | { |
| 266 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath); | 258 | // TODO: Consider passing in the this.AllFileFacades as an indexed collection instead of searching through |
| 267 | 259 | // all files like this. Even though this is a rare case it looks like we might be able to index the | |
| 268 | if (!String.IsNullOrEmpty(assemblyName.Name)) | 260 | // file earlier. |
| 261 | var fileManifest = this.AllFileFacades.FirstOrDefault(r => r.Id.Equals(assemblySymbol.ManifestFileRef, StringComparison.Ordinal)); | ||
| 262 | if (null == fileManifest) | ||
| 269 | { | 263 | { |
| 270 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "name", assemblyName.Name); | 264 | this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.SourceLineNumber, facade.Id, assemblySymbol.ManifestFileRef)); |
| 271 | } | 265 | } |
| 272 | 266 | ||
| 273 | if (!String.IsNullOrEmpty(assemblyName.Version)) | 267 | try |
| 274 | { | 268 | { |
| 275 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "version", assemblyName.Version); | 269 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath); |
| 276 | } | ||
| 277 | 270 | ||
| 278 | if (!String.IsNullOrEmpty(assemblyName.Type)) | 271 | if (!String.IsNullOrEmpty(assemblyName.Name)) |
| 279 | { | 272 | { |
| 280 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "type", assemblyName.Type); | 273 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name); |
| 281 | } | 274 | } |
| 282 | 275 | ||
| 283 | if (!String.IsNullOrEmpty(assemblyName.Architecture)) | 276 | if (!String.IsNullOrEmpty(assemblyName.Version)) |
| 284 | { | 277 | { |
| 285 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "processorArchitecture", assemblyName.Architecture); | 278 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "version", assemblyName.Version); |
| 286 | } | 279 | } |
| 280 | |||
| 281 | if (!String.IsNullOrEmpty(assemblyName.Type)) | ||
| 282 | { | ||
| 283 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "type", assemblyName.Type); | ||
| 284 | } | ||
| 285 | |||
| 286 | if (!String.IsNullOrEmpty(assemblyName.Architecture)) | ||
| 287 | { | ||
| 288 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "processorArchitecture", assemblyName.Architecture); | ||
| 289 | } | ||
| 287 | 290 | ||
| 288 | if (!String.IsNullOrEmpty(assemblyName.PublicKeyToken)) | 291 | if (!String.IsNullOrEmpty(assemblyName.PublicKeyToken)) |
| 292 | { | ||
| 293 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "publicKeyToken", assemblyName.PublicKeyToken); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | catch (WixException e) | ||
| 289 | { | 297 | { |
| 290 | this.SetMsiAssemblyName(assemblyNameSymbols, facade, "publicKeyToken", assemblyName.PublicKeyToken); | 298 | this.Messaging.Write(e.Error); |
| 291 | } | 299 | } |
| 292 | } | 300 | } |
| 293 | catch (WixException e) | ||
| 294 | { | ||
| 295 | this.Messaging.Write(e.Error); | ||
| 296 | } | ||
| 297 | } | 301 | } |
| 298 | } | 302 | } |
| 299 | 303 | ||
| 300 | /// <summary> | 304 | private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameSymbol> assemblyNameSymbols, IFileFacade facade, AssemblySymbol assemblySymbol, string name, string value) |
| 301 | /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise | ||
| 302 | /// create a new row. | ||
| 303 | /// </summary> | ||
| 304 | /// <param name="assemblyNameSymbols">MsiAssemblyName table.</param> | ||
| 305 | /// <param name="facade">FileFacade containing the assembly read for the MsiAssemblyName row.</param> | ||
| 306 | /// <param name="name">MsiAssemblyName name.</param> | ||
| 307 | /// <param name="value">MsiAssemblyName value.</param> | ||
| 308 | private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameSymbol> assemblyNameSymbols, IFileFacade facade, string name, string value) | ||
| 309 | { | 305 | { |
| 310 | // check for null value (this can occur when grabbing the file version from an assembly without one) | 306 | // check for null value (this can occur when grabbing the file version from an assembly without one) |
| 311 | if (String.IsNullOrEmpty(value)) | 307 | if (String.IsNullOrEmpty(value)) |
| @@ -315,36 +311,32 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 315 | else | 311 | else |
| 316 | { | 312 | { |
| 317 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. | 313 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. |
| 318 | if ("name" == name && AssemblyType.DotNetAssembly == facade.AssemblyType && | 314 | if ("name" == name && AssemblyType.DotNetAssembly == assemblySymbol.Type && |
| 319 | String.IsNullOrEmpty(facade.AssemblyApplicationFileRef) && | 315 | String.IsNullOrEmpty(assemblySymbol.ApplicationFileRef) && |
| 320 | !String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase)) | 316 | !String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase)) |
| 321 | { | 317 | { |
| 322 | this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(facade.SourceLineNumber, Path.GetFileNameWithoutExtension(facade.FileName), value)); | 318 | this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(facade.SourceLineNumber, Path.GetFileNameWithoutExtension(facade.FileName), value)); |
| 323 | } | 319 | } |
| 324 | 320 | ||
| 325 | // override directly authored value | 321 | // Override directly authored value, otherwise remember the gathered information on the facade for use later. |
| 326 | var lookup = String.Concat(facade.ComponentRef, "/", name); | 322 | var lookup = String.Concat(facade.ComponentRef, "/", name); |
| 327 | if (!assemblyNameSymbols.TryGetValue(lookup, out var assemblyNameSymbol)) | 323 | if (assemblyNameSymbols.TryGetValue(lookup, out var assemblyNameSymbol)) |
| 328 | { | 324 | { |
| 329 | assemblyNameSymbol = this.Section.AddSymbol(new MsiAssemblyNameSymbol(facade.SourceLineNumber, new Identifier(AccessModifier.Section, facade.ComponentRef, name)) | 325 | assemblyNameSymbol.Value = value; |
| 326 | } | ||
| 327 | else | ||
| 328 | { | ||
| 329 | assemblyNameSymbol = new MsiAssemblyNameSymbol(assemblySymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, facade.ComponentRef, name)) | ||
| 330 | { | 330 | { |
| 331 | ComponentRef = facade.ComponentRef, | 331 | ComponentRef = facade.ComponentRef, |
| 332 | Name = name, | 332 | Name = name, |
| 333 | Value = value, | 333 | Value = value, |
| 334 | }); | 334 | }; |
| 335 | |||
| 336 | if (null == facade.AssemblyNames) | ||
| 337 | { | ||
| 338 | facade.AssemblyNames = new List<MsiAssemblyNameSymbol>(); | ||
| 339 | } | ||
| 340 | |||
| 341 | facade.AssemblyNames.Add(assemblyNameSymbol); | ||
| 342 | 335 | ||
| 336 | facade.AssemblyNameSymbols.Add(assemblyNameSymbol); | ||
| 343 | assemblyNameSymbols.Add(assemblyNameSymbol.Id.Id, assemblyNameSymbol); | 337 | assemblyNameSymbols.Add(assemblyNameSymbol.Id.Id, assemblyNameSymbol); |
| 344 | } | 338 | } |
| 345 | 339 | ||
| 346 | assemblyNameSymbol.Value = value; | ||
| 347 | |||
| 348 | if (this.VariableCache != null) | 340 | if (this.VariableCache != null) |
| 349 | { | 341 | { |
| 350 | var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, facade.Id).ToLowerInvariant(); | 342 | var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, facade.Id).ToLowerInvariant(); |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateSymbolsWithFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateSymbolsWithFileFacadesCommand.cs new file mode 100644 index 00000000..4b332363 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateSymbolsWithFileFacadesCommand.cs | |||
| @@ -0,0 +1,73 @@ | |||
| 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.Collections.Generic; | ||
| 6 | using System.Linq; | ||
| 7 | using WixToolset.Data; | ||
| 8 | using WixToolset.Data.Symbols; | ||
| 9 | using WixToolset.Extensibility.Data; | ||
| 10 | using WixToolset.Extensibility.Services; | ||
| 11 | |||
| 12 | internal class UpdateSymbolsWithFileFacadesCommand | ||
| 13 | { | ||
| 14 | private readonly IntermediateSection section; | ||
| 15 | private readonly List<IFileFacade> allFileFacades; | ||
| 16 | |||
| 17 | public UpdateSymbolsWithFileFacadesCommand(IntermediateSection section, List<IFileFacade> allFileFacades) | ||
| 18 | { | ||
| 19 | this.section = section; | ||
| 20 | this.allFileFacades = allFileFacades; | ||
| 21 | } | ||
| 22 | |||
| 23 | public void Execute() | ||
| 24 | { | ||
| 25 | var fileSymbolsById = this.section.Symbols.OfType<FileSymbol>().ToDictionary(f => f.Id.Id); | ||
| 26 | |||
| 27 | foreach (var facade in this.allFileFacades) | ||
| 28 | { | ||
| 29 | if (fileSymbolsById.TryGetValue(facade.Id, out var fileSymbol)) | ||
| 30 | { | ||
| 31 | // Only update the file symbol if the facade value changed | ||
| 32 | if (fileSymbol.DiskId != facade.DiskId) | ||
| 33 | { | ||
| 34 | fileSymbol.DiskId = facade.DiskId; | ||
| 35 | } | ||
| 36 | |||
| 37 | if (fileSymbol.FileSize != facade.FileSize) | ||
| 38 | { | ||
| 39 | fileSymbol.FileSize = facade.FileSize; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (fileSymbol.Language != facade.Language) | ||
| 43 | { | ||
| 44 | fileSymbol.Language = facade.Language; | ||
| 45 | } | ||
| 46 | |||
| 47 | if (fileSymbol.Sequence != facade.Sequence) | ||
| 48 | { | ||
| 49 | fileSymbol.Sequence = facade.Sequence; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (fileSymbol.Version != facade.Version) | ||
| 53 | { | ||
| 54 | fileSymbol.Version = facade.Version; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | if (facade.MsiFileHashSymbol != null) | ||
| 59 | { | ||
| 60 | this.section.AddSymbol(facade.MsiFileHashSymbol); | ||
| 61 | } | ||
| 62 | |||
| 63 | if (facade.AssemblyNameSymbols != null) | ||
| 64 | { | ||
| 65 | foreach (var assemblyNameSymbol in facade.AssemblyNameSymbols) | ||
| 66 | { | ||
| 67 | this.section.AddSymbol(assemblyNameSymbol); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs index ed865450..7b6c21ef 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs | |||
| @@ -4,6 +4,7 @@ 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.IO; | ||
| 7 | using System.Linq; | 8 | using System.Linq; |
| 8 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 9 | using WixToolset.Data.Symbols; | 10 | using WixToolset.Data.Symbols; |
| @@ -14,10 +15,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 14 | 15 | ||
| 15 | internal class UpdateTransformsWithFileFacades | 16 | internal class UpdateTransformsWithFileFacades |
| 16 | { | 17 | { |
| 17 | public UpdateTransformsWithFileFacades(IMessaging messaging, WindowsInstallerData output, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions, IEnumerable<IFileFacade> fileFacades) | 18 | public UpdateTransformsWithFileFacades(IMessaging messaging, IntermediateSection section, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions, IEnumerable<IFileFacade> fileFacades) |
| 18 | { | 19 | { |
| 19 | this.Messaging = messaging; | 20 | this.Messaging = messaging; |
| 20 | this.Output = output; | 21 | this.Section = section; |
| 21 | this.SubStorages = subStorages; | 22 | this.SubStorages = subStorages; |
| 22 | this.TableDefinitions = tableDefinitions; | 23 | this.TableDefinitions = tableDefinitions; |
| 23 | this.FileFacades = fileFacades; | 24 | this.FileFacades = fileFacades; |
| @@ -25,7 +26,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 25 | 26 | ||
| 26 | private IMessaging Messaging { get; } | 27 | private IMessaging Messaging { get; } |
| 27 | 28 | ||
| 28 | private WindowsInstallerData Output { get; } | 29 | private IntermediateSection Section { get; } |
| 29 | 30 | ||
| 30 | private IEnumerable<SubStorage> SubStorages { get; } | 31 | private IEnumerable<SubStorage> SubStorages { get; } |
| 31 | 32 | ||
| @@ -35,190 +36,97 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 35 | 36 | ||
| 36 | public void Execute() | 37 | public void Execute() |
| 37 | { | 38 | { |
| 38 | var fileFacadesByDiskId = new Dictionary<int, Dictionary<string, IFileFacade>>(); | 39 | var fileFacadesByDiskId = this.IndexFileFacadesByDiskId(); |
| 39 | |||
| 40 | // Index patch file facades by diskId+fileId. | ||
| 41 | foreach (var facade in this.FileFacades) | ||
| 42 | { | ||
| 43 | if (!fileFacadesByDiskId.TryGetValue(facade.DiskId, out var mediaFacades)) | ||
| 44 | { | ||
| 45 | mediaFacades = new Dictionary<string, IFileFacade>(); | ||
| 46 | fileFacadesByDiskId.Add(facade.DiskId, mediaFacades); | ||
| 47 | } | ||
| 48 | |||
| 49 | mediaFacades.Add(facade.Id, facade); | ||
| 50 | } | ||
| 51 | 40 | ||
| 52 | var patchMediaRows = new RowDictionary<MediaRow>(this.Output.Tables["Media"]); | 41 | var mediaSymbolsByDiskId = this.Section.Symbols.OfType<MediaSymbol>().ToDictionary(m => m.DiskId); |
| 53 | 42 | ||
| 54 | // Index paired transforms by name without the "#" prefix. | 43 | // Index paired transforms by name without the "#" prefix. |
| 55 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data); | 44 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data); |
| 56 | 45 | ||
| 57 | // Copy File bind data into substorages | ||
| 58 | foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix))) | 46 | foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix))) |
| 59 | { | 47 | { |
| 60 | var mainTransform = substorage.Data; | 48 | var mainTransform = substorage.Data; |
| 61 | 49 | ||
| 62 | var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]); | ||
| 63 | |||
| 64 | var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name]; | 50 | var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name]; |
| 65 | 51 | ||
| 66 | // Copy Media.LastSequence. | 52 | // Update the Media.LastSequence in the paired transforms. |
| 67 | var pairedMediaTable = pairedTransform.Tables["Media"]; | 53 | foreach (var pairedMediaRow in pairedTransform.Tables["Media"].Rows.Cast<MediaRow>()) |
| 68 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | ||
| 69 | { | 54 | { |
| 70 | var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | 55 | if (mediaSymbolsByDiskId.TryGetValue(pairedMediaRow.DiskId, out var mediaSymbol) && mediaSymbol.LastSequence.HasValue) |
| 71 | pairedMediaRow.LastSequence = patchMediaRow.LastSequence; | 56 | { |
| 57 | pairedMediaRow.LastSequence = mediaSymbol.LastSequence.Value; | ||
| 58 | } | ||
| 59 | else // TODO: This shouldn't be possible. | ||
| 60 | { | ||
| 61 | throw new InvalidDataException(); | ||
| 62 | } | ||
| 72 | } | 63 | } |
| 73 | 64 | ||
| 74 | // Validate file row changes for keypath-related issues | 65 | // Validate file row changes for keypath-related issues |
| 75 | this.ValidateFileRowChanges(mainTransform); | 66 | this.ValidateFileRowChanges(mainTransform); |
| 76 | 67 | ||
| 77 | // Index File table of pairedTransform | 68 | // Copy File bind data into transforms |
| 78 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | 69 | if (mainTransform.Tables.TryGetTable("File", out var mainFileTable)) |
| 79 | |||
| 80 | var mainFileTable = mainTransform.Tables["File"]; | ||
| 81 | if (null != mainFileTable) | ||
| 82 | { | 70 | { |
| 71 | // Index File table of pairedTransform | ||
| 72 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | ||
| 73 | |||
| 74 | var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]); | ||
| 75 | |||
| 83 | // Remove the MsiFileHash table because it will be updated later with the final file hash for each file | 76 | // Remove the MsiFileHash table because it will be updated later with the final file hash for each file |
| 84 | mainTransform.Tables.Remove("MsiFileHash"); | 77 | mainTransform.Tables.Remove("MsiFileHash"); |
| 85 | 78 | ||
| 86 | foreach (FileRow mainFileRow in mainFileTable.Rows) | 79 | foreach (var mainFileRow in mainFileTable.Rows.Where(r => r.Operation == RowOperation.Add || r.Operation == RowOperation.Modify).Cast<FileRow>()) |
| 87 | { | 80 | { |
| 88 | if (RowOperation.Delete == mainFileRow.Operation) | 81 | // TODO: Wasn't this indexing done at the top of this method? |
| 89 | { | 82 | // Index main transform files by diskId+fileId |
| 90 | continue; | ||
| 91 | } | ||
| 92 | else if (RowOperation.None == mainFileRow.Operation) | ||
| 93 | { | ||
| 94 | continue; | ||
| 95 | } | ||
| 96 | |||
| 97 | // Index patch files by diskId+fileId | ||
| 98 | if (!fileFacadesByDiskId.TryGetValue(mainFileRow.DiskId, out var mediaFacades)) | 83 | if (!fileFacadesByDiskId.TryGetValue(mainFileRow.DiskId, out var mediaFacades)) |
| 99 | { | 84 | { |
| 100 | mediaFacades = new Dictionary<string, IFileFacade>(); | 85 | mediaFacades = new Dictionary<string, IFileFacade>(); |
| 101 | fileFacadesByDiskId.Add(mainFileRow.DiskId, mediaFacades); | 86 | fileFacadesByDiskId.Add(mainFileRow.DiskId, mediaFacades); |
| 102 | } | 87 | } |
| 103 | 88 | ||
| 104 | // copy data from the patch back to the transform | 89 | // Copy data from the facade back to the appropriate transform. |
| 105 | if (mediaFacades.TryGetValue(mainFileRow.File, out var facade)) | 90 | if (mediaFacades.TryGetValue(mainFileRow.File, out var facade)) |
| 106 | { | 91 | { |
| 107 | var patchFileRow = facade.GetFileRow(); | ||
| 108 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); | 92 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); |
| 109 | 93 | ||
| 110 | for (var i = 0; i < patchFileRow.Fields.Length; i++) | 94 | TryModifyField(mainFileRow, 3, facade.FileSize); |
| 111 | { | 95 | |
| 112 | var patchValue = patchFileRow.FieldAsString(i) ?? String.Empty; | 96 | TryModifyField(mainFileRow, 4, facade.Version); |
| 113 | var mainValue = mainFileRow.FieldAsString(i) ?? String.Empty; | 97 | |
| 98 | TryModifyField(mainFileRow, 5, facade.Language); | ||
| 114 | 99 | ||
| 115 | if (1 == i) | ||
| 116 | { | ||
| 117 | // File.Component_ changes should not come from the shared file rows | ||
| 118 | // that contain the file information as each individual transform might | ||
| 119 | // have different changes (or no changes at all). | ||
| 120 | } | ||
| 121 | else if (6 == i) // File.Attributes should not changed for binary deltas | ||
| 122 | { | ||
| 123 | #if TODO_PATCHING_DELTA | ||
| 124 | if (null != patchFileRow.Patch) | ||
| 125 | { | ||
| 126 | // File.Attribute should not change for binary deltas | ||
| 127 | pairedFileRow.Attributes = mainFileRow.Attributes; | ||
| 128 | mainFileRow.Fields[i].Modified = false; | ||
| 129 | } | ||
| 130 | #endif | ||
| 131 | } | ||
| 132 | else if (7 == i) // File.Sequence is updated in pairedTransform, not mainTransform | ||
| 133 | { | ||
| 134 | // file sequence is updated in Patch table instead of File table for delta patches | ||
| 135 | #if TODO_PATCHING_DELTA | 100 | #if TODO_PATCHING_DELTA |
| 136 | if (null != patchFileRow.Patch) | 101 | // File.Attribute should not change for binary deltas, otherwise copy File Attributes from main transform row. |
| 137 | { | 102 | if (null != facade.Patch) |
| 138 | pairedFileRow.Fields[i].Modified = false; | ||
| 139 | } | ||
| 140 | else | ||
| 141 | #endif | 103 | #endif |
| 142 | { | 104 | { |
| 143 | pairedFileRow[i] = patchFileRow[i]; | 105 | TryModifyField(pairedFileRow, 6, mainFileRow.Attributes); |
| 144 | pairedFileRow.Fields[i].Modified = true; | 106 | mainFileRow.Fields[6].Modified = false; |
| 145 | } | ||
| 146 | mainFileRow.Fields[i].Modified = false; | ||
| 147 | } | ||
| 148 | else if (patchValue != mainValue) | ||
| 149 | { | ||
| 150 | mainFileRow[i] = patchFileRow[i]; | ||
| 151 | mainFileRow.Fields[i].Modified = true; | ||
| 152 | if (mainFileRow.Operation == RowOperation.None) | ||
| 153 | { | ||
| 154 | mainFileRow.Operation = RowOperation.Modify; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | 107 | } |
| 158 | 108 | ||
| 159 | // Copy MsiFileHash row for this File. | 109 | #if TODO_PATCHING_DELTA |
| 160 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) | 110 | // File.Sequence is updated in Patch table instead of File table for delta patches |
| 111 | if (null != facade.Patch) | ||
| 161 | { | 112 | { |
| 162 | //patchHashRow = patchFileRow.Hash; | 113 | pairedFileRow.Fields[7].Modified = false; |
| 163 | throw new NotImplementedException(); | ||
| 164 | } | 114 | } |
| 165 | 115 | else | |
| 166 | if (null != patchHashRow) | 116 | #endif |
| 167 | { | 117 | { |
| 168 | var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | 118 | // File.Sequence is updated in pairedTransform, not mainTransform. |
| 169 | var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | 119 | TryModifyField(pairedFileRow, 7, facade.Sequence); |
| 170 | for (var i = 0; i < patchHashRow.Fields.Length; i++) | ||
| 171 | { | ||
| 172 | mainHashRow[i] = patchHashRow[i]; | ||
| 173 | if (i > 1) | ||
| 174 | { | ||
| 175 | // assume all hash fields have been modified | ||
| 176 | mainHashRow.Fields[i].Modified = true; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | // assume the MsiFileHash operation follows the File one | ||
| 181 | mainHashRow.Operation = mainFileRow.Operation; | ||
| 182 | } | 120 | } |
| 121 | mainFileRow.Fields[7].Modified = false; | ||
| 183 | 122 | ||
| 184 | // copy MsiAssemblyName rows for this File | 123 | this.ProcessMsiFileHash(mainTransform, mainFileRow, facade.MsiFileHashSymbol, mainMsiFileHashIndex); |
| 185 | #if TODO_PATCHING | ||
| 186 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | ||
| 187 | if (null != patchAssemblyNameRows) | ||
| 188 | { | ||
| 189 | var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
| 190 | foreach (var patchAssemblyNameRow in patchAssemblyNameRows) | ||
| 191 | { | ||
| 192 | // Copy if there isn't an identical modified/added row already in the transform. | ||
| 193 | var foundMatchingModifiedRow = false; | ||
| 194 | foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) | ||
| 195 | { | ||
| 196 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | ||
| 197 | { | ||
| 198 | foundMatchingModifiedRow = true; | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | 124 | ||
| 203 | if (!foundMatchingModifiedRow) | 125 | this.ProcessMsiAssemblyName(mainTransform, mainFileRow, facade); |
| 204 | { | ||
| 205 | var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 206 | for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | ||
| 207 | { | ||
| 208 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | ||
| 209 | } | ||
| 210 | |||
| 211 | // assume value field has been modified | ||
| 212 | mainAssemblyNameRow.Fields[2].Modified = true; | ||
| 213 | mainAssemblyNameRow.Operation = mainFileRow.Operation; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | #endif | ||
| 218 | 126 | ||
| 219 | // Add patch header for this file | ||
| 220 | #if TODO_PATCHING_DELTA | 127 | #if TODO_PATCHING_DELTA |
| 221 | if (null != patchFileRow.Patch) | 128 | // Add patch header for this file |
| 129 | if (null != facade.Patch) | ||
| 222 | { | 130 | { |
| 223 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | 131 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. |
| 224 | this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | 132 | this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); |
| @@ -232,12 +140,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 232 | } | 140 | } |
| 233 | 141 | ||
| 234 | var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | 142 | var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 235 | patchRow[0] = patchFileRow.File; | 143 | patchRow[0] = facade.File; |
| 236 | patchRow[1] = patchFileRow.Sequence; | 144 | patchRow[1] = facade.Sequence; |
| 237 | 145 | ||
| 238 | var patchFile = new FileInfo(patchFileRow.Source); | 146 | var patchFile = new FileInfo(facade.Source); |
| 239 | patchRow[2] = (int)patchFile.Length; | 147 | patchRow[2] = (int)patchFile.Length; |
| 240 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | 148 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & facade.PatchAttributes) ? 0 : 1; |
| 241 | 149 | ||
| 242 | var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | 150 | var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; |
| 243 | if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) | 151 | if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) |
| @@ -252,13 +160,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 252 | 160 | ||
| 253 | var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | 161 | var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 254 | patchHeadersRow[0] = streamName; | 162 | patchHeadersRow[0] = streamName; |
| 255 | patchHeadersRow[1] = patchFileRow.Patch; | 163 | patchHeadersRow[1] = facade.Patch; |
| 256 | patchRow[5] = streamName; | 164 | patchRow[5] = streamName; |
| 257 | patchHeadersRow.Operation = RowOperation.Add; | 165 | patchHeadersRow.Operation = RowOperation.Add; |
| 258 | } | 166 | } |
| 259 | else | 167 | else |
| 260 | { | 168 | { |
| 261 | patchRow[4] = patchFileRow.Patch; | 169 | patchRow[4] = facade.Patch; |
| 262 | } | 170 | } |
| 263 | patchRow.Operation = RowOperation.Add; | 171 | patchRow.Operation = RowOperation.Add; |
| 264 | } | 172 | } |
| @@ -270,14 +178,89 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 270 | } | 178 | } |
| 271 | } | 179 | } |
| 272 | } | 180 | } |
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | private void ProcessMsiFileHash(WindowsInstallerData transform, FileRow fileRow, MsiFileHashSymbol msiFileHashSymbol, RowDictionary<Row> msiFileHashIndex) | ||
| 185 | { | ||
| 186 | Row msiFileHashRow = null; | ||
| 187 | |||
| 188 | if (msiFileHashSymbol != null || msiFileHashIndex.TryGetValue(fileRow.File, out msiFileHashRow)) | ||
| 189 | { | ||
| 190 | var sourceLineNumbers = msiFileHashSymbol?.SourceLineNumbers ?? msiFileHashRow?.SourceLineNumbers; | ||
| 191 | |||
| 192 | var transformHashTable = transform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
| 193 | |||
| 194 | var transformHashRow = transformHashTable.CreateRow(sourceLineNumbers); | ||
| 195 | transformHashRow.Operation = fileRow.Operation; // Assume the MsiFileHash operation follows the File one. | ||
| 196 | |||
| 197 | transformHashRow[0] = fileRow.File; | ||
| 198 | transformHashRow[1] = msiFileHashSymbol?.Options ?? msiFileHashRow?.Fields[1].Data; | ||
| 199 | |||
| 200 | // Assume all hash fields have been modified. | ||
| 201 | TryModifyField(transformHashRow, 2, msiFileHashSymbol?.HashPart1 ?? msiFileHashRow?.Fields[2].Data); | ||
| 202 | TryModifyField(transformHashRow, 3, msiFileHashSymbol?.HashPart2 ?? msiFileHashRow?.Fields[3].Data); | ||
| 203 | TryModifyField(transformHashRow, 4, msiFileHashSymbol?.HashPart3 ?? msiFileHashRow?.Fields[4].Data); | ||
| 204 | TryModifyField(transformHashRow, 5, msiFileHashSymbol?.HashPart4 ?? msiFileHashRow?.Fields[5].Data); | ||
| 205 | } | ||
| 206 | } | ||
| 273 | 207 | ||
| 274 | this.Output.Tables.Remove("Media"); | 208 | private void ProcessMsiAssemblyName(WindowsInstallerData transform, FileRow fileRow, IFileFacade facade) |
| 275 | this.Output.Tables.Remove("File"); | 209 | { |
| 276 | this.Output.Tables.Remove("MsiFileHash"); | 210 | if (facade.AssemblyNameSymbols.Count > 0) |
| 277 | this.Output.Tables.Remove("MsiAssemblyName"); | 211 | { |
| 212 | var assemblyNameTable = transform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
| 213 | |||
| 214 | foreach (var assemblyNameSymbol in facade.AssemblyNameSymbols) | ||
| 215 | { | ||
| 216 | // Copy if there isn't an identical modified/added row already in the transform. | ||
| 217 | var foundMatchingModifiedRow = false; | ||
| 218 | foreach (var mainAssemblyNameRow in assemblyNameTable.Rows.Where(r => r.Operation != RowOperation.None)) | ||
| 219 | { | ||
| 220 | var component = mainAssemblyNameRow.FieldAsString(0); | ||
| 221 | var name = mainAssemblyNameRow.FieldAsString(1); | ||
| 222 | |||
| 223 | if (assemblyNameSymbol.ComponentRef == component && assemblyNameSymbol.Name == name) | ||
| 224 | { | ||
| 225 | foundMatchingModifiedRow = true; | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | if (!foundMatchingModifiedRow) | ||
| 231 | { | ||
| 232 | var assemblyNameRow = assemblyNameTable.CreateRow(fileRow.SourceLineNumbers); | ||
| 233 | assemblyNameRow[0] = assemblyNameSymbol.ComponentRef; | ||
| 234 | assemblyNameRow[1] = assemblyNameSymbol.Name; | ||
| 235 | assemblyNameRow[2] = assemblyNameSymbol.Value; | ||
| 236 | |||
| 237 | // assume value field has been modified | ||
| 238 | assemblyNameRow.Fields[2].Modified = true; | ||
| 239 | assemblyNameRow.Operation = fileRow.Operation; | ||
| 240 | } | ||
| 241 | } | ||
| 278 | } | 242 | } |
| 279 | } | 243 | } |
| 280 | 244 | ||
| 245 | private Dictionary<int, Dictionary<string, IFileFacade>> IndexFileFacadesByDiskId() | ||
| 246 | { | ||
| 247 | var fileFacadesByDiskId = new Dictionary<int, Dictionary<string, IFileFacade>>(); | ||
| 248 | |||
| 249 | // Index patch file facades by diskId+fileId. | ||
| 250 | foreach (var facade in this.FileFacades) | ||
| 251 | { | ||
| 252 | if (!fileFacadesByDiskId.TryGetValue(facade.DiskId, out var mediaFacades)) | ||
| 253 | { | ||
| 254 | mediaFacades = new Dictionary<string, IFileFacade>(); | ||
| 255 | fileFacadesByDiskId.Add(facade.DiskId, mediaFacades); | ||
| 256 | } | ||
| 257 | |||
| 258 | mediaFacades.Add(facade.Id, facade); | ||
| 259 | } | ||
| 260 | |||
| 261 | return fileFacadesByDiskId; | ||
| 262 | } | ||
| 263 | |||
| 281 | /// <summary> | 264 | /// <summary> |
| 282 | /// Adds the PatchFiles action to the sequence table if it does not already exist. | 265 | /// Adds the PatchFiles action to the sequence table if it does not already exist. |
| 283 | /// </summary> | 266 | /// </summary> |
| @@ -349,6 +332,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 349 | } | 332 | } |
| 350 | } | 333 | } |
| 351 | 334 | ||
| 335 | private static bool TryModifyField(Row row, int index, object value) | ||
| 336 | { | ||
| 337 | var field = row.Fields[index]; | ||
| 338 | |||
| 339 | if (field.Data != value) | ||
| 340 | { | ||
| 341 | field.Data = value; | ||
| 342 | field.Modified = true; | ||
| 343 | |||
| 344 | if (row.Operation == RowOperation.None) | ||
| 345 | { | ||
| 346 | row.Operation = RowOperation.Modify; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | return field.Modified; | ||
| 351 | } | ||
| 352 | |||
| 352 | /// <summary> | 353 | /// <summary> |
| 353 | /// Tests sequence table for PatchFiles and associated actions | 354 | /// Tests sequence table for PatchFiles and associated actions |
| 354 | /// </summary> | 355 | /// </summary> |
| @@ -396,33 +397,29 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 396 | return; | 397 | return; |
| 397 | } | 398 | } |
| 398 | 399 | ||
| 399 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | ||
| 400 | |||
| 401 | // Index the Component table for non-directory & non-registry key paths. | 400 | // Index the Component table for non-directory & non-registry key paths. |
| 402 | foreach (var row in componentTable.Rows) | 401 | var componentKeyPath = new Dictionary<string, string>(); |
| 402 | foreach (var row in componentTable.Rows.Cast<ComponentRow>().Where(r => !r.IsRegistryKeyPath)) | ||
| 403 | { | 403 | { |
| 404 | var keyPath = row.FieldAsString(5); | 404 | var keyPath = row.KeyPath; |
| 405 | if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) | 405 | |
| 406 | if (!String.IsNullOrEmpty(keyPath)) | ||
| 406 | { | 407 | { |
| 407 | componentKeyPath.Add(row.FieldAsString(0), keyPath); | 408 | componentKeyPath.Add(row.Component, keyPath); |
| 408 | } | 409 | } |
| 409 | } | 410 | } |
| 410 | 411 | ||
| 411 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | 412 | var componentWithChangedKeyPath = new Dictionary<string, string>(); |
| 412 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); | 413 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); |
| 414 | |||
| 413 | // Verify changes in the file table, now that file diffing has occurred | 415 | // Verify changes in the file table, now that file diffing has occurred |
| 414 | foreach (FileRow row in fileTable.Rows) | 416 | foreach (var row in fileTable.Rows.Cast<FileRow>().Where(r => r.Operation == RowOperation.Modify)) |
| 415 | { | 417 | { |
| 416 | if (RowOperation.Modify != row.Operation) | 418 | var fileId = row.File; |
| 417 | { | 419 | var componentId = row.Component; |
| 418 | continue; | ||
| 419 | } | ||
| 420 | |||
| 421 | var fileId = row.FieldAsString(0); | ||
| 422 | var componentId = row.FieldAsString(1); | ||
| 423 | 420 | ||
| 424 | // If this file is the keypath of a component | 421 | // If this file is the keypath of a component |
| 425 | if (componentKeyPath.ContainsValue(fileId)) | 422 | if (componentKeyPath.ContainsValue(componentId)) |
| 426 | { | 423 | { |
| 427 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | 424 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) |
| 428 | { | 425 | { |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs index 727e20b2..68cb6554 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs | |||
| @@ -21,6 +21,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 21 | { | 21 | { |
| 22 | this.Messaging = serviceProvider.GetService<IMessaging>(); | 22 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
| 23 | this.BackendHelper = serviceProvider.GetService<IBackendHelper>(); | 23 | this.BackendHelper = serviceProvider.GetService<IBackendHelper>(); |
| 24 | this.PathResolver = serviceProvider.GetService<IPathResolver>(); | ||
| 24 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); | 25 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); |
| 25 | } | 26 | } |
| 26 | 27 | ||
| @@ -28,6 +29,8 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 28 | 29 | ||
| 29 | private IBackendHelper BackendHelper { get; } | 30 | private IBackendHelper BackendHelper { get; } |
| 30 | 31 | ||
| 32 | private IPathResolver PathResolver { get; } | ||
| 33 | |||
| 31 | private IExtensionManager ExtensionManager { get; } | 34 | private IExtensionManager ExtensionManager { get; } |
| 32 | 35 | ||
| 33 | private string OutputPath { get; set; } | 36 | private string OutputPath { get; set; } |
| @@ -40,8 +43,6 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 40 | 43 | ||
| 41 | private string IntermediateFolder { get; set; } | 44 | private string IntermediateFolder { get; set; } |
| 42 | 45 | ||
| 43 | private bool IsAdminImage { get; set; } | ||
| 44 | |||
| 45 | private bool PreserveUnchangedRows { get; set; } | 46 | private bool PreserveUnchangedRows { get; set; } |
| 46 | 47 | ||
| 47 | private bool ShowPedanticMessages { get; set; } | 48 | private bool ShowPedanticMessages { get; set; } |
| @@ -133,10 +134,6 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 133 | var parameter = argument.Substring(1); | 134 | var parameter = argument.Substring(1); |
| 134 | switch (parameter.ToLowerInvariant()) | 135 | switch (parameter.ToLowerInvariant()) |
| 135 | { | 136 | { |
| 136 | case "a": | ||
| 137 | this.IsAdminImage = true; | ||
| 138 | return true; | ||
| 139 | |||
| 140 | case "intermediatefolder": | 137 | case "intermediatefolder": |
| 141 | this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); | 138 | this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); |
| 142 | return true; | 139 | return true; |
| @@ -194,15 +191,15 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 194 | } | 191 | } |
| 195 | } | 192 | } |
| 196 | 193 | ||
| 197 | case "val": | 194 | case "t": |
| 198 | { | 195 | { |
| 199 | var val = parser.GetNextArgumentOrError(argument); | 196 | var type = parser.GetNextArgumentOrError(argument); |
| 200 | if (String.IsNullOrEmpty(val)) | 197 | if (String.IsNullOrEmpty(type)) |
| 201 | { | 198 | { |
| 202 | return true; | 199 | return true; |
| 203 | } | 200 | } |
| 204 | 201 | ||
| 205 | switch (val.ToLowerInvariant()) | 202 | switch (type.ToLowerInvariant()) |
| 206 | { | 203 | { |
| 207 | case "language": | 204 | case "language": |
| 208 | this.ValidationFlags |= TransformFlags.LanguageTransformDefault; | 205 | this.ValidationFlags |= TransformFlags.LanguageTransformDefault; |
| @@ -216,6 +213,22 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 216 | this.ValidationFlags |= TransformFlags.PatchTransformDefault; | 213 | this.ValidationFlags |= TransformFlags.PatchTransformDefault; |
| 217 | return true; | 214 | return true; |
| 218 | 215 | ||
| 216 | default: | ||
| 217 | parser.ReportErrorArgument(argument, ErrorMessages.IllegalCommandLineArgumentValue(argument, type, new[] { "language", "instance", "patch" })); | ||
| 218 | return true; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | case "val": | ||
| 223 | { | ||
| 224 | var val = parser.GetNextArgumentOrError(argument); | ||
| 225 | if (String.IsNullOrEmpty(val)) | ||
| 226 | { | ||
| 227 | return true; | ||
| 228 | } | ||
| 229 | |||
| 230 | switch (val.ToLowerInvariant()) | ||
| 231 | { | ||
| 219 | case "g": | 232 | case "g": |
| 220 | this.ValidationFlags |= TransformFlags.ValidateUpgradeCode; | 233 | this.ValidationFlags |= TransformFlags.ValidateUpgradeCode; |
| 221 | return true; | 234 | return true; |
| @@ -261,7 +274,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 261 | return true; | 274 | return true; |
| 262 | 275 | ||
| 263 | default: | 276 | default: |
| 264 | parser.ReportErrorArgument(argument, ErrorMessages.IllegalCommandLineArgumentValue(argument, val, new[] { "language", "instance", "patch", "g", "l", "r", "s", "t", "u", "v", "w", "x", "y", "z" })); | 277 | parser.ReportErrorArgument(argument, ErrorMessages.IllegalCommandLineArgumentValue(argument, val, new[] { "g", "l", "r", "s", "t", "u", "v", "w", "x", "y", "z" })); |
| 265 | return true; | 278 | return true; |
| 266 | } | 279 | } |
| 267 | } | 280 | } |
| @@ -297,7 +310,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 297 | { | 310 | { |
| 298 | Exception exception; | 311 | Exception exception; |
| 299 | 312 | ||
| 300 | (transform, exception) = LoadWindowsInstallerDataSafely(this.TargetPath); | 313 | (transform, exception) = DataLoader.LoadWindowsInstallerDataSafely(this.TargetPath); |
| 301 | 314 | ||
| 302 | if (transform?.Type != OutputType.Transform) | 315 | if (transform?.Type != OutputType.Transform) |
| 303 | { | 316 | { |
| @@ -340,17 +353,9 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 340 | 353 | ||
| 341 | private WindowsInstallerData CreateTransform() | 354 | private WindowsInstallerData CreateTransform() |
| 342 | { | 355 | { |
| 343 | if (!TryLoadWindowsInstallerData(this.TargetPath, out var targetOutput)) | 356 | var targetData = this.GetWindowsInstallerData(this.TargetPath); |
| 344 | { | ||
| 345 | var unbindCommand = new UnbindMsiOrMsmCommand(this.Messaging, this.BackendHelper, this.TargetPath, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, suppressDemodularization: true, suppressExtractCabinets: true); | ||
| 346 | targetOutput = unbindCommand.Execute(); | ||
| 347 | } | ||
| 348 | 357 | ||
| 349 | if (!TryLoadWindowsInstallerData(this.TargetPath, out var updatedOutput)) | 358 | var updatedData = this.GetWindowsInstallerData(this.UpdatedPath); |
| 350 | { | ||
| 351 | var unbindCommand = new UnbindMsiOrMsmCommand(this.Messaging, this.BackendHelper, this.UpdatedPath, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, suppressDemodularization: true, suppressExtractCabinets: true); | ||
| 352 | updatedOutput = unbindCommand.Execute(); | ||
| 353 | } | ||
| 354 | 359 | ||
| 355 | var differ = new Differ(this.Messaging) | 360 | var differ = new Differ(this.Messaging) |
| 356 | { | 361 | { |
| @@ -359,7 +364,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 359 | SuppressKeepingSpecialRows = this.SuppressKeepingSpecialRows | 364 | SuppressKeepingSpecialRows = this.SuppressKeepingSpecialRows |
| 360 | }; | 365 | }; |
| 361 | 366 | ||
| 362 | return differ.Diff(targetOutput, updatedOutput, this.ValidationFlags); | 367 | return differ.Diff(targetData, updatedData, this.ValidationFlags); |
| 363 | } | 368 | } |
| 364 | 369 | ||
| 365 | private TableDefinitionCollection GetTableDefinitions() | 370 | private TableDefinitionCollection GetTableDefinitions() |
| @@ -370,37 +375,15 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine | |||
| 370 | return loadTableDefinitions.Execute(); | 375 | return loadTableDefinitions.Execute(); |
| 371 | } | 376 | } |
| 372 | 377 | ||
| 373 | private static bool TryLoadWindowsInstallerData(string path, out WindowsInstallerData data) | 378 | private WindowsInstallerData GetWindowsInstallerData(string path) |
| 374 | { | 379 | { |
| 375 | data = null; | 380 | if (!DataLoader.TryLoadWindowsInstallerData(path, out var data)) |
| 376 | |||
| 377 | var extension = Path.GetExtension(path); | ||
| 378 | |||
| 379 | // If the path is _not_ obviously a Windows Installer database, let's try opening it as | ||
| 380 | // our own data file format. | ||
| 381 | if (!extension.Equals(".msi", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".msm", StringComparison.OrdinalIgnoreCase)) | ||
| 382 | { | ||
| 383 | (data, _) = LoadWindowsInstallerDataSafely(path); | ||
| 384 | } | ||
| 385 | |||
| 386 | return data != null; | ||
| 387 | } | ||
| 388 | |||
| 389 | private static (WindowsInstallerData, Exception) LoadWindowsInstallerDataSafely(string path) | ||
| 390 | { | ||
| 391 | WindowsInstallerData data = null; | ||
| 392 | Exception exception = null; | ||
| 393 | |||
| 394 | try | ||
| 395 | { | ||
| 396 | data = WindowsInstallerData.Load(path); | ||
| 397 | } | ||
| 398 | catch (Exception e) | ||
| 399 | { | 381 | { |
| 400 | exception = e; | 382 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); |
| 383 | data = unbindCommand.Execute(); | ||
| 401 | } | 384 | } |
| 402 | 385 | ||
| 403 | return (data, exception); | 386 | return data; |
| 404 | } | 387 | } |
| 405 | } | 388 | } |
| 406 | } | 389 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs index ae9492eb..f4e4a1fc 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs | |||
| @@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 10 | using WixToolset.Data; | 10 | using WixToolset.Data; |
| 11 | using WixToolset.Data.Symbols; | 11 | using WixToolset.Data.Symbols; |
| 12 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
| 13 | using WixToolset.Extensibility; | ||
| 14 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility.Services; |
| 15 | 14 | ||
| 16 | /// <summary> | 15 | /// <summary> |
| @@ -18,7 +17,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 18 | /// </summary> | 17 | /// </summary> |
| 19 | public sealed class Differ | 18 | public sealed class Differ |
| 20 | { | 19 | { |
| 21 | private const char sectionDelimiter = '/'; | 20 | private const char SectionDelimiter = '/'; |
| 22 | private readonly IMessaging messaging; | 21 | private readonly IMessaging messaging; |
| 23 | private SummaryInformationStreams transformSummaryInfo; | 22 | private SummaryInformationStreams transformSummaryInfo; |
| 24 | 23 | ||
| @@ -53,24 +52,16 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 53 | /// </summary> | 52 | /// </summary> |
| 54 | /// <param name="targetOutput">The target output.</param> | 53 | /// <param name="targetOutput">The target output.</param> |
| 55 | /// <param name="updatedOutput">The updated output.</param> | 54 | /// <param name="updatedOutput">The updated output.</param> |
| 56 | /// <returns>The transform.</returns> | ||
| 57 | public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput) | ||
| 58 | { | ||
| 59 | return this.Diff(targetOutput, updatedOutput, 0); | ||
| 60 | } | ||
| 61 | |||
| 62 | /// <summary> | ||
| 63 | /// Creates a transform by diffing two outputs. | ||
| 64 | /// </summary> | ||
| 65 | /// <param name="targetOutput">The target output.</param> | ||
| 66 | /// <param name="updatedOutput">The updated output.</param> | ||
| 67 | /// <param name="validationFlags"></param> | 55 | /// <param name="validationFlags"></param> |
| 68 | /// <returns>The transform.</returns> | 56 | /// <returns>The transform.</returns> |
| 69 | public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags) | 57 | public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags) |
| 70 | { | 58 | { |
| 71 | var transform = new WindowsInstallerData(null); | 59 | var transform = new WindowsInstallerData(null) |
| 72 | transform.Type = OutputType.Transform; | 60 | { |
| 73 | transform.Codepage = updatedOutput.Codepage; | 61 | Type = OutputType.Transform, |
| 62 | Codepage = updatedOutput.Codepage | ||
| 63 | }; | ||
| 64 | |||
| 74 | this.transformSummaryInfo = new SummaryInformationStreams(); | 65 | this.transformSummaryInfo = new SummaryInformationStreams(); |
| 75 | 66 | ||
| 76 | // compare the codepages | 67 | // compare the codepages |
| @@ -120,7 +111,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 120 | foreach (var updatedRow in updatedTable.Rows) | 111 | foreach (var updatedRow in updatedTable.Rows) |
| 121 | { | 112 | { |
| 122 | updatedRow.Operation = RowOperation.Add; | 113 | updatedRow.Operation = RowOperation.Add; |
| 123 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 114 | updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; |
| 124 | addedTable.Rows.Add(updatedRow); | 115 | addedTable.Rows.Add(updatedRow); |
| 125 | } | 116 | } |
| 126 | } | 117 | } |
| @@ -209,7 +200,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 209 | else if (null == updatedRow) | 200 | else if (null == updatedRow) |
| 210 | { | 201 | { |
| 211 | operation = targetRow.Operation = RowOperation.Delete; | 202 | operation = targetRow.Operation = RowOperation.Delete; |
| 212 | targetRow.SectionId += sectionDelimiter; | 203 | targetRow.SectionId += SectionDelimiter; |
| 213 | comparedRow = targetRow; | 204 | comparedRow = targetRow; |
| 214 | keepRow = true; | 205 | keepRow = true; |
| 215 | } | 206 | } |
| @@ -222,7 +213,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 222 | // ignore rows that shouldn't be in a transform | 213 | // ignore rows that shouldn't be in a transform |
| 223 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) | 214 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) |
| 224 | { | 215 | { |
| 225 | updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | 216 | updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; |
| 226 | comparedRow = updatedRow; | 217 | comparedRow = updatedRow; |
| 227 | keepRow = true; | 218 | keepRow = true; |
| 228 | operation = RowOperation.Modify; | 219 | operation = RowOperation.Modify; |
| @@ -306,7 +297,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 306 | if (keepRow) | 297 | if (keepRow) |
| 307 | { | 298 | { |
| 308 | comparedRow = updatedRow; | 299 | comparedRow = updatedRow; |
| 309 | comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | 300 | comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; |
| 310 | } | 301 | } |
| 311 | } | 302 | } |
| 312 | } | 303 | } |
| @@ -350,8 +341,9 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 350 | // diff the target and updated rows | 341 | // diff the target and updated rows |
| 351 | foreach (var targetPrimaryKeyEntry in targetPrimaryKeys) | 342 | foreach (var targetPrimaryKeyEntry in targetPrimaryKeys) |
| 352 | { | 343 | { |
| 353 | var targetPrimaryKey = targetPrimaryKeyEntry.Key; | 344 | updatedPrimaryKeys.TryGetValue(targetPrimaryKeyEntry.Key, out var updatedRow); |
| 354 | var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedPrimaryKeys[targetPrimaryKey], out var _, out var keepRow); | 345 | |
| 346 | var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedRow, out var _, out var keepRow); | ||
| 355 | 347 | ||
| 356 | if (keepRow) | 348 | if (keepRow) |
| 357 | { | 349 | { |
| @@ -369,7 +361,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 369 | var updatedRow = (Row)updatedPrimaryKeyEntry.Value; | 361 | var updatedRow = (Row)updatedPrimaryKeyEntry.Value; |
| 370 | 362 | ||
| 371 | updatedRow.Operation = RowOperation.Add; | 363 | updatedRow.Operation = RowOperation.Add; |
| 372 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | 364 | updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; |
| 373 | rows.Add(updatedRow); | 365 | rows.Add(updatedRow); |
| 374 | } | 366 | } |
| 375 | } | 367 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/FileFacade.cs b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/FileFacade.cs new file mode 100644 index 00000000..eede6e24 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/FileFacade.cs | |||
| @@ -0,0 +1,82 @@ | |||
| 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.ExtensibilityServices | ||
| 4 | { | ||
| 5 | using System.Collections.Generic; | ||
| 6 | using WixToolset.Data; | ||
| 7 | using WixToolset.Data.Symbols; | ||
| 8 | using WixToolset.Data.WindowsInstaller; | ||
| 9 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 10 | using WixToolset.Extensibility.Data; | ||
| 11 | |||
| 12 | internal class FileFacade : IFileFacade | ||
| 13 | { | ||
| 14 | public FileFacade(FileSymbol file) | ||
| 15 | { | ||
| 16 | this.Identifier = file.Id; | ||
| 17 | this.ComponentRef = file.ComponentRef; | ||
| 18 | this.DiskId = file.DiskId ?? 1; | ||
| 19 | this.FileName = file.Name; | ||
| 20 | this.FileSize = file.FileSize; | ||
| 21 | this.Language = file.Language; | ||
| 22 | this.PatchGroup = file.PatchGroup; | ||
| 23 | this.Sequence = file.Sequence; | ||
| 24 | this.SourceLineNumber = file.SourceLineNumbers; | ||
| 25 | this.SourcePath = file.Source?.Path; | ||
| 26 | this.Compressed = (file.Attributes & FileSymbolAttributes.Compressed) == FileSymbolAttributes.Compressed; | ||
| 27 | this.Uncompressed = (file.Attributes & FileSymbolAttributes.Uncompressed) == FileSymbolAttributes.Uncompressed; | ||
| 28 | this.Version = file.Version; | ||
| 29 | this.AssemblyNameSymbols = new List<MsiAssemblyNameSymbol>(); | ||
| 30 | } | ||
| 31 | |||
| 32 | public FileFacade(FileRow row) | ||
| 33 | { | ||
| 34 | this.Identifier = new Identifier(AccessModifier.Section, row.File); | ||
| 35 | this.ComponentRef = row.Component; | ||
| 36 | this.DiskId = row.DiskId; | ||
| 37 | this.FileName = row.FileName; | ||
| 38 | this.FileSize = row.FileSize; | ||
| 39 | this.Language = row.Language; | ||
| 40 | this.PatchGroup = null; | ||
| 41 | this.Sequence = row.Sequence; | ||
| 42 | this.SourceLineNumber = row.SourceLineNumbers; | ||
| 43 | this.SourcePath = row.Source; | ||
| 44 | this.Compressed = (row.Attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed; | ||
| 45 | this.Uncompressed = (row.Attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
| 46 | this.Version = row.Version; | ||
| 47 | this.AssemblyNameSymbols = new List<MsiAssemblyNameSymbol>(); | ||
| 48 | } | ||
| 49 | |||
| 50 | public string Id => this.Identifier.Id; | ||
| 51 | |||
| 52 | public Identifier Identifier { get; } | ||
| 53 | |||
| 54 | public string ComponentRef { get; } | ||
| 55 | |||
| 56 | public int DiskId { get; set; } | ||
| 57 | |||
| 58 | public string FileName { get; } | ||
| 59 | |||
| 60 | public int FileSize { get; set; } | ||
| 61 | |||
| 62 | public string Language { get; set; } | ||
| 63 | |||
| 64 | public int? PatchGroup { get; } | ||
| 65 | |||
| 66 | public int Sequence { get; set; } | ||
| 67 | |||
| 68 | public SourceLineNumber SourceLineNumber { get; } | ||
| 69 | |||
| 70 | public string SourcePath { get; } | ||
| 71 | |||
| 72 | public bool Compressed { get; } | ||
| 73 | |||
| 74 | public bool Uncompressed { get; } | ||
| 75 | |||
| 76 | public string Version { get; set; } | ||
| 77 | |||
| 78 | public MsiFileHashSymbol MsiFileHashSymbol { get; set; } | ||
| 79 | |||
| 80 | public ICollection<MsiAssemblyNameSymbol> AssemblyNameSymbols { get; } | ||
| 81 | } | ||
| 82 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs index ad738321..f372af82 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs | |||
| @@ -23,19 +23,14 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices | |||
| 23 | 23 | ||
| 24 | #region IBackendHelper interfaces | 24 | #region IBackendHelper interfaces |
| 25 | 25 | ||
| 26 | public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly) | 26 | public IFileFacade CreateFileFacade(FileSymbol file) |
| 27 | { | 27 | { |
| 28 | return this.backendHelper.CreateFileFacade(file, assembly); | 28 | return new FileFacade(file); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | public IFileFacade CreateFileFacade(FileRow fileRow) | 31 | public IFileFacade CreateFileFacade(FileRow fileRow) |
| 32 | { | 32 | { |
| 33 | return this.backendHelper.CreateFileFacade(fileRow); | 33 | return new FileFacade(fileRow); |
| 34 | } | ||
| 35 | |||
| 36 | public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol) | ||
| 37 | { | ||
| 38 | return this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol); | ||
| 39 | } | 34 | } |
| 40 | 35 | ||
| 41 | public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) | 36 | public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null) |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs index f73791aa..27bb282b 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs | |||
| @@ -2,11 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | ||
| 6 | using WixToolset.Core.WindowsInstaller.Bind; | 5 | using WixToolset.Core.WindowsInstaller.Bind; |
| 7 | using WixToolset.Core.WindowsInstaller.Decompile; | ||
| 8 | using WixToolset.Core.WindowsInstaller.Unbind; | ||
| 9 | using WixToolset.Data; | ||
| 10 | using WixToolset.Extensibility; | 6 | using WixToolset.Extensibility; |
| 11 | using WixToolset.Extensibility.Data; | 7 | using WixToolset.Extensibility.Data; |
| 12 | using WixToolset.Extensibility.Services; | 8 | using WixToolset.Extensibility.Services; |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs index 38a4ab34..bccdd3d4 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | 5 | using System.Collections.Generic; |
| 7 | using WixToolset.Core.WindowsInstaller.Bind; | 6 | using WixToolset.Core.WindowsInstaller.Bind; |
| 8 | using WixToolset.Data.WindowsInstaller; | 7 | using WixToolset.Data.WindowsInstaller; |
| @@ -18,8 +17,14 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 18 | 17 | ||
| 19 | var backendHelper = context.ServiceProvider.GetService<IBackendHelper>(); | 18 | var backendHelper = context.ServiceProvider.GetService<IBackendHelper>(); |
| 20 | 19 | ||
| 20 | var pathResolver = context.ServiceProvider.GetService<IPathResolver>(); | ||
| 21 | |||
| 22 | var fileResolver = context.ServiceProvider.GetService<IFileResolver>(); | ||
| 23 | |||
| 21 | var extensionManager = context.ServiceProvider.GetService<IExtensionManager>(); | 24 | var extensionManager = context.ServiceProvider.GetService<IExtensionManager>(); |
| 22 | 25 | ||
| 26 | var resolveExtensions = extensionManager.GetServices<IResolverExtension>(); | ||
| 27 | |||
| 23 | var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>(); | 28 | var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>(); |
| 24 | 29 | ||
| 25 | foreach (var extension in backendExtensions) | 30 | foreach (var extension in backendExtensions) |
| @@ -30,7 +35,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 30 | // Create transforms named in patch transforms. | 35 | // Create transforms named in patch transforms. |
| 31 | IEnumerable<PatchTransform> patchTransforms; | 36 | IEnumerable<PatchTransform> patchTransforms; |
| 32 | { | 37 | { |
| 33 | var command = new CreatePatchTransformsCommand(messaging, backendHelper, context.IntermediateRepresentation, context.IntermediateFolder); | 38 | var command = new CreatePatchTransformsCommand(messaging, backendHelper, pathResolver, fileResolver, resolveExtensions, context.IntermediateRepresentation, context.IntermediateFolder, context.BindPaths); |
| 34 | patchTransforms = command.Execute(); | 39 | patchTransforms = command.Execute(); |
| 35 | } | 40 | } |
| 36 | 41 | ||
| @@ -42,7 +47,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | // Create WindowsInstallerData with patch metdata and transforms as sub-storages | 49 | // Create WindowsInstallerData with patch metdata and transforms as sub-storages |
| 45 | // Create MSP from WindowsInstallerData | 50 | // and create MSP from that WindowsInstallerData. |
| 46 | IBindResult result = null; | 51 | IBindResult result = null; |
| 47 | try | 52 | try |
| 48 | { | 53 | { |
| @@ -62,90 +67,5 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 62 | throw; | 67 | throw; |
| 63 | } | 68 | } |
| 64 | } | 69 | } |
| 65 | |||
| 66 | #if TODO_PATCHING | ||
| 67 | public Intermediate Unbind(IUnbindContext context) | ||
| 68 | { | ||
| 69 | Output patch; | ||
| 70 | |||
| 71 | // patch files are essentially database files (use a special flag to let the API know its a patch file) | ||
| 72 | try | ||
| 73 | { | ||
| 74 | using (Database database = new Database(context.InputFilePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile)) | ||
| 75 | { | ||
| 76 | var unbindCommand = new UnbindDatabaseCommand(context.Messaging, database, context.InputFilePath, OutputType.Patch, context.ExportBasePath, context.IntermediateFolder, context.IsAdminImage, context.SuppressDemodularization, skipSummaryInfo: false); | ||
| 77 | patch = unbindCommand.Execute(); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | catch (Win32Exception e) | ||
| 81 | { | ||
| 82 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED | ||
| 83 | { | ||
| 84 | throw new WixException(WixErrors.OpenDatabaseFailed(context.InputFilePath)); | ||
| 85 | } | ||
| 86 | |||
| 87 | throw; | ||
| 88 | } | ||
| 89 | |||
| 90 | // retrieve the transforms (they are in substorages) | ||
| 91 | using (Storage storage = Storage.Open(context.InputFilePath, StorageMode.Read | StorageMode.ShareDenyWrite)) | ||
| 92 | { | ||
| 93 | Table summaryInformationTable = patch.Tables["_SummaryInformation"]; | ||
| 94 | foreach (Row row in summaryInformationTable.Rows) | ||
| 95 | { | ||
| 96 | if (8 == (int)row[0]) // PID_LASTAUTHOR | ||
| 97 | { | ||
| 98 | string value = (string)row[1]; | ||
| 99 | |||
| 100 | foreach (string decoratedSubStorageName in value.Split(';')) | ||
| 101 | { | ||
| 102 | string subStorageName = decoratedSubStorageName.Substring(1); | ||
| 103 | string transformFile = Path.Combine(context.IntermediateFolder, String.Concat("Transform", Path.DirectorySeparatorChar, subStorageName, ".mst")); | ||
| 104 | |||
| 105 | // ensure the parent directory exists | ||
| 106 | Directory.CreateDirectory(Path.GetDirectoryName(transformFile)); | ||
| 107 | |||
| 108 | // copy the substorage to a new storage for the transform file | ||
| 109 | using (Storage subStorage = storage.OpenStorage(subStorageName)) | ||
| 110 | { | ||
| 111 | using (Storage transformStorage = Storage.CreateDocFile(transformFile, StorageMode.ReadWrite | StorageMode.ShareExclusive | StorageMode.Create)) | ||
| 112 | { | ||
| 113 | subStorage.CopyTo(transformStorage); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | // unbind the transform | ||
| 118 | var unbindCommand= new UnbindTransformCommand(context.Messaging, transformFile, (null == context.ExportBasePath ? null : Path.Combine(context.ExportBasePath, subStorageName)), context.IntermediateFolder); | ||
| 119 | var transform = unbindCommand.Execute(); | ||
| 120 | |||
| 121 | patch.SubStorages.Add(new SubStorage(subStorageName, transform)); | ||
| 122 | } | ||
| 123 | |||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | // extract the files from the cabinets | ||
| 130 | // TODO: use per-transform export paths for support of multi-product patches | ||
| 131 | if (null != context.ExportBasePath && !context.SuppressExtractCabinets) | ||
| 132 | { | ||
| 133 | using (Database database = new Database(context.InputFilePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile)) | ||
| 134 | { | ||
| 135 | foreach (SubStorage subStorage in patch.SubStorages) | ||
| 136 | { | ||
| 137 | // only patch transforms should carry files | ||
| 138 | if (subStorage.Name.StartsWith("#", StringComparison.Ordinal)) | ||
| 139 | { | ||
| 140 | var extractCommand = new ExtractCabinetsCommand(subStorage.Data, database, context.InputFilePath, context.ExportBasePath, context.IntermediateFolder); | ||
| 141 | extractCommand.Execute(); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | return patch; | ||
| 148 | } | ||
| 149 | #endif | ||
| 150 | } | 70 | } |
| 151 | } | 71 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs index 8f52bed9..9aa6f3d4 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | namespace WixToolset.Core.WindowsInstaller.Unbind | 3 | namespace WixToolset.Core.WindowsInstaller.Unbind |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections; | ||
| 7 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 8 | using System.Globalization; | 7 | using System.Globalization; |
| 9 | using System.IO; | 8 | using System.IO; |
| @@ -26,7 +25,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 26 | this.TreatOutputAsModule = treatOutputAsModule; | 25 | this.TreatOutputAsModule = treatOutputAsModule; |
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | public string[] ExtractedFiles { get; private set; } | 28 | public Dictionary<string, MediaRow> ExtractedFileIdsWithMediaRow { get; private set; } |
| 30 | 29 | ||
| 31 | private WindowsInstallerData Output { get; } | 30 | private WindowsInstallerData Output { get; } |
| 32 | 31 | ||
| @@ -42,47 +41,51 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 42 | 41 | ||
| 43 | public void Execute() | 42 | public void Execute() |
| 44 | { | 43 | { |
| 44 | var extractedFileIdsWithMediaRow = new Dictionary<string, MediaRow>(); | ||
| 45 | var databaseBasePath = Path.GetDirectoryName(this.InputFilePath); | 45 | var databaseBasePath = Path.GetDirectoryName(this.InputFilePath); |
| 46 | var cabinetFiles = new List<string>(); | 46 | |
| 47 | var embeddedCabinets = new SortedList(); | 47 | var cabinetPathsWithMediaRow = new Dictionary<string, MediaRow>(); |
| 48 | var embeddedCabinetNamesByDiskId = new SortedDictionary<int, string>(); | ||
| 49 | var embeddedCabinetRowsByDiskId = new SortedDictionary<int, MediaRow>(); | ||
| 48 | 50 | ||
| 49 | // index all of the cabinet files | 51 | // index all of the cabinet files |
| 50 | if (OutputType.Module == this.Output.Type || this.TreatOutputAsModule) | 52 | if (OutputType.Module == this.Output.Type || this.TreatOutputAsModule) |
| 51 | { | 53 | { |
| 52 | embeddedCabinets.Add(0, "MergeModule.CABinet"); | 54 | embeddedCabinetNamesByDiskId.Add(0, "MergeModule.CABinet"); |
| 53 | } | 55 | } |
| 54 | else if (null != this.Output.Tables["Media"]) | 56 | else if (this.Output.Tables.TryGetTable("Media", out var mediaTable)) |
| 55 | { | 57 | { |
| 56 | foreach (MediaRow mediaRow in this.Output.Tables["Media"].Rows) | 58 | foreach (var mediaRow in mediaTable.Rows.Cast<MediaRow>().Where(r => !String.IsNullOrEmpty(r.Cabinet))) |
| 57 | { | 59 | { |
| 58 | if (null != mediaRow.Cabinet) | 60 | if (OutputType.Product == this.Output.Type || |
| 61 | (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation)) | ||
| 59 | { | 62 | { |
| 60 | if (OutputType.Product == this.Output.Type || | 63 | if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) |
| 61 | (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation)) | ||
| 62 | { | 64 | { |
| 63 | if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) | 65 | embeddedCabinetNamesByDiskId.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1)); |
| 64 | { | 66 | embeddedCabinetRowsByDiskId.Add(mediaRow.DiskId, mediaRow); |
| 65 | embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1)); | 67 | } |
| 66 | } | 68 | else |
| 67 | else | 69 | { |
| 68 | { | 70 | cabinetPathsWithMediaRow.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet), mediaRow); |
| 69 | cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet)); | ||
| 70 | } | ||
| 71 | } | 71 | } |
| 72 | } | 72 | } |
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | // extract the embedded cabinet files from the database | 76 | // Extract any embedded cabinet files from the database. |
| 77 | if (0 < embeddedCabinets.Count) | 77 | if (0 < embeddedCabinetRowsByDiskId.Count) |
| 78 | { | 78 | { |
| 79 | using (var streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?")) | 79 | using (var streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?")) |
| 80 | { | 80 | { |
| 81 | foreach (int diskId in embeddedCabinets.Keys) | 81 | foreach (var diskIdWithCabinetName in embeddedCabinetNamesByDiskId) |
| 82 | { | 82 | { |
| 83 | var diskId = diskIdWithCabinetName.Key; | ||
| 84 | var cabinetName = diskIdWithCabinetName.Value; | ||
| 85 | |||
| 83 | using (var record = new Record(1)) | 86 | using (var record = new Record(1)) |
| 84 | { | 87 | { |
| 85 | record.SetString(1, (string)embeddedCabinets[diskId]); | 88 | record.SetString(1, cabinetName); |
| 86 | streamsView.Execute(record); | 89 | streamsView.Execute(record); |
| 87 | } | 90 | } |
| 88 | 91 | ||
| @@ -92,15 +95,15 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 92 | { | 95 | { |
| 93 | // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive, | 96 | // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive, |
| 94 | // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work | 97 | // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work |
| 95 | var cabinetFile = Path.Combine(this.IntermediateFolder, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab")); | 98 | var cabinetPath = Path.Combine(this.IntermediateFolder, "Media", diskId.ToString(CultureInfo.InvariantCulture), ".cab"); |
| 96 | 99 | ||
| 97 | // ensure the parent directory exists | 100 | // ensure the parent directory exists |
| 98 | Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile)); | 101 | Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath)); |
| 99 | 102 | ||
| 100 | using (var fs = File.Create(cabinetFile)) | 103 | using (var fs = File.Create(cabinetPath)) |
| 101 | { | 104 | { |
| 102 | int bytesRead; | 105 | int bytesRead; |
| 103 | var buffer = new byte[512]; | 106 | var buffer = new byte[4096]; |
| 104 | 107 | ||
| 105 | while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length))) | 108 | while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length))) |
| 106 | { | 109 | { |
| @@ -108,7 +111,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 108 | } | 111 | } |
| 109 | } | 112 | } |
| 110 | 113 | ||
| 111 | cabinetFiles.Add(cabinetFile); | 114 | embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow); |
| 115 | cabinetPathsWithMediaRow.Add(cabinetPath, cabinetMediaRow); | ||
| 112 | } | 116 | } |
| 113 | else | 117 | else |
| 114 | { | 118 | { |
| @@ -119,29 +123,34 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 119 | } | 123 | } |
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | // extract the cabinet files | 126 | // Extract files from any available cabinets. |
| 123 | if (0 < cabinetFiles.Count) | 127 | if (0 < cabinetPathsWithMediaRow.Count) |
| 124 | { | 128 | { |
| 125 | // ensure the directory exists or extraction will fail | ||
| 126 | Directory.CreateDirectory(this.ExportBasePath); | 129 | Directory.CreateDirectory(this.ExportBasePath); |
| 127 | 130 | ||
| 128 | foreach (var cabinetFile in cabinetFiles) | 131 | foreach (var cabinetPathWithMediaRow in cabinetPathsWithMediaRow) |
| 129 | { | 132 | { |
| 133 | var cabinetPath = cabinetPathWithMediaRow.Key; | ||
| 134 | var cabinetMediaRow = cabinetPathWithMediaRow.Value; | ||
| 135 | |||
| 130 | try | 136 | try |
| 131 | { | 137 | { |
| 132 | var cabinet = new Cabinet(cabinetFile); | 138 | var cabinet = new Cabinet(cabinetPath); |
| 133 | this.ExtractedFiles = cabinet.Extract(this.ExportBasePath).ToArray(); | 139 | var cabinetFilesExtracted = cabinet.Extract(this.ExportBasePath); |
| 140 | |||
| 141 | foreach (var extractedFile in cabinetFilesExtracted) | ||
| 142 | { | ||
| 143 | extractedFileIdsWithMediaRow.Add(extractedFile, cabinetMediaRow); | ||
| 144 | } | ||
| 134 | } | 145 | } |
| 135 | catch (FileNotFoundException) | 146 | catch (FileNotFoundException) |
| 136 | { | 147 | { |
| 137 | throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetFile)); | 148 | throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetPath)); |
| 138 | } | 149 | } |
| 139 | } | 150 | } |
| 140 | } | 151 | } |
| 141 | else | 152 | |
| 142 | { | 153 | this.ExtractedFileIdsWithMediaRow = extractedFileIdsWithMediaRow; |
| 143 | this.ExtractedFiles = new string[0]; | ||
| 144 | } | ||
| 145 | } | 154 | } |
| 146 | } | 155 | } |
| 147 | } | 156 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs index c1fb7f12..7bbbbd76 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs | |||
| @@ -5,29 +5,35 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections; | 6 | using System.Collections; |
| 7 | using System.Collections.Generic; | 7 | using System.Collections.Generic; |
| 8 | using System.ComponentModel; | ||
| 8 | using System.Globalization; | 9 | using System.Globalization; |
| 9 | using System.IO; | 10 | using System.IO; |
| 11 | using System.Linq; | ||
| 10 | using System.Text.RegularExpressions; | 12 | using System.Text.RegularExpressions; |
| 11 | using WixToolset.Core.Native.Msi; | 13 | using WixToolset.Core.Native.Msi; |
| 12 | using WixToolset.Data; | 14 | using WixToolset.Data; |
| 13 | using WixToolset.Data.WindowsInstaller; | 15 | using WixToolset.Data.WindowsInstaller; |
| 16 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 17 | using WixToolset.Extensibility.Data; | ||
| 14 | using WixToolset.Extensibility.Services; | 18 | using WixToolset.Extensibility.Services; |
| 15 | 19 | ||
| 16 | internal class UnbindDatabaseCommand | 20 | internal class UnbindDatabaseCommand |
| 17 | { | 21 | { |
| 18 | private List<string> exportedFiles; | 22 | private static readonly Regex Modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); |
| 19 | 23 | ||
| 20 | public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, Database database, string databasePath, OutputType outputType, string exportBasePath, string intermediateFolder, bool isAdminImage, bool suppressDemodularization, bool skipSummaryInfo) | 24 | private int sectionCount; |
| 25 | |||
| 26 | public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, string databasePath, OutputType outputType, string exportBasePath, string extractFilesFolder, string intermediateFolder, bool enableDemodularization, bool skipSummaryInfo) | ||
| 21 | { | 27 | { |
| 22 | this.Messaging = messaging; | 28 | this.Messaging = messaging; |
| 23 | this.BackendHelper = backendHelper; | 29 | this.BackendHelper = backendHelper; |
| 24 | this.Database = database; | 30 | this.PathResolver = pathResolver; |
| 25 | this.DatabasePath = databasePath; | 31 | this.DatabasePath = databasePath; |
| 26 | this.OutputType = outputType; | 32 | this.OutputType = outputType; |
| 27 | this.ExportBasePath = exportBasePath; | 33 | this.ExportBasePath = exportBasePath; |
| 34 | this.ExtractFilesFolder = extractFilesFolder; | ||
| 28 | this.IntermediateFolder = intermediateFolder; | 35 | this.IntermediateFolder = intermediateFolder; |
| 29 | this.IsAdminImage = isAdminImage; | 36 | this.EnableDemodularization = enableDemodularization; |
| 30 | this.SuppressDemodularization = suppressDemodularization; | ||
| 31 | this.SkipSummaryInfo = skipSummaryInfo; | 37 | this.SkipSummaryInfo = skipSummaryInfo; |
| 32 | 38 | ||
| 33 | this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); | 39 | this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); |
| @@ -37,7 +43,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 37 | 43 | ||
| 38 | public IBackendHelper BackendHelper { get; } | 44 | public IBackendHelper BackendHelper { get; } |
| 39 | 45 | ||
| 40 | public Database Database { get; } | 46 | private IPathResolver PathResolver { get; } |
| 47 | |||
| 48 | private Database Database { get; set; } | ||
| 41 | 49 | ||
| 42 | public string DatabasePath { get; } | 50 | public string DatabasePath { get; } |
| 43 | 51 | ||
| @@ -45,73 +53,91 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 45 | 53 | ||
| 46 | public string ExportBasePath { get; } | 54 | public string ExportBasePath { get; } |
| 47 | 55 | ||
| 48 | public string IntermediateFolder { get; } | 56 | public string ExtractFilesFolder { get; } |
| 49 | 57 | ||
| 50 | public bool IsAdminImage { get; } | 58 | public string IntermediateFolder { get; } |
| 51 | 59 | ||
| 52 | public bool SuppressDemodularization { get; } | 60 | public bool EnableDemodularization { get; } |
| 53 | 61 | ||
| 54 | public bool SkipSummaryInfo { get; } | 62 | public bool SkipSummaryInfo { get; } |
| 55 | 63 | ||
| 56 | public TableDefinitionCollection TableDefinitions { get; } | 64 | public TableDefinitionCollection TableDefinitions { get; } |
| 57 | 65 | ||
| 58 | public IEnumerable<string> ExportedFiles => this.exportedFiles; | 66 | public bool AdminImage { get; private set; } |
| 59 | 67 | ||
| 60 | private int SectionCount { get; set; } | 68 | public IEnumerable<string> ExportedFiles { get; private set; } |
| 61 | 69 | ||
| 62 | public WindowsInstallerData Execute() | 70 | public WindowsInstallerData Execute() |
| 63 | { | 71 | { |
| 64 | this.exportedFiles = new List<string>(); | 72 | var adminImage = false; |
| 73 | var exportedFiles = new List<string>(); | ||
| 65 | 74 | ||
| 66 | string modularizationGuid = null; | 75 | var output = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath)) |
| 67 | var output = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath)); | 76 | { |
| 68 | View validationView = null; | 77 | Type = this.OutputType |
| 78 | }; | ||
| 79 | |||
| 80 | try | ||
| 81 | { | ||
| 82 | using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) | ||
| 83 | { | ||
| 84 | this.Database = database; | ||
| 69 | 85 | ||
| 70 | // set the output type | 86 | Directory.CreateDirectory(this.IntermediateFolder); |
| 71 | output.Type = this.OutputType; | ||
| 72 | 87 | ||
| 73 | Directory.CreateDirectory(this.IntermediateFolder); | 88 | output.Codepage = this.GetCodePage(); |
| 74 | 89 | ||
| 75 | // get the codepage | 90 | var modularizationGuid = this.ProcessTables(output, exportedFiles); |
| 76 | this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt"); | ||
| 77 | using (var sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt"))) | ||
| 78 | { | ||
| 79 | string line; | ||
| 80 | 91 | ||
| 81 | while (null != (line = sr.ReadLine())) | 92 | var summaryInfo = this.ProcessSummaryInfo(output, modularizationGuid); |
| 82 | { | ||
| 83 | var data = line.Split('\t'); | ||
| 84 | 93 | ||
| 85 | if (2 == data.Length) | 94 | this.UpdateUnrealFileColumns(this.DatabasePath, output, summaryInfo, exportedFiles); |
| 86 | { | 95 | |
| 87 | output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture); | 96 | this.GenerateSectionIds(output); |
| 88 | } | ||
| 89 | } | 97 | } |
| 90 | } | 98 | } |
| 91 | 99 | catch (Win32Exception e) | |
| 92 | // get the summary information table if it exists; it won't if unbinding a transform | ||
| 93 | if (!this.SkipSummaryInfo) | ||
| 94 | { | 100 | { |
| 95 | using (var summaryInformation = new SummaryInformation(this.Database)) | 101 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED |
| 96 | { | 102 | { |
| 97 | var table = new Table(this.TableDefinitions["_SummaryInformation"]); | 103 | throw new WixException(ErrorMessages.OpenDatabaseFailed(this.DatabasePath)); |
| 104 | } | ||
| 98 | 105 | ||
| 99 | for (var i = 1; 19 >= i; i++) | 106 | throw; |
| 100 | { | 107 | } |
| 101 | var value = summaryInformation.GetProperty(i); | ||
| 102 | 108 | ||
| 103 | if (0 < value.Length) | 109 | this.AdminImage = adminImage; |
| 104 | { | 110 | this.ExportedFiles = exportedFiles; |
| 105 | var row = table.CreateRow(output.SourceLineNumbers); | ||
| 106 | row[0] = i; | ||
| 107 | row[1] = value; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | 111 | ||
| 111 | output.Tables.Add(table); | 112 | return output; |
| 113 | } | ||
| 114 | |||
| 115 | private int GetCodePage() | ||
| 116 | { | ||
| 117 | var codepage = 0; | ||
| 118 | |||
| 119 | this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt"); | ||
| 120 | |||
| 121 | var lines = File.ReadAllLines(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt")); | ||
| 122 | |||
| 123 | if (lines.Length == 3) | ||
| 124 | { | ||
| 125 | var data = lines[2].Split('\t'); | ||
| 126 | |||
| 127 | if (2 == data.Length) | ||
| 128 | { | ||
| 129 | codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture); | ||
| 112 | } | 130 | } |
| 113 | } | 131 | } |
| 114 | 132 | ||
| 133 | return codepage; | ||
| 134 | } | ||
| 135 | |||
| 136 | private string ProcessTables(WindowsInstallerData output, List<string> exportedFiles) | ||
| 137 | { | ||
| 138 | View validationView = null; | ||
| 139 | string modularizationGuid = null; | ||
| 140 | |||
| 115 | try | 141 | try |
| 116 | { | 142 | { |
| 117 | // open a view on the validation table if it exists | 143 | // open a view on the validation table if it exists |
| @@ -127,7 +153,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 127 | { | 153 | { |
| 128 | var tableName = tableRecord.GetString(1); | 154 | var tableName = tableRecord.GetString(1); |
| 129 | 155 | ||
| 130 | using (var tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) | 156 | using (var tableView = this.Database.OpenExecuteView($"SELECT * FROM `{tableName}`")) |
| 131 | { | 157 | { |
| 132 | var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView); | 158 | var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView); |
| 133 | var table = new Table(tableDefinition); | 159 | var table = new Table(tableDefinition); |
| @@ -154,16 +180,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 154 | switch (row.Fields[i].Column.Type) | 180 | switch (row.Fields[i].Column.Type) |
| 155 | { | 181 | { |
| 156 | case ColumnType.Number: | 182 | case ColumnType.Number: |
| 157 | var success = false; | ||
| 158 | var intValue = rowRecord.GetInteger(i + 1); | 183 | var intValue = rowRecord.GetInteger(i + 1); |
| 159 | if (row.Fields[i].Column.IsLocalizable) | 184 | var success = row.Fields[i].Column.IsLocalizable ? row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture)) : row.BestEffortSetField(i, intValue); |
| 160 | { | ||
| 161 | success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture)); | ||
| 162 | } | ||
| 163 | else | ||
| 164 | { | ||
| 165 | success = row.BestEffortSetField(i, intValue); | ||
| 166 | } | ||
| 167 | 185 | ||
| 168 | if (!success) | 186 | if (!success) |
| 169 | { | 187 | { |
| @@ -171,20 +189,18 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 171 | } | 189 | } |
| 172 | break; | 190 | break; |
| 173 | case ColumnType.Object: | 191 | case ColumnType.Object: |
| 174 | var sourceFile = "FILE NOT EXPORTED"; | 192 | var source = "FILE NOT EXPORTED"; |
| 175 | 193 | ||
| 176 | if (null != this.ExportBasePath) | 194 | if (null != this.ExportBasePath) |
| 177 | { | 195 | { |
| 178 | var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); | 196 | source = Path.Combine(this.ExportBasePath, tableName, row.GetPrimaryKey('.')); |
| 179 | sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile); | ||
| 180 | 197 | ||
| 181 | // ensure the parent directory exists | 198 | Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); |
| 182 | System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); | ||
| 183 | 199 | ||
| 184 | using (var fs = System.IO.File.Create(sourceFile)) | 200 | using (var fs = File.Create(source)) |
| 185 | { | 201 | { |
| 186 | int bytesRead; | 202 | int bytesRead; |
| 187 | var buffer = new byte[512]; | 203 | var buffer = new byte[4096]; |
| 188 | 204 | ||
| 189 | while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) | 205 | while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) |
| 190 | { | 206 | { |
| @@ -192,10 +208,10 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 192 | } | 208 | } |
| 193 | } | 209 | } |
| 194 | 210 | ||
| 195 | this.exportedFiles.Add(sourceFile); | 211 | exportedFiles.Add(source); |
| 196 | } | 212 | } |
| 197 | 213 | ||
| 198 | row[i] = sourceFile; | 214 | row[i] = source; |
| 199 | break; | 215 | break; |
| 200 | default: | 216 | default: |
| 201 | var value = rowRecord.GetString(i + 1); | 217 | var value = rowRecord.GetString(i + 1); |
| @@ -203,27 +219,26 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 203 | switch (row.Fields[i].Column.Category) | 219 | switch (row.Fields[i].Column.Category) |
| 204 | { | 220 | { |
| 205 | case ColumnCategory.Guid: | 221 | case ColumnCategory.Guid: |
| 206 | value = value.ToUpper(CultureInfo.InvariantCulture); | 222 | value = value.ToUpperInvariant(); |
| 207 | break; | 223 | break; |
| 208 | } | 224 | } |
| 209 | 225 | ||
| 210 | // de-modularize | 226 | // De-modularize |
| 211 | if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType) | 227 | if (this.EnableDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType) |
| 212 | { | 228 | { |
| 213 | var modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); | ||
| 214 | |||
| 215 | if (null == modularizationGuid) | 229 | if (null == modularizationGuid) |
| 216 | { | 230 | { |
| 217 | var match = modularization.Match(value); | 231 | var match = Modularization.Match(value); |
| 218 | if (match.Success) | 232 | if (match.Success) |
| 219 | { | 233 | { |
| 220 | modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}'); | 234 | modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}'); |
| 221 | } | 235 | } |
| 222 | } | 236 | } |
| 223 | 237 | ||
| 224 | value = modularization.Replace(value, String.Empty); | 238 | value = Modularization.Replace(value, String.Empty); |
| 225 | } | 239 | } |
| 226 | 240 | ||
| 241 | #if TODO_MOVE_TO_DECOMPILER | ||
| 227 | // escape "$(" for the preprocessor | 242 | // escape "$(" for the preprocessor |
| 228 | value = value.Replace("$(", "$$("); | 243 | value = value.Replace("$(", "$$("); |
| 229 | 244 | ||
| @@ -234,6 +249,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 234 | //{ | 249 | //{ |
| 235 | // value = value.Insert(matches[j].Index, "!"); | 250 | // value = value.Insert(matches[j].Index, "!"); |
| 236 | //} | 251 | //} |
| 252 | #endif | ||
| 237 | 253 | ||
| 238 | row[i] = value; | 254 | row[i] = value; |
| 239 | break; | 255 | break; |
| @@ -249,33 +265,54 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 249 | } | 265 | } |
| 250 | finally | 266 | finally |
| 251 | { | 267 | { |
| 252 | if (null != validationView) | 268 | validationView?.Close(); |
| 253 | { | ||
| 254 | validationView.Close(); | ||
| 255 | } | ||
| 256 | } | 269 | } |
| 257 | 270 | ||
| 258 | // set the modularization guid as the PackageCode | 271 | return modularizationGuid; |
| 259 | if (null != modularizationGuid) | 272 | } |
| 260 | { | ||
| 261 | var table = output.Tables["_SummaryInformation"]; | ||
| 262 | 273 | ||
| 263 | foreach (var row in table.Rows) | 274 | private SummaryInformationBits ProcessSummaryInfo(WindowsInstallerData output, string modularizationGuid) |
| 275 | { | ||
| 276 | var result = new SummaryInformationBits(); | ||
| 277 | |||
| 278 | if (!this.SkipSummaryInfo) | ||
| 279 | { | ||
| 280 | using (var summaryInformation = new SummaryInformation(this.Database)) | ||
| 264 | { | 281 | { |
| 265 | if (9 == (int)row[0]) // PID_REVNUMBER | 282 | var table = new Table(this.TableDefinitions["_SummaryInformation"]); |
| 283 | |||
| 284 | for (var i = 1; 19 >= i; i++) | ||
| 266 | { | 285 | { |
| 267 | row[1] = modularizationGuid; | 286 | var value = summaryInformation.GetProperty(i); |
| 287 | |||
| 288 | // Set the modularization guid as the PackageCode, for merge modules. | ||
| 289 | if (i == (int)SummaryInformation.Package.PackageCode && !String.IsNullOrEmpty(modularizationGuid)) | ||
| 290 | { | ||
| 291 | var row = table.CreateRow(output.SourceLineNumbers); | ||
| 292 | row[0] = i; | ||
| 293 | row[1] = modularizationGuid; | ||
| 294 | } | ||
| 295 | else if (0 < value.Length) | ||
| 296 | { | ||
| 297 | var row = table.CreateRow(output.SourceLineNumbers); | ||
| 298 | row[0] = i; | ||
| 299 | row[1] = value; | ||
| 300 | |||
| 301 | if (i == (int)SummaryInformation.Package.FileAndElevatedFlags) | ||
| 302 | { | ||
| 303 | var wordcount = Convert.ToInt32(value, CultureInfo.InvariantCulture); | ||
| 304 | result.LongFilenames = (wordcount & 0x1) != 0x1; | ||
| 305 | result.Compressed = (wordcount & 0x2) == 0x2; | ||
| 306 | result.AdminImage = (wordcount & 0x4) == 0x4; | ||
| 307 | } | ||
| 308 | } | ||
| 268 | } | 309 | } |
| 269 | } | ||
| 270 | } | ||
| 271 | 310 | ||
| 272 | if (this.IsAdminImage) | 311 | output.Tables.Add(table); |
| 273 | { | 312 | } |
| 274 | this.GenerateWixFileTable(this.DatabasePath, output); | ||
| 275 | this.GenerateSectionIds(output); | ||
| 276 | } | 313 | } |
| 277 | 314 | ||
| 278 | return output; | 315 | return result; |
| 279 | } | 316 | } |
| 280 | 317 | ||
| 281 | private TableDefinition GetTableDefinition(string tableName, View tableView, View validationView) | 318 | private TableDefinition GetTableDefinition(string tableName, View tableView, View validationView) |
| @@ -310,10 +347,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 310 | var columnName = columnNameRecord.GetString(i); | 347 | var columnName = columnNameRecord.GetString(i); |
| 311 | var idtType = columnTypeRecord.GetString(i); | 348 | var idtType = columnTypeRecord.GetString(i); |
| 312 | 349 | ||
| 313 | ColumnType columnType; | ||
| 314 | int length; | ||
| 315 | bool nullable; | ||
| 316 | |||
| 317 | var columnCategory = ColumnCategory.Unknown; | 350 | var columnCategory = ColumnCategory.Unknown; |
| 318 | var columnModularizeType = ColumnModularizeType.None; | 351 | var columnModularizeType = ColumnModularizeType.None; |
| 319 | var primary = tablePrimaryKeys.Contains(columnName); | 352 | var primary = tablePrimaryKeys.Contains(columnName); |
| @@ -326,7 +359,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 326 | string description = null; | 359 | string description = null; |
| 327 | 360 | ||
| 328 | // get the column type, length, and whether its nullable | 361 | // get the column type, length, and whether its nullable |
| 329 | switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture)) | 362 | ColumnType columnType; |
| 363 | switch (Char.ToLowerInvariant(idtType[0])) | ||
| 330 | { | 364 | { |
| 331 | case 'i': | 365 | case 'i': |
| 332 | columnType = ColumnType.Number; | 366 | columnType = ColumnType.Number; |
| @@ -345,8 +379,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 345 | columnType = ColumnType.Unknown; | 379 | columnType = ColumnType.Unknown; |
| 346 | break; | 380 | break; |
| 347 | } | 381 | } |
| 348 | length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture); | 382 | var length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture); |
| 349 | nullable = Char.IsUpper(idtType[0]); | 383 | var nullable = Char.IsUpper(idtType[0]); |
| 350 | 384 | ||
| 351 | // try to get validation information | 385 | // try to get validation information |
| 352 | if (null != validationView) | 386 | if (null != validationView) |
| @@ -385,11 +419,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 385 | // convert category to ColumnCategory | 419 | // convert category to ColumnCategory |
| 386 | if (null != category) | 420 | if (null != category) |
| 387 | { | 421 | { |
| 388 | try | 422 | if (!Enum.TryParse(category, true, out columnCategory)) |
| 389 | { | ||
| 390 | columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true); | ||
| 391 | } | ||
| 392 | catch (ArgumentException) | ||
| 393 | { | 423 | { |
| 394 | columnCategory = ColumnCategory.Unknown; | 424 | columnCategory = ColumnCategory.Unknown; |
| 395 | } | 425 | } |
| @@ -427,67 +457,103 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 427 | return new TableDefinition(tableName, null, columns, false); | 457 | return new TableDefinition(tableName, null, columns, false); |
| 428 | } | 458 | } |
| 429 | 459 | ||
| 430 | /// <summary> | 460 | private void UpdateUnrealFileColumns(string databaseFile, WindowsInstallerData output, SummaryInformationBits summaryInformation, List<string> exportedFiles) |
| 431 | /// Generates the WixFile table based on a path to an admin image msi and an Output. | ||
| 432 | /// </summary> | ||
| 433 | /// <param name="databaseFile">The path to the msi database file in an admin image.</param> | ||
| 434 | /// <param name="output">The Output that represents the msi database.</param> | ||
| 435 | private void GenerateWixFileTable(string databaseFile, WindowsInstallerData output) | ||
| 436 | { | 461 | { |
| 437 | throw new NotImplementedException(); | 462 | var fileRows = output.Tables["File"]?.Rows; |
| 438 | #if TODO_FIX_UNBINDING_FILES | ||
| 439 | var adminRootPath = Path.GetDirectoryName(databaseFile); | ||
| 440 | 463 | ||
| 441 | var componentDirectoryIndex = new Hashtable(); | 464 | if (fileRows == null || fileRows.Count == 0) |
| 442 | var componentTable = output.Tables["Component"]; | ||
| 443 | foreach (var row in componentTable.Rows) | ||
| 444 | { | 465 | { |
| 445 | componentDirectoryIndex.Add(row[0], row[2]); | 466 | return; |
| 446 | } | 467 | } |
| 447 | 468 | ||
| 469 | this.UpdateFileRowsDiskId(output, fileRows); | ||
| 470 | |||
| 471 | this.UpdateFileRowsSource(databaseFile, output, fileRows, summaryInformation, exportedFiles); | ||
| 472 | } | ||
| 473 | |||
| 474 | private void UpdateFileRowsDiskId(WindowsInstallerData output, IList<Row> fileRows) | ||
| 475 | { | ||
| 476 | var mediaRows = output.Tables["Media"]?.Rows?.Cast<MediaRow>()?.OrderBy(r => r.LastSequence)?.ToList(); | ||
| 477 | |||
| 478 | var lastMediaRowIndex = 0; | ||
| 479 | var lastMediaRow = (mediaRows == null || mediaRows.Count == 0) ? null : mediaRows[lastMediaRowIndex]; | ||
| 480 | |||
| 481 | foreach (var fileRow in fileRows.Cast<FileRow>()?.OrderBy(r => r.Sequence)) | ||
| 482 | { | ||
| 483 | while (lastMediaRow != null && fileRow.Sequence > lastMediaRow.LastSequence) | ||
| 484 | { | ||
| 485 | ++lastMediaRowIndex; | ||
| 486 | |||
| 487 | lastMediaRow = lastMediaRowIndex < mediaRows.Count ? mediaRows[lastMediaRowIndex] : null; | ||
| 488 | } | ||
| 489 | |||
| 490 | fileRow.DiskId = lastMediaRow?.DiskId ?? 1; | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | private void UpdateFileRowsSource(string databasePath, WindowsInstallerData output, IList<Row> fileRows, SummaryInformationBits summaryInformation, List<string> exportedFiles) | ||
| 495 | { | ||
| 496 | var databaseFolder = Path.GetDirectoryName(databasePath); | ||
| 497 | |||
| 498 | var componentDirectoryIndex = output.Tables["Component"].Rows.Cast<ComponentRow>().ToDictionary(r => r.Component, r => r.Directory); | ||
| 499 | |||
| 448 | // Index full source paths for all directories | 500 | // Index full source paths for all directories |
| 449 | var directoryDirectoryParentIndex = new Hashtable(); | 501 | var directories = new Dictionary<string, IResolvedDirectory>(); |
| 450 | var directoryFullPathIndex = new Hashtable(); | 502 | |
| 451 | var directorySourceNameIndex = new Hashtable(); | ||
| 452 | var directoryTable = output.Tables["Directory"]; | 503 | var directoryTable = output.Tables["Directory"]; |
| 453 | foreach (var row in directoryTable.Rows) | 504 | foreach (var row in directoryTable.Rows) |
| 454 | { | 505 | { |
| 455 | directoryDirectoryParentIndex.Add(row[0], row[1]); | 506 | var sourceName = this.BackendHelper.GetMsiFileName(row.FieldAsString(2), source: true, longName: summaryInformation.LongFilenames); |
| 456 | if (null == row[1]) | 507 | var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(row.FieldAsString(1), sourceName); |
| 457 | { | 508 | |
| 458 | directoryFullPathIndex.Add(row[0], adminRootPath); | 509 | directories.Add(row.FieldAsString(0), resolvedDirectory); |
| 459 | } | ||
| 460 | else | ||
| 461 | { | ||
| 462 | directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2])); | ||
| 463 | } | ||
| 464 | } | 510 | } |
| 465 | 511 | ||
| 466 | foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex) | 512 | if (summaryInformation.AdminImage) |
| 467 | { | 513 | { |
| 468 | if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key)) | 514 | foreach (var fileRow in fileRows.Cast<FileRow>()) |
| 469 | { | 515 | { |
| 470 | this.GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex); | 516 | var directoryId = componentDirectoryIndex[fileRow.Component]; |
| 517 | var relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, directoryId, fileRow.FileName, compressed: false, useLongName: summaryInformation.LongFilenames); | ||
| 518 | |||
| 519 | fileRow.Source = Path.Combine(databaseFolder, relativeFileLayoutPath); | ||
| 471 | } | 520 | } |
| 472 | } | 521 | } |
| 473 | 522 | else | |
| 474 | var fileTable = output.Tables["File"]; | ||
| 475 | var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]); | ||
| 476 | foreach (var row in fileTable.Rows) | ||
| 477 | { | 523 | { |
| 478 | var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]); | 524 | var extractedFileIds = new HashSet<string>(); |
| 479 | wixFileRow.File = (string)row[0]; | ||
| 480 | wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]]; | ||
| 481 | wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2])); | ||
| 482 | 525 | ||
| 483 | if (!File.Exists(wixFileRow.Source)) | 526 | if (!String.IsNullOrEmpty(this.ExtractFilesFolder)) |
| 484 | { | 527 | { |
| 485 | throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source)); | 528 | var extractCommand = new ExtractCabinetsCommand(output, this.Database, this.DatabasePath, this.ExtractFilesFolder, this.IntermediateFolder); |
| 529 | extractCommand.Execute(); | ||
| 530 | |||
| 531 | extractedFileIds = new HashSet<string>(extractCommand.ExtractedFileIdsWithMediaRow.Keys, StringComparer.OrdinalIgnoreCase); | ||
| 532 | exportedFiles.AddRange(extractedFileIds); | ||
| 486 | } | 533 | } |
| 487 | 534 | ||
| 488 | wixFileTable.Rows.Add(wixFileRow); | 535 | foreach (var fileRow in fileRows.Cast<FileRow>()) |
| 536 | { | ||
| 537 | var source = "FILE NOT EXPORTED"; | ||
| 538 | |||
| 539 | if (fileRow.Compressed == YesNoType.Yes || (fileRow.Compressed == YesNoType.NotSet && summaryInformation.Compressed)) | ||
| 540 | { | ||
| 541 | if (extractedFileIds.Contains(fileRow.File)) | ||
| 542 | { | ||
| 543 | source = Path.Combine(this.ExtractFilesFolder, fileRow.File); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | else | ||
| 547 | { | ||
| 548 | var directoryId = componentDirectoryIndex[fileRow.Component]; | ||
| 549 | var relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, directoryId, fileRow.FileName, compressed: false, useLongName: summaryInformation.LongFilenames); | ||
| 550 | |||
| 551 | source = Path.Combine(databaseFolder, relativeFileLayoutPath); | ||
| 552 | } | ||
| 553 | |||
| 554 | fileRow.Source = source; | ||
| 555 | } | ||
| 489 | } | 556 | } |
| 490 | #endif | ||
| 491 | } | 557 | } |
| 492 | 558 | ||
| 493 | /// <summary> | 559 | /// <summary> |
| @@ -620,7 +686,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 620 | { | 686 | { |
| 621 | switch (table.Name) | 687 | switch (table.Name) |
| 622 | { | 688 | { |
| 623 | case "WixFile": | ||
| 624 | case "MsiFileHash": | 689 | case "MsiFileHash": |
| 625 | ConnectTableToSection(table, fileSectionIdIndex, 0); | 690 | ConnectTableToSection(table, fileSectionIdIndex, 0); |
| 626 | break; | 691 | break; |
| @@ -699,7 +764,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 699 | } | 764 | } |
| 700 | } | 765 | } |
| 701 | 766 | ||
| 702 | // Now pass the output to each unbinder extension to allow them to analyze the output and determine thier proper section ids. | 767 | // Now pass the output to each unbinder extension to allow them to analyze the output and determine their proper section ids. |
| 703 | //foreach (IUnbinderExtension extension in this.unbinderExtensions) | 768 | //foreach (IUnbinderExtension extension in this.unbinderExtensions) |
| 704 | //{ | 769 | //{ |
| 705 | // extension.GenerateSectionIds(output); | 770 | // extension.GenerateSectionIds(output); |
| @@ -711,19 +776,22 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 711 | /// </summary> | 776 | /// </summary> |
| 712 | /// <param name="table">The table to add sections to.</param> | 777 | /// <param name="table">The table to add sections to.</param> |
| 713 | /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param> | 778 | /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param> |
| 714 | /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns> | 779 | /// <returns>A dictionary containing the tables key for each row paired with its assigned section id.</returns> |
| 715 | private Hashtable AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex) | 780 | private Dictionary<string, string> AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex) |
| 716 | { | 781 | { |
| 717 | var hashtable = new Hashtable(); | 782 | var primaryKeyToSectionId = new Dictionary<string, string>(); |
| 783 | |||
| 718 | if (null != table) | 784 | if (null != table) |
| 719 | { | 785 | { |
| 720 | foreach (var row in table.Rows) | 786 | foreach (var row in table.Rows) |
| 721 | { | 787 | { |
| 722 | row.SectionId = this.GetNewSectionId(); | 788 | row.SectionId = this.GetNewSectionId(); |
| 723 | hashtable.Add(row[rowPrimaryKeyIndex], row.SectionId); | 789 | |
| 790 | primaryKeyToSectionId.Add(row.FieldAsString(rowPrimaryKeyIndex), row.SectionId); | ||
| 724 | } | 791 | } |
| 725 | } | 792 | } |
| 726 | return hashtable; | 793 | |
| 794 | return primaryKeyToSectionId; | ||
| 727 | } | 795 | } |
| 728 | 796 | ||
| 729 | /// <summary> | 797 | /// <summary> |
| @@ -732,15 +800,15 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 732 | /// <param name="table">The table containing rows that need to be connected to sections.</param> | 800 | /// <param name="table">The table containing rows that need to be connected to sections.</param> |
| 733 | /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param> | 801 | /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param> |
| 734 | /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param> | 802 | /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param> |
| 735 | private static void ConnectTableToSection(Table table, Hashtable sectionIdIndex, int rowIndex) | 803 | private static void ConnectTableToSection(Table table, Dictionary<string, string> sectionIdIndex, int rowIndex) |
| 736 | { | 804 | { |
| 737 | if (null != table) | 805 | if (null != table) |
| 738 | { | 806 | { |
| 739 | foreach (var row in table.Rows) | 807 | foreach (var row in table.Rows) |
| 740 | { | 808 | { |
| 741 | if (sectionIdIndex.ContainsKey(row[rowIndex])) | 809 | if (sectionIdIndex.TryGetValue(row.FieldAsString(rowIndex), out var sectionId)) |
| 742 | { | 810 | { |
| 743 | row.SectionId = (string)sectionIdIndex[row[rowIndex]]; | 811 | row.SectionId = sectionId; |
| 744 | } | 812 | } |
| 745 | } | 813 | } |
| 746 | } | 814 | } |
| @@ -750,40 +818,53 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 750 | /// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it. | 818 | /// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it. |
| 751 | /// </summary> | 819 | /// </summary> |
| 752 | /// <param name="table">The table containing rows that need to be connected to sections.</param> | 820 | /// <param name="table">The table containing rows that need to be connected to sections.</param> |
| 753 | /// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</param> | 821 | /// <param name="sectionIdIndex">A dictionary containing keys to map table to its section.</param> |
| 754 | /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param> | 822 | /// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</param> |
| 755 | /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param> | 823 | /// <param name="rowPrimaryKeyIndex">The index of the column which is used by other tables to reference this table.</param> |
| 756 | /// <returns>A Hashtable containing the tables key for each row paired with its assigned section id.</returns> | 824 | /// <returns>A dictionary containing the tables key for each row paired with its assigned section id.</returns> |
| 757 | private static Hashtable ConnectTableToSectionAndIndex(Table table, Hashtable sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex) | 825 | private static Dictionary<string, string> ConnectTableToSectionAndIndex(Table table, Dictionary<string, string> sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex) |
| 758 | { | 826 | { |
| 759 | var newHashTable = new Hashtable(); | 827 | var newPrimaryKeyToSectionId = new Dictionary<string, string>(); |
| 828 | |||
| 760 | if (null != table) | 829 | if (null != table) |
| 761 | { | 830 | { |
| 762 | foreach (var row in table.Rows) | 831 | foreach (var row in table.Rows) |
| 763 | { | 832 | { |
| 764 | if (!sectionIdIndex.ContainsKey(row[rowIndex])) | 833 | var foreignKey = row.FieldAsString(rowIndex); |
| 834 | |||
| 835 | if (!sectionIdIndex.TryGetValue(foreignKey, out var sectionId)) | ||
| 765 | { | 836 | { |
| 766 | continue; | 837 | continue; |
| 767 | } | 838 | } |
| 768 | 839 | ||
| 769 | row.SectionId = (string)sectionIdIndex[row[rowIndex]]; | 840 | row.SectionId = sectionId; |
| 770 | if (null != row[rowPrimaryKeyIndex]) | 841 | |
| 842 | var primaryKey = row.FieldAsString(rowPrimaryKeyIndex); | ||
| 843 | |||
| 844 | if (!String.IsNullOrEmpty(primaryKey) && sectionIdIndex.ContainsKey(primaryKey)) | ||
| 771 | { | 845 | { |
| 772 | newHashTable.Add(row[rowPrimaryKeyIndex], row.SectionId); | 846 | newPrimaryKeyToSectionId.Add(primaryKey, row.SectionId); |
| 773 | } | 847 | } |
| 774 | } | 848 | } |
| 775 | } | 849 | } |
| 776 | return newHashTable; | 850 | |
| 851 | return newPrimaryKeyToSectionId; | ||
| 777 | } | 852 | } |
| 778 | 853 | ||
| 779 | /// <summary> | ||
| 780 | /// Creates a new section identifier to be used when adding a section to an output. | ||
| 781 | /// </summary> | ||
| 782 | /// <returns>A string representing a new section id.</returns> | ||
| 783 | private string GetNewSectionId() | 854 | private string GetNewSectionId() |
| 784 | { | 855 | { |
| 785 | this.SectionCount++; | 856 | this.sectionCount++; |
| 786 | return "wix.section." + this.SectionCount.ToString(CultureInfo.InvariantCulture); | 857 | |
| 858 | return "wix.section." + this.sectionCount.ToString(CultureInfo.InvariantCulture); | ||
| 859 | } | ||
| 860 | |||
| 861 | private class SummaryInformationBits | ||
| 862 | { | ||
| 863 | public bool AdminImage { get; set; } | ||
| 864 | |||
| 865 | public bool Compressed { get; set; } | ||
| 866 | |||
| 867 | public bool LongFilenames { get; set; } | ||
| 787 | } | 868 | } |
| 788 | } | 869 | } |
| 789 | } | 870 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs deleted file mode 100644 index 8070d42d..00000000 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs +++ /dev/null | |||
| @@ -1,72 +0,0 @@ | |||
| 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.Unbind | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.ComponentModel; | ||
| 7 | using WixToolset.Core.Native.Msi; | ||
| 8 | using WixToolset.Data; | ||
| 9 | using WixToolset.Data.WindowsInstaller; | ||
| 10 | using WixToolset.Extensibility.Services; | ||
| 11 | |||
| 12 | internal class UnbindMsiOrMsmCommand | ||
| 13 | { | ||
| 14 | public UnbindMsiOrMsmCommand(IMessaging messaging, IBackendHelper backendHelper, string databasePath, string exportBasePath, string intermediateFolder, bool adminImage, bool suppressDemodularization, bool suppressExtractCabinets) | ||
| 15 | { | ||
| 16 | this.Messaging = messaging; | ||
| 17 | this.BackendHelper = backendHelper; | ||
| 18 | this.DatabasePath = databasePath; | ||
| 19 | this.ExportBasePath = exportBasePath; | ||
| 20 | this.IntermediateFolder = intermediateFolder; | ||
| 21 | this.IsAdminImage = adminImage; | ||
| 22 | this.SuppressDemodularization = suppressDemodularization; | ||
| 23 | this.SuppressExtractCabinets = suppressExtractCabinets; | ||
| 24 | } | ||
| 25 | |||
| 26 | private IMessaging Messaging { get; } | ||
| 27 | |||
| 28 | private IBackendHelper BackendHelper { get; } | ||
| 29 | |||
| 30 | private string DatabasePath { get; } | ||
| 31 | |||
| 32 | private string ExportBasePath { get; } | ||
| 33 | |||
| 34 | private string IntermediateFolder { get; } | ||
| 35 | |||
| 36 | private bool IsAdminImage { get; } | ||
| 37 | |||
| 38 | private bool SuppressDemodularization { get; } | ||
| 39 | |||
| 40 | private bool SuppressExtractCabinets { get; } | ||
| 41 | |||
| 42 | public WindowsInstallerData Execute() | ||
| 43 | { | ||
| 44 | try | ||
| 45 | { | ||
| 46 | using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) | ||
| 47 | { | ||
| 48 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, this.DatabasePath, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, this.IsAdminImage, this.SuppressDemodularization, skipSummaryInfo: false); | ||
| 49 | var data = unbindCommand.Execute(); | ||
| 50 | |||
| 51 | // extract the files from the cabinets | ||
| 52 | if (!String.IsNullOrEmpty(this.ExportBasePath) && !this.SuppressExtractCabinets) | ||
| 53 | { | ||
| 54 | var extractCommand = new ExtractCabinetsCommand(data, database, this.DatabasePath, this.ExportBasePath, this.IntermediateFolder); | ||
| 55 | extractCommand.Execute(); | ||
| 56 | } | ||
| 57 | |||
| 58 | return data; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | catch (Win32Exception e) | ||
| 62 | { | ||
| 63 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED | ||
| 64 | { | ||
| 65 | //throw new WixException(WixErrors.OpenDatabaseFailed(this.DatabasePath)); | ||
| 66 | } | ||
| 67 | |||
| 68 | throw; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs index 01ff1a80..bce60e40 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs | |||
| @@ -1,5 +1,7 @@ | |||
| 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. | 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 | 2 | ||
| 3 | #if TODO_KEEP_FOR_PATCH_UNBINDING_CONSIDERATION_IN_FUTURE | ||
| 4 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller.Unbind | 5 | namespace WixToolset.Core.WindowsInstaller.Unbind |
| 4 | { | 6 | { |
| 5 | using System; | 7 | using System; |
| @@ -17,10 +19,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 17 | 19 | ||
| 18 | internal class UnbindTransformCommand | 20 | internal class UnbindTransformCommand |
| 19 | { | 21 | { |
| 20 | public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, string transformFile, string exportBasePath, string intermediateFolder) | 22 | public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, string transformFile, string exportBasePath, string intermediateFolder) |
| 21 | { | 23 | { |
| 22 | this.Messaging = messaging; | 24 | this.Messaging = messaging; |
| 23 | this.BackendHelper = backendHelper; | 25 | this.BackendHelper = backendHelper; |
| 26 | this.FileSystemManager = fileSystemManager; | ||
| 24 | this.TransformFile = transformFile; | 27 | this.TransformFile = transformFile; |
| 25 | this.ExportBasePath = exportBasePath; | 28 | this.ExportBasePath = exportBasePath; |
| 26 | this.IntermediateFolder = intermediateFolder; | 29 | this.IntermediateFolder = intermediateFolder; |
| @@ -32,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 32 | 35 | ||
| 33 | private IBackendHelper BackendHelper { get; } | 36 | private IBackendHelper BackendHelper { get; } |
| 34 | 37 | ||
| 38 | private FileSystemManager FileSystemManager { get; } | ||
| 39 | |||
| 35 | private string TransformFile { get; } | 40 | private string TransformFile { get; } |
| 36 | 41 | ||
| 37 | private string ExportBasePath { get; } | 42 | private string ExportBasePath { get; } |
| @@ -90,7 +95,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 90 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); | 95 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); |
| 91 | 96 | ||
| 92 | // unbind the database | 97 | // unbind the database |
| 93 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 98 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); |
| 94 | var transformViewOutput = unbindCommand.Execute(); | 99 | var transformViewOutput = unbindCommand.Execute(); |
| 95 | 100 | ||
| 96 | // index the added and possibly modified rows (added rows may also appears as modified rows) | 101 | // index the added and possibly modified rows (added rows may also appears as modified rows) |
| @@ -160,7 +165,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 160 | } | 165 | } |
| 161 | 166 | ||
| 162 | // unbind the database | 167 | // unbind the database |
| 163 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 168 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); |
| 164 | var output = unbindCommand.Execute(); | 169 | var output = unbindCommand.Execute(); |
| 165 | 170 | ||
| 166 | // index all the rows to easily find modified rows | 171 | // index all the rows to easily find modified rows |
| @@ -302,8 +307,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 302 | 307 | ||
| 303 | private void GenerateDatabase(WindowsInstallerData output, string databaseFile) | 308 | private void GenerateDatabase(WindowsInstallerData output, string databaseFile) |
| 304 | { | 309 | { |
| 305 | var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); | 310 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); |
| 306 | command.Execute(); | 311 | command.Execute(); |
| 307 | } | 312 | } |
| 308 | } | 313 | } |
| 309 | } | 314 | } |
| 315 | #endif | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs index b35e3587..c78ea93a 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs | |||
| @@ -3,11 +3,9 @@ | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.ComponentModel.Design; |
| 7 | using System.ComponentModel; | ||
| 8 | using System.IO; | 7 | using System.IO; |
| 9 | using System.Linq; | 8 | using System.Linq; |
| 10 | using WixToolset.Core.Native.Msi; | ||
| 11 | using WixToolset.Core.WindowsInstaller.Decompile; | 9 | using WixToolset.Core.WindowsInstaller.Decompile; |
| 12 | using WixToolset.Core.WindowsInstaller.Unbind; | 10 | using WixToolset.Core.WindowsInstaller.Unbind; |
| 13 | using WixToolset.Data; | 11 | using WixToolset.Data; |
| @@ -63,56 +61,32 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 63 | 61 | ||
| 64 | private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context) | 62 | private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context) |
| 65 | { | 63 | { |
| 66 | var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>(); | 64 | // Delete the directory and its files to prevent cab extraction failure due to an existing file. |
| 67 | 65 | if (Directory.Exists(context.ExtractFolder)) | |
| 68 | try | ||
| 69 | { | 66 | { |
| 70 | using (var database = new Database(context.DecompilePath, OpenDatabase.ReadOnly)) | 67 | Directory.Delete(context.ExtractFolder, true); |
| 71 | { | ||
| 72 | // Delete the directory and its files to prevent cab extraction failure due to an existing file. | ||
| 73 | if (Directory.Exists(context.ExtractFolder)) | ||
| 74 | { | ||
| 75 | Directory.Delete(context.ExtractFolder, true); | ||
| 76 | } | ||
| 77 | |||
| 78 | var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>(); | ||
| 79 | var decompilerHelper = context.ServiceProvider.GetService<IWindowsInstallerDecompilerHelper>(); | ||
| 80 | |||
| 81 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, database, context.DecompilePath, context.DecompileType, context.ExtractFolder, context.IntermediateFolder, context.IsAdminImage, suppressDemodularization: false, skipSummaryInfo: false); | ||
| 82 | var output = unbindCommand.Execute(); | ||
| 83 | var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles); | ||
| 84 | |||
| 85 | var decompiler = new Decompiler(this.Messaging, backendHelper, decompilerHelper, context.Extensions, context.ExtensionData, context.SymbolDefinitionCreator, context.BaseSourcePath, context.SuppressCustomTables, context.SuppressDroppingEmptyTables, context.SuppressRelativeActionSequencing, context.SuppressUI, context.TreatProductAsModule); | ||
| 86 | result.Document = decompiler.Decompile(output); | ||
| 87 | |||
| 88 | result.Platform = GetPlatformFromOutput(output); | ||
| 89 | |||
| 90 | // extract the files from the cabinets | ||
| 91 | if (!String.IsNullOrEmpty(context.ExtractFolder) && !context.SuppressExtractCabinets) | ||
| 92 | { | ||
| 93 | var fileDirectory = String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder; | ||
| 94 | |||
| 95 | var extractCommand = new ExtractCabinetsCommand(output, database, context.DecompilePath, fileDirectory, context.IntermediateFolder, context.TreatProductAsModule); | ||
| 96 | extractCommand.Execute(); | ||
| 97 | |||
| 98 | extractedFilePaths.AddRange(extractCommand.ExtractedFiles); | ||
| 99 | result.ExtractedFilePaths = extractedFilePaths; | ||
| 100 | } | ||
| 101 | else | ||
| 102 | { | ||
| 103 | result.ExtractedFilePaths = new string[0]; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | 68 | } |
| 107 | catch (Win32Exception e) | ||
| 108 | { | ||
| 109 | if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED | ||
| 110 | { | ||
| 111 | throw new WixException(ErrorMessages.OpenDatabaseFailed(context.DecompilePath)); | ||
| 112 | } | ||
| 113 | 69 | ||
| 114 | throw; | 70 | var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>(); |
| 115 | } | 71 | |
| 72 | var pathResolver = context.ServiceProvider.GetService<IPathResolver>(); | ||
| 73 | |||
| 74 | var extractFilesFolder = context.SuppressExtractCabinets || (String.IsNullOrEmpty(context.CabinetExtractFolder) && String.IsNullOrEmpty(context.ExtractFolder)) ? null : | ||
| 75 | String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder; | ||
| 76 | |||
| 77 | var outputType = context.TreatProductAsModule ? OutputType.Module : context.DecompileType; | ||
| 78 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, pathResolver, context.DecompilePath, outputType, context.ExtractFolder, extractFilesFolder, context.IntermediateFolder, enableDemodularization: true, skipSummaryInfo: false); | ||
| 79 | var output = unbindCommand.Execute(); | ||
| 80 | var extractedFilePaths = unbindCommand.ExportedFiles; | ||
| 81 | |||
| 82 | var decompilerHelper = context.ServiceProvider.GetService<IWindowsInstallerDecompilerHelper>(); | ||
| 83 | var decompiler = new Decompiler(this.Messaging, backendHelper, decompilerHelper, context.Extensions, context.ExtensionData, context.SymbolDefinitionCreator, context.BaseSourcePath, context.SuppressCustomTables, context.SuppressDroppingEmptyTables, context.SuppressRelativeActionSequencing, context.SuppressUI, context.TreatProductAsModule); | ||
| 84 | var document = decompiler.Decompile(output); | ||
| 85 | |||
| 86 | var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>(); | ||
| 87 | result.Document = document; | ||
| 88 | result.Platform = GetPlatformFromOutput(output); | ||
| 89 | result.ExtractedFilePaths = extractedFilePaths.ToList(); | ||
| 116 | 90 | ||
| 117 | return result; | 91 | return result; |
| 118 | } | 92 | } |
diff --git a/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs b/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs index a0798e62..025affd3 100644 --- a/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs +++ b/src/wix/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs | |||
| @@ -4,7 +4,6 @@ namespace WixToolset.Core.Bind | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Globalization; | ||
| 8 | using System.IO; | 7 | using System.IO; |
| 9 | using System.Linq; | 8 | using System.Linq; |
| 10 | using System.Security.Cryptography; | 9 | using System.Security.Cryptography; |
| @@ -41,9 +40,9 @@ namespace WixToolset.Core.Bind | |||
| 41 | { | 40 | { |
| 42 | var localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(uri.LocalPath); | 41 | var localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(uri.LocalPath); |
| 43 | var unique = this.HashUri(uri.AbsoluteUri); | 42 | var unique = this.HashUri(uri.AbsoluteUri); |
| 44 | var extractedName = String.Format(CultureInfo.InvariantCulture, @"{0}_{1}\{2}", localFileNameWithoutExtension, unique, embeddedFileId); | 43 | var extractedName = String.Concat(localFileNameWithoutExtension, "_", unique); |
| 45 | 44 | ||
| 46 | extractPath = Path.GetFullPath(Path.Combine(extractFolder, extractedName)); | 45 | extractPath = Path.GetFullPath(Path.Combine(extractFolder, extractedName, embeddedFileId)); |
| 47 | extracts.Add(embeddedFileId, extractPath); | 46 | extracts.Add(embeddedFileId, extractPath); |
| 48 | } | 47 | } |
| 49 | 48 | ||
diff --git a/src/wix/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/wix/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 794208e5..8a5299d9 100644 --- a/src/wix/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/wix/WixToolset.Core/Bind/ResolveFieldsCommand.cs | |||
| @@ -16,186 +16,121 @@ namespace WixToolset.Core.Bind | |||
| 16 | /// </summary> | 16 | /// </summary> |
| 17 | internal class ResolveFieldsCommand | 17 | internal class ResolveFieldsCommand |
| 18 | { | 18 | { |
| 19 | public IMessaging Messaging { private get; set; } | 19 | public ResolveFieldsCommand(IMessaging messaging, IFileResolver fileResolver, IVariableResolver variableResolver, IReadOnlyCollection<IBindPath> bindPaths, IReadOnlyCollection<IResolverExtension> extensions, ExtractEmbeddedFiles filesWithEmbeddedFiles, string intermediateFolder, Intermediate intermediate, bool allowUnresolvedVariables) |
| 20 | { | ||
| 21 | this.Messaging = messaging; | ||
| 22 | this.FileResolver = fileResolver; | ||
| 23 | this.VariableResolver = variableResolver; | ||
| 24 | this.BindPaths = bindPaths; | ||
| 25 | this.Extensions = extensions; | ||
| 26 | this.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | ||
| 27 | this.IntermediateFolder = intermediateFolder; | ||
| 28 | this.Intermediate = intermediate; | ||
| 29 | this.AllowUnresolvedVariables = allowUnresolvedVariables; | ||
| 30 | } | ||
| 20 | 31 | ||
| 21 | public bool BuildingPatch { private get; set; } | 32 | private IMessaging Messaging { get; } |
| 22 | 33 | ||
| 23 | public IVariableResolver VariableResolver { private get; set; } | 34 | private IFileResolver FileResolver { get; } |
| 24 | 35 | ||
| 25 | public IEnumerable<IBindPath> BindPaths { private get; set; } | 36 | private IVariableResolver VariableResolver { get; } |
| 26 | 37 | ||
| 27 | public IEnumerable<IResolverExtension> Extensions { private get; set; } | 38 | private IEnumerable<IBindPath> BindPaths { get; } |
| 28 | 39 | ||
| 29 | public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } | 40 | private IEnumerable<IResolverExtension> Extensions { get; } |
| 30 | 41 | ||
| 31 | public string IntermediateFolder { private get; set; } | 42 | private ExtractEmbeddedFiles FilesWithEmbeddedFiles { get; } |
| 32 | 43 | ||
| 33 | public Intermediate Intermediate { private get; set; } | 44 | private string IntermediateFolder { get; } |
| 34 | 45 | ||
| 35 | public bool SupportDelayedResolution { private get; set; } | 46 | private Intermediate Intermediate { get; } |
| 36 | 47 | ||
| 37 | public bool AllowUnresolvedVariables { private get; set; } | 48 | private bool AllowUnresolvedVariables { get; } |
| 38 | 49 | ||
| 39 | public IReadOnlyCollection<DelayedField> DelayedFields { get; private set; } | 50 | public IReadOnlyCollection<DelayedField> DelayedFields { get; private set; } |
| 40 | 51 | ||
| 41 | public void Execute() | 52 | public void Execute() |
| 42 | { | 53 | { |
| 43 | var delayedFields = this.SupportDelayedResolution ? new List<DelayedField>() : null; | 54 | var delayedFields = new List<DelayedField>(); |
| 44 | 55 | ||
| 45 | var fileResolver = new FileResolver(this.BindPaths, this.Extensions); | 56 | var bindPaths = this.BindPaths.Where(b => b.Stage == BindStage.Normal).ToList(); |
| 46 | 57 | ||
| 47 | // Build the column lookup only when needed. | 58 | // Build the column lookup only when needed. |
| 48 | Dictionary<string, WixCustomTableColumnSymbol> customColumnsById = null; | 59 | Dictionary<string, WixCustomTableColumnSymbol> customColumnsById = null; |
| 49 | 60 | ||
| 50 | foreach (var sections in this.Intermediate.Sections) | 61 | foreach (var symbol in this.Intermediate.Sections.SelectMany(s => s.Symbols)) |
| 51 | { | 62 | { |
| 52 | foreach (var symbol in sections.Symbols) | 63 | foreach (var field in symbol.Fields.Where(f => !f.IsNull())) |
| 53 | { | 64 | { |
| 54 | foreach (var field in symbol.Fields) | 65 | var fieldType = field.Type; |
| 66 | |||
| 67 | // Custom table cells require an extra look up to the column definition as the | ||
| 68 | // cell's data type is always a string (because strings can store anything) but | ||
| 69 | // the column definition may be more specific. | ||
| 70 | if (symbol.Definition.Type == SymbolDefinitionType.WixCustomTableCell) | ||
| 55 | { | 71 | { |
| 56 | if (field.IsNull()) | 72 | // We only care about the Data in a CustomTable cell. |
| 73 | if (field.Name != nameof(WixCustomTableCellSymbolFields.Data)) | ||
| 57 | { | 74 | { |
| 58 | continue; | 75 | continue; |
| 59 | } | 76 | } |
| 60 | 77 | ||
| 61 | var fieldType = field.Type; | 78 | if (customColumnsById == null) |
| 62 | |||
| 63 | // Custom table cells require an extra look up to the column definition as the | ||
| 64 | // cell's data type is always a string (because strings can store anything) but | ||
| 65 | // the column definition may be more specific. | ||
| 66 | if (symbol.Definition.Type == SymbolDefinitionType.WixCustomTableCell) | ||
| 67 | { | 79 | { |
| 68 | // We only care about the Data in a CustomTable cell. | 80 | customColumnsById = this.Intermediate.Sections.SelectMany(s => s.Symbols.OfType<WixCustomTableColumnSymbol>()).ToDictionary(t => t.Id.Id); |
| 69 | if (field.Name != nameof(WixCustomTableCellSymbolFields.Data)) | ||
| 70 | { | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (customColumnsById == null) | ||
| 75 | { | ||
| 76 | customColumnsById = this.Intermediate.Sections.SelectMany(s => s.Symbols.OfType<WixCustomTableColumnSymbol>()).ToDictionary(t => t.Id.Id); | ||
| 77 | } | ||
| 78 | |||
| 79 | if (customColumnsById.TryGetValue(symbol.Fields[(int)WixCustomTableCellSymbolFields.TableRef].AsString() + "/" + symbol.Fields[(int)WixCustomTableCellSymbolFields.ColumnRef].AsString(), out var customColumn)) | ||
| 80 | { | ||
| 81 | fieldType = customColumn.Type; | ||
| 82 | } | ||
| 83 | } | 81 | } |
| 84 | 82 | ||
| 85 | // Check to make sure we're in a scenario where we can handle variable resolution. | 83 | if (customColumnsById.TryGetValue(symbol.Fields[(int)WixCustomTableCellSymbolFields.TableRef].AsString() + "/" + symbol.Fields[(int)WixCustomTableCellSymbolFields.ColumnRef].AsString(), out var customColumn)) |
| 86 | if (null != delayedFields) | ||
| 87 | { | 84 | { |
| 88 | // resolve localization and wix variables | 85 | fieldType = customColumn.Type; |
| 89 | if (fieldType == IntermediateFieldType.String) | ||
| 90 | { | ||
| 91 | var original = field.AsString(); | ||
| 92 | if (!String.IsNullOrEmpty(original)) | ||
| 93 | { | ||
| 94 | var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, original, !this.AllowUnresolvedVariables); | ||
| 95 | if (resolution.UpdatedValue) | ||
| 96 | { | ||
| 97 | field.Set(resolution.Value); | ||
| 98 | } | ||
| 99 | |||
| 100 | if (resolution.DelayedResolve) | ||
| 101 | { | ||
| 102 | delayedFields.Add(new DelayedField(symbol, field)); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // Move to next symbol if we've hit an error resolving variables. | ||
| 109 | if (this.Messaging.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field. | ||
| 110 | { | ||
| 111 | continue; | ||
| 112 | } | 86 | } |
| 87 | } | ||
| 113 | 88 | ||
| 114 | // Resolve file paths | 89 | // Check to make sure we're in a scenario where we can handle variable resolution. |
| 115 | if (fieldType == IntermediateFieldType.Path) | 90 | if (null != delayedFields) |
| 91 | { | ||
| 92 | // resolve localization and wix variables | ||
| 93 | if (fieldType == IntermediateFieldType.String) | ||
| 116 | { | 94 | { |
| 117 | this.ResolvePathField(fileResolver, symbol, field); | 95 | var original = field.AsString(); |
| 118 | 96 | if (!String.IsNullOrEmpty(original)) | |
| 119 | #if TODO_PATCHING | ||
| 120 | if (null != objectField.PreviousData) | ||
| 121 | { | 97 | { |
| 122 | objectField.PreviousData = this.BindVariableResolver.ResolveVariables(symbol.SourceLineNumbers, objectField.PreviousData, false, out isDefault); | 98 | var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, original, !this.AllowUnresolvedVariables); |
| 123 | 99 | if (resolution.UpdatedValue) | |
| 124 | if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field. | ||
| 125 | { | 100 | { |
| 126 | // file is compressed in a cabinet (and not modified above) | 101 | field.Set(resolution.Value); |
| 127 | if (objectField.PreviousEmbeddedFileIndex.HasValue && isDefault) | 102 | } |
| 128 | { | ||
| 129 | // when loading transforms from disk, PreviousBaseUri may not have been set | ||
| 130 | if (null == objectField.PreviousBaseUri) | ||
| 131 | { | ||
| 132 | objectField.PreviousBaseUri = objectField.BaseUri; | ||
| 133 | } | ||
| 134 | |||
| 135 | string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.PreviousBaseUri, objectField.PreviousEmbeddedFileIndex.Value, this.IntermediateFolder); | ||
| 136 | |||
| 137 | // set the path to the file once its extracted from the cabinet | ||
| 138 | objectField.PreviousData = extractPath; | ||
| 139 | } | ||
| 140 | else if (null != objectField.PreviousData) // non-compressed file (or localized value) | ||
| 141 | { | ||
| 142 | try | ||
| 143 | { | ||
| 144 | if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) | ||
| 145 | { | ||
| 146 | // resolve the path to the file | ||
| 147 | objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, symbol.Definition.Name, symbol.SourceLineNumbers, BindStage.Normal); | ||
| 148 | } | ||
| 149 | else | ||
| 150 | { | ||
| 151 | if (fileResolver.RebaseTarget) | ||
| 152 | { | ||
| 153 | // if -bt is used, it come here | ||
| 154 | // Try to use the original unresolved source from either target build or update build | ||
| 155 | // If both target and updated are of old wixpdb, it behaves the same as today, no re-base logic here | ||
| 156 | // If target is old version and updated is new version, it uses unresolved path from updated build | ||
| 157 | // If both target and updated are of new versions, it uses unresolved path from target build | ||
| 158 | if (null != objectField.UnresolvedPreviousData || null != objectField.UnresolvedData) | ||
| 159 | { | ||
| 160 | objectField.PreviousData = objectField.UnresolvedPreviousData ?? objectField.UnresolvedData; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | // resolve the path to the file | ||
| 165 | objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, symbol.Definition.Name, symbol.SourceLineNumbers, BindStage.Target); | ||
| 166 | 103 | ||
| 167 | } | 104 | if (resolution.DelayedResolve) |
| 168 | } | 105 | { |
| 169 | catch (WixFileNotFoundException) | 106 | delayedFields.Add(new DelayedField(symbol, field)); |
| 170 | { | ||
| 171 | // display the error with source line information | ||
| 172 | Messaging.Instance.Write(WixErrors.FileNotFound(symbol.SourceLineNumbers, (string)objectField.PreviousData)); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | 107 | } |
| 176 | } | 108 | } |
| 177 | #endif | ||
| 178 | } | 109 | } |
| 179 | } | 110 | } |
| 111 | |||
| 112 | // Move to next symbol if we've hit an error resolving variables. | ||
| 113 | if (this.Messaging.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field. | ||
| 114 | { | ||
| 115 | continue; | ||
| 116 | } | ||
| 117 | |||
| 118 | // Resolve file paths | ||
| 119 | if (fieldType == IntermediateFieldType.Path) | ||
| 120 | { | ||
| 121 | this.ResolvePathField(this.FileResolver, bindPaths, symbol, field); | ||
| 122 | } | ||
| 180 | } | 123 | } |
| 181 | } | 124 | } |
| 182 | 125 | ||
| 183 | this.DelayedFields = delayedFields; | 126 | this.DelayedFields = delayedFields; |
| 184 | } | 127 | } |
| 185 | 128 | ||
| 186 | private void ResolvePathField(FileResolver fileResolver, IntermediateSymbol symbol, IntermediateField field) | 129 | private void ResolvePathField(IFileResolver fileResolver, IEnumerable<IBindPath> bindPaths, IntermediateSymbol symbol, IntermediateField field) |
| 187 | { | 130 | { |
| 188 | var fieldValue = field.AsPath(); | 131 | var fieldValue = field.AsPath(); |
| 189 | var originalFieldPath = fieldValue.Path; | 132 | var originalFieldPath = fieldValue.Path; |
| 190 | 133 | ||
| 191 | #if TODO_PATCHING | ||
| 192 | // Skip file resolution if the file is to be deleted. | ||
| 193 | if (RowOperation.Delete == symbol.Operation) | ||
| 194 | { | ||
| 195 | continue; | ||
| 196 | } | ||
| 197 | #endif | ||
| 198 | |||
| 199 | // If the file is embedded and if the previous value has a bind variable in the path | 134 | // If the file is embedded and if the previous value has a bind variable in the path |
| 200 | // which gets modified by resolving the previous value again then switch to that newly | 135 | // which gets modified by resolving the previous value again then switch to that newly |
| 201 | // resolved path instead of using the embedded file. | 136 | // resolved path instead of using the embedded file. |
| @@ -221,45 +156,7 @@ namespace WixToolset.Core.Bind | |||
| 221 | { | 156 | { |
| 222 | try | 157 | try |
| 223 | { | 158 | { |
| 224 | var resolvedPath = fieldValue.Path; | 159 | var resolvedPath = fileResolver.ResolveFile(fieldValue.Path, this.Extensions, bindPaths, BindStage.Normal, symbol.SourceLineNumbers, symbol.Definition); |
| 225 | |||
| 226 | if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) | ||
| 227 | { | ||
| 228 | #if TODO_PATCHING | ||
| 229 | // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file | ||
| 230 | if (null == objectField.UnresolvedData) | ||
| 231 | { | ||
| 232 | objectField.UnresolvedData = (string)objectField.Data; | ||
| 233 | } | ||
| 234 | #endif | ||
| 235 | resolvedPath = fileResolver.ResolveFile(fieldValue.Path, symbol.Definition, symbol.SourceLineNumbers, BindStage.Normal); | ||
| 236 | } | ||
| 237 | else if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) // Normal binding for Patch Scenario (normal patch, no re-basing logic) | ||
| 238 | { | ||
| 239 | resolvedPath = fileResolver.ResolveFile(fieldValue.Path, symbol.Definition, symbol.SourceLineNumbers, BindStage.Normal); | ||
| 240 | } | ||
| 241 | #if TODO_PATCHING | ||
| 242 | else // Re-base binding path scenario caused by pyro.exe -bt -bu | ||
| 243 | { | ||
| 244 | // by default, use the resolved Data for file lookup | ||
| 245 | string filePathToResolve = (string)objectField.Data; | ||
| 246 | |||
| 247 | // if -bu is used in pyro command, this condition holds true and the tool | ||
| 248 | // will use pre-resolved source for new wixpdb file | ||
| 249 | if (fileResolver.RebaseUpdated) | ||
| 250 | { | ||
| 251 | // try to use the unResolved Source if it exists. | ||
| 252 | // New version of wixpdb file keeps a copy of pre-resolved Source. i.e. !(bindpath.test)\foo.dll | ||
| 253 | // Old version of winpdb file does not contain this attribute and the value is null. | ||
| 254 | if (null != objectField.UnresolvedData) | ||
| 255 | { | ||
| 256 | filePathToResolve = objectField.UnresolvedData; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | objectField.Data = fileResolver.ResolveFile(filePathToResolve, symbol.Definition.Name, symbol.SourceLineNumbers, BindStage.Updated); | ||
| 261 | } | ||
| 262 | #endif | ||
| 263 | 160 | ||
| 264 | if (!String.Equals(originalFieldPath, resolvedPath, StringComparison.OrdinalIgnoreCase)) | 161 | if (!String.Equals(originalFieldPath, resolvedPath, StringComparison.OrdinalIgnoreCase)) |
| 265 | { | 162 | { |
diff --git a/src/wix/WixToolset.Core/BindContext.cs b/src/wix/WixToolset.Core/BindContext.cs index 9dd6aa46..1c1f7528 100644 --- a/src/wix/WixToolset.Core/BindContext.cs +++ b/src/wix/WixToolset.Core/BindContext.cs | |||
| @@ -18,7 +18,7 @@ namespace WixToolset.Core | |||
| 18 | 18 | ||
| 19 | public IServiceProvider ServiceProvider { get; } | 19 | public IServiceProvider ServiceProvider { get; } |
| 20 | 20 | ||
| 21 | public IReadOnlyCollection<BindPath> BindPaths { get; set; } | 21 | public IReadOnlyCollection<IBindPath> BindPaths { get; set; } |
| 22 | 22 | ||
| 23 | public string BurnStubPath { get; set; } | 23 | public string BurnStubPath { get; set; } |
| 24 | 24 | ||
diff --git a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs index dc4e0a6d..34520fc0 100644 --- a/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/wix/WixToolset.Core/CommandLine/BuildCommand.cs | |||
| @@ -48,7 +48,9 @@ namespace WixToolset.Core.CommandLine | |||
| 48 | { | 48 | { |
| 49 | new CommandLineHelpSwitch("-arch", "Set the architecture of the output."), | 49 | new CommandLineHelpSwitch("-arch", "Set the architecture of the output."), |
| 50 | new CommandLineHelpSwitch("-bindfiles", "-bf", "Bind files into an output .wixlib. Ignored if not building a .wixlib."), | 50 | new CommandLineHelpSwitch("-bindfiles", "-bf", "Bind files into an output .wixlib. Ignored if not building a .wixlib."), |
| 51 | new CommandLineHelpSwitch("-bindpath", "Bind path to search for content files."), | 51 | new CommandLineHelpSwitch("-bindpath", "-b", "Bind path to search for content files."), |
| 52 | new CommandLineHelpSwitch("-bindpath:target", "-bt", "Bind path to search for target package's content files. Only used when building a patch."), | ||
| 53 | new CommandLineHelpSwitch("-bindpath:update", "-bu", "Bind path to search for update package's content files. Only used when building a patch."), | ||
| 52 | new CommandLineHelpSwitch("-cabcache", "-cc", "Set a folder to cache cabinets across builds."), | 54 | new CommandLineHelpSwitch("-cabcache", "-cc", "Set a folder to cache cabinets across builds."), |
| 53 | new CommandLineHelpSwitch("-culture", "Adds a culture to filter localization files."), | 55 | new CommandLineHelpSwitch("-culture", "Adds a culture to filter localization files."), |
| 54 | new CommandLineHelpSwitch("-define", "-d", "Sets a preprocessor variable."), | 56 | new CommandLineHelpSwitch("-define", "-d", "Sets a preprocessor variable."), |
| @@ -292,6 +294,7 @@ namespace WixToolset.Core.CommandLine | |||
| 292 | { | 294 | { |
| 293 | { | 295 | { |
| 294 | var context = this.ServiceProvider.GetService<IBindContext>(); | 296 | var context = this.ServiceProvider.GetService<IBindContext>(); |
| 297 | context.BindPaths = bindPaths; | ||
| 295 | //context.CabbingThreadCount = this.CabbingThreadCount; | 298 | //context.CabbingThreadCount = this.CabbingThreadCount; |
| 296 | context.CabCachePath = cabCachePath; | 299 | context.CabCachePath = cabCachePath; |
| 297 | context.ResolvedCodepage = resolveResult.Codepage; | 300 | context.ResolvedCodepage = resolveResult.Codepage; |
| @@ -534,11 +537,25 @@ namespace WixToolset.Core.CommandLine | |||
| 534 | this.BindFiles = true; | 537 | this.BindFiles = true; |
| 535 | return true; | 538 | return true; |
| 536 | 539 | ||
| 540 | case "b": | ||
| 537 | case "bindpath": | 541 | case "bindpath": |
| 542 | case "bt": | ||
| 543 | case "bindpath:target": | ||
| 544 | case "bu": | ||
| 545 | case "bindpath:update": | ||
| 538 | { | 546 | { |
| 539 | var value = parser.GetNextArgumentOrError(arg); | 547 | var value = parser.GetNextArgumentOrError(arg); |
| 540 | if (value != null && this.TryParseBindPath(value, out var bindPath)) | 548 | if (value != null && this.TryParseBindPath(arg, value, out var bindPath)) |
| 541 | { | 549 | { |
| 550 | if (parameter == "bt" || parameter.EndsWith("target")) | ||
| 551 | { | ||
| 552 | bindPath.Stage = BindStage.Target; | ||
| 553 | } | ||
| 554 | else if (parameter == "bu" || parameter.EndsWith("update")) | ||
| 555 | { | ||
| 556 | bindPath.Stage = BindStage.Updated; | ||
| 557 | } | ||
| 558 | |||
| 542 | this.BindPaths.Add(bindPath); | 559 | this.BindPaths.Add(bindPath); |
| 543 | } | 560 | } |
| 544 | return true; | 561 | return true; |
| @@ -830,7 +847,7 @@ namespace WixToolset.Core.CommandLine | |||
| 830 | return new InputsAndOutputs(codePaths, localizationPaths, libraryPaths, wixipls, outputPath, outputType, pdbPath, this.PdbType); | 847 | return new InputsAndOutputs(codePaths, localizationPaths, libraryPaths, wixipls, outputPath, outputType, pdbPath, this.PdbType); |
| 831 | } | 848 | } |
| 832 | 849 | ||
| 833 | private bool TryParseBindPath(string bindPath, out IBindPath bp) | 850 | private bool TryParseBindPath(string argument, string bindPath, out IBindPath bp) |
| 834 | { | 851 | { |
| 835 | var namedPath = bindPath.Split(BindPathSplit, 2); | 852 | var namedPath = bindPath.Split(BindPathSplit, 2); |
| 836 | 853 | ||
| @@ -848,7 +865,7 @@ namespace WixToolset.Core.CommandLine | |||
| 848 | 865 | ||
| 849 | if (File.Exists(bp.Path)) | 866 | if (File.Exists(bp.Path)) |
| 850 | { | 867 | { |
| 851 | this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile("-bindpath", bp.Path)); | 868 | this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(argument, bp.Path)); |
| 852 | return false; | 869 | return false; |
| 853 | } | 870 | } |
| 854 | 871 | ||
diff --git a/src/wix/WixToolset.Core/Compiler.cs b/src/wix/WixToolset.Core/Compiler.cs index d04ae491..8f1ae7fb 100644 --- a/src/wix/WixToolset.Core/Compiler.cs +++ b/src/wix/WixToolset.Core/Compiler.cs | |||
| @@ -7706,7 +7706,6 @@ namespace WixToolset.Core | |||
| 7706 | var validationFlags = TransformFlags.PatchTransformDefault; | 7706 | var validationFlags = TransformFlags.PatchTransformDefault; |
| 7707 | string baselineFile = null; | 7707 | string baselineFile = null; |
| 7708 | string updateFile = null; | 7708 | string updateFile = null; |
| 7709 | string transformFile = null; | ||
| 7710 | 7709 | ||
| 7711 | foreach (var attrib in node.Attributes()) | 7710 | foreach (var attrib in node.Attributes()) |
| 7712 | { | 7711 | { |
| @@ -7726,9 +7725,6 @@ namespace WixToolset.Core | |||
| 7726 | case "UpdateFile": | 7725 | case "UpdateFile": |
| 7727 | updateFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | 7726 | updateFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); |
| 7728 | break; | 7727 | break; |
| 7729 | case "TransformFile": | ||
| 7730 | transformFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
| 7731 | break; | ||
| 7732 | default: | 7728 | default: |
| 7733 | this.Core.UnexpectedAttribute(node, attrib); | 7729 | this.Core.UnexpectedAttribute(node, attrib); |
| 7734 | break; | 7730 | break; |
| @@ -7746,26 +7742,14 @@ namespace WixToolset.Core | |||
| 7746 | id = Identifier.Invalid; | 7742 | id = Identifier.Invalid; |
| 7747 | } | 7743 | } |
| 7748 | 7744 | ||
| 7749 | if (!String.IsNullOrEmpty(baselineFile) || !String.IsNullOrEmpty(updateFile)) | 7745 | if (String.IsNullOrEmpty(baselineFile)) |
| 7750 | { | 7746 | { |
| 7751 | if (String.IsNullOrEmpty(baselineFile)) | 7747 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile")); |
| 7752 | { | ||
| 7753 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "UpdateFile")); | ||
| 7754 | } | ||
| 7755 | |||
| 7756 | if (String.IsNullOrEmpty(updateFile)) | ||
| 7757 | { | ||
| 7758 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpdateFile", "BaselineFile")); | ||
| 7759 | } | ||
| 7760 | |||
| 7761 | if (!String.IsNullOrEmpty(transformFile)) | ||
| 7762 | { | ||
| 7763 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TransformFile", !String.IsNullOrEmpty(baselineFile) ? "BaselineFile" : "UpdateFile")); | ||
| 7764 | } | ||
| 7765 | } | 7748 | } |
| 7766 | else if (String.IsNullOrEmpty(transformFile)) | 7749 | |
| 7750 | if (String.IsNullOrEmpty(updateFile)) | ||
| 7767 | { | 7751 | { |
| 7768 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "TransformFile", true)); | 7752 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpdateFile")); |
| 7769 | } | 7753 | } |
| 7770 | 7754 | ||
| 7771 | foreach (var child in node.Elements()) | 7755 | foreach (var child in node.Elements()) |
| @@ -7805,7 +7789,6 @@ namespace WixToolset.Core | |||
| 7805 | ValidationFlags = validationFlags, | 7789 | ValidationFlags = validationFlags, |
| 7806 | BaselineFile = new IntermediateFieldPathValue { Path = baselineFile }, | 7790 | BaselineFile = new IntermediateFieldPathValue { Path = baselineFile }, |
| 7807 | UpdateFile = new IntermediateFieldPathValue { Path = updateFile }, | 7791 | UpdateFile = new IntermediateFieldPathValue { Path = updateFile }, |
| 7808 | TransformFile = new IntermediateFieldPathValue { Path = transformFile }, | ||
| 7809 | }); | 7792 | }); |
| 7810 | } | 7793 | } |
| 7811 | } | 7794 | } |
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs index 20a47637..4a5cc607 100644 --- a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs | |||
| @@ -6,8 +6,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using WixToolset.Core.Bind; | 7 | using WixToolset.Core.Bind; |
| 8 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 9 | using WixToolset.Data.Symbols; | ||
| 10 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 11 | using WixToolset.Extensibility.Data; | 9 | using WixToolset.Extensibility.Data; |
| 12 | using WixToolset.Extensibility.Services; | 10 | using WixToolset.Extensibility.Services; |
| 13 | 11 | ||
| @@ -17,21 +15,6 @@ namespace WixToolset.Core.ExtensibilityServices | |||
| 17 | { | 15 | { |
| 18 | } | 16 | } |
| 19 | 17 | ||
| 20 | public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly) | ||
| 21 | { | ||
| 22 | return new FileFacade(file, assembly); | ||
| 23 | } | ||
| 24 | |||
| 25 | public IFileFacade CreateFileFacade(FileRow fileRow) | ||
| 26 | { | ||
| 27 | return new FileFacade(fileRow); | ||
| 28 | } | ||
| 29 | |||
| 30 | public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol) | ||
| 31 | { | ||
| 32 | return new FileFacade(true, fileSymbol); | ||
| 33 | } | ||
| 34 | |||
| 35 | public string CreateGuid() | 18 | public string CreateGuid() |
| 36 | { | 19 | { |
| 37 | return Common.GenerateGuid(); | 20 | return Common.GenerateGuid(); |
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs deleted file mode 100644 index 65043658..00000000 --- a/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs +++ /dev/null | |||
| @@ -1,175 +0,0 @@ | |||
| 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.ExtensibilityServices | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using WixToolset.Data; | ||
| 8 | using WixToolset.Data.Symbols; | ||
| 9 | using WixToolset.Data.WindowsInstaller; | ||
| 10 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 11 | using WixToolset.Extensibility.Data; | ||
| 12 | |||
| 13 | internal class FileFacade : IFileFacade | ||
| 14 | { | ||
| 15 | public FileFacade(FileSymbol file, AssemblySymbol assembly) | ||
| 16 | { | ||
| 17 | this.FileSymbol = file; | ||
| 18 | this.AssemblySymbol = assembly; | ||
| 19 | |||
| 20 | this.Identifier = file.Id; | ||
| 21 | this.ComponentRef = file.ComponentRef; | ||
| 22 | } | ||
| 23 | |||
| 24 | public FileFacade(bool fromModule, FileSymbol file) | ||
| 25 | { | ||
| 26 | this.FromModule = fromModule; | ||
| 27 | this.FileSymbol = file; | ||
| 28 | |||
| 29 | this.Identifier = file.Id; | ||
| 30 | this.ComponentRef = file.ComponentRef; | ||
| 31 | } | ||
| 32 | |||
| 33 | public FileFacade(FileRow row) | ||
| 34 | { | ||
| 35 | this.FromTransform = true; | ||
| 36 | this.FileRow = row; | ||
| 37 | |||
| 38 | this.Identifier = new Identifier(AccessModifier.Section, row.File); | ||
| 39 | this.ComponentRef = row.Component; | ||
| 40 | } | ||
| 41 | |||
| 42 | public bool FromModule { get; } | ||
| 43 | |||
| 44 | public bool FromTransform { get; } | ||
| 45 | |||
| 46 | private FileRow FileRow { get; } | ||
| 47 | |||
| 48 | private FileSymbol FileSymbol { get; } | ||
| 49 | |||
| 50 | private AssemblySymbol AssemblySymbol { get; } | ||
| 51 | |||
| 52 | public string Id => this.Identifier.Id; | ||
| 53 | |||
| 54 | public Identifier Identifier { get; } | ||
| 55 | |||
| 56 | public string ComponentRef { get; } | ||
| 57 | |||
| 58 | public int DiskId | ||
| 59 | { | ||
| 60 | get => this.FileRow == null ? this.FileSymbol.DiskId ?? 1 : this.FileRow.DiskId; | ||
| 61 | set | ||
| 62 | { | ||
| 63 | if (this.FileRow == null) | ||
| 64 | { | ||
| 65 | this.FileSymbol.DiskId = value; | ||
| 66 | } | ||
| 67 | else | ||
| 68 | { | ||
| 69 | this.FileRow.DiskId = value; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | public string FileName => this.FileRow == null ? this.FileSymbol.Name : this.FileRow.FileName; | ||
| 75 | |||
| 76 | public int FileSize | ||
| 77 | { | ||
| 78 | get => this.FileRow == null ? this.FileSymbol.FileSize : this.FileRow.FileSize; | ||
| 79 | set | ||
| 80 | { | ||
| 81 | if (this.FileRow == null) | ||
| 82 | { | ||
| 83 | this.FileSymbol.FileSize = value; | ||
| 84 | } | ||
| 85 | else | ||
| 86 | { | ||
| 87 | this.FileRow.FileSize = value; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | public string Language | ||
| 93 | { | ||
| 94 | get => this.FileRow == null ? this.FileSymbol.Language : this.FileRow.Language; | ||
| 95 | set | ||
| 96 | { | ||
| 97 | if (this.FileRow == null) | ||
| 98 | { | ||
| 99 | this.FileSymbol.Language = value; | ||
| 100 | } | ||
| 101 | else | ||
| 102 | { | ||
| 103 | this.FileRow.Language = value; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | public int? PatchGroup => this.FileRow == null ? this.FileSymbol.PatchGroup : null; | ||
| 109 | |||
| 110 | public int Sequence | ||
| 111 | { | ||
| 112 | get => this.FileRow == null ? this.FileSymbol.Sequence : this.FileRow.Sequence; | ||
| 113 | set | ||
| 114 | { | ||
| 115 | if (this.FileRow == null) | ||
| 116 | { | ||
| 117 | this.FileSymbol.Sequence = value; | ||
| 118 | } | ||
| 119 | else | ||
| 120 | { | ||
| 121 | this.FileRow.Sequence = value; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | public SourceLineNumber SourceLineNumber => this.FileRow == null ? this.FileSymbol.SourceLineNumbers : this.FileRow.SourceLineNumbers; | ||
| 127 | |||
| 128 | public string SourcePath => this.FileRow == null ? this.FileSymbol.Source?.Path : this.FileRow.Source; | ||
| 129 | |||
| 130 | public bool Compressed => this.FileRow == null ? (this.FileSymbol.Attributes & FileSymbolAttributes.Compressed) == FileSymbolAttributes.Compressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed; | ||
| 131 | |||
| 132 | public bool Uncompressed => this.FileRow == null ? (this.FileSymbol.Attributes & FileSymbolAttributes.Uncompressed) == FileSymbolAttributes.Uncompressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
| 133 | |||
| 134 | public string Version | ||
| 135 | { | ||
| 136 | get => this.FileRow == null ? this.FileSymbol.Version : this.FileRow.Version; | ||
| 137 | set | ||
| 138 | { | ||
| 139 | if (this.FileRow == null) | ||
| 140 | { | ||
| 141 | this.FileSymbol.Version = value; | ||
| 142 | } | ||
| 143 | else | ||
| 144 | { | ||
| 145 | this.FileRow.Version = value; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblySymbol?.Type : null; | ||
| 151 | |||
| 152 | public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblySymbol?.ApplicationFileRef : throw new NotImplementedException(); | ||
| 153 | |||
| 154 | public string AssemblyManifestFileRef => this.FileRow == null ? this.AssemblySymbol?.ManifestFileRef : throw new NotImplementedException(); | ||
| 155 | |||
| 156 | /// <summary> | ||
| 157 | /// Gets the set of MsiAssemblyName rows created for this file. | ||
| 158 | /// </summary> | ||
| 159 | /// <value>RowCollection of MsiAssemblyName table.</value> | ||
| 160 | public List<MsiAssemblyNameSymbol> AssemblyNames { get; set; } | ||
| 161 | |||
| 162 | /// <summary> | ||
| 163 | /// Gets or sets the MsiFileHash row for this file. | ||
| 164 | /// </summary> | ||
| 165 | public MsiFileHashSymbol Hash { get; set; } | ||
| 166 | |||
| 167 | /// <summary> | ||
| 168 | /// Allows direct access to the underlying FileRow as requried for patching. | ||
| 169 | /// </summary> | ||
| 170 | public FileRow GetFileRow() | ||
| 171 | { | ||
| 172 | return this.FileRow ?? throw new NotImplementedException(); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
diff --git a/src/wix/WixToolset.Core/Bind/FileResolver.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileResolver.cs index eb878239..8f08e75e 100644 --- a/src/wix/WixToolset.Core/Bind/FileResolver.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/FileResolver.cs | |||
| @@ -1,51 +1,24 @@ | |||
| 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. | 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 | 2 | ||
| 3 | namespace WixToolset.Core.Bind | 3 | namespace WixToolset.Core.ExtensibilityServices |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 7 | using System.IO; |
| 8 | using System.Linq; | ||
| 9 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 10 | using WixToolset.Extensibility; | 9 | using WixToolset.Extensibility; |
| 11 | using WixToolset.Extensibility.Data; | 10 | using WixToolset.Extensibility.Data; |
| 11 | using WixToolset.Extensibility.Services; | ||
| 12 | 12 | ||
| 13 | internal class FileResolver | 13 | internal class FileResolver : IFileResolver |
| 14 | { | 14 | { |
| 15 | private const string BindPathOpenString = "!(bindpath."; | 15 | private const string BindPathOpenString = "!(bindpath."; |
| 16 | 16 | ||
| 17 | private FileResolver(IEnumerable<IBindPath> bindPaths) | 17 | public string ResolveFile(string source, IEnumerable<ILibrarianExtension> librarianExtensions, IEnumerable<IBindPath> bindPaths, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition) |
| 18 | { | ||
| 19 | this.BindPaths = (bindPaths ?? Array.Empty<IBindPath>()).ToLookup(b => b.Stage); | ||
| 20 | this.RebaseTarget = this.BindPaths[BindStage.Target].Any(); | ||
| 21 | this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any(); | ||
| 22 | } | ||
| 23 | |||
| 24 | public FileResolver(IEnumerable<IBindPath> bindPaths, IEnumerable<IResolverExtension> extensions) : this(bindPaths) | ||
| 25 | { | ||
| 26 | this.ResolverExtensions = extensions ?? Array.Empty<IResolverExtension>(); | ||
| 27 | } | ||
| 28 | |||
| 29 | public FileResolver(IEnumerable<IBindPath> bindPaths, IEnumerable<ILibrarianExtension> extensions) : this(bindPaths) | ||
| 30 | { | ||
| 31 | this.LibrarianExtensions = extensions ?? Array.Empty<ILibrarianExtension>(); | ||
| 32 | } | ||
| 33 | |||
| 34 | private ILookup<BindStage, IBindPath> BindPaths { get; } | ||
| 35 | |||
| 36 | public bool RebaseTarget { get; } | ||
| 37 | |||
| 38 | public bool RebaseUpdated { get; } | ||
| 39 | |||
| 40 | private IEnumerable<IResolverExtension> ResolverExtensions { get; } | ||
| 41 | |||
| 42 | private IEnumerable<ILibrarianExtension> LibrarianExtensions { get; } | ||
| 43 | |||
| 44 | public string Resolve(SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, string source) | ||
| 45 | { | 18 | { |
| 46 | var checkedPaths = new List<string>(); | 19 | var checkedPaths = new List<string>(); |
| 47 | 20 | ||
| 48 | foreach (var extension in this.LibrarianExtensions) | 21 | foreach (var extension in librarianExtensions) |
| 49 | { | 22 | { |
| 50 | var resolved = extension.ResolveFile(sourceLineNumbers, symbolDefinition, source); | 23 | var resolved = extension.ResolveFile(sourceLineNumbers, symbolDefinition, source); |
| 51 | 24 | ||
| @@ -60,19 +33,10 @@ namespace WixToolset.Core.Bind | |||
| 60 | } | 33 | } |
| 61 | } | 34 | } |
| 62 | 35 | ||
| 63 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, BindStage.Normal, checkedPaths); | 36 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindPaths, checkedPaths); |
| 64 | } | 37 | } |
| 65 | 38 | ||
| 66 | /// <summary> | 39 | public string ResolveFile(string source, IEnumerable<IResolverExtension> resolverExtensions, IEnumerable<IBindPath> bindPaths, BindStage bindStage, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, IEnumerable<string> alreadyCheckedPaths = null) |
| 67 | /// Resolves the source path of a file using binder extensions. | ||
| 68 | /// </summary> | ||
| 69 | /// <param name="source">Original source value.</param> | ||
| 70 | /// <param name="symbolDefinition">Optional type of source file being resolved.</param> | ||
| 71 | /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param> | ||
| 72 | /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param> | ||
| 73 | /// <param name="alreadyCheckedPaths">Optional collection of paths already checked.</param> | ||
| 74 | /// <returns>Should return a valid path for the stream to be imported.</returns> | ||
| 75 | public string ResolveFile(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, IEnumerable<string> alreadyCheckedPaths = null) | ||
| 76 | { | 40 | { |
| 77 | var checkedPaths = new List<string>(); | 41 | var checkedPaths = new List<string>(); |
| 78 | 42 | ||
| @@ -81,7 +45,7 @@ namespace WixToolset.Core.Bind | |||
| 81 | checkedPaths.AddRange(alreadyCheckedPaths); | 45 | checkedPaths.AddRange(alreadyCheckedPaths); |
| 82 | } | 46 | } |
| 83 | 47 | ||
| 84 | foreach (var extension in this.ResolverExtensions) | 48 | foreach (var extension in resolverExtensions) |
| 85 | { | 49 | { |
| 86 | var resolved = extension.ResolveFile(source, symbolDefinition, sourceLineNumbers, bindStage); | 50 | var resolved = extension.ResolveFile(source, symbolDefinition, sourceLineNumbers, bindStage); |
| 87 | 51 | ||
| @@ -96,10 +60,10 @@ namespace WixToolset.Core.Bind | |||
| 96 | } | 60 | } |
| 97 | } | 61 | } |
| 98 | 62 | ||
| 99 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindStage, checkedPaths); | 63 | return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindPaths, checkedPaths); |
| 100 | } | 64 | } |
| 101 | 65 | ||
| 102 | private string MustResolveUsingBindPaths(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, List<string> checkedPaths) | 66 | private string MustResolveUsingBindPaths(string source, IntermediateSymbolDefinition symbolDefinition, SourceLineNumber sourceLineNumbers, IEnumerable<IBindPath> bindPaths, List<string> checkedPaths) |
| 103 | { | 67 | { |
| 104 | string resolved = null; | 68 | string resolved = null; |
| 105 | 69 | ||
| @@ -135,8 +99,6 @@ namespace WixToolset.Core.Bind | |||
| 135 | pathWithoutSourceDir = path.Substring(10); | 99 | pathWithoutSourceDir = path.Substring(10); |
| 136 | } | 100 | } |
| 137 | 101 | ||
| 138 | var bindPaths = this.BindPaths[bindStage]; | ||
| 139 | |||
| 140 | foreach (var bindPath in bindPaths) | 102 | foreach (var bindPath in bindPaths) |
| 141 | { | 103 | { |
| 142 | if (String.IsNullOrEmpty(bindName)) | 104 | if (String.IsNullOrEmpty(bindName)) |
| @@ -145,36 +107,18 @@ namespace WixToolset.Core.Bind | |||
| 145 | { | 107 | { |
| 146 | if (!String.IsNullOrEmpty(pathWithoutSourceDir)) | 108 | if (!String.IsNullOrEmpty(pathWithoutSourceDir)) |
| 147 | { | 109 | { |
| 148 | var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir); | 110 | resolved = ResolveWithBindPath(bindPath.Path, pathWithoutSourceDir, checkedPaths); |
| 149 | |||
| 150 | checkedPaths.Add(filePath); | ||
| 151 | if (CheckFileExists(filePath)) | ||
| 152 | { | ||
| 153 | resolved = filePath; | ||
| 154 | } | ||
| 155 | } | 111 | } |
| 156 | 112 | ||
| 157 | if (String.IsNullOrEmpty(resolved)) | 113 | if (String.IsNullOrEmpty(resolved)) |
| 158 | { | 114 | { |
| 159 | var filePath = Path.Combine(bindPath.Path, path); | 115 | resolved = ResolveWithBindPath(bindPath.Path, path, checkedPaths); |
| 160 | |||
| 161 | checkedPaths.Add(filePath); | ||
| 162 | if (CheckFileExists(filePath)) | ||
| 163 | { | ||
| 164 | resolved = filePath; | ||
| 165 | } | ||
| 166 | } | 116 | } |
| 167 | } | 117 | } |
| 168 | } | 118 | } |
| 169 | else if (bindName.Equals(bindPath.Name, StringComparison.OrdinalIgnoreCase)) | 119 | else if (bindName.Equals(bindPath.Name, StringComparison.OrdinalIgnoreCase)) |
| 170 | { | 120 | { |
| 171 | var filePath = Path.Combine(bindPath.Path, path); | 121 | resolved = ResolveWithBindPath(bindPath.Path, path, checkedPaths); |
| 172 | |||
| 173 | checkedPaths.Add(filePath); | ||
| 174 | if (CheckFileExists(filePath)) | ||
| 175 | { | ||
| 176 | resolved = filePath; | ||
| 177 | } | ||
| 178 | } | 122 | } |
| 179 | 123 | ||
| 180 | if (!String.IsNullOrEmpty(resolved)) | 124 | if (!String.IsNullOrEmpty(resolved)) |
| @@ -186,12 +130,26 @@ namespace WixToolset.Core.Bind | |||
| 186 | 130 | ||
| 187 | if (null == resolved) | 131 | if (null == resolved) |
| 188 | { | 132 | { |
| 189 | throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, source, symbolDefinition.Name, checkedPaths)); | 133 | throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, source, symbolDefinition?.Name, checkedPaths)); |
| 190 | } | 134 | } |
| 191 | 135 | ||
| 192 | return resolved; | 136 | return resolved; |
| 193 | } | 137 | } |
| 194 | 138 | ||
| 139 | private static string ResolveWithBindPath(string bindPath, string relativePath, List<string> checkedPaths) | ||
| 140 | { | ||
| 141 | var filePath = Path.Combine(bindPath, relativePath); | ||
| 142 | |||
| 143 | checkedPaths.Add(filePath); | ||
| 144 | |||
| 145 | if (CheckFileExists(filePath)) | ||
| 146 | { | ||
| 147 | return filePath; | ||
| 148 | } | ||
| 149 | |||
| 150 | return null; | ||
| 151 | } | ||
| 152 | |||
| 195 | private static bool CheckFileExists(string path) | 153 | private static bool CheckFileExists(string path) |
| 196 | { | 154 | { |
| 197 | try | 155 | try |
diff --git a/src/wix/WixToolset.Core/Librarian.cs b/src/wix/WixToolset.Core/Librarian.cs index 2762fb33..968dd946 100644 --- a/src/wix/WixToolset.Core/Librarian.cs +++ b/src/wix/WixToolset.Core/Librarian.cs | |||
| @@ -5,7 +5,6 @@ namespace WixToolset.Core | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Linq; | 7 | using System.Linq; |
| 8 | using WixToolset.Core.Bind; | ||
| 9 | using WixToolset.Core.Link; | 8 | using WixToolset.Core.Link; |
| 10 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 11 | using WixToolset.Extensibility.Data; | 10 | using WixToolset.Extensibility.Data; |
| @@ -21,6 +20,7 @@ namespace WixToolset.Core | |||
| 21 | this.ServiceProvider = serviceProvider; | 20 | this.ServiceProvider = serviceProvider; |
| 22 | 21 | ||
| 23 | this.Messaging = this.ServiceProvider.GetService<IMessaging>(); | 22 | this.Messaging = this.ServiceProvider.GetService<IMessaging>(); |
| 23 | this.FileResolver = this.ServiceProvider.GetService<IFileResolver>(); | ||
| 24 | this.LayoutServices = this.ServiceProvider.GetService<ILayoutServices>(); | 24 | this.LayoutServices = this.ServiceProvider.GetService<ILayoutServices>(); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| @@ -28,6 +28,8 @@ namespace WixToolset.Core | |||
| 28 | 28 | ||
| 29 | private IMessaging Messaging { get; } | 29 | private IMessaging Messaging { get; } |
| 30 | 30 | ||
| 31 | private IFileResolver FileResolver { get; } | ||
| 32 | |||
| 31 | private ILayoutServices LayoutServices { get; } | 33 | private ILayoutServices LayoutServices { get; } |
| 32 | 34 | ||
| 33 | /// <summary> | 35 | /// <summary> |
| @@ -97,7 +99,7 @@ namespace WixToolset.Core | |||
| 97 | { | 99 | { |
| 98 | var variableResolver = this.ServiceProvider.GetService<IVariableResolver>(); | 100 | var variableResolver = this.ServiceProvider.GetService<IVariableResolver>(); |
| 99 | 101 | ||
| 100 | var fileResolver = new FileResolver(context.BindPaths, context.Extensions); | 102 | var bindPaths = context.BindPaths.Where(b => b.Stage == BindStage.Normal).ToList(); |
| 101 | 103 | ||
| 102 | foreach (var symbol in sections.SelectMany(s => s.Symbols)) | 104 | foreach (var symbol in sections.SelectMany(s => s.Symbols)) |
| 103 | { | 105 | { |
| @@ -109,7 +111,7 @@ namespace WixToolset.Core | |||
| 109 | { | 111 | { |
| 110 | var resolution = variableResolver.ResolveVariables(symbol.SourceLineNumbers, pathField.Path); | 112 | var resolution = variableResolver.ResolveVariables(symbol.SourceLineNumbers, pathField.Path); |
| 111 | 113 | ||
| 112 | var file = fileResolver.Resolve(symbol.SourceLineNumbers, symbol.Definition, resolution.Value); | 114 | var file = this.FileResolver.ResolveFile(resolution.Value, context.Extensions, bindPaths, symbol.SourceLineNumbers, symbol.Definition); |
| 113 | 115 | ||
| 114 | if (!String.IsNullOrEmpty(file)) | 116 | if (!String.IsNullOrEmpty(file)) |
| 115 | { | 117 | { |
diff --git a/src/wix/WixToolset.Core/Resolver.cs b/src/wix/WixToolset.Core/Resolver.cs index e93f8e1b..f7aa6ff9 100644 --- a/src/wix/WixToolset.Core/Resolver.cs +++ b/src/wix/WixToolset.Core/Resolver.cs | |||
| @@ -23,12 +23,16 @@ namespace WixToolset.Core | |||
| 23 | this.ServiceProvider = serviceProvider; | 23 | this.ServiceProvider = serviceProvider; |
| 24 | 24 | ||
| 25 | this.Messaging = serviceProvider.GetService<IMessaging>(); | 25 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
| 26 | |||
| 27 | this.FileResolver = serviceProvider.GetService<IFileResolver>(); | ||
| 26 | } | 28 | } |
| 27 | 29 | ||
| 28 | private IServiceProvider ServiceProvider { get; } | 30 | private IServiceProvider ServiceProvider { get; } |
| 29 | 31 | ||
| 30 | private IMessaging Messaging { get; } | 32 | private IMessaging Messaging { get; } |
| 31 | 33 | ||
| 34 | private IFileResolver FileResolver { get; } | ||
| 35 | |||
| 32 | public IResolveResult Resolve(IResolveContext context) | 36 | public IResolveResult Resolve(IResolveContext context) |
| 33 | { | 37 | { |
| 34 | foreach (var extension in context.Extensions) | 38 | foreach (var extension in context.Extensions) |
| @@ -45,7 +49,7 @@ namespace WixToolset.Core | |||
| 45 | 49 | ||
| 46 | this.LocalizeUI(variableResolver, context.IntermediateRepresentation); | 50 | this.LocalizeUI(variableResolver, context.IntermediateRepresentation); |
| 47 | 51 | ||
| 48 | resolveResult = this.DoResolve(context, variableResolver); | 52 | resolveResult = this.ResolveFields(context, variableResolver); |
| 49 | 53 | ||
| 50 | var primaryLocalization = filteredLocalizations.FirstOrDefault(); | 54 | var primaryLocalization = filteredLocalizations.FirstOrDefault(); |
| 51 | 55 | ||
| @@ -71,49 +75,18 @@ namespace WixToolset.Core | |||
| 71 | return resolveResult; | 75 | return resolveResult; |
| 72 | } | 76 | } |
| 73 | 77 | ||
| 74 | private ResolveResult DoResolve(IResolveContext context, IVariableResolver variableResolver) | 78 | private ResolveResult ResolveFields(IResolveContext context, IVariableResolver variableResolver) |
| 75 | { | 79 | { |
| 76 | var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch); | ||
| 77 | |||
| 78 | var filesWithEmbeddedFiles = new ExtractEmbeddedFiles(); | 80 | var filesWithEmbeddedFiles = new ExtractEmbeddedFiles(); |
| 79 | 81 | ||
| 80 | IReadOnlyCollection<DelayedField> delayedFields; | 82 | IReadOnlyCollection<DelayedField> delayedFields; |
| 81 | { | 83 | { |
| 82 | var command = new ResolveFieldsCommand(); | 84 | var command = new ResolveFieldsCommand(this.Messaging, this.FileResolver, variableResolver, context.BindPaths, context.Extensions, filesWithEmbeddedFiles, context.IntermediateFolder, context.IntermediateRepresentation, context.AllowUnresolvedVariables); |
| 83 | command.Messaging = this.Messaging; | ||
| 84 | command.BuildingPatch = buildingPatch; | ||
| 85 | command.VariableResolver = variableResolver; | ||
| 86 | command.BindPaths = context.BindPaths; | ||
| 87 | command.Extensions = context.Extensions; | ||
| 88 | command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | ||
| 89 | command.IntermediateFolder = context.IntermediateFolder; | ||
| 90 | command.Intermediate = context.IntermediateRepresentation; | ||
| 91 | command.SupportDelayedResolution = true; | ||
| 92 | command.AllowUnresolvedVariables = context.AllowUnresolvedVariables; | ||
| 93 | command.Execute(); | 85 | command.Execute(); |
| 94 | 86 | ||
| 95 | delayedFields = command.DelayedFields; | 87 | delayedFields = command.DelayedFields; |
| 96 | } | 88 | } |
| 97 | 89 | ||
| 98 | #if TODO_PATCHING | ||
| 99 | if (context.IntermediateRepresentation.SubStorages != null) | ||
| 100 | { | ||
| 101 | foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) | ||
| 102 | { | ||
| 103 | var command = new ResolveFieldsCommand(); | ||
| 104 | command.BuildingPatch = buildingPatch; | ||
| 105 | command.BindVariableResolver = context.WixVariableResolver; | ||
| 106 | command.BindPaths = context.BindPaths; | ||
| 107 | command.Extensions = context.Extensions; | ||
| 108 | command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | ||
| 109 | command.IntermediateFolder = context.IntermediateFolder; | ||
| 110 | command.Intermediate = context.IntermediateRepresentation; | ||
| 111 | command.SupportDelayedResolution = false; | ||
| 112 | command.Execute(); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | #endif | ||
| 116 | |||
| 117 | var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles(); | 90 | var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles(); |
| 118 | 91 | ||
| 119 | context.IntermediateRepresentation.UpdateLevel(IntermediateLevels.Resolved); | 92 | context.IntermediateRepresentation.UpdateLevel(IntermediateLevels.Resolved); |
diff --git a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs index fa6b369d..5620bcd2 100644 --- a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs | |||
| @@ -27,6 +27,7 @@ namespace WixToolset.Core | |||
| 27 | this.AddService((provider, singletons) => AddSingleton<ILayoutServices>(singletons, new LayoutServices(provider))); | 27 | this.AddService((provider, singletons) => AddSingleton<ILayoutServices>(singletons, new LayoutServices(provider))); |
| 28 | this.AddService((provider, singletons) => AddSingleton<IBackendHelper>(singletons, new BackendHelper(provider))); | 28 | this.AddService((provider, singletons) => AddSingleton<IBackendHelper>(singletons, new BackendHelper(provider))); |
| 29 | this.AddService((provider, singletons) => AddSingleton<IPathResolver>(singletons, new PathResolver())); | 29 | this.AddService((provider, singletons) => AddSingleton<IPathResolver>(singletons, new PathResolver())); |
| 30 | this.AddService((provider, singletons) => AddSingleton<IFileResolver>(singletons, new FileResolver())); | ||
| 30 | this.AddService((provider, singletons) => AddSingleton<IFileSystem>(singletons, new FileSystem())); | 31 | this.AddService((provider, singletons) => AddSingleton<IFileSystem>(singletons, new FileSystem())); |
| 31 | this.AddService((provider, singletons) => AddSingleton<IWixBranding>(singletons, new WixBranding())); | 32 | this.AddService((provider, singletons) => AddSingleton<IWixBranding>(singletons, new WixBranding())); |
| 32 | 33 | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs index a8aff8d8..c330fd02 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs | |||
| @@ -5,6 +5,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.ComponentModel; | 7 | using System.ComponentModel; |
| 8 | using System.Diagnostics; | ||
| 8 | using System.IO; | 9 | using System.IO; |
| 9 | using System.Linq; | 10 | using System.Linq; |
| 10 | using System.Runtime.InteropServices; | 11 | using System.Runtime.InteropServices; |
| @@ -22,8 +23,11 @@ namespace WixToolsetTest.CoreIntegration | |||
| 22 | public class PatchFixture : IDisposable | 23 | public class PatchFixture : IDisposable |
| 23 | { | 24 | { |
| 24 | private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd"; | 25 | private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd"; |
| 26 | private static readonly XName TargetProductCodeName = PatchNamespace + "TargetProductCode"; | ||
| 27 | |||
| 25 | private readonly DisposableFileSystem tempFileSystem; | 28 | private readonly DisposableFileSystem tempFileSystem; |
| 26 | private readonly string tempBaseFolder; | 29 | private readonly string tempBaseFolder; |
| 30 | private readonly string templateSourceFolder; | ||
| 27 | private readonly string templateBaselinePdb; | 31 | private readonly string templateBaselinePdb; |
| 28 | private readonly string templateUpdatePdb; | 32 | private readonly string templateUpdatePdb; |
| 29 | private readonly string templateUpdateNoFilesChangedPdb; | 33 | private readonly string templateUpdateNoFilesChangedPdb; |
| @@ -33,14 +37,14 @@ namespace WixToolsetTest.CoreIntegration | |||
| 33 | this.tempFileSystem = new DisposableFileSystem(); | 37 | this.tempFileSystem = new DisposableFileSystem(); |
| 34 | this.tempBaseFolder = this.tempFileSystem.GetFolder(); | 38 | this.tempBaseFolder = this.tempFileSystem.GetFolder(); |
| 35 | 39 | ||
| 36 | var templateSourceFolder = TestData.Get(@"TestData", "PatchTemplatePackage"); | 40 | this.templateSourceFolder = TestData.Get(@"TestData", "PatchTemplatePackage"); |
| 37 | var tempFolderBaseline = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "baseline"); | 41 | var tempFolderBaseline = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "baseline"); |
| 38 | var tempFolderUpdate = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "update"); | 42 | var tempFolderUpdate = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "update"); |
| 39 | var tempFolderUpdateNoFileChanges = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "updatewithoutfilechanges"); | 43 | var tempFolderUpdateNoFileChanges = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "updatewithoutfilechanges"); |
| 40 | 44 | ||
| 41 | this.templateBaselinePdb = BuildMsi("Baseline.msi", templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", new[] { Path.Combine(templateSourceFolder, ".baseline-data") }); | 45 | this.templateBaselinePdb = BuildMsi("Baseline.msi", this.templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); |
| 42 | this.templateUpdatePdb = BuildMsi("Update.msi", templateSourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(templateSourceFolder, ".update-data") }); | 46 | this.templateUpdatePdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(this.templateSourceFolder, ".update-data") }); |
| 43 | this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(templateSourceFolder, ".baseline-data") }); | 47 | this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", this.templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", new[] { Path.Combine(this.templateSourceFolder, ".baseline-data") }); |
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | public void Dispose() | 50 | public void Dispose() |
| @@ -49,7 +53,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 49 | } | 53 | } |
| 50 | 54 | ||
| 51 | [Fact] | 55 | [Fact] |
| 52 | public void CanBuildSimplePatch() | 56 | public void CanBuildSimplePatchUsingWixpdbs() |
| 53 | { | 57 | { |
| 54 | var folder = TestData.Get(@"TestData", "PatchSingle"); | 58 | var folder = TestData.Get(@"TestData", "PatchSingle"); |
| 55 | 59 | ||
| @@ -66,14 +70,13 @@ namespace WixToolsetTest.CoreIntegration | |||
| 66 | Assert.True(File.Exists(update1Pdb)); | 70 | Assert.True(File.Exists(update1Pdb)); |
| 67 | 71 | ||
| 68 | var doc = GetExtractPatchXml(patchPath); | 72 | var doc = GetExtractPatchXml(patchPath); |
| 69 | WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | 73 | WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value); |
| 70 | 74 | ||
| 71 | var names = Query.GetSubStorageNames(patchPath); | 75 | var names = Query.GetSubStorageNames(patchPath); |
| 72 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | 76 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); |
| 73 | 77 | ||
| 74 | var cab = Path.Combine(tempFolder, "foo.cab"); | 78 | var cab = Path.Combine(tempFolder, "foo.cab"); |
| 75 | Query.ExtractStream(patchPath, "foo.cab", cab); | 79 | Query.ExtractStream(patchPath, "foo.cab", cab); |
| 76 | Assert.True(File.Exists(cab)); | ||
| 77 | 80 | ||
| 78 | var files = Query.GetCabinetFiles(cab); | 81 | var files = Query.GetCabinetFiles(cab); |
| 79 | WixAssert.CompareLineByLine(new[] { "a.txt", "b.txt" }, files.Select(f => f.Name).ToArray()); | 82 | WixAssert.CompareLineByLine(new[] { "a.txt", "b.txt" }, files.Select(f => f.Name).ToArray()); |
| @@ -81,7 +84,37 @@ namespace WixToolsetTest.CoreIntegration | |||
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | [Fact] | 86 | [Fact] |
| 84 | public void CanBuildSimplePatchWithFileChanges() | 87 | public void CanBuildSimplePatchWithFileChangesUsingMsi() |
| 88 | { | ||
| 89 | var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChangesUsingMsi"); | ||
| 90 | |||
| 91 | using (var fs = new DisposableFileSystem()) | ||
| 92 | { | ||
| 93 | var baseFolder = fs.GetFolder(); | ||
| 94 | var tempFolderPatch = Path.Combine(baseFolder, "patch"); | ||
| 95 | |||
| 96 | var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }); | ||
| 97 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | ||
| 98 | |||
| 99 | var doc = GetExtractPatchXml(patchPath); | ||
| 100 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); | ||
| 101 | |||
| 102 | var names = Query.GetSubStorageNames(patchPath); | ||
| 103 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | ||
| 104 | |||
| 105 | var cab = Path.Combine(baseFolder, "foo.cab"); | ||
| 106 | Query.ExtractStream(patchPath, "foo.cab", cab); | ||
| 107 | |||
| 108 | var files = Query.GetCabinetFiles(cab); | ||
| 109 | var file = files.Single(); | ||
| 110 | WixAssert.StringEqual("a.txt", file.Name); | ||
| 111 | var contents = file.OpenText().ReadToEnd(); | ||
| 112 | WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | [Fact] | ||
| 117 | public void CanBuildSimplePatchWithFileChangesUsingWixpdb() | ||
| 85 | { | 118 | { |
| 86 | var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges"); | 119 | var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges"); |
| 87 | 120 | ||
| @@ -94,7 +127,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 94 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | 127 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); |
| 95 | 128 | ||
| 96 | var doc = GetExtractPatchXml(patchPath); | 129 | var doc = GetExtractPatchXml(patchPath); |
| 97 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | 130 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); |
| 98 | 131 | ||
| 99 | var names = Query.GetSubStorageNames(patchPath); | 132 | var names = Query.GetSubStorageNames(patchPath); |
| 100 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | 133 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); |
| @@ -106,11 +139,73 @@ namespace WixToolsetTest.CoreIntegration | |||
| 106 | var file = files.Single(); | 139 | var file = files.Single(); |
| 107 | WixAssert.StringEqual("a.txt", file.Name); | 140 | WixAssert.StringEqual("a.txt", file.Name); |
| 108 | var contents = file.OpenText().ReadToEnd(); | 141 | var contents = file.OpenText().ReadToEnd(); |
| 109 | WixAssert.StringEqual("This is A v1.0.1\r\n", contents); | 142 | WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents); |
| 110 | } | 143 | } |
| 111 | } | 144 | } |
| 112 | 145 | ||
| 113 | [Fact] | 146 | [Fact] |
| 147 | public void CanBuildSimplePatchWithFileChangesUsingWixpdbAndAlternativeUpdatedSourceFolder() | ||
| 148 | { | ||
| 149 | var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges"); | ||
| 150 | |||
| 151 | using (var fs = new DisposableFileSystem()) | ||
| 152 | { | ||
| 153 | var baseFolder = fs.GetFolder(); | ||
| 154 | var tempFolderPatch = Path.Combine(baseFolder, "patch"); | ||
| 155 | |||
| 156 | var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }, updateBindpaths: new[] { Path.Combine(this.templateSourceFolder, ".update-data-alternative") }); | ||
| 157 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | ||
| 158 | |||
| 159 | var doc = GetExtractPatchXml(patchPath); | ||
| 160 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); | ||
| 161 | |||
| 162 | var names = Query.GetSubStorageNames(patchPath); | ||
| 163 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | ||
| 164 | |||
| 165 | var cab = Path.Combine(baseFolder, "foo.cab"); | ||
| 166 | Query.ExtractStream(patchPath, "foo.cab", cab); | ||
| 167 | |||
| 168 | var files = Query.GetCabinetFiles(cab); | ||
| 169 | var file = files.Single(); | ||
| 170 | WixAssert.StringEqual("a.txt", file.Name); | ||
| 171 | var contents = file.OpenText().ReadToEnd(); | ||
| 172 | WixAssert.StringEqual("This is A v1.0.1 from the '.update-data-alternative' folder in 'PatchTemplatePackage'.\r\n\r\nDiam quis enim lobortis scelerisque fermentum dui faucibus in ornare.\r\n", contents); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | [Fact] | ||
| 177 | public void CanBuildPatchFromAdminImage() | ||
| 178 | { | ||
| 179 | var sourceFolder = TestData.Get(@"TestData", "PatchUsingAdminImages"); | ||
| 180 | |||
| 181 | var baseFolder = this.tempFileSystem.GetFolder(); | ||
| 182 | var tempFolderPatch = Path.Combine(baseFolder, "patch"); | ||
| 183 | var adminBaselineFolder = Path.Combine(baseFolder, "admin-baseline"); | ||
| 184 | var adminUpdateFolder = Path.Combine(baseFolder, "admin-update"); | ||
| 185 | |||
| 186 | CreateAdminImage(this.templateBaselinePdb, adminBaselineFolder); | ||
| 187 | CreateAdminImage(this.templateUpdatePdb, adminUpdateFolder); | ||
| 188 | |||
| 189 | var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { adminBaselineFolder, adminUpdateFolder }); | ||
| 190 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | ||
| 191 | |||
| 192 | var doc = GetExtractPatchXml(patchPath); | ||
| 193 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); | ||
| 194 | |||
| 195 | var names = Query.GetSubStorageNames(patchPath); | ||
| 196 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | ||
| 197 | |||
| 198 | var cab = Path.Combine(baseFolder, "foo.cab"); | ||
| 199 | Query.ExtractStream(patchPath, "foo.cab", cab); | ||
| 200 | |||
| 201 | var files = Query.GetCabinetFiles(cab); | ||
| 202 | var file = files.Single(); | ||
| 203 | WixAssert.StringEqual("a.txt", file.Name); | ||
| 204 | var contents = file.OpenText().ReadToEnd(); | ||
| 205 | WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents); | ||
| 206 | } | ||
| 207 | |||
| 208 | [Fact] | ||
| 114 | public void CanBuildSimplePatchWithNoFileChanges() | 209 | public void CanBuildSimplePatchWithNoFileChanges() |
| 115 | { | 210 | { |
| 116 | var folder = TestData.Get(@"TestData", "PatchNoFileChanges"); | 211 | var folder = TestData.Get(@"TestData", "PatchNoFileChanges"); |
| @@ -123,14 +218,13 @@ namespace WixToolsetTest.CoreIntegration | |||
| 123 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | 218 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); |
| 124 | 219 | ||
| 125 | var doc = GetExtractPatchXml(patchPath); | 220 | var doc = GetExtractPatchXml(patchPath); |
| 126 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | 221 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); |
| 127 | 222 | ||
| 128 | var names = Query.GetSubStorageNames(patchPath); | 223 | var names = Query.GetSubStorageNames(patchPath); |
| 129 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | 224 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); |
| 130 | 225 | ||
| 131 | var cab = Path.Combine(tempFolder, "foo.cab"); | 226 | var cab = Path.Combine(tempFolder, "foo.cab"); |
| 132 | Query.ExtractStream(patchPath, "foo.cab", cab); | 227 | Query.ExtractStream(patchPath, "foo.cab", cab); |
| 133 | Assert.True(File.Exists(cab)); | ||
| 134 | 228 | ||
| 135 | var files = Query.GetCabinetFiles(cab); | 229 | var files = Query.GetCabinetFiles(cab); |
| 136 | Assert.Empty(files); | 230 | Assert.Empty(files); |
| @@ -155,14 +249,13 @@ namespace WixToolsetTest.CoreIntegration | |||
| 155 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | 249 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); |
| 156 | 250 | ||
| 157 | var doc = GetExtractPatchXml(patchPath); | 251 | var doc = GetExtractPatchXml(patchPath); |
| 158 | WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | 252 | WixAssert.StringEqual("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(TargetProductCodeName).Value); |
| 159 | 253 | ||
| 160 | var names = Query.GetSubStorageNames(patchPath); | 254 | var names = Query.GetSubStorageNames(patchPath); |
| 161 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); | 255 | WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); |
| 162 | 256 | ||
| 163 | var cab = Path.Combine(baseFolder, "foo.cab"); | 257 | var cab = Path.Combine(baseFolder, "foo.cab"); |
| 164 | Query.ExtractStream(patchPath, "foo.cab", cab); | 258 | Query.ExtractStream(patchPath, "foo.cab", cab); |
| 165 | Assert.True(File.Exists(cab)); | ||
| 166 | 259 | ||
| 167 | var files = Query.GetCabinetFiles(cab); | 260 | var files = Query.GetCabinetFiles(cab); |
| 168 | var file = files.Single(); | 261 | var file = files.Single(); |
| @@ -197,7 +290,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 197 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | 290 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); |
| 198 | 291 | ||
| 199 | var doc = GetExtractPatchXml(patchPath); | 292 | var doc = GetExtractPatchXml(patchPath); |
| 200 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | 293 | WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); |
| 201 | 294 | ||
| 202 | var names = Query.GetSubStorageNames(patchPath); | 295 | var names = Query.GetSubStorageNames(patchPath); |
| 203 | WixAssert.CompareLineByLine(new[] { "#ThisBaseLineIdIsTooLongAndGe.1", "ThisBaseLineIdIsTooLongAndGe.1" }, names); | 296 | WixAssert.CompareLineByLine(new[] { "#ThisBaseLineIdIsTooLongAndGe.1", "ThisBaseLineIdIsTooLongAndGe.1" }, names); |
| @@ -217,12 +310,12 @@ namespace WixToolsetTest.CoreIntegration | |||
| 217 | var tempFolderPatch = Path.Combine(baseFolder, "patch"); | 310 | var tempFolderPatch = Path.Combine(baseFolder, "patch"); |
| 218 | 311 | ||
| 219 | var baselinePdb = BuildMsi("Baseline.msi", sourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0"); | 312 | var baselinePdb = BuildMsi("Baseline.msi", sourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0"); |
| 220 | var update1Pdb = BuildMsi("Update.msi", sourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1"); | 313 | var updatePdb = BuildMsi("Update.msi", sourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1"); |
| 221 | var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePdb), Path.GetDirectoryName(update1Pdb) }, hasNoFiles: true); | 314 | var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePdb), Path.GetDirectoryName(updatePdb) }, hasNoFiles: true); |
| 222 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | 315 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); |
| 223 | 316 | ||
| 224 | Assert.True(File.Exists(baselinePdb)); | 317 | var doc = GetExtractPatchXml(patchPath); |
| 225 | Assert.True(File.Exists(update1Pdb)); | 318 | WixAssert.StringEqual("{7C871EC1-1F89-4850-A6A9-D7A4C21769F6}", doc.Root.Element(TargetProductCodeName).Value); |
| 226 | } | 319 | } |
| 227 | } | 320 | } |
| 228 | 321 | ||
| @@ -317,7 +410,28 @@ namespace WixToolsetTest.CoreIntegration | |||
| 317 | return Path.ChangeExtension(outputPath, ".wixpdb"); | 410 | return Path.ChangeExtension(outputPath, ".wixpdb"); |
| 318 | } | 411 | } |
| 319 | 412 | ||
| 320 | private static string BuildMsp(string outputName, string sourceFolder, string baseFolder, string defineV, IEnumerable<string> bindpaths = null, bool hasNoFiles = false, bool warningsAsErrors = true) | 413 | private static string BuildMst(string transformName, string baseFolder, string templateBaselinePdb, string templateUpdatePdb) |
| 414 | { | ||
| 415 | var outputPath = Path.Combine(baseFolder, transformName); | ||
| 416 | |||
| 417 | var args = new List<string> | ||
| 418 | { | ||
| 419 | "msi", "transform", | ||
| 420 | templateBaselinePdb, | ||
| 421 | templateUpdatePdb, | ||
| 422 | "-intermediateFolder", Path.Combine(baseFolder), | ||
| 423 | "-t", "patch", | ||
| 424 | "-o", outputPath, | ||
| 425 | }; | ||
| 426 | |||
| 427 | var result = WixRunner.Execute(args.ToArray()); | ||
| 428 | |||
| 429 | result.AssertSuccess(); | ||
| 430 | |||
| 431 | return outputPath; | ||
| 432 | } | ||
| 433 | |||
| 434 | private static string BuildMsp(string outputName, string sourceFolder, string baseFolder, string defineV, IEnumerable<string> bindpaths = null, IEnumerable<string> targetBindpaths = null, IEnumerable<string> updateBindpaths = null, bool hasNoFiles = false, bool warningsAsErrors = true) | ||
| 321 | { | 435 | { |
| 322 | var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName)); | 436 | var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName)); |
| 323 | 437 | ||
| @@ -332,10 +446,22 @@ namespace WixToolsetTest.CoreIntegration | |||
| 332 | "-o", outputPath | 446 | "-o", outputPath |
| 333 | }; | 447 | }; |
| 334 | 448 | ||
| 335 | foreach (var additionaBindPath in bindpaths ?? Enumerable.Empty<string>()) | 449 | foreach (var additionalBindPath in bindpaths ?? Enumerable.Empty<string>()) |
| 336 | { | 450 | { |
| 337 | args.Add("-bindpath"); | 451 | args.Add("-bindpath"); |
| 338 | args.Add(additionaBindPath); | 452 | args.Add(additionalBindPath); |
| 453 | } | ||
| 454 | |||
| 455 | foreach (var targetBindPath in targetBindpaths ?? Enumerable.Empty<string>()) | ||
| 456 | { | ||
| 457 | args.Add("-bindpath:target"); | ||
| 458 | args.Add(targetBindPath); | ||
| 459 | } | ||
| 460 | |||
| 461 | foreach (var updateBindpath in updateBindpaths ?? Enumerable.Empty<string>()) | ||
| 462 | { | ||
| 463 | args.Add("-bindpath:update"); | ||
| 464 | args.Add(updateBindpath); | ||
| 339 | } | 465 | } |
| 340 | 466 | ||
| 341 | var result = WixRunner.Execute(warningsAsErrors, args.ToArray()); | 467 | var result = WixRunner.Execute(warningsAsErrors, args.ToArray()); |
| @@ -365,6 +491,16 @@ namespace WixToolsetTest.CoreIntegration | |||
| 365 | return Path.ChangeExtension(outputPath, ".wixpdb"); | 491 | return Path.ChangeExtension(outputPath, ".wixpdb"); |
| 366 | } | 492 | } |
| 367 | 493 | ||
| 494 | private static void CreateAdminImage(string msiPath, string targetDir) | ||
| 495 | { | ||
| 496 | var args = $"/a {Path.ChangeExtension(msiPath, "msi")} TARGETDIR={targetDir} /qn"; | ||
| 497 | |||
| 498 | var proc = Process.Start("msiexec.exe", args); | ||
| 499 | proc.WaitForExit(5000); | ||
| 500 | |||
| 501 | Assert.Equal(0, proc.ExitCode); | ||
| 502 | } | ||
| 503 | |||
| 368 | private static XDocument GetExtractPatchXml(string path) | 504 | private static XDocument GetExtractPatchXml(string path) |
| 369 | { | 505 | { |
| 370 | var buffer = new StringBuilder(65535); | 506 | var buffer = new StringBuilder(65535); |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.baseline-data/A.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.baseline-data/A.txt index 6fd385bd..f6c69979 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.baseline-data/A.txt +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.baseline-data/A.txt | |||
| @@ -1 +1 @@ | |||
| This is A v1.0.0 | This is A v1.0.0 from the "PatchTemplatePackage\.baseline-data" folder. | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data-alternative/A.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data-alternative/A.txt new file mode 100644 index 00000000..17e8c861 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data-alternative/A.txt | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | This is A v1.0.1 from the '.update-data-alternative' folder in 'PatchTemplatePackage'. | ||
| 2 | |||
| 3 | Diam quis enim lobortis scelerisque fermentum dui faucibus in ornare. | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data/A.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data/A.txt index f7ec8b4e..26832a47 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data/A.txt +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data/A.txt | |||
| @@ -1 +1,3 @@ | |||
| 1 | This is A v1.0.1 | 1 | This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'. |
| 2 | |||
| 3 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod. | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchUsingAdminImages/Patch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchUsingAdminImages/Patch.wxs new file mode 100644 index 00000000..f9ea1f5d --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchUsingAdminImages/Patch.wxs | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'> | ||
| 2 | <Patch | ||
| 3 | AllowRemoval="yes" | ||
| 4 | DisplayName="~Test Patch v$(V)" | ||
| 5 | Description="~Test Small Update Patch v$(V)" | ||
| 6 | MoreInfoURL="http://www.example.com/" | ||
| 7 | Manufacturer="Example Corporation" | ||
| 8 | Classification="Update"> | ||
| 9 | |||
| 10 | <Media Id="1" Cabinet="foo.cab"> | ||
| 11 | <PatchBaseline Id="RTM" BaselineFile="Baseline.msi" UpdateFile="Update.msi" /> | ||
| 12 | </Media> | ||
| 13 | |||
| 14 | <PatchFamily Id='SequenceFamily' Version='$(V)' /> | ||
| 15 | </Patch> | ||
| 16 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithFileChangesUsingMsi/Patch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithFileChangesUsingMsi/Patch.wxs new file mode 100644 index 00000000..f9ea1f5d --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithFileChangesUsingMsi/Patch.wxs | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | <Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'> | ||
| 2 | <Patch | ||
| 3 | AllowRemoval="yes" | ||
| 4 | DisplayName="~Test Patch v$(V)" | ||
| 5 | Description="~Test Small Update Patch v$(V)" | ||
| 6 | MoreInfoURL="http://www.example.com/" | ||
| 7 | Manufacturer="Example Corporation" | ||
| 8 | Classification="Update"> | ||
| 9 | |||
| 10 | <Media Id="1" Cabinet="foo.cab"> | ||
| 11 | <PatchBaseline Id="RTM" BaselineFile="Baseline.msi" UpdateFile="Update.msi" /> | ||
| 12 | </Media> | ||
| 13 | |||
| 14 | <PatchFamily Id='SequenceFamily' Version='$(V)' /> | ||
| 15 | </Patch> | ||
| 16 | </Wix> | ||
