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
Rob Mensching committed
Sep 3, 2022 at 16:08 UTC
63b588262aa0b7869741aa888905c1eeda7ae2f8
56 files changed
+1508
-1676
src/api/wix/WixToolset.Data/ErrorMessages.cs
+2
-1
@@ -651,7 +651,8 @@ namespace WixToolset.Data
651
public static Message FileNotFound(SourceLineNumber sourceLineNumbers, string file, string fileType, IEnumerable<string> checkedPaths)
652
{
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);
656
}
657
658
public static Message FileOrDirectoryPathRequired(string parameter)
src/api/wix/WixToolset.Data/Symbols/WixPatchBaselineSymbol.cs
-8
@@ -14,7 +14,6 @@ namespace WixToolset.Data
14
new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.ValidationFlags), IntermediateFieldType.Number),
15
new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.BaselineFile), IntermediateFieldType.Path),
16
new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.UpdateFile), IntermediateFieldType.Path),
17
- new IntermediateFieldDefinition(nameof(WixPatchBaselineSymbolFields.TransformFile), IntermediateFieldType.Path),
17
},
18
typeof(WixPatchBaselineSymbol));
19
}
@@ -28,7 +27,6 @@ namespace WixToolset.Data.Symbols
27
ValidationFlags,
28
BaselineFile,
29
UpdateFile,
31
- TransformFile,
30
}
31
32
public class WixPatchBaselineSymbol : IntermediateSymbol
@@ -66,11 +64,5 @@ namespace WixToolset.Data.Symbols
64
get => this.Fields[(int)WixPatchBaselineSymbolFields.UpdateFile].AsPath();
65
set => this.Set((int)WixPatchBaselineSymbolFields.UpdateFile, value);
66
}
69
-
70
- public IntermediateFieldPathValue TransformFile
71
- {
72
- get => this.Fields[(int)WixPatchBaselineSymbolFields.TransformFile].AsPath();
73
- set => this.Set((int)WixPatchBaselineSymbolFields.TransformFile, value);
74
- }
67
}
68
}
src/api/wix/WixToolset.Extensibility/Data/IBindContext.cs
+5
@@ -17,6 +17,11 @@ namespace WixToolset.Extensibility.Data
17
/// </summary>
18
IServiceProvider ServiceProvider { get; }
19
20
+ /// <summary>
21
+ /// Bind paths used during resolution.
22
+ /// </summary>
23
+ IReadOnlyCollection<IBindPath> BindPaths { get; set; }
24
+
25
/// <summary>
26
/// Counnt of threads to use in cabbing.
27
/// </summary>
src/api/wix/WixToolset.Extensibility/Data/IFileFacade.cs
+8
-40
@@ -5,33 +5,12 @@ namespace WixToolset.Extensibility.Data
5
using System.Collections.Generic;
6
using WixToolset.Data;
7
using WixToolset.Data.Symbols;
8
- using WixToolset.Data.WindowsInstaller.Rows;
8
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.
11
/// </summary>
12
public interface IFileFacade
13
{
15
- /// <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
-
14
/// <summary>
15
/// Component containing the file.
16
/// </summary>
@@ -57,21 +36,6 @@ namespace WixToolset.Extensibility.Data
36
/// </summary>
37
int FileSize { get; set; }
38
60
- /// <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
-
39
/// <summary>
40
/// Underlying identifier of the file.
41
/// </summary>
@@ -118,9 +82,13 @@ namespace WixToolset.Extensibility.Data
82
string Version { get; set; }
83
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.
91
/// </summary>
123
- /// <returns><c>FileRow</c> if one is present, otherwise throws.</returns>
124
- FileRow GetFileRow();
92
+ ICollection<MsiAssemblyNameSymbol> AssemblyNameSymbols { get; }
93
}
94
}
src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
-5
@@ -62,11 +62,6 @@ namespace WixToolset.Extensibility.Data
62
/// </summary>
63
string IntermediateFolder { get; set; }
64
65
- /// <summary>
66
- /// Gets or sets whether the decompiler admin image.
67
- /// </summary>
68
- bool IsAdminImage { get; set; }
69
-
65
/// <summary>
66
/// Gets or sets where to output the result.
67
/// </summary>
src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
-24
@@ -5,8 +5,6 @@ namespace WixToolset.Extensibility.Services
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Data;
8
- using WixToolset.Data.Symbols;
9
- using WixToolset.Data.WindowsInstaller.Rows;
8
using WixToolset.Extensibility.Data;
9
10
/// <summary>
@@ -14,28 +12,6 @@ namespace WixToolset.Extensibility.Services
12
/// </summary>
13
public interface IBackendHelper : ILayoutServices
14
{
17
- /// <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
-
15
/// <summary>
16
/// Creates a MSI compatible GUID.
17
/// </summary>
src/api/wix/WixToolset.Extensibility/Services/IFileResolver.cs
new
+38
@@ -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
+}
src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerBackendHelper.cs
+17
@@ -3,13 +3,30 @@
3
namespace WixToolset.Extensibility.Services
4
{
5
using WixToolset.Data;
6
+ using WixToolset.Data.Symbols;
7
using WixToolset.Data.WindowsInstaller;
8
+ using WixToolset.Data.WindowsInstaller.Rows;
9
+ using WixToolset.Extensibility.Data;
10
11
/// <summary>
12
/// Interface provided to help Windows Installer backend extensions.
13
/// </summary>
14
public interface IWindowsInstallerBackendHelper : IBackendHelper
15
{
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>
31
/// Creates a <see cref="Row"/> in the specified table.
32
/// </summary>
src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
-15
@@ -35,21 +35,6 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
35
36
#region IBackendHelper interfaces
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
-
38
public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null)
39
{
40
return this.backendHelper.CreateFileTransfer(source, destination, move, sourceLineNumbers);
src/wix/WixToolset.Core.WindowsInstaller/Bind/AddCreateFoldersCommand.cs
+1
-1
@@ -35,4 +35,4 @@ namespace WixToolset.Core.WindowsInstaller.Bind
35
}
36
}
37
}
38
-}
\ No newline at end of file
38
+}
src/wix/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
+14
-19
@@ -145,8 +145,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
145
var mediaSymbol = patchMediaByDiskId[baselineSymbol.DiskId];
146
147
// Ensure that files are sequenced after the last file in any transform.
148
- var transformMediaTable = mainTransform.Transform.Tables["Media"];
149
- if (null != transformMediaTable && 0 < transformMediaTable.Rows.Count)
148
+ if (mainTransform.Transform.Tables.TryGetTable("Media", out var transformMediaTable))
149
{
150
foreach (MediaRow transformMediaRow in transformMediaTable.Rows)
151
{
@@ -370,16 +369,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
369
{
370
if (transform.TryGetTable(tableName, out var table))
371
{
373
- foreach (var row in table.Rows)
372
+ foreach (var row in table.Rows.Where(r => r.Operation == RowOperation.Add))
373
{
375
- if (row.Operation == RowOperation.Add)
376
- {
377
- success = false;
374
+ success = false;
375
379
- var primaryKey = row.GetPrimaryKey('/') ?? String.Empty;
376
+ var primaryKey = row.GetPrimaryKey('/') ?? String.Empty;
377
381
- this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey));
382
- }
378
+ this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey));
379
}
380
}
381
}
@@ -925,7 +921,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
921
for (var i = 0; i < propertyTable.Rows.Count; ++i)
922
{
923
var propertyRow = propertyTable.Rows[i];
928
- var property = (string)propertyRow[0];
924
+ var property = propertyRow.FieldAsString(0);
925
926
if ("ProductCode" == property)
927
{
@@ -1173,10 +1169,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1169
{
1170
var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition);
1171
1176
- foreach (FileRow mainFileRow in mainFileTable.Rows)
1172
+ foreach (var mainFileRow in mainFileTable.Rows.Cast<FileRow>())
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.
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;
1181
1182
// Delete's don't need rows in the paired transform.
1183
if (mainFileRow.Operation == RowOperation.Delete)
@@ -1188,13 +1189,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1189
pairedFileRow.Operation = RowOperation.Modify;
1190
mainFileRow.CopyTo(pairedFileRow);
1191
1191
- // Override authored media for patch bind.
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.
1192
+ // Force modified File rows to appear in the transform.
1193
switch (mainFileRow.Operation)
1194
{
1195
case RowOperation.Modify:
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+93
-106
@@ -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.
22
internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
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
{
26
this.ServiceProvider = context.ServiceProvider;
27
@@ -47,7 +47,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
47
this.ResolvedLcid = context.ResolvedLcid;
48
this.SuppressLayout = context.SuppressLayout;
49
50
- this.SubStorages = subStorages;
50
+ this.PatchSubStorages = patchSubStorages;
51
52
this.BackendExtensions = backendExtension;
53
}
@@ -76,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
76
77
private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; }
78
79
- private IEnumerable<SubStorage> SubStorages { get; }
79
+ private IEnumerable<SubStorage> PatchSubStorages { get; }
80
81
private Intermediate Intermediate { get; }
82
@@ -180,35 +180,42 @@ namespace WixToolset.Core.WindowsInstaller.Bind
180
command.Execute();
181
}
182
183
-#if TODO_PATCHING
184
- ////if (OutputType.Patch == this.Output.Type)
185
- ////{
186
- //// foreach (SubStorage substorage in this.Output.SubStorages)
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
183
+ // Add missing CreateFolder symbols to null-keypath components.
184
+ {
185
+ var command = new AddCreateFoldersCommand(section);
186
+ command.Execute();
187
+ }
188
189
if (this.Messaging.EncounteredError)
190
{
191
return null;
192
}
193
210
- this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound);
211
- this.Messaging.Write(VerboseMessages.UpdatingFileInformation());
194
+ // Process dependency references.
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
+ }
219
220
// Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
221
{
@@ -217,21 +224,33 @@ namespace WixToolset.Core.WindowsInstaller.Bind
224
trackedFiles.AddRange(extractedFiles);
225
}
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
+
237
// This must occur after all variables and source paths have been resolved.
221
- List<IFileFacade> fileFacades;
222
- if (SectionType.Patch == section.Type)
238
+ List<IFileFacade> allFileFacades;
239
+ List<IFileFacade> fileFacadesFromIntermediate;
240
+ List<IFileFacade> fileFacadesFromModule = null;
241
+ if (section.Type == SectionType.Patch)
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);
244
command.Execute();
245
227
- fileFacades = command.FileFacades;
246
+ allFileFacades = fileFacadesFromIntermediate = command.FileFacades;
247
}
248
else
249
{
250
var command = new GetFileFacadesCommand(section, this.WindowsInstallerBackendHelper);
251
command.Execute();
252
234
- fileFacades = command.FileFacades;
253
+ allFileFacades = fileFacadesFromIntermediate = command.FileFacades;
254
}
255
256
// Retrieve file information from merge modules.
@@ -243,10 +262,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
262
{
263
containsMergeModules = true;
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);
266
command.Execute();
267
249
- fileFacades.AddRange(command.MergeModulesFileFacades);
268
+ fileFacadesFromModule = new List<IFileFacade>(command.MergeModulesFileFacades);
269
+ allFileFacades.AddRange(fileFacadesFromModule);
270
trackedFiles.AddRange(command.TrackedFiles);
271
}
272
}
@@ -257,23 +277,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
277
return null;
278
}
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
-
280
// Gather information about files that do not come from merge modules.
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);
283
command.Execute();
284
}
285
@@ -289,18 +295,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
295
this.WindowsInstallerBackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
296
}
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
-
298
// Now that delayed fields are processed, fixup the package version (if needed) and validate it
299
// which will short circuit duplicate errors later if the ProductVersion is invalid.
300
if (SectionType.Product == section.Type)
@@ -308,18 +302,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
302
this.ProcessProductVersion(packageSymbol, section, validate: true);
303
}
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
-
305
// If there are any backend extensions, give them the opportunity to process
306
// the section now that the fields have all be resolved.
307
//
@@ -339,9 +321,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
321
322
if (reresolvedFiles.Any())
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));
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);
327
command.Execute();
328
}
329
}
@@ -363,28 +345,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind
345
}
346
}
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
-
348
// Assign files to media and update file sequences.
349
Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia;
350
IEnumerable<IFileFacade> uncompressedFiles;
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);
353
order.Execute();
354
379
- fileFacades = order.FileFacades;
355
+ allFileFacades = order.FileFacades;
356
381
- var assign = new AssignMediaCommand(section, this.Messaging, fileFacades, compressed);
357
+ var assign = new AssignMediaCommand(section, this.Messaging, allFileFacades, compressed);
358
assign.Execute();
359
360
filesByCabinetMedia = assign.FileFacadesByCabinetMedia;
361
uncompressedFiles = assign.UncompressedFileFacades;
362
387
- var update = new UpdateMediaSequencesCommand(section, fileFacades);
363
+ var update = new UpdateMediaSequencesCommand(section, allFileFacades);
364
update.Execute();
365
}
366
@@ -394,6 +370,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
370
return null;
371
}
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
+
391
// Time to create the WindowsInstallerData object. Try to put as much above here as possible, updating the IR is better.
392
WindowsInstallerData data;
393
{
@@ -412,9 +406,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
406
var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions);
407
suppressedTableNames = unsuppress.Execute();
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
+ }
419
else if (data.Type == OutputType.Patch)
420
{
417
- foreach (var storage in this.SubStorages)
421
+ foreach (var storage in this.PatchSubStorages)
422
{
423
data.SubStorages.Add(storage);
424
}
@@ -426,13 +430,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
430
return null;
431
}
432
429
- // Ensure the intermediate folder is created since delta patches will be
430
- // created there.
431
- Directory.CreateDirectory(this.IntermediateFolder);
432
-
433
- if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
433
+ if (section.Type == SectionType.Patch && this.DeltaBinaryPatch)
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();
437
}
438
@@ -454,19 +454,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
454
return null;
455
}
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
-
457
// Generate database file.
458
{
459
this.Messaging.Write(VerboseMessages.GeneratingDatabase());
@@ -491,7 +478,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
478
{
479
this.Messaging.Write(VerboseMessages.MergingModules());
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);
482
command.Execute();
483
484
trackedFiles.AddRange(command.TrackedFiles);
@@ -518,7 +505,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
505
var fi = new FileInfo(this.OutputPath);
506
if (fi.Length > Int32.MaxValue)
507
{
521
- this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(null, this.OutputPath, "MSI"));
508
+ this.Messaging.Write(WarningMessages.WindowsInstallerFileTooLarge(null, this.OutputPath, data.Type.ToString()));
509
}
510
}
511
catch
src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
+15
-9
@@ -169,18 +169,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
169
}
170
}
171
172
- // create the cabinet file
173
- var cabinetPath = Path.GetFullPath(cabinetWorkItem.CabinetFile);
172
+ // Calculate the files to be compressed into the cabinet.
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;
178
175
- var files = cabinetWorkItem.FileFacades
176
- .OrderBy(f => f.Sequence)
177
- .Select(facade => facade.Hash == null ?
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();
179
+ var compressFile = cabinetWorkItem.HashesByFileId.TryGetValue(facade.Id, out var hash) ?
180
+ new CabinetCompressFile(facade.SourcePath, modularizedId, hash.HashPart1, hash.HashPart2, hash.HashPart3, hash.HashPart4) :
181
+ new CabinetCompressFile(facade.SourcePath, modularizedId);
182
183
+ compressFiles.Add(compressFile);
184
+ }
185
+
186
+ // create the cabinet file
187
+ var cabinetPath = Path.GetFullPath(cabinetWorkItem.CabinetFile);
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);
190
191
// Best effort check to see if the cabinet is too large for the Windows Installer.
192
try
src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
+9
-1
@@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
4
{
5
using System.Collections.Generic;
6
using WixToolset.Data;
7
+ using WixToolset.Data.Symbols;
8
using WixToolset.Extensibility.Data;
9
10
/// <summary>
@@ -17,11 +18,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18
/// <param name="sourceLineNumber">Source line number that requires the cabinet creation.</param>
19
/// <param name="diskId"></param>
20
/// <param name="fileFacades">The collection of files in this cabinet.</param>
21
+ /// <param name="hashesByFileId">The hashes for unversioned files.</param>
22
/// <param name="cabinetFile">The cabinet file.</param>
23
/// <param name="maxThreshold">Maximum threshold for each cabinet.</param>
24
/// <param name="compressionLevel">The compression level of the cabinet.</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)
27
{
28
this.SourceLineNumber = sourceLineNumber;
29
this.DiskId = diskId;
@@ -29,6 +31,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
31
this.CompressionLevel = compressionLevel;
32
this.ModularizationSuffix = modularizationSuffix;
33
this.FileFacades = fileFacades;
34
+ this.HashesByFileId = hashesByFileId;
35
this.MaxThreshold = maxThreshold;
36
}
37
@@ -65,6 +68,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
68
/// <value>The collection of files in this cabinet.</value>
69
public IEnumerable<IFileFacade> FileFacades { get; }
70
71
+ /// <summary>
72
+ /// The hashes for unversioned files.
73
+ /// </summary>
74
+ public Dictionary<string, MsiFileHashSymbol> HashesByFileId { get; }
75
+
76
/// <summary>
77
/// Gets the max threshold.
78
/// </summary>
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
+5
-12
@@ -95,6 +95,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
95
96
var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, maximumCabinetSizeForLargeFileSplitting, maximumUncompressedMediaSize);
97
98
+ var hashesByFileId = this.Section.Symbols.OfType<MsiFileHashSymbol>().ToDictionary(s => s.Id.Id);
99
+
100
foreach (var entry in this.FileFacadesByCabinet)
101
{
102
var mediaSymbol = entry.Key;
@@ -102,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
104
var compressionLevel = mediaSymbol.CompressionLevel ?? this.DefaultCompressionLevel ?? CompressionLevel.Medium;
105
var cabinetDir = this.ResolveMedia(mediaSymbol, mediaSymbol.Layout, this.LayoutDirectory);
106
105
- var cabinetWorkItem = this.CreateCabinetWorkItem(this.Data, cabinetDir, mediaSymbol, compressionLevel, files);
107
+ var cabinetWorkItem = this.CreateCabinetWorkItem(this.Data, cabinetDir, mediaSymbol, compressionLevel, files, hashesByFileId);
108
if (null != cabinetWorkItem)
109
{
110
cabinetBuilder.Enqueue(cabinetWorkItem);
@@ -140,16 +142,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
142
return cabbingThreadCount;
143
}
144
143
- /// <summary>
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)
145
+ private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades, Dictionary<string, MsiFileHashSymbol> hashesByFileId)
146
{
147
CabinetWorkItem cabinetWorkItem = null;
148
@@ -171,7 +164,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
164
if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption)
165
{
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);
168
}
169
else // reuse the cabinet from the cabinet cache.
170
{
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
+8
-2
@@ -30,8 +30,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
30
31
private IBackendHelper BackendHelper { get; }
32
33
- public void Execute()
33
+ public IReadOnlyCollection<SubStorage> SubStorages { get; private set; }
34
+
35
+ public IReadOnlyCollection<SubStorage> Execute()
36
{
37
+ var subStorages = new List<SubStorage>();
38
+
39
// Create and add substorages for instance transforms.
40
var wixInstanceTransformsSymbols = this.Section.Symbols.OfType<WixInstanceTransformsSymbol>();
41
@@ -252,9 +256,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
256
summaryRow[1] = "4";
257
}
258
255
- this.Output.SubStorages.Add(new SubStorage(instanceId, instanceTransform));
259
+ subStorages.Add(new SubStorage(instanceId, instanceTransform));
260
}
261
}
262
+
263
+ return this.SubStorages = subStorages;
264
}
265
}
266
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
+68
-32
@@ -6,31 +6,44 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6
using System.Collections.Generic;
7
using System.IO;
8
using System.Linq;
9
- using WixToolset.Core.Native.Msi;
9
using WixToolset.Core.WindowsInstaller.Unbind;
10
using WixToolset.Data;
11
using WixToolset.Data.Symbols;
12
using WixToolset.Data.WindowsInstaller;
13
+ using WixToolset.Extensibility;
14
+ using WixToolset.Extensibility.Data;
15
using WixToolset.Extensibility.Services;
16
17
internal class CreatePatchTransformsCommand
18
{
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)
20
{
21
this.Messaging = messaging;
22
this.BackendHelper = backendHelper;
23
+ this.PathResolver = pathResolver;
24
+ this.FileResolver = fileResolver;
25
+ this.ResolverExtensions = resolverExtensions;
26
this.Intermediate = intermediate;
27
this.IntermediateFolder = intermediateFolder;
28
+ this.BindPaths = bindPaths;
29
}
30
31
private IMessaging Messaging { get; }
32
33
private IBackendHelper BackendHelper { get; }
34
35
+ private IPathResolver PathResolver { get; }
36
+
37
+ private IFileResolver FileResolver { get; }
38
+
39
+ private IReadOnlyCollection<IResolverExtension> ResolverExtensions { get; }
40
+
41
private Intermediate Intermediate { get; }
42
43
private string IntermediateFolder { get; }
44
45
+ private IReadOnlyCollection<IBindPath> BindPaths { get; }
46
+
47
public IEnumerable<PatchTransform> PatchTransforms { get; private set; }
48
49
public IEnumerable<PatchTransform> Execute()
@@ -41,23 +54,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
54
55
foreach (var symbol in symbols)
56
{
44
- WindowsInstallerData transform;
45
-
46
- if (symbol.TransformFile is null)
47
- {
48
- var baselineData = this.GetData(symbol.BaselineFile.Path);
49
- var updateData = this.GetData(symbol.UpdateFile.Path);
57
+ var targetData = this.GetWindowsInstallerData(symbol.BaselineFile.Path, BindStage.Target);
58
+ var updatedData = this.GetWindowsInstallerData(symbol.UpdateFile.Path, BindStage.Updated);
59
51
- var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, preserveUnchangedRows: true, showPedanticMessages: false);
52
- 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
- }
60
+ var command = new GenerateTransformCommand(this.Messaging, targetData, updatedData, preserveUnchangedRows: true, showPedanticMessages: false);
61
+ var transform = command.Execute();
62
63
patchTransforms.Add(new PatchTransform(symbol.Id.Id, transform));
64
}
@@ -67,26 +68,61 @@ namespace WixToolset.Core.WindowsInstaller.Bind
68
return this.PatchTransforms;
69
}
70
70
- private WindowsInstallerData GetData(string path)
71
+ private WindowsInstallerData GetWindowsInstallerData(string path, BindStage stage)
72
{
72
- var ext = Path.GetExtension(path);
73
-
74
- if (".msi".Equals(ext, StringComparison.OrdinalIgnoreCase))
73
+ if (DataLoader.TryLoadWindowsInstallerData(path, true, out var data))
74
{
76
- using (var database = new Database(path, OpenDatabase.ReadOnly))
77
- {
78
- var exportBasePath = Path.Combine(this.IntermediateFolder, "_msi"); // TODO: come up with a better path.
75
+ // Re-resolve file paths only when loading from .wixpdb.
76
+ this.ReResolveWindowsInstallerData(data, stage);
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
+ }
87
80
- var isAdminImage = false; // TODO: need a better way to set this
88
+ return data;
89
+ }
90
82
- var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, database, path, OutputType.Product, exportBasePath, this.IntermediateFolder, isAdminImage, suppressDemodularization: true, skipSummaryInfo: true);
83
- return command.Execute();
84
- }
91
+ private void ReResolveWindowsInstallerData(WindowsInstallerData data, BindStage stage)
92
+ {
93
+ var bindPaths = this.BindPaths.Where(b => b.Stage == stage).ToList();
94
+
95
+ if (bindPaths.Count == 0)
96
+ {
97
+ return;
98
}
86
- else // assume .wixpdb (or .wixout)
99
+
100
+ foreach (var table in data.Tables)
101
{
88
- var data = WindowsInstallerData.Load(path, true);
89
- return data;
102
+ foreach (var row in table.Rows)
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
+ }
126
}
127
}
128
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+4
-1
@@ -662,10 +662,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
662
row.FileSize = symbol.FileSize;
663
row.Version = symbol.Version;
664
row.Language = symbol.Language;
665
- row.DiskId = symbol.DiskId ?? 1; // TODO: is 1 the correct thing to default here
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;
668
669
+ var previousSourceField = symbol.Fields[(int)FileSymbolFields.Source]?.PreviousValue;
670
+ row.PreviousSource = previousSourceField?.AsPath().Path;
671
+
672
var attributes = (symbol.Attributes & FileSymbolAttributes.Checksum) == FileSymbolAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0;
673
attributes |= (symbol.Attributes & FileSymbolAttributes.Compressed) == FileSymbolAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0;
674
attributes |= (symbol.Attributes & FileSymbolAttributes.Uncompressed) == FileSymbolAttributes.Uncompressed ? WindowsInstallerConstants.MsidbFileAttributesNoncompressed : 0;
src/wix/WixToolset.Core.WindowsInstaller/Bind/DataLoader.cs
new
+49
@@ -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
+}
src/wix/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+5
-5
@@ -22,12 +22,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
22
/// </summary>
23
internal class ExtractMergeModuleFilesCommand
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
{
27
this.Messaging = messaging;
28
this.BackendHelper = backendHelper;
29
this.WixMergeSymbols = wixMergeSymbols;
30
- this.FileFacades = fileFacades;
30
+ this.FileFacadesFromIntermediate = fileFacadesFromIntermediate;
31
this.OutputInstallerVersion = installerVersion;
32
this.IntermediateFolder = intermediateFolder;
33
this.SuppressLayout = suppressLayout;
@@ -39,7 +39,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
39
40
private IEnumerable<WixMergeSymbol> WixMergeSymbols { get; }
41
42
- private IEnumerable<IFileFacade> FileFacades { get; }
42
+ private IEnumerable<IFileFacade> FileFacadesFromIntermediate { get; }
43
44
private int OutputInstallerVersion { get; }
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
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.
68
- var indexedFileFacades = this.FileFacades.ToDictionary(f => f.Id, StringComparer.Ordinal);
68
+ var indexedFileFacades = this.FileFacadesFromIntermediate.ToDictionary(f => f.Id, StringComparer.Ordinal);
69
70
foreach (var wixMergeRow in this.WixMergeSymbols)
71
{
@@ -115,7 +115,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
115
fileSymbol.DiskId = wixMergeRow.DiskId;
116
fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) };
117
118
- var mergeModuleFileFacade = this.BackendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
118
+ var mergeModuleFileFacade = this.BackendHelper.CreateFileFacade(fileSymbol);
119
120
// If case-sensitive collision with another merge module or a user-authored file identifier.
121
if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade))
src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
+15
-46
@@ -16,7 +16,7 @@ namespace WixToolset.Core.WindowsInstaller
16
/// </summary>
17
internal class GenerateTransformCommand
18
{
19
- private const char sectionDelimiter = '/';
19
+ private const char SectionDelimiter = '/';
20
private readonly IMessaging messaging;
21
private SummaryInformationStreams transformSummaryInfo;
22
@@ -38,22 +38,10 @@ namespace WixToolset.Core.WindowsInstaller
38
39
private TransformFlags ValidationFlags { get; }
40
41
- /// <summary>
42
- /// Gets or sets the option to show pedantic messages.
43
- /// </summary>
44
- /// <value>The option to show pedantic messages.</value>
41
private bool ShowPedanticMessages { get; }
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>
43
private bool SuppressKeepingSpecialRows { get; }
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>
45
private bool PreserveUnchangedRows { get; }
46
47
public WindowsInstallerData Transform { get; private set; }
@@ -124,7 +112,7 @@ namespace WixToolset.Core.WindowsInstaller
112
foreach (var updatedRow in updatedTable.Rows)
113
{
114
updatedRow.Operation = RowOperation.Add;
127
- updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
115
+ updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId;
116
addedTable.Rows.Add(updatedRow);
117
}
118
}
@@ -152,27 +140,8 @@ namespace WixToolset.Core.WindowsInstaller
140
141
if (null != primaryKey)
142
{
155
- if (index.TryGetValue(primaryKey, out var collisionRow))
143
+ if (index.ContainsKey(primaryKey))
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
-
145
if (this.ShowPedanticMessages)
146
{
147
this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name));
@@ -208,7 +177,7 @@ namespace WixToolset.Core.WindowsInstaller
177
else if (null == updatedRow)
178
{
179
targetRow.Operation = RowOperation.Delete;
211
- targetRow.SectionId += sectionDelimiter;
180
+ targetRow.SectionId += SectionDelimiter;
181
182
comparedRow = targetRow;
183
keepRow = true;
@@ -219,10 +188,10 @@ namespace WixToolset.Core.WindowsInstaller
188
updatedRow.Operation = RowOperation.None;
189
if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name)
190
{
222
- // ignore rows that shouldn't be in a transform
191
+ // Include only summary information rows that are allowed in a transform.
192
if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0]))
193
{
225
- updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
194
+ updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId;
195
comparedRow = updatedRow;
196
keepRow = true;
197
}
@@ -273,14 +242,14 @@ namespace WixToolset.Core.WindowsInstaller
242
updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex;
243
updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri;
244
276
- // 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
245
+ // Always keep a copy of the previous data even if they are identical.
246
+ // This makes diff data clean and easier to control in patch logic.
247
updatedObjectField.PreviousData = (string)targetObjectField.Data;
248
280
- // always remember the unresolved data for target build
249
+ // Always remember the unresolved data for target build.
250
updatedObjectField.UnresolvedPreviousData = targetObjectField.UnresolvedData;
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.
253
keepRow = !this.SuppressKeepingSpecialRows;
254
}
255
else
@@ -305,7 +274,7 @@ namespace WixToolset.Core.WindowsInstaller
274
if (keepRow)
275
{
276
comparedRow = updatedRow;
308
- comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
277
+ comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId;
278
}
279
}
280
}
@@ -333,9 +302,6 @@ namespace WixToolset.Core.WindowsInstaller
302
}
303
else // possibly modified table.
304
{
336
- var updatedPrimaryKeys = new Dictionary<string, Row>();
337
- var targetPrimaryKeys = new Dictionary<string, Row>();
338
-
305
// compare the table definitions
306
if (0 != targetTable.Definition.CompareTo(updatedTable.Definition))
307
{
@@ -344,6 +310,9 @@ namespace WixToolset.Core.WindowsInstaller
310
}
311
else
312
{
313
+ var updatedPrimaryKeys = new Dictionary<string, Row>();
314
+ var targetPrimaryKeys = new Dictionary<string, Row>();
315
+
316
this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys);
317
318
// diff the target and updated rows
@@ -371,7 +340,7 @@ namespace WixToolset.Core.WindowsInstaller
340
var updatedRow = updatedPrimaryKeyEntry.Value;
341
342
updatedRow.Operation = RowOperation.Add;
374
- updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
343
+ updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId;
344
rows.Add(updatedRow);
345
}
346
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
+1
-6
@@ -2,9 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller.Bind
4
{
5
- using System;
5
using System.Collections.Generic;
7
- using System.Globalization;
6
using System.Linq;
7
using WixToolset.Data;
8
using WixToolset.Data.Symbols;
@@ -29,20 +27,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
27
{
28
var facades = new List<IFileFacade>();
29
32
- var assemblyFile = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id);
30
#if TODO_PATCHING_DELTA
31
//var deltaPatchFiles = this.Section.Symbols.OfType<WixDeltaPatchFileSymbol>().ToDictionary(t => t.Id.Id);
32
#endif
33
34
foreach (var file in this.Section.Symbols.OfType<FileSymbol>())
35
{
39
- assemblyFile.TryGetValue(file.Id.Id, out var assembly);
40
-
36
#if TODO_PATCHING_DELTA
37
//deltaPatchFiles.TryGetValue(file.Id.Id, out var deltaPatchFile);
38
// TODO: should we be passing along delta information to the file facade? Probably, right?
39
#endif
45
- var fileFacade = this.BackendHelper.CreateFileFacade(file, assembly);
40
+ var fileFacade = this.BackendHelper.CreateFileFacade(file);
41
42
facades.Add(fileFacade);
43
}
src/wix/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
+6
-9
@@ -37,8 +37,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
37
38
var patchMediaFileRows = new Dictionary<int, RowDictionary<FileRow>>();
39
40
- //var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]);
41
-
40
// Index paired transforms by name without their "#" prefix.
41
var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data);
42
@@ -57,7 +55,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
55
var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name];
56
var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]);
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>())
59
{
60
var mainFileId = mainFileRow.File;
61
@@ -89,8 +87,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
87
}
88
else
89
{
92
- // TODO: should this entire condition be placed in the binder file manager?
93
- if (/*(0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&*/
90
+ if (
91
+#if TODO_PATCHING_DELTA
92
+ (0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&
93
+#endif
94
!this.FileSystemManager.CompareFiles(objectField.PreviousData, objectField.Data.ToString()))
95
{
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);
134
}
135
136
- var patchFileRow = mediaFileRows.Get(mainFileId);
137
-
138
- if (null == patchFileRow)
136
+ if (!mediaFileRows.TryGetValue(mainFileId, out var patchFileRow))
137
{
140
- //patchFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
138
patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers);
139
mainFileRow.CopyTo(patchFileRow);
140
src/wix/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+4
-10
@@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
10
using System.Runtime.InteropServices;
11
using System.Text;
12
using System.Threading;
13
- using WixToolset.Core.Native;
13
using WixToolset.Core.Native.Msi;
14
using WixToolset.Core.Native.Msm;
15
using WixToolset.Data;
@@ -24,11 +23,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
23
/// </summary>
24
internal class MergeModulesCommand
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)
27
{
28
this.Messaging = messaging;
29
this.BackendHelper = backendHelper;
31
- this.FileFacades = fileFacades;
30
+ this.FileFacadesFromModule = fileFacadesFromModule;
31
this.Section = section;
32
this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>();
33
this.OutputPath = outputPath;
@@ -39,7 +38,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
38
39
private IBackendHelper BackendHelper { get; }
40
42
- private IEnumerable<IFileFacade> FileFacades { get; }
41
+ private IEnumerable<IFileFacade> FileFacadesFromModule { get; }
42
43
private IntermediateSection Section { get; }
44
@@ -280,13 +279,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
279
this.Messaging.Write(VerboseMessages.ResequencingMergeModuleFiles());
280
using (var view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?"))
281
{
283
- foreach (var file in this.FileFacades)
282
+ foreach (var file in this.FileFacadesFromModule)
283
{
285
- if (!file.FromModule)
286
- {
287
- continue;
288
- }
289
-
284
using (var record = new Record(1))
285
{
286
record.SetString(1, file.Id);
src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPropertiesCommand.cs
-22
@@ -47,28 +47,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
47
if ("ProductCode" == propertySymbol.Id.Id && "*".Equals(propertySymbol.Value, StringComparison.Ordinal))
48
{
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
50
}
51
else if ("ProductLanguage" == propertySymbol.Id.Id)
52
{
src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+101
-109
@@ -19,11 +19,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19
/// </summary>
20
internal class UpdateFileFacadesCommand
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
{
24
this.Messaging = messaging;
25
this.Section = section;
26
- this.FileFacades = fileFacades;
26
+ this.AllFileFacades = allFileFacades;
27
this.UpdateFileFacades = updateFileFacades;
28
this.VariableCache = variableCache;
29
this.OverwriteHash = overwriteHash;
@@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
33
34
private IntermediateSection Section { get; }
35
36
- private IEnumerable<IFileFacade> FileFacades { get; }
36
+ private IEnumerable<IFileFacade> AllFileFacades { get; }
37
38
private IEnumerable<IFileFacade> UpdateFileFacades { get; }
39
@@ -43,15 +43,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
43
44
public void Execute()
45
{
46
+ var assemblySymbols = this.Section.Symbols.OfType<AssemblySymbol>().ToDictionary(t => t.Id.Id);
47
var assemblyNameSymbols = this.Section.Symbols.OfType<MsiAssemblyNameSymbol>().ToDictionary(t => t.Id.Id);
48
49
foreach (var file in this.UpdateFileFacades.Where(f => f.SourcePath != null))
50
{
50
- this.UpdateFileFacade(file, assemblyNameSymbols);
51
+ this.UpdateFileFacade(file, assemblySymbols, assemblyNameSymbols);
52
}
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)
56
{
57
FileInfo fileInfo = null;
58
try
@@ -124,7 +125,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
125
//
126
// Also, if we do not find a matching file identifier then the user provided a default version and is providing a version
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)))
129
{
130
this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.SourceLineNumber, facade.Version, facade.Id));
131
}
@@ -153,16 +154,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
154
}
155
}
156
156
- if (null == facade.Hash)
157
+ // Remember the hash symbol for use later.
158
+ facade.MsiFileHashSymbol = new MsiFileHashSymbol(facade.SourceLineNumber, facade.Identifier)
159
{
158
- facade.Hash = this.Section.AddSymbol(new MsiFileHashSymbol(facade.SourceLineNumber, facade.Identifier));
159
- }
160
-
161
- facade.Hash.Options = 0;
162
- facade.Hash.HashPart1 = hash[0];
163
- facade.Hash.HashPart2 = hash[1];
164
- facade.Hash.HashPart3 = hash[2];
165
- facade.Hash.HashPart4 = hash[3];
160
+ Options = 0,
161
+ HashPart1 = hash[0],
162
+ HashPart2 = hash[1],
163
+ HashPart3 = hash[2],
164
+ HashPart4 = hash[3],
165
+ };
166
}
167
}
168
else // update the file row with the version and language information.
@@ -173,7 +173,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
173
{
174
facade.Version = version;
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
{
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
@@ -204,108 +204,104 @@ namespace WixToolset.Core.WindowsInstaller.Bind
204
this.VariableCache[$"filelanguage.{facade.Id}"] = facade.Language ?? String.Empty;
205
}
206
207
- // If this is a CLR assembly, load the assembly and get the assembly name information
208
- if (AssemblyType.DotNetAssembly == facade.AssemblyType)
207
+ // If there is an assembly for this file.
208
+ if (assemblySymbols.TryGetValue(facade.Id, out var assemblySymbol))
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)
212
{
212
- var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version);
213
+ try
214
+ {
215
+ var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version);
216
214
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "name", assemblyName.Name);
215
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "culture", assemblyName.Culture);
216
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "version", assemblyName.Version);
217
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name);
218
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "culture", assemblyName.Culture);
219
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "version", assemblyName.Version);
220
218
- if (!String.IsNullOrEmpty(assemblyName.Architecture))
219
- {
220
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "processorArchitecture", assemblyName.Architecture);
221
- }
222
- // TODO: WiX v3 seemed to do this but not clear it should actually be done.
223
- //else if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture))
224
- //{
225
- // this.SetMsiAssemblyName(assemblyNameSymbols, file, "processorArchitecture", file.WixFile.ProcessorArchitecture);
226
- //}
221
+ if (!String.IsNullOrEmpty(assemblyName.Architecture))
222
+ {
223
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "processorArchitecture", assemblyName.Architecture);
224
+ }
225
+ // TODO: WiX v3 seemed to do this but not clear it should actually be done.
226
+ //else if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture))
227
+ //{
228
+ // this.SetMsiAssemblyName(assemblyNameSymbols, file, "processorArchitecture", file.WixFile.ProcessorArchitecture);
229
+ //}
230
228
- if (assemblyName.StrongNamedSigned)
229
- {
230
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "publicKeyToken", assemblyName.PublicKeyToken);
231
- }
232
- else if (facade.AssemblyApplicationFileRef == null)
233
- {
234
- throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef));
235
- }
231
+ if (assemblyName.StrongNamedSigned)
232
+ {
233
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "publicKeyToken", assemblyName.PublicKeyToken);
234
+ }
235
+ else if (assemblySymbol.ApplicationFileRef == null)
236
+ {
237
+ throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef));
238
+ }
239
237
- if (!String.IsNullOrEmpty(assemblyName.FileVersion))
238
- {
239
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "fileVersion", assemblyName.FileVersion);
240
- }
240
+ if (!String.IsNullOrEmpty(assemblyName.FileVersion))
241
+ {
242
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "fileVersion", assemblyName.FileVersion);
243
+ }
244
242
- // add the assembly name to the information cache
243
- if (null != this.VariableCache)
245
+ // add the assembly name to the information cache
246
+ if (null != this.VariableCache)
247
+ {
248
+ this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName();
249
+ }
250
+ }
251
+ catch (WixException e)
252
{
245
- this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName();
253
+ this.Messaging.Write(e.Error);
254
}
255
}
248
- catch (WixException e)
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
256
+ else if (AssemblyType.Win32Assembly == assemblySymbol.Type)
257
{
266
- var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath);
267
-
268
- if (!String.IsNullOrEmpty(assemblyName.Name))
258
+ // TODO: Consider passing in the this.AllFileFacades as an indexed collection instead of searching through
259
+ // all files like this. Even though this is a rare case it looks like we might be able to index the
260
+ // file earlier.
261
+ var fileManifest = this.AllFileFacades.FirstOrDefault(r => r.Id.Equals(assemblySymbol.ManifestFileRef, StringComparison.Ordinal));
262
+ if (null == fileManifest)
263
{
270
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "name", assemblyName.Name);
264
+ this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.SourceLineNumber, facade.Id, assemblySymbol.ManifestFileRef));
265
}
266
273
- if (!String.IsNullOrEmpty(assemblyName.Version))
267
+ try
268
{
275
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "version", assemblyName.Version);
276
- }
269
+ var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath);
270
278
- if (!String.IsNullOrEmpty(assemblyName.Type))
279
- {
280
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "type", assemblyName.Type);
281
- }
271
+ if (!String.IsNullOrEmpty(assemblyName.Name))
272
+ {
273
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name);
274
+ }
275
283
- if (!String.IsNullOrEmpty(assemblyName.Architecture))
284
- {
285
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "processorArchitecture", assemblyName.Architecture);
286
- }
276
+ if (!String.IsNullOrEmpty(assemblyName.Version))
277
+ {
278
+ this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "version", assemblyName.Version);
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
+ }
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)
297
{
290
- this.SetMsiAssemblyName(assemblyNameSymbols, facade, "publicKeyToken", assemblyName.PublicKeyToken);
298
+ this.Messaging.Write(e.Error);
299
}
300
}
293
- catch (WixException e)
294
- {
295
- this.Messaging.Write(e.Error);
296
- }
301
}
302
}
303
300
- /// <summary>
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)
304
+ private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameSymbol> assemblyNameSymbols, IFileFacade facade, AssemblySymbol assemblySymbol, string name, string value)
305
{
306
// check for null value (this can occur when grabbing the file version from an assembly without one)
307
if (String.IsNullOrEmpty(value))
@@ -315,36 +311,32 @@ namespace WixToolset.Core.WindowsInstaller.Bind
311
else
312
{
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 &&
319
- String.IsNullOrEmpty(facade.AssemblyApplicationFileRef) &&
314
+ if ("name" == name && AssemblyType.DotNetAssembly == assemblySymbol.Type &&
315
+ String.IsNullOrEmpty(assemblySymbol.ApplicationFileRef) &&
316
!String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase))
317
{
318
this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(facade.SourceLineNumber, Path.GetFileNameWithoutExtension(facade.FileName), value));
319
}
320
325
- // override directly authored value
321
+ // Override directly authored value, otherwise remember the gathered information on the facade for use later.
322
var lookup = String.Concat(facade.ComponentRef, "/", name);
327
- if (!assemblyNameSymbols.TryGetValue(lookup, out var assemblyNameSymbol))
323
+ if (assemblyNameSymbols.TryGetValue(lookup, out var assemblyNameSymbol))
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
{
331
ComponentRef = facade.ComponentRef,
332
Name = name,
333
Value = value,
334
- });
335
-
336
- if (null == facade.AssemblyNames)
337
- {
338
- facade.AssemblyNames = new List<MsiAssemblyNameSymbol>();
339
- }
340
-
341
- facade.AssemblyNames.Add(assemblyNameSymbol);
334
+ };
335
336
+ facade.AssemblyNameSymbols.Add(assemblyNameSymbol);
337
assemblyNameSymbols.Add(assemblyNameSymbol.Id.Id, assemblyNameSymbol);
338
}
339
346
- assemblyNameSymbol.Value = value;
347
-
340
if (this.VariableCache != null)
341
{
342
var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, facade.Id).ToLowerInvariant();
src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateSymbolsWithFileFacadesCommand.cs
new
+73
@@ -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
+}
src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs
+164
-167
@@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
4
{
5
using System;
6
using System.Collections.Generic;
7
+ using System.IO;
8
using System.Linq;
9
using WixToolset.Data;
10
using WixToolset.Data.Symbols;
@@ -14,10 +15,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15
16
internal class UpdateTransformsWithFileFacades
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)
19
{
20
this.Messaging = messaging;
20
- this.Output = output;
21
+ this.Section = section;
22
this.SubStorages = subStorages;
23
this.TableDefinitions = tableDefinitions;
24
this.FileFacades = fileFacades;
@@ -25,7 +26,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
26
27
private IMessaging Messaging { get; }
28
28
- private WindowsInstallerData Output { get; }
29
+ private IntermediateSection Section { get; }
30
31
private IEnumerable<SubStorage> SubStorages { get; }
32
@@ -35,190 +36,97 @@ namespace WixToolset.Core.WindowsInstaller.Bind
36
37
public void Execute()
38
{
38
- var fileFacadesByDiskId = new Dictionary<int, Dictionary<string, IFileFacade>>();
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
- }
39
+ var fileFacadesByDiskId = this.IndexFileFacadesByDiskId();
40
52
- var patchMediaRows = new RowDictionary<MediaRow>(this.Output.Tables["Media"]);
41
+ var mediaSymbolsByDiskId = this.Section.Symbols.OfType<MediaSymbol>().ToDictionary(m => m.DiskId);
42
43
// Index paired transforms by name without the "#" prefix.
44
var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)).ToDictionary(s => s.Name, s => s.Data);
45
57
- // Copy File bind data into substorages
46
foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith(PatchConstants.PairedPatchTransformPrefix)))
47
{
48
var mainTransform = substorage.Data;
49
62
- var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]);
63
-
50
var pairedTransform = pairedTransforms[PatchConstants.PairedPatchTransformPrefix + substorage.Name];
51
66
- // Copy Media.LastSequence.
67
- var pairedMediaTable = pairedTransform.Tables["Media"];
68
- foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows)
52
+ // Update the Media.LastSequence in the paired transforms.
53
+ foreach (var pairedMediaRow in pairedTransform.Tables["Media"].Rows.Cast<MediaRow>())
54
{
70
- var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId);
71
- pairedMediaRow.LastSequence = patchMediaRow.LastSequence;
55
+ if (mediaSymbolsByDiskId.TryGetValue(pairedMediaRow.DiskId, out var mediaSymbol) && mediaSymbol.LastSequence.HasValue)
56
+ {
57
+ pairedMediaRow.LastSequence = mediaSymbol.LastSequence.Value;
58
+ }
59
+ else // TODO: This shouldn't be possible.
60
+ {
61
+ throw new InvalidDataException();
62
+ }
63
}
64
65
// Validate file row changes for keypath-related issues
66
this.ValidateFileRowChanges(mainTransform);
67
77
- // Index File table of pairedTransform
78
- var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]);
79
-
80
- var mainFileTable = mainTransform.Tables["File"];
81
- if (null != mainFileTable)
68
+ // Copy File bind data into transforms
69
+ if (mainTransform.Tables.TryGetTable("File", out var mainFileTable))
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
+
76
// Remove the MsiFileHash table because it will be updated later with the final file hash for each file
77
mainTransform.Tables.Remove("MsiFileHash");
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>())
80
{
88
- if (RowOperation.Delete == mainFileRow.Operation)
89
- {
90
- continue;
91
- }
92
- else if (RowOperation.None == mainFileRow.Operation)
93
- {
94
- continue;
95
- }
96
-
97
- // Index patch files by diskId+fileId
81
+ // TODO: Wasn't this indexing done at the top of this method?
82
+ // Index main transform files by diskId+fileId
83
if (!fileFacadesByDiskId.TryGetValue(mainFileRow.DiskId, out var mediaFacades))
84
{
85
mediaFacades = new Dictionary<string, IFileFacade>();
86
fileFacadesByDiskId.Add(mainFileRow.DiskId, mediaFacades);
87
}
88
104
- // copy data from the patch back to the transform
89
+ // Copy data from the facade back to the appropriate transform.
90
if (mediaFacades.TryGetValue(mainFileRow.File, out var facade))
91
{
107
- var patchFileRow = facade.GetFileRow();
92
var pairedFileRow = pairedFileRows.Get(mainFileRow.File);
93
110
- for (var i = 0; i < patchFileRow.Fields.Length; i++)
111
- {
112
- var patchValue = patchFileRow.FieldAsString(i) ?? String.Empty;
113
- var mainValue = mainFileRow.FieldAsString(i) ?? String.Empty;
94
+ TryModifyField(mainFileRow, 3, facade.FileSize);
95
+
96
+ TryModifyField(mainFileRow, 4, facade.Version);
97
+
98
+ TryModifyField(mainFileRow, 5, facade.Language);
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
100
#if TODO_PATCHING_DELTA
136
- if (null != patchFileRow.Patch)
137
- {
138
- pairedFileRow.Fields[i].Modified = false;
139
- }
140
- else
101
+ // File.Attribute should not change for binary deltas, otherwise copy File Attributes from main transform row.
102
+ if (null != facade.Patch)
103
#endif
142
- {
143
- pairedFileRow[i] = patchFileRow[i];
144
- pairedFileRow.Fields[i].Modified = true;
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
- }
104
+ {
105
+ TryModifyField(pairedFileRow, 6, mainFileRow.Attributes);
106
+ mainFileRow.Fields[6].Modified = false;
107
}
108
159
- // Copy MsiFileHash row for this File.
160
- if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow))
109
+#if TODO_PATCHING_DELTA
110
+ // File.Sequence is updated in Patch table instead of File table for delta patches
111
+ if (null != facade.Patch)
112
{
162
- //patchHashRow = patchFileRow.Hash;
163
- throw new NotImplementedException();
113
+ pairedFileRow.Fields[7].Modified = false;
114
}
165
-
166
- if (null != patchHashRow)
115
+ else
116
+#endif
117
{
168
- var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]);
169
- var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers);
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;
118
+ // File.Sequence is updated in pairedTransform, not mainTransform.
119
+ TryModifyField(pairedFileRow, 7, facade.Sequence);
120
}
121
+ mainFileRow.Fields[7].Modified = false;
122
184
- // copy MsiAssemblyName rows for this File
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
- }
123
+ this.ProcessMsiFileHash(mainTransform, mainFileRow, facade.MsiFileHashSymbol, mainMsiFileHashIndex);
124
203
- if (!foundMatchingModifiedRow)
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
125
+ this.ProcessMsiAssemblyName(mainTransform, mainFileRow, facade);
126
219
- // Add patch header for this file
127
#if TODO_PATCHING_DELTA
221
- if (null != patchFileRow.Patch)
128
+ // Add patch header for this file
129
+ if (null != facade.Patch)
130
{
131
// Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables.
132
this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow);
@@ -232,12 +140,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
140
}
141
142
var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers);
235
- patchRow[0] = patchFileRow.File;
236
- patchRow[1] = patchFileRow.Sequence;
143
+ patchRow[0] = facade.File;
144
+ patchRow[1] = facade.Sequence;
145
238
- var patchFile = new FileInfo(patchFileRow.Source);
146
+ var patchFile = new FileInfo(facade.Source);
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;
149
150
var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1];
151
if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length)
@@ -252,13 +160,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
160
161
var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers);
162
patchHeadersRow[0] = streamName;
255
- patchHeadersRow[1] = patchFileRow.Patch;
163
+ patchHeadersRow[1] = facade.Patch;
164
patchRow[5] = streamName;
165
patchHeadersRow.Operation = RowOperation.Add;
166
}
167
else
168
{
261
- patchRow[4] = patchFileRow.Patch;
169
+ patchRow[4] = facade.Patch;
170
}
171
patchRow.Operation = RowOperation.Add;
172
}
@@ -270,14 +178,89 @@ namespace WixToolset.Core.WindowsInstaller.Bind
178
}
179
}
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
+ }
207
274
- this.Output.Tables.Remove("Media");
275
- this.Output.Tables.Remove("File");
276
- this.Output.Tables.Remove("MsiFileHash");
277
- this.Output.Tables.Remove("MsiAssemblyName");
208
+ private void ProcessMsiAssemblyName(WindowsInstallerData transform, FileRow fileRow, IFileFacade facade)
209
+ {
210
+ if (facade.AssemblyNameSymbols.Count > 0)
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
+ }
242
}
243
}
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
+
264
/// <summary>
265
/// Adds the PatchFiles action to the sequence table if it does not already exist.
266
/// </summary>
@@ -349,6 +332,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
332
}
333
}
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
+
353
/// <summary>
354
/// Tests sequence table for PatchFiles and associated actions
355
/// </summary>
@@ -396,33 +397,29 @@ namespace WixToolset.Core.WindowsInstaller.Bind
397
return;
398
}
399
399
- var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count);
400
-
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
{
404
- var keyPath = row.FieldAsString(5);
405
- if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
404
+ var keyPath = row.KeyPath;
405
+
406
+ if (!String.IsNullOrEmpty(keyPath))
407
{
407
- componentKeyPath.Add(row.FieldAsString(0), keyPath);
408
+ componentKeyPath.Add(row.Component, keyPath);
409
}
410
}
411
412
var componentWithChangedKeyPath = new Dictionary<string, string>();
413
var componentWithNonKeyPathChanged = new Dictionary<string, string>();
414
+
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))
417
{
416
- if (RowOperation.Modify != row.Operation)
417
- {
418
- continue;
419
- }
420
-
421
- var fileId = row.FieldAsString(0);
422
- var componentId = row.FieldAsString(1);
418
+ var fileId = row.File;
419
+ var componentId = row.Component;
420
421
// If this file is the keypath of a component
425
- if (componentKeyPath.ContainsValue(fileId))
422
+ if (componentKeyPath.ContainsValue(componentId))
423
{
424
if (!componentWithChangedKeyPath.ContainsKey(componentId))
425
{
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
+33
-50
@@ -21,6 +21,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
21
{
22
this.Messaging = serviceProvider.GetService<IMessaging>();
23
this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
24
+ this.PathResolver = serviceProvider.GetService<IPathResolver>();
25
this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
26
}
27
@@ -28,6 +29,8 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
29
30
private IBackendHelper BackendHelper { get; }
31
32
+ private IPathResolver PathResolver { get; }
33
+
34
private IExtensionManager ExtensionManager { get; }
35
36
private string OutputPath { get; set; }
@@ -40,8 +43,6 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
43
44
private string IntermediateFolder { get; set; }
45
43
- private bool IsAdminImage { get; set; }
44
-
46
private bool PreserveUnchangedRows { get; set; }
47
48
private bool ShowPedanticMessages { get; set; }
@@ -133,10 +134,6 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
134
var parameter = argument.Substring(1);
135
switch (parameter.ToLowerInvariant())
136
{
136
- case "a":
137
- this.IsAdminImage = true;
138
- return true;
139
-
137
case "intermediatefolder":
138
this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
139
return true;
@@ -194,15 +191,15 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
191
}
192
}
193
197
- case "val":
194
+ case "t":
195
{
199
- var val = parser.GetNextArgumentOrError(argument);
200
- if (String.IsNullOrEmpty(val))
196
+ var type = parser.GetNextArgumentOrError(argument);
197
+ if (String.IsNullOrEmpty(type))
198
{
199
return true;
200
}
201
205
- switch (val.ToLowerInvariant())
202
+ switch (type.ToLowerInvariant())
203
{
204
case "language":
205
this.ValidationFlags |= TransformFlags.LanguageTransformDefault;
@@ -216,6 +213,22 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
213
this.ValidationFlags |= TransformFlags.PatchTransformDefault;
214
return true;
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
+ {
232
case "g":
233
this.ValidationFlags |= TransformFlags.ValidateUpgradeCode;
234
return true;
@@ -261,7 +274,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
274
return true;
275
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" }));
278
return true;
279
}
280
}
@@ -297,7 +310,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
310
{
311
Exception exception;
312
300
- (transform, exception) = LoadWindowsInstallerDataSafely(this.TargetPath);
313
+ (transform, exception) = DataLoader.LoadWindowsInstallerDataSafely(this.TargetPath);
314
315
if (transform?.Type != OutputType.Transform)
316
{
@@ -340,17 +353,9 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
353
354
private WindowsInstallerData CreateTransform()
355
{
343
- if (!TryLoadWindowsInstallerData(this.TargetPath, out var targetOutput))
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
- }
356
+ var targetData = this.GetWindowsInstallerData(this.TargetPath);
357
349
- if (!TryLoadWindowsInstallerData(this.TargetPath, out var updatedOutput))
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
- }
358
+ var updatedData = this.GetWindowsInstallerData(this.UpdatedPath);
359
360
var differ = new Differ(this.Messaging)
361
{
@@ -359,7 +364,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
364
SuppressKeepingSpecialRows = this.SuppressKeepingSpecialRows
365
};
366
362
- return differ.Diff(targetOutput, updatedOutput, this.ValidationFlags);
367
+ return differ.Diff(targetData, updatedData, this.ValidationFlags);
368
}
369
370
private TableDefinitionCollection GetTableDefinitions()
@@ -370,37 +375,15 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
375
return loadTableDefinitions.Execute();
376
}
377
373
- private static bool TryLoadWindowsInstallerData(string path, out WindowsInstallerData data)
378
+ private WindowsInstallerData GetWindowsInstallerData(string path)
379
{
375
- data = null;
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)
380
+ if (!DataLoader.TryLoadWindowsInstallerData(path, out var data))
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();
384
}
385
403
- return (data, exception);
386
+ return data;
387
}
388
}
389
}
src/wix/WixToolset.Core.WindowsInstaller/Differ.cs
+15
-23
@@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller
10
using WixToolset.Data;
11
using WixToolset.Data.Symbols;
12
using WixToolset.Data.WindowsInstaller;
13
- using WixToolset.Extensibility;
13
using WixToolset.Extensibility.Services;
14
15
/// <summary>
@@ -18,7 +17,7 @@ namespace WixToolset.Core.WindowsInstaller
17
/// </summary>
18
public sealed class Differ
19
{
21
- private const char sectionDelimiter = '/';
20
+ private const char SectionDelimiter = '/';
21
private readonly IMessaging messaging;
22
private SummaryInformationStreams transformSummaryInfo;
23
@@ -48,17 +47,6 @@ namespace WixToolset.Core.WindowsInstaller
47
/// <value>The option to keep all rows including unchanged rows.</value>
48
public bool PreserveUnchangedRows { get; set; }
49
51
- /// <summary>
52
- /// Creates a transform by diffing two outputs.
53
- /// </summary>
54
- /// <param name="targetOutput">The target output.</param>
55
- /// <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
-
50
/// <summary>
51
/// Creates a transform by diffing two outputs.
52
/// </summary>
@@ -68,9 +56,12 @@ namespace WixToolset.Core.WindowsInstaller
56
/// <returns>The transform.</returns>
57
public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags)
58
{
71
- var transform = new WindowsInstallerData(null);
72
- transform.Type = OutputType.Transform;
73
- transform.Codepage = updatedOutput.Codepage;
59
+ var transform = new WindowsInstallerData(null)
60
+ {
61
+ Type = OutputType.Transform,
62
+ Codepage = updatedOutput.Codepage
63
+ };
64
+
65
this.transformSummaryInfo = new SummaryInformationStreams();
66
67
// compare the codepages
@@ -120,7 +111,7 @@ namespace WixToolset.Core.WindowsInstaller
111
foreach (var updatedRow in updatedTable.Rows)
112
{
113
updatedRow.Operation = RowOperation.Add;
123
- updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
114
+ updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId;
115
addedTable.Rows.Add(updatedRow);
116
}
117
}
@@ -209,7 +200,7 @@ namespace WixToolset.Core.WindowsInstaller
200
else if (null == updatedRow)
201
{
202
operation = targetRow.Operation = RowOperation.Delete;
212
- targetRow.SectionId += sectionDelimiter;
203
+ targetRow.SectionId += SectionDelimiter;
204
comparedRow = targetRow;
205
keepRow = true;
206
}
@@ -222,7 +213,7 @@ namespace WixToolset.Core.WindowsInstaller
213
// ignore rows that shouldn't be in a transform
214
if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0]))
215
{
225
- updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
216
+ updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId;
217
comparedRow = updatedRow;
218
keepRow = true;
219
operation = RowOperation.Modify;
@@ -306,7 +297,7 @@ namespace WixToolset.Core.WindowsInstaller
297
if (keepRow)
298
{
299
comparedRow = updatedRow;
309
- comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
300
+ comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId;
301
}
302
}
303
}
@@ -350,8 +341,9 @@ namespace WixToolset.Core.WindowsInstaller
341
// diff the target and updated rows
342
foreach (var targetPrimaryKeyEntry in targetPrimaryKeys)
343
{
353
- var targetPrimaryKey = targetPrimaryKeyEntry.Key;
354
- var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedPrimaryKeys[targetPrimaryKey], out var _, out var keepRow);
344
+ updatedPrimaryKeys.TryGetValue(targetPrimaryKeyEntry.Key, out var updatedRow);
345
+
346
+ var compared = this.CompareRows(targetTable, targetPrimaryKeyEntry.Value, updatedRow, out var _, out var keepRow);
347
348
if (keepRow)
349
{
@@ -369,7 +361,7 @@ namespace WixToolset.Core.WindowsInstaller
361
var updatedRow = (Row)updatedPrimaryKeyEntry.Value;
362
363
updatedRow.Operation = RowOperation.Add;
372
- updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
364
+ updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId;
365
rows.Add(updatedRow);
366
}
367
}
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/FileFacade.cs
new
+82
@@ -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
+}
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+3
-8
@@ -23,19 +23,14 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
23
24
#region IBackendHelper interfaces
25
26
- public IFileFacade CreateFileFacade(FileSymbol file, AssemblySymbol assembly)
26
+ public IFileFacade CreateFileFacade(FileSymbol file)
27
{
28
- return this.backendHelper.CreateFileFacade(file, assembly);
28
+ return new FileFacade(file);
29
}
30
31
public IFileFacade CreateFileFacade(FileRow fileRow)
32
{
33
- return this.backendHelper.CreateFileFacade(fileRow);
34
- }
35
-
36
- public IFileFacade CreateFileFacadeFromMergeModule(FileSymbol fileSymbol)
37
- {
38
- return this.backendHelper.CreateFileFacadeFromMergeModule(fileSymbol);
33
+ return new FileFacade(fileRow);
34
}
35
36
public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null)
src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
-4
@@ -2,11 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
- using System;
5
using WixToolset.Core.WindowsInstaller.Bind;
7
- using WixToolset.Core.WindowsInstaller.Decompile;
8
- using WixToolset.Core.WindowsInstaller.Unbind;
9
- using WixToolset.Data;
6
using WixToolset.Extensibility;
7
using WixToolset.Extensibility.Data;
8
using WixToolset.Extensibility.Services;
src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
+8
-88
@@ -2,7 +2,6 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
- using System;
5
using System.Collections.Generic;
6
using WixToolset.Core.WindowsInstaller.Bind;
7
using WixToolset.Data.WindowsInstaller;
@@ -18,8 +17,14 @@ namespace WixToolset.Core.WindowsInstaller
17
18
var backendHelper = context.ServiceProvider.GetService<IBackendHelper>();
19
20
+ var pathResolver = context.ServiceProvider.GetService<IPathResolver>();
21
+
22
+ var fileResolver = context.ServiceProvider.GetService<IFileResolver>();
23
+
24
var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
25
26
+ var resolveExtensions = extensionManager.GetServices<IResolverExtension>();
27
+
28
var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>();
29
30
foreach (var extension in backendExtensions)
@@ -30,7 +35,7 @@ namespace WixToolset.Core.WindowsInstaller
35
// Create transforms named in patch transforms.
36
IEnumerable<PatchTransform> patchTransforms;
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);
39
patchTransforms = command.Execute();
40
}
41
@@ -42,7 +47,7 @@ namespace WixToolset.Core.WindowsInstaller
47
}
48
49
// Create WindowsInstallerData with patch metdata and transforms as sub-storages
45
- // Create MSP from WindowsInstallerData
50
+ // and create MSP from that WindowsInstallerData.
51
IBindResult result = null;
52
try
53
{
@@ -62,90 +67,5 @@ namespace WixToolset.Core.WindowsInstaller
67
throw;
68
}
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
70
}
71
}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
+47
-38
@@ -3,7 +3,6 @@
3
namespace WixToolset.Core.WindowsInstaller.Unbind
4
{
5
using System;
6
- using System.Collections;
6
using System.Collections.Generic;
7
using System.Globalization;
8
using System.IO;
@@ -26,7 +25,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
25
this.TreatOutputAsModule = treatOutputAsModule;
26
}
27
29
- public string[] ExtractedFiles { get; private set; }
28
+ public Dictionary<string, MediaRow> ExtractedFileIdsWithMediaRow { get; private set; }
29
30
private WindowsInstallerData Output { get; }
31
@@ -42,47 +41,51 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
41
42
public void Execute()
43
{
44
+ var extractedFileIdsWithMediaRow = new Dictionary<string, MediaRow>();
45
var databaseBasePath = Path.GetDirectoryName(this.InputFilePath);
46
- var cabinetFiles = new List<string>();
47
- var embeddedCabinets = new SortedList();
46
+
47
+ var cabinetPathsWithMediaRow = new Dictionary<string, MediaRow>();
48
+ var embeddedCabinetNamesByDiskId = new SortedDictionary<int, string>();
49
+ var embeddedCabinetRowsByDiskId = new SortedDictionary<int, MediaRow>();
50
51
// index all of the cabinet files
52
if (OutputType.Module == this.Output.Type || this.TreatOutputAsModule)
53
{
52
- embeddedCabinets.Add(0, "MergeModule.CABinet");
54
+ embeddedCabinetNamesByDiskId.Add(0, "MergeModule.CABinet");
55
}
54
- else if (null != this.Output.Tables["Media"])
56
+ else if (this.Output.Tables.TryGetTable("Media", out var mediaTable))
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)))
59
{
58
- if (null != mediaRow.Cabinet)
60
+ if (OutputType.Product == this.Output.Type ||
61
+ (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation))
62
{
60
- if (OutputType.Product == this.Output.Type ||
61
- (OutputType.Transform == this.Output.Type && RowOperation.Add == mediaRow.Operation))
63
+ if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal))
64
{
63
- if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal))
64
- {
65
- embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1));
66
- }
67
- else
68
- {
69
- cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet));
70
- }
65
+ embeddedCabinetNamesByDiskId.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1));
66
+ embeddedCabinetRowsByDiskId.Add(mediaRow.DiskId, mediaRow);
67
+ }
68
+ else
69
+ {
70
+ cabinetPathsWithMediaRow.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet), mediaRow);
71
}
72
}
73
}
74
}
75
76
- // extract the embedded cabinet files from the database
77
- if (0 < embeddedCabinets.Count)
76
+ // Extract any embedded cabinet files from the database.
77
+ if (0 < embeddedCabinetRowsByDiskId.Count)
78
{
79
using (var streamsView = this.Database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?"))
80
{
81
- foreach (int diskId in embeddedCabinets.Keys)
81
+ foreach (var diskIdWithCabinetName in embeddedCabinetNamesByDiskId)
82
{
83
+ var diskId = diskIdWithCabinetName.Key;
84
+ var cabinetName = diskIdWithCabinetName.Value;
85
+
86
using (var record = new Record(1))
87
{
85
- record.SetString(1, (string)embeddedCabinets[diskId]);
88
+ record.SetString(1, cabinetName);
89
streamsView.Execute(record);
90
}
91
@@ -92,15 +95,15 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
95
{
96
// since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not (typically) case-sensitive,
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");
99
100
// ensure the parent directory exists
98
- Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile));
101
+ Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath));
102
100
- using (var fs = File.Create(cabinetFile))
103
+ using (var fs = File.Create(cabinetPath))
104
{
105
int bytesRead;
103
- var buffer = new byte[512];
106
+ var buffer = new byte[4096];
107
108
while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length)))
109
{
@@ -108,7 +111,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
111
}
112
}
113
111
- cabinetFiles.Add(cabinetFile);
114
+ embeddedCabinetRowsByDiskId.TryGetValue(diskId, out var cabinetMediaRow);
115
+ cabinetPathsWithMediaRow.Add(cabinetPath, cabinetMediaRow);
116
}
117
else
118
{
@@ -119,29 +123,34 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
123
}
124
}
125
122
- // extract the cabinet files
123
- if (0 < cabinetFiles.Count)
126
+ // Extract files from any available cabinets.
127
+ if (0 < cabinetPathsWithMediaRow.Count)
128
{
125
- // ensure the directory exists or extraction will fail
129
Directory.CreateDirectory(this.ExportBasePath);
130
128
- foreach (var cabinetFile in cabinetFiles)
131
+ foreach (var cabinetPathWithMediaRow in cabinetPathsWithMediaRow)
132
{
133
+ var cabinetPath = cabinetPathWithMediaRow.Key;
134
+ var cabinetMediaRow = cabinetPathWithMediaRow.Value;
135
+
136
try
137
{
132
- var cabinet = new Cabinet(cabinetFile);
133
- this.ExtractedFiles = cabinet.Extract(this.ExportBasePath).ToArray();
138
+ var cabinet = new Cabinet(cabinetPath);
139
+ var cabinetFilesExtracted = cabinet.Extract(this.ExportBasePath);
140
+
141
+ foreach (var extractedFile in cabinetFilesExtracted)
142
+ {
143
+ extractedFileIdsWithMediaRow.Add(extractedFile, cabinetMediaRow);
144
+ }
145
}
146
catch (FileNotFoundException)
147
{
137
- throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetFile));
148
+ throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.InputFilePath), cabinetPath));
149
}
150
}
151
}
141
- else
142
- {
143
- this.ExtractedFiles = new string[0];
144
- }
152
+
153
+ this.ExtractedFileIdsWithMediaRow = extractedFileIdsWithMediaRow;
154
}
155
}
156
}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+248
-167
@@ -5,29 +5,35 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
5
using System;
6
using System.Collections;
7
using System.Collections.Generic;
8
+ using System.ComponentModel;
9
using System.Globalization;
10
using System.IO;
11
+ using System.Linq;
12
using System.Text.RegularExpressions;
13
using WixToolset.Core.Native.Msi;
14
using WixToolset.Data;
15
using WixToolset.Data.WindowsInstaller;
16
+ using WixToolset.Data.WindowsInstaller.Rows;
17
+ using WixToolset.Extensibility.Data;
18
using WixToolset.Extensibility.Services;
19
20
internal class UnbindDatabaseCommand
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}");
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)
27
{
28
this.Messaging = messaging;
29
this.BackendHelper = backendHelper;
24
- this.Database = database;
30
+ this.PathResolver = pathResolver;
31
this.DatabasePath = databasePath;
32
this.OutputType = outputType;
33
this.ExportBasePath = exportBasePath;
34
+ this.ExtractFilesFolder = extractFilesFolder;
35
this.IntermediateFolder = intermediateFolder;
29
- this.IsAdminImage = isAdminImage;
30
- this.SuppressDemodularization = suppressDemodularization;
36
+ this.EnableDemodularization = enableDemodularization;
37
this.SkipSummaryInfo = skipSummaryInfo;
38
39
this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
@@ -37,7 +43,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
43
44
public IBackendHelper BackendHelper { get; }
45
40
- public Database Database { get; }
46
+ private IPathResolver PathResolver { get; }
47
+
48
+ private Database Database { get; set; }
49
50
public string DatabasePath { get; }
51
@@ -45,73 +53,91 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
53
54
public string ExportBasePath { get; }
55
48
- public string IntermediateFolder { get; }
56
+ public string ExtractFilesFolder { get; }
57
50
- public bool IsAdminImage { get; }
58
+ public string IntermediateFolder { get; }
59
52
- public bool SuppressDemodularization { get; }
60
+ public bool EnableDemodularization { get; }
61
62
public bool SkipSummaryInfo { get; }
63
64
public TableDefinitionCollection TableDefinitions { get; }
65
58
- public IEnumerable<string> ExportedFiles => this.exportedFiles;
66
+ public bool AdminImage { get; private set; }
67
60
- private int SectionCount { get; set; }
68
+ public IEnumerable<string> ExportedFiles { get; private set; }
69
70
public WindowsInstallerData Execute()
71
{
64
- this.exportedFiles = new List<string>();
72
+ var adminImage = false;
73
+ var exportedFiles = new List<string>();
74
66
- string modularizationGuid = null;
67
- var output = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath));
68
- View validationView = null;
75
+ var output = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath))
76
+ {
77
+ Type = this.OutputType
78
+ };
79
+
80
+ try
81
+ {
82
+ using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly))
83
+ {
84
+ this.Database = database;
85
70
- // set the output type
71
- output.Type = this.OutputType;
86
+ Directory.CreateDirectory(this.IntermediateFolder);
87
73
- Directory.CreateDirectory(this.IntermediateFolder);
88
+ output.Codepage = this.GetCodePage();
89
75
- // get the codepage
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;
90
+ var modularizationGuid = this.ProcessTables(output, exportedFiles);
91
81
- while (null != (line = sr.ReadLine()))
82
- {
83
- var data = line.Split('\t');
92
+ var summaryInfo = this.ProcessSummaryInfo(output, modularizationGuid);
93
85
- if (2 == data.Length)
86
- {
87
- output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
88
- }
94
+ this.UpdateUnrealFileColumns(this.DatabasePath, output, summaryInfo, exportedFiles);
95
+
96
+ this.GenerateSectionIds(output);
97
}
98
}
91
-
92
- // get the summary information table if it exists; it won't if unbinding a transform
93
- if (!this.SkipSummaryInfo)
99
+ catch (Win32Exception e)
100
{
95
- using (var summaryInformation = new SummaryInformation(this.Database))
101
+ if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
102
{
97
- var table = new Table(this.TableDefinitions["_SummaryInformation"]);
103
+ throw new WixException(ErrorMessages.OpenDatabaseFailed(this.DatabasePath));
104
+ }
105
99
- for (var i = 1; 19 >= i; i++)
100
- {
101
- var value = summaryInformation.GetProperty(i);
106
+ throw;
107
+ }
108
103
- if (0 < value.Length)
104
- {
105
- var row = table.CreateRow(output.SourceLineNumbers);
106
- row[0] = i;
107
- row[1] = value;
108
- }
109
- }
109
+ this.AdminImage = adminImage;
110
+ this.ExportedFiles = exportedFiles;
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);
130
}
131
}
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
+
141
try
142
{
143
// open a view on the validation table if it exists
@@ -127,7 +153,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
153
{
154
var tableName = tableRecord.GetString(1);
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}`"))
157
{
158
var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView);
159
var table = new Table(tableDefinition);
@@ -154,16 +180,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
180
switch (row.Fields[i].Column.Type)
181
{
182
case ColumnType.Number:
157
- var success = false;
183
var intValue = rowRecord.GetInteger(i + 1);
159
- if (row.Fields[i].Column.IsLocalizable)
160
- {
161
- success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
162
- }
163
- else
164
- {
165
- success = row.BestEffortSetField(i, intValue);
166
- }
184
+ var success = row.Fields[i].Column.IsLocalizable ? row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture)) : row.BestEffortSetField(i, intValue);
185
186
if (!success)
187
{
@@ -171,20 +189,18 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
189
}
190
break;
191
case ColumnType.Object:
174
- var sourceFile = "FILE NOT EXPORTED";
192
+ var source = "FILE NOT EXPORTED";
193
194
if (null != this.ExportBasePath)
195
{
178
- var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
179
- sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile);
196
+ source = Path.Combine(this.ExportBasePath, tableName, row.GetPrimaryKey('.'));
197
181
- // ensure the parent directory exists
182
- System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
198
+ Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
199
184
- using (var fs = System.IO.File.Create(sourceFile))
200
+ using (var fs = File.Create(source))
201
{
202
int bytesRead;
187
- var buffer = new byte[512];
203
+ var buffer = new byte[4096];
204
205
while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
206
{
@@ -192,10 +208,10 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
208
}
209
}
210
195
- this.exportedFiles.Add(sourceFile);
211
+ exportedFiles.Add(source);
212
}
213
198
- row[i] = sourceFile;
214
+ row[i] = source;
215
break;
216
default:
217
var value = rowRecord.GetString(i + 1);
@@ -203,27 +219,26 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
219
switch (row.Fields[i].Column.Category)
220
{
221
case ColumnCategory.Guid:
206
- value = value.ToUpper(CultureInfo.InvariantCulture);
222
+ value = value.ToUpperInvariant();
223
break;
224
}
225
210
- // de-modularize
211
- if (!this.SuppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
226
+ // De-modularize
227
+ if (this.EnableDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
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
-
229
if (null == modularizationGuid)
230
{
217
- var match = modularization.Match(value);
231
+ var match = Modularization.Match(value);
232
if (match.Success)
233
{
234
modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
235
}
236
}
237
224
- value = modularization.Replace(value, String.Empty);
238
+ value = Modularization.Replace(value, String.Empty);
239
}
240
241
+#if TODO_MOVE_TO_DECOMPILER
242
// escape "$(" for the preprocessor
243
value = value.Replace("$(", "$$(");
244
@@ -234,6 +249,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
249
//{
250
// value = value.Insert(matches[j].Index, "!");
251
//}
252
+#endif
253
254
row[i] = value;
255
break;
@@ -249,33 +265,54 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
265
}
266
finally
267
{
252
- if (null != validationView)
253
- {
254
- validationView.Close();
255
- }
268
+ validationView?.Close();
269
}
270
258
- // set the modularization guid as the PackageCode
259
- if (null != modularizationGuid)
260
- {
261
- var table = output.Tables["_SummaryInformation"];
271
+ return modularizationGuid;
272
+ }
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))
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++)
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
+ }
309
}
269
- }
270
- }
310
272
- if (this.IsAdminImage)
273
- {
274
- this.GenerateWixFileTable(this.DatabasePath, output);
275
- this.GenerateSectionIds(output);
311
+ output.Tables.Add(table);
312
+ }
313
}
314
278
- return output;
315
+ return result;
316
}
317
318
private TableDefinition GetTableDefinition(string tableName, View tableView, View validationView)
@@ -310,10 +347,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
347
var columnName = columnNameRecord.GetString(i);
348
var idtType = columnTypeRecord.GetString(i);
349
313
- ColumnType columnType;
314
- int length;
315
- bool nullable;
316
-
350
var columnCategory = ColumnCategory.Unknown;
351
var columnModularizeType = ColumnModularizeType.None;
352
var primary = tablePrimaryKeys.Contains(columnName);
@@ -326,7 +359,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
359
string description = null;
360
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]))
364
{
365
case 'i':
366
columnType = ColumnType.Number;
@@ -345,8 +379,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
379
columnType = ColumnType.Unknown;
380
break;
381
}
348
- length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture);
349
- nullable = Char.IsUpper(idtType[0]);
382
+ var length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture);
383
+ var nullable = Char.IsUpper(idtType[0]);
384
385
// try to get validation information
386
if (null != validationView)
@@ -385,11 +419,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
419
// convert category to ColumnCategory
420
if (null != category)
421
{
388
- try
389
- {
390
- columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true);
391
- }
392
- catch (ArgumentException)
422
+ if (!Enum.TryParse(category, true, out columnCategory))
423
{
424
columnCategory = ColumnCategory.Unknown;
425
}
@@ -427,67 +457,103 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
457
return new TableDefinition(tableName, null, columns, false);
458
}
459
430
- /// <summary>
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)
460
+ private void UpdateUnrealFileColumns(string databaseFile, WindowsInstallerData output, SummaryInformationBits summaryInformation, List<string> exportedFiles)
461
{
437
- throw new NotImplementedException();
438
-#if TODO_FIX_UNBINDING_FILES
439
- var adminRootPath = Path.GetDirectoryName(databaseFile);
462
+ var fileRows = output.Tables["File"]?.Rows;
463
441
- var componentDirectoryIndex = new Hashtable();
442
- var componentTable = output.Tables["Component"];
443
- foreach (var row in componentTable.Rows)
464
+ if (fileRows == null || fileRows.Count == 0)
465
{
445
- componentDirectoryIndex.Add(row[0], row[2]);
466
+ return;
467
}
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
+
500
// Index full source paths for all directories
449
- var directoryDirectoryParentIndex = new Hashtable();
450
- var directoryFullPathIndex = new Hashtable();
451
- var directorySourceNameIndex = new Hashtable();
501
+ var directories = new Dictionary<string, IResolvedDirectory>();
502
+
503
var directoryTable = output.Tables["Directory"];
504
foreach (var row in directoryTable.Rows)
505
{
455
- directoryDirectoryParentIndex.Add(row[0], row[1]);
456
- if (null == row[1])
457
- {
458
- directoryFullPathIndex.Add(row[0], adminRootPath);
459
- }
460
- else
461
- {
462
- directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2]));
463
- }
506
+ var sourceName = this.BackendHelper.GetMsiFileName(row.FieldAsString(2), source: true, longName: summaryInformation.LongFilenames);
507
+ var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(row.FieldAsString(1), sourceName);
508
+
509
+ directories.Add(row.FieldAsString(0), resolvedDirectory);
510
}
511
466
- foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex)
512
+ if (summaryInformation.AdminImage)
513
{
468
- if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key))
514
+ foreach (var fileRow in fileRows.Cast<FileRow>())
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);
520
}
521
}
473
-
474
- var fileTable = output.Tables["File"];
475
- var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]);
476
- foreach (var row in fileTable.Rows)
522
+ else
523
{
478
- var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]);
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]));
524
+ var extractedFileIds = new HashSet<string>();
525
483
- if (!File.Exists(wixFileRow.Source))
526
+ if (!String.IsNullOrEmpty(this.ExtractFilesFolder))
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);
533
}
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
+ }
556
}
490
-#endif
557
}
558
559
/// <summary>
@@ -620,7 +686,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
686
{
687
switch (table.Name)
688
{
623
- case "WixFile":
689
case "MsiFileHash":
690
ConnectTableToSection(table, fileSectionIdIndex, 0);
691
break;
@@ -699,7 +764,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
764
}
765
}
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.
768
//foreach (IUnbinderExtension extension in this.unbinderExtensions)
769
//{
770
// extension.GenerateSectionIds(output);
@@ -711,19 +776,22 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
776
/// </summary>
777
/// <param name="table">The table to add sections to.</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>
715
- private Hashtable AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex)
779
+ /// <returns>A dictionary containing the tables key for each row paired with its assigned section id.</returns>
780
+ private Dictionary<string, string> AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex)
781
{
717
- var hashtable = new Hashtable();
782
+ var primaryKeyToSectionId = new Dictionary<string, string>();
783
+
784
if (null != table)
785
{
786
foreach (var row in table.Rows)
787
{
788
row.SectionId = this.GetNewSectionId();
723
- hashtable.Add(row[rowPrimaryKeyIndex], row.SectionId);
789
+
790
+ primaryKeyToSectionId.Add(row.FieldAsString(rowPrimaryKeyIndex), row.SectionId);
791
}
792
}
726
- return hashtable;
793
+
794
+ return primaryKeyToSectionId;
795
}
796
797
/// <summary>
@@ -732,15 +800,15 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
800
/// <param name="table">The table containing rows that need to be connected to sections.</param>
801
/// <param name="sectionIdIndex">A hashtable containing keys to map table to its section.</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)
804
{
805
if (null != table)
806
{
807
foreach (var row in table.Rows)
808
{
741
- if (sectionIdIndex.ContainsKey(row[rowIndex]))
809
+ if (sectionIdIndex.TryGetValue(row.FieldAsString(rowIndex), out var sectionId))
810
{
743
- row.SectionId = (string)sectionIdIndex[row[rowIndex]];
811
+ row.SectionId = sectionId;
812
}
813
}
814
}
@@ -750,40 +818,53 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
818
/// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it.
819
/// </summary>
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>
822
/// <param name="rowIndex">The index of the column which is used as the foreign key in to the sectionIdIndex.</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>
757
- private static Hashtable ConnectTableToSectionAndIndex(Table table, Hashtable sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex)
824
+ /// <returns>A dictionary containing the tables key for each row paired with its assigned section id.</returns>
825
+ private static Dictionary<string, string> ConnectTableToSectionAndIndex(Table table, Dictionary<string, string> sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex)
826
{
759
- var newHashTable = new Hashtable();
827
+ var newPrimaryKeyToSectionId = new Dictionary<string, string>();
828
+
829
if (null != table)
830
{
831
foreach (var row in table.Rows)
832
{
764
- if (!sectionIdIndex.ContainsKey(row[rowIndex]))
833
+ var foreignKey = row.FieldAsString(rowIndex);
834
+
835
+ if (!sectionIdIndex.TryGetValue(foreignKey, out var sectionId))
836
{
837
continue;
838
}
839
769
- row.SectionId = (string)sectionIdIndex[row[rowIndex]];
770
- if (null != row[rowPrimaryKeyIndex])
840
+ row.SectionId = sectionId;
841
+
842
+ var primaryKey = row.FieldAsString(rowPrimaryKeyIndex);
843
+
844
+ if (!String.IsNullOrEmpty(primaryKey) && sectionIdIndex.ContainsKey(primaryKey))
845
{
772
- newHashTable.Add(row[rowPrimaryKeyIndex], row.SectionId);
846
+ newPrimaryKeyToSectionId.Add(primaryKey, row.SectionId);
847
}
848
}
849
}
776
- return newHashTable;
850
+
851
+ return newPrimaryKeyToSectionId;
852
}
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>
854
private string GetNewSectionId()
855
{
785
- this.SectionCount++;
786
- return "wix.section." + this.SectionCount.ToString(CultureInfo.InvariantCulture);
856
+ this.sectionCount++;
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; }
868
}
869
}
870
}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
deleted
-72
@@ -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
-}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+10
-4
@@ -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.
2
3
+#if TODO_KEEP_FOR_PATCH_UNBINDING_CONSIDERATION_IN_FUTURE
4
+
5
namespace WixToolset.Core.WindowsInstaller.Unbind
6
{
7
using System;
@@ -17,10 +19,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
19
20
internal class UnbindTransformCommand
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)
23
{
24
this.Messaging = messaging;
25
this.BackendHelper = backendHelper;
26
+ this.FileSystemManager = fileSystemManager;
27
this.TransformFile = transformFile;
28
this.ExportBasePath = exportBasePath;
29
this.IntermediateFolder = intermediateFolder;
@@ -32,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
35
36
private IBackendHelper BackendHelper { get; }
37
38
+ private FileSystemManager FileSystemManager { get; }
39
+
40
private string TransformFile { get; }
41
42
private string ExportBasePath { get; }
@@ -90,7 +95,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
95
msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);
96
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);
99
var transformViewOutput = unbindCommand.Execute();
100
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
165
}
166
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);
169
var output = unbindCommand.Execute();
170
171
// index all the rows to easily find modified rows
@@ -302,8 +307,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
307
308
private void GenerateDatabase(WindowsInstallerData output, string databaseFile)
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);
311
command.Execute();
312
}
313
}
314
}
315
+#endif
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
+24
-50
@@ -3,11 +3,9 @@
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
- using System.Collections.Generic;
7
- using System.ComponentModel;
6
+ using System.ComponentModel.Design;
7
using System.IO;
8
using System.Linq;
10
- using WixToolset.Core.Native.Msi;
9
using WixToolset.Core.WindowsInstaller.Decompile;
10
using WixToolset.Core.WindowsInstaller.Unbind;
11
using WixToolset.Data;
@@ -63,56 +61,32 @@ namespace WixToolset.Core.WindowsInstaller
61
62
private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context)
63
{
66
- var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
67
-
68
- try
64
+ // Delete the directory and its files to prevent cab extraction failure due to an existing file.
65
+ if (Directory.Exists(context.ExtractFolder))
66
{
70
- using (var database = new Database(context.DecompilePath, OpenDatabase.ReadOnly))
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
- }
67
+ Directory.Delete(context.ExtractFolder, true);
68
}
107
- catch (Win32Exception e)
108
- {
109
- if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
110
- {
111
- throw new WixException(ErrorMessages.OpenDatabaseFailed(context.DecompilePath));
112
- }
69
114
- throw;
115
- }
70
+ var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
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();
90
91
return result;
92
}
src/wix/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
+2
-3
@@ -4,7 +4,6 @@ namespace WixToolset.Core.Bind
4
{
5
using System;
6
using System.Collections.Generic;
7
- using System.Globalization;
7
using System.IO;
8
using System.Linq;
9
using System.Security.Cryptography;
@@ -41,9 +40,9 @@ namespace WixToolset.Core.Bind
40
{
41
var localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(uri.LocalPath);
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);
44
46
- extractPath = Path.GetFullPath(Path.Combine(extractFolder, extractedName));
45
+ extractPath = Path.GetFullPath(Path.Combine(extractFolder, extractedName, embeddedFileId));
46
extracts.Add(embeddedFileId, extractPath);
47
}
48
src/wix/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+66
-169
@@ -16,186 +16,121 @@ namespace WixToolset.Core.Bind
16
/// </summary>
17
internal class ResolveFieldsCommand
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
+ }
31
21
- public bool BuildingPatch { private get; set; }
32
+ private IMessaging Messaging { get; }
33
23
- public IVariableResolver VariableResolver { private get; set; }
34
+ private IFileResolver FileResolver { get; }
35
25
- public IEnumerable<IBindPath> BindPaths { private get; set; }
36
+ private IVariableResolver VariableResolver { get; }
37
27
- public IEnumerable<IResolverExtension> Extensions { private get; set; }
38
+ private IEnumerable<IBindPath> BindPaths { get; }
39
29
- public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; }
40
+ private IEnumerable<IResolverExtension> Extensions { get; }
41
31
- public string IntermediateFolder { private get; set; }
42
+ private ExtractEmbeddedFiles FilesWithEmbeddedFiles { get; }
43
33
- public Intermediate Intermediate { private get; set; }
44
+ private string IntermediateFolder { get; }
45
35
- public bool SupportDelayedResolution { private get; set; }
46
+ private Intermediate Intermediate { get; }
47
37
- public bool AllowUnresolvedVariables { private get; set; }
48
+ private bool AllowUnresolvedVariables { get; }
49
50
public IReadOnlyCollection<DelayedField> DelayedFields { get; private set; }
51
52
public void Execute()
53
{
43
- var delayedFields = this.SupportDelayedResolution ? new List<DelayedField>() : null;
54
+ var delayedFields = new List<DelayedField>();
55
45
- var fileResolver = new FileResolver(this.BindPaths, this.Extensions);
56
+ var bindPaths = this.BindPaths.Where(b => b.Stage == BindStage.Normal).ToList();
57
58
// Build the column lookup only when needed.
59
Dictionary<string, WixCustomTableColumnSymbol> customColumnsById = null;
60
50
- foreach (var sections in this.Intermediate.Sections)
61
+ foreach (var symbol in this.Intermediate.Sections.SelectMany(s => s.Symbols))
62
{
52
- foreach (var symbol in sections.Symbols)
63
+ foreach (var field in symbol.Fields.Where(f => !f.IsNull()))
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)
71
{
56
- if (field.IsNull())
72
+ // We only care about the Data in a CustomTable cell.
73
+ if (field.Name != nameof(WixCustomTableCellSymbolFields.Data))
74
{
75
continue;
76
}
77
61
- var fieldType = field.Type;
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)
78
+ if (customColumnsById == null)
79
{
68
- // We only care about the Data in a CustomTable cell.
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
- }
80
+ customColumnsById = this.Intermediate.Sections.SelectMany(s => s.Symbols.OfType<WixCustomTableColumnSymbol>()).ToDictionary(t => t.Id.Id);
81
}
82
85
- // Check to make sure we're in a scenario where we can handle variable resolution.
86
- if (null != delayedFields)
83
+ if (customColumnsById.TryGetValue(symbol.Fields[(int)WixCustomTableCellSymbolFields.TableRef].AsString() + "/" + symbol.Fields[(int)WixCustomTableCellSymbolFields.ColumnRef].AsString(), out var customColumn))
84
{
88
- // resolve localization and wix variables
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;
85
+ fieldType = customColumn.Type;
86
}
87
+ }
88
114
- // Resolve file paths
115
- if (fieldType == IntermediateFieldType.Path)
89
+ // Check to make sure we're in a scenario where we can handle variable resolution.
90
+ if (null != delayedFields)
91
+ {
92
+ // resolve localization and wix variables
93
+ if (fieldType == IntermediateFieldType.String)
94
{
117
- this.ResolvePathField(fileResolver, symbol, field);
118
-
119
-#if TODO_PATCHING
120
- if (null != objectField.PreviousData)
95
+ var original = field.AsString();
96
+ if (!String.IsNullOrEmpty(original))
97
{
122
- objectField.PreviousData = this.BindVariableResolver.ResolveVariables(symbol.SourceLineNumbers, objectField.PreviousData, false, out isDefault);
123
-
124
- if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field.
98
+ var resolution = this.VariableResolver.ResolveVariables(symbol.SourceLineNumbers, original, !this.AllowUnresolvedVariables);
99
+ if (resolution.UpdatedValue)
100
{
126
- // file is compressed in a cabinet (and not modified above)
127
- if (objectField.PreviousEmbeddedFileIndex.HasValue && isDefault)
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);
101
+ field.Set(resolution.Value);
102
+ }
103
167
- }
168
- }
169
- catch (WixFileNotFoundException)
170
- {
171
- // display the error with source line information
172
- Messaging.Instance.Write(WixErrors.FileNotFound(symbol.SourceLineNumbers, (string)objectField.PreviousData));
173
- }
174
- }
104
+ if (resolution.DelayedResolve)
105
+ {
106
+ delayedFields.Add(new DelayedField(symbol, field));
107
}
108
}
177
-#endif
109
}
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
+ }
123
}
124
}
125
126
this.DelayedFields = delayedFields;
127
}
128
186
- private void ResolvePathField(FileResolver fileResolver, IntermediateSymbol symbol, IntermediateField field)
129
+ private void ResolvePathField(IFileResolver fileResolver, IEnumerable<IBindPath> bindPaths, IntermediateSymbol symbol, IntermediateField field)
130
{
131
var fieldValue = field.AsPath();
132
var originalFieldPath = fieldValue.Path;
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
-
134
// If the file is embedded and if the previous value has a bind variable in the path
135
// which gets modified by resolving the previous value again then switch to that newly
136
// resolved path instead of using the embedded file.
@@ -221,45 +156,7 @@ namespace WixToolset.Core.Bind
156
{
157
try
158
{
224
- var resolvedPath = fieldValue.Path;
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
159
+ var resolvedPath = fileResolver.ResolveFile(fieldValue.Path, this.Extensions, bindPaths, BindStage.Normal, symbol.SourceLineNumbers, symbol.Definition);
160
161
if (!String.Equals(originalFieldPath, resolvedPath, StringComparison.OrdinalIgnoreCase))
162
{
src/wix/WixToolset.Core/BindContext.cs
+1
-1
@@ -18,7 +18,7 @@ namespace WixToolset.Core
18
19
public IServiceProvider ServiceProvider { get; }
20
21
- public IReadOnlyCollection<BindPath> BindPaths { get; set; }
21
+ public IReadOnlyCollection<IBindPath> BindPaths { get; set; }
22
23
public string BurnStubPath { get; set; }
24
src/wix/WixToolset.Core/CommandLine/BuildCommand.cs
+21
-4
@@ -48,7 +48,9 @@ namespace WixToolset.Core.CommandLine
48
{
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."),
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."),
54
new CommandLineHelpSwitch("-cabcache", "-cc", "Set a folder to cache cabinets across builds."),
55
new CommandLineHelpSwitch("-culture", "Adds a culture to filter localization files."),
56
new CommandLineHelpSwitch("-define", "-d", "Sets a preprocessor variable."),
@@ -292,6 +294,7 @@ namespace WixToolset.Core.CommandLine
294
{
295
{
296
var context = this.ServiceProvider.GetService<IBindContext>();
297
+ context.BindPaths = bindPaths;
298
//context.CabbingThreadCount = this.CabbingThreadCount;
299
context.CabCachePath = cabCachePath;
300
context.ResolvedCodepage = resolveResult.Codepage;
@@ -534,11 +537,25 @@ namespace WixToolset.Core.CommandLine
537
this.BindFiles = true;
538
return true;
539
540
+ case "b":
541
case "bindpath":
542
+ case "bt":
543
+ case "bindpath:target":
544
+ case "bu":
545
+ case "bindpath:update":
546
{
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))
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
+
559
this.BindPaths.Add(bindPath);
560
}
561
return true;
@@ -830,7 +847,7 @@ namespace WixToolset.Core.CommandLine
847
return new InputsAndOutputs(codePaths, localizationPaths, libraryPaths, wixipls, outputPath, outputType, pdbPath, this.PdbType);
848
}
849
833
- private bool TryParseBindPath(string bindPath, out IBindPath bp)
850
+ private bool TryParseBindPath(string argument, string bindPath, out IBindPath bp)
851
{
852
var namedPath = bindPath.Split(BindPathSplit, 2);
853
@@ -848,7 +865,7 @@ namespace WixToolset.Core.CommandLine
865
866
if (File.Exists(bp.Path))
867
{
851
- this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile("-bindpath", bp.Path));
868
+ this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(argument, bp.Path));
869
return false;
870
}
871
src/wix/WixToolset.Core/Compiler.cs
+5
-22
@@ -7706,7 +7706,6 @@ namespace WixToolset.Core
7706
var validationFlags = TransformFlags.PatchTransformDefault;
7707
string baselineFile = null;
7708
string updateFile = null;
7709
- string transformFile = null;
7709
7710
foreach (var attrib in node.Attributes())
7711
{
@@ -7726,9 +7725,6 @@ namespace WixToolset.Core
7725
case "UpdateFile":
7726
updateFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
7727
break;
7729
- case "TransformFile":
7730
- transformFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
7731
- break;
7728
default:
7729
this.Core.UnexpectedAttribute(node, attrib);
7730
break;
@@ -7746,26 +7742,14 @@ namespace WixToolset.Core
7742
id = Identifier.Invalid;
7743
}
7744
7749
- if (!String.IsNullOrEmpty(baselineFile) || !String.IsNullOrEmpty(updateFile))
7745
+ if (String.IsNullOrEmpty(baselineFile))
7746
{
7751
- if (String.IsNullOrEmpty(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
- }
7747
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile"));
7748
}
7766
- else if (String.IsNullOrEmpty(transformFile))
7749
+
7750
+ if (String.IsNullOrEmpty(updateFile))
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"));
7753
}
7754
7755
foreach (var child in node.Elements())
@@ -7805,7 +7789,6 @@ namespace WixToolset.Core
7789
ValidationFlags = validationFlags,
7790
BaselineFile = new IntermediateFieldPathValue { Path = baselineFile },
7791
UpdateFile = new IntermediateFieldPathValue { Path = updateFile },
7808
- TransformFile = new IntermediateFieldPathValue { Path = transformFile },
7792
});
7793
}
7794
}
src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
-17
@@ -6,8 +6,6 @@ namespace WixToolset.Core.ExtensibilityServices
6
using System.Collections.Generic;
7
using WixToolset.Core.Bind;
8
using WixToolset.Data;
9
- using WixToolset.Data.Symbols;
10
- using WixToolset.Data.WindowsInstaller.Rows;
9
using WixToolset.Extensibility.Data;
10
using WixToolset.Extensibility.Services;
11
@@ -17,21 +15,6 @@ namespace WixToolset.Core.ExtensibilityServices
15
{
16
}
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
-
18
public string CreateGuid()
19
{
20
return Common.GenerateGuid();
src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs
deleted
-175
@@ -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
-}
src/wix/WixToolset.Core/ExtensibilityServices/FileResolver.cs
renamed
+28
-70
@@ -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.
2
3
-namespace WixToolset.Core.Bind
3
+namespace WixToolset.Core.ExtensibilityServices
4
{
5
using System;
6
using System.Collections.Generic;
7
using System.IO;
8
- using System.Linq;
8
using WixToolset.Data;
9
using WixToolset.Extensibility;
10
using WixToolset.Extensibility.Data;
11
+ using WixToolset.Extensibility.Services;
12
13
- internal class FileResolver
13
+ internal class FileResolver : IFileResolver
14
{
15
private const string BindPathOpenString = "!(bindpath.";
16
17
- private FileResolver(IEnumerable<IBindPath> bindPaths)
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)
17
+ public string ResolveFile(string source, IEnumerable<ILibrarianExtension> librarianExtensions, IEnumerable<IBindPath> bindPaths, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition)
18
{
19
var checkedPaths = new List<string>();
20
48
- foreach (var extension in this.LibrarianExtensions)
21
+ foreach (var extension in librarianExtensions)
22
{
23
var resolved = extension.ResolveFile(sourceLineNumbers, symbolDefinition, source);
24
@@ -60,19 +33,10 @@ namespace WixToolset.Core.Bind
33
}
34
}
35
63
- return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, BindStage.Normal, checkedPaths);
36
+ return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindPaths, checkedPaths);
37
}
38
66
- /// <summary>
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)
39
+ public string ResolveFile(string source, IEnumerable<IResolverExtension> resolverExtensions, IEnumerable<IBindPath> bindPaths, BindStage bindStage, SourceLineNumber sourceLineNumbers, IntermediateSymbolDefinition symbolDefinition, IEnumerable<string> alreadyCheckedPaths = null)
40
{
41
var checkedPaths = new List<string>();
42
@@ -81,7 +45,7 @@ namespace WixToolset.Core.Bind
45
checkedPaths.AddRange(alreadyCheckedPaths);
46
}
47
84
- foreach (var extension in this.ResolverExtensions)
48
+ foreach (var extension in resolverExtensions)
49
{
50
var resolved = extension.ResolveFile(source, symbolDefinition, sourceLineNumbers, bindStage);
51
@@ -96,10 +60,10 @@ namespace WixToolset.Core.Bind
60
}
61
}
62
99
- return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindStage, checkedPaths);
63
+ return this.MustResolveUsingBindPaths(source, symbolDefinition, sourceLineNumbers, bindPaths, checkedPaths);
64
}
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)
67
{
68
string resolved = null;
69
@@ -135,8 +99,6 @@ namespace WixToolset.Core.Bind
99
pathWithoutSourceDir = path.Substring(10);
100
}
101
138
- var bindPaths = this.BindPaths[bindStage];
139
-
102
foreach (var bindPath in bindPaths)
103
{
104
if (String.IsNullOrEmpty(bindName))
@@ -145,36 +107,18 @@ namespace WixToolset.Core.Bind
107
{
108
if (!String.IsNullOrEmpty(pathWithoutSourceDir))
109
{
148
- var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir);
149
-
150
- checkedPaths.Add(filePath);
151
- if (CheckFileExists(filePath))
152
- {
153
- resolved = filePath;
154
- }
110
+ resolved = ResolveWithBindPath(bindPath.Path, pathWithoutSourceDir, checkedPaths);
111
}
112
113
if (String.IsNullOrEmpty(resolved))
114
{
159
- var filePath = Path.Combine(bindPath.Path, path);
160
-
161
- checkedPaths.Add(filePath);
162
- if (CheckFileExists(filePath))
163
- {
164
- resolved = filePath;
165
- }
115
+ resolved = ResolveWithBindPath(bindPath.Path, path, checkedPaths);
116
}
117
}
118
}
119
else if (bindName.Equals(bindPath.Name, StringComparison.OrdinalIgnoreCase))
120
{
171
- var filePath = Path.Combine(bindPath.Path, path);
172
-
173
- checkedPaths.Add(filePath);
174
- if (CheckFileExists(filePath))
175
- {
176
- resolved = filePath;
177
- }
121
+ resolved = ResolveWithBindPath(bindPath.Path, path, checkedPaths);
122
}
123
124
if (!String.IsNullOrEmpty(resolved))
@@ -186,12 +130,26 @@ namespace WixToolset.Core.Bind
130
131
if (null == resolved)
132
{
189
- throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, source, symbolDefinition.Name, checkedPaths));
133
+ throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, source, symbolDefinition?.Name, checkedPaths));
134
}
135
136
return resolved;
137
}
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
+
153
private static bool CheckFileExists(string path)
154
{
155
try
src/wix/WixToolset.Core/Librarian.cs
+5
-3
@@ -5,7 +5,6 @@ namespace WixToolset.Core
5
using System;
6
using System.Collections.Generic;
7
using System.Linq;
8
- using WixToolset.Core.Bind;
8
using WixToolset.Core.Link;
9
using WixToolset.Data;
10
using WixToolset.Extensibility.Data;
@@ -21,6 +20,7 @@ namespace WixToolset.Core
20
this.ServiceProvider = serviceProvider;
21
22
this.Messaging = this.ServiceProvider.GetService<IMessaging>();
23
+ this.FileResolver = this.ServiceProvider.GetService<IFileResolver>();
24
this.LayoutServices = this.ServiceProvider.GetService<ILayoutServices>();
25
}
26
@@ -28,6 +28,8 @@ namespace WixToolset.Core
28
29
private IMessaging Messaging { get; }
30
31
+ private IFileResolver FileResolver { get; }
32
+
33
private ILayoutServices LayoutServices { get; }
34
35
/// <summary>
@@ -97,7 +99,7 @@ namespace WixToolset.Core
99
{
100
var variableResolver = this.ServiceProvider.GetService<IVariableResolver>();
101
100
- var fileResolver = new FileResolver(context.BindPaths, context.Extensions);
102
+ var bindPaths = context.BindPaths.Where(b => b.Stage == BindStage.Normal).ToList();
103
104
foreach (var symbol in sections.SelectMany(s => s.Symbols))
105
{
@@ -109,7 +111,7 @@ namespace WixToolset.Core
111
{
112
var resolution = variableResolver.ResolveVariables(symbol.SourceLineNumbers, pathField.Path);
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);
115
116
if (!String.IsNullOrEmpty(file))
117
{
src/wix/WixToolset.Core/Resolver.cs
+7
-34
@@ -23,12 +23,16 @@ namespace WixToolset.Core
23
this.ServiceProvider = serviceProvider;
24
25
this.Messaging = serviceProvider.GetService<IMessaging>();
26
+
27
+ this.FileResolver = serviceProvider.GetService<IFileResolver>();
28
}
29
30
private IServiceProvider ServiceProvider { get; }
31
32
private IMessaging Messaging { get; }
33
34
+ private IFileResolver FileResolver { get; }
35
+
36
public IResolveResult Resolve(IResolveContext context)
37
{
38
foreach (var extension in context.Extensions)
@@ -45,7 +49,7 @@ namespace WixToolset.Core
49
50
this.LocalizeUI(variableResolver, context.IntermediateRepresentation);
51
48
- resolveResult = this.DoResolve(context, variableResolver);
52
+ resolveResult = this.ResolveFields(context, variableResolver);
53
54
var primaryLocalization = filteredLocalizations.FirstOrDefault();
55
@@ -71,49 +75,18 @@ namespace WixToolset.Core
75
return resolveResult;
76
}
77
74
- private ResolveResult DoResolve(IResolveContext context, IVariableResolver variableResolver)
78
+ private ResolveResult ResolveFields(IResolveContext context, IVariableResolver variableResolver)
79
{
76
- var buildingPatch = context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
77
-
80
var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
81
82
IReadOnlyCollection<DelayedField> delayedFields;
83
{
82
- var command = new ResolveFieldsCommand();
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;
84
+ var command = new ResolveFieldsCommand(this.Messaging, this.FileResolver, variableResolver, context.BindPaths, context.Extensions, filesWithEmbeddedFiles, context.IntermediateFolder, context.IntermediateRepresentation, context.AllowUnresolvedVariables);
85
command.Execute();
86
87
delayedFields = command.DelayedFields;
88
}
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
-
90
var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
91
92
context.IntermediateRepresentation.UpdateLevel(IntermediateLevels.Resolved);
src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+1
@@ -27,6 +27,7 @@ namespace WixToolset.Core
27
this.AddService((provider, singletons) => AddSingleton<ILayoutServices>(singletons, new LayoutServices(provider)));
28
this.AddService((provider, singletons) => AddSingleton<IBackendHelper>(singletons, new BackendHelper(provider)));
29
this.AddService((provider, singletons) => AddSingleton<IPathResolver>(singletons, new PathResolver()));
30
+ this.AddService((provider, singletons) => AddSingleton<IFileResolver>(singletons, new FileResolver()));
31
this.AddService((provider, singletons) => AddSingleton<IFileSystem>(singletons, new FileSystem()));
32
this.AddService((provider, singletons) => AddSingleton<IWixBranding>(singletons, new WixBranding()));
33
src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs
+158
-22
@@ -5,6 +5,7 @@ namespace WixToolsetTest.CoreIntegration
5
using System;
6
using System.Collections.Generic;
7
using System.ComponentModel;
8
+ using System.Diagnostics;
9
using System.IO;
10
using System.Linq;
11
using System.Runtime.InteropServices;
@@ -22,8 +23,11 @@ namespace WixToolsetTest.CoreIntegration
23
public class PatchFixture : IDisposable
24
{
25
private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd";
26
+ private static readonly XName TargetProductCodeName = PatchNamespace + "TargetProductCode";
27
+
28
private readonly DisposableFileSystem tempFileSystem;
29
private readonly string tempBaseFolder;
30
+ private readonly string templateSourceFolder;
31
private readonly string templateBaselinePdb;
32
private readonly string templateUpdatePdb;
33
private readonly string templateUpdateNoFilesChangedPdb;
@@ -33,14 +37,14 @@ namespace WixToolsetTest.CoreIntegration
37
this.tempFileSystem = new DisposableFileSystem();
38
this.tempBaseFolder = this.tempFileSystem.GetFolder();
39
36
- var templateSourceFolder = TestData.Get(@"TestData", "PatchTemplatePackage");
40
+ this.templateSourceFolder = TestData.Get(@"TestData", "PatchTemplatePackage");
41
var tempFolderBaseline = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "baseline");
42
var tempFolderUpdate = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "update");
43
var tempFolderUpdateNoFileChanges = Path.Combine(this.tempBaseFolder, "PatchTemplatePackage", "updatewithoutfilechanges");
44
41
- this.templateBaselinePdb = BuildMsi("Baseline.msi", templateSourceFolder, tempFolderBaseline, "1.0.0", "1.0.0", "1.0.0", new[] { Path.Combine(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") });
43
- this.templateUpdateNoFilesChangedPdb = BuildMsi("Update.msi", templateSourceFolder, tempFolderUpdateNoFileChanges, "1.0.1", "1.0.1", "1.0.1", 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") });
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") });
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") });
48
}
49
50
public void Dispose()
@@ -49,7 +53,7 @@ namespace WixToolsetTest.CoreIntegration
53
}
54
55
[Fact]
52
- public void CanBuildSimplePatch()
56
+ public void CanBuildSimplePatchUsingWixpdbs()
57
{
58
var folder = TestData.Get(@"TestData", "PatchSingle");
59
@@ -66,14 +70,13 @@ namespace WixToolsetTest.CoreIntegration
70
Assert.True(File.Exists(update1Pdb));
71
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);
74
75
var names = Query.GetSubStorageNames(patchPath);
76
WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
77
78
var cab = Path.Combine(tempFolder, "foo.cab");
79
Query.ExtractStream(patchPath, "foo.cab", cab);
76
- Assert.True(File.Exists(cab));
80
81
var files = Query.GetCabinetFiles(cab);
82
WixAssert.CompareLineByLine(new[] { "a.txt", "b.txt" }, files.Select(f => f.Name).ToArray());
@@ -81,7 +84,37 @@ namespace WixToolsetTest.CoreIntegration
84
}
85
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()
118
{
119
var sourceFolder = TestData.Get(@"TestData", "PatchWithFileChanges");
120
@@ -94,7 +127,7 @@ namespace WixToolsetTest.CoreIntegration
127
var patchPath = Path.ChangeExtension(patchPdb, ".msp");
128
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);
131
132
var names = Query.GetSubStorageNames(patchPath);
133
WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
@@ -106,10 +139,72 @@ namespace WixToolsetTest.CoreIntegration
139
var file = files.Single();
140
WixAssert.StringEqual("a.txt", file.Name);
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);
143
}
144
}
145
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]
209
public void CanBuildSimplePatchWithNoFileChanges()
210
{
@@ -123,14 +218,13 @@ namespace WixToolsetTest.CoreIntegration
218
var patchPath = Path.ChangeExtension(patchPdb, ".msp");
219
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);
222
223
var names = Query.GetSubStorageNames(patchPath);
224
WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
225
226
var cab = Path.Combine(tempFolder, "foo.cab");
227
Query.ExtractStream(patchPath, "foo.cab", cab);
133
- Assert.True(File.Exists(cab));
228
229
var files = Query.GetCabinetFiles(cab);
230
Assert.Empty(files);
@@ -155,14 +249,13 @@ namespace WixToolsetTest.CoreIntegration
249
var patchPath = Path.ChangeExtension(patchPdb, ".msp");
250
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);
253
254
var names = Query.GetSubStorageNames(patchPath);
255
WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names);
256
257
var cab = Path.Combine(baseFolder, "foo.cab");
258
Query.ExtractStream(patchPath, "foo.cab", cab);
165
- Assert.True(File.Exists(cab));
259
260
var files = Query.GetCabinetFiles(cab);
261
var file = files.Single();
@@ -197,7 +290,7 @@ namespace WixToolsetTest.CoreIntegration
290
var patchPath = Path.ChangeExtension(patchPdb, ".msp");
291
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);
294
295
var names = Query.GetSubStorageNames(patchPath);
296
WixAssert.CompareLineByLine(new[] { "#ThisBaseLineIdIsTooLongAndGe.1", "ThisBaseLineIdIsTooLongAndGe.1" }, names);
@@ -217,12 +310,12 @@ namespace WixToolsetTest.CoreIntegration
310
var tempFolderPatch = Path.Combine(baseFolder, "patch");
311
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");
221
- var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePdb), Path.GetDirectoryName(update1Pdb) }, hasNoFiles: true);
313
+ var updatePdb = BuildMsi("Update.msi", sourceFolder, tempFolderUpdate, "1.0.1", "1.0.1", "1.0.1");
314
+ var patchPdb = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(baselinePdb), Path.GetDirectoryName(updatePdb) }, hasNoFiles: true);
315
var patchPath = Path.ChangeExtension(patchPdb, ".msp");
316
224
- Assert.True(File.Exists(baselinePdb));
225
- Assert.True(File.Exists(update1Pdb));
317
+ var doc = GetExtractPatchXml(patchPath);
318
+ WixAssert.StringEqual("{7C871EC1-1F89-4850-A6A9-D7A4C21769F6}", doc.Root.Element(TargetProductCodeName).Value);
319
}
320
}
321
@@ -317,7 +410,28 @@ namespace WixToolsetTest.CoreIntegration
410
return Path.ChangeExtension(outputPath, ".wixpdb");
411
}
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)
435
{
436
var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName));
437
@@ -332,10 +446,22 @@ namespace WixToolsetTest.CoreIntegration
446
"-o", outputPath
447
};
448
335
- foreach (var additionaBindPath in bindpaths ?? Enumerable.Empty<string>())
449
+ foreach (var additionalBindPath in bindpaths ?? Enumerable.Empty<string>())
450
{
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);
465
}
466
467
var result = WixRunner.Execute(warningsAsErrors, args.ToArray());
@@ -365,6 +491,16 @@ namespace WixToolsetTest.CoreIntegration
491
return Path.ChangeExtension(outputPath, ".wixpdb");
492
}
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
+
504
private static XDocument GetExtractPatchXml(string path)
505
{
506
var buffer = new StringBuilder(65535);
src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.baseline-data/A.txt
+1
-1
@@ -1 +1 @@
1
-This is A v1.0.0
1
+This is A v1.0.0 from the "PatchTemplatePackage\.baseline-data" folder.
src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data-alternative/A.txt
new
+3
@@ -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.
src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchTemplatePackage/.update-data/A.txt
+3
-1
@@ -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.
src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchUsingAdminImages/Patch.wxs
new
+16
@@ -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>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchWithFileChangesUsingMsi/Patch.wxs
new
+16
@@ -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>