diff options
author | Rob Mensching <rob@firegiant.com> | 2022-09-03 16:08:33 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-09-23 09:50:52 -0700 |
commit | 63b588262aa0b7869741aa888905c1eeda7ae2f8 (patch) | |
tree | 54ec7ccb6e1a6f4859d3d2f0bbd8c79cff50b1ed | |
parent | db2fe6554f449a108b77cd3d2c7f30a68d54ddc2 (diff) | |
download | wix-63b588262aa0b7869741aa888905c1eeda7ae2f8.tar.gz wix-63b588262aa0b7869741aa888905c1eeda7ae2f8.tar.bz2 wix-63b588262aa0b7869741aa888905c1eeda7ae2f8.zip |
Implement single pass patch build
This new implementation of patching in WiX v4 creates an MSP's transforms and
MSP file in a single pass. This single pass allows the build to use MSI as the
source of files for diffing purposes.
Completes 6401
Fixes 4629
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> | ||