@joebigelow / wix-1 / commits / f89238b2

Drive merge process from tuples not rows and fix merge table suppression

Rob Mensching committed Jun 4, 2020 at 10:32 UTC f89238b23402e33c2fc09b13a02217f03b722216
3 files changed +119 -98
src/WixToolset.Core.WindowsInstaller/Bind/AddBackSuppresedSequenceTablesCommand.cs new
+52
@@ -0,0 +1,52 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.WindowsInstaller.Bind
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using WixToolset.Data.Tuples;
8 + using WixToolset.Data.WindowsInstaller;
9 +
10 + /// <summary>
11 + /// Add back possibly suppressed sequence tables since all sequence tables must be present
12 + /// for the merge process to work. We'll drop the suppressed sequence tables again as
13 + /// necessary.
14 + /// </summary>
15 + internal class AddBackSuppresedSequenceTablesCommand
16 + {
17 + public AddBackSuppresedSequenceTablesCommand(WindowsInstallerData output, TableDefinitionCollection tableDefinitions)
18 + {
19 + this.Output = output;
20 + this.TableDefinitions = tableDefinitions;
21 + }
22 +
23 + private WindowsInstallerData Output { get; }
24 +
25 + private TableDefinitionCollection TableDefinitions { get; }
26 +
27 + public IEnumerable<string> SuppressedTableNames { get; private set; }
28 +
29 + public IEnumerable<string> Execute()
30 + {
31 + var suppressedTableNames = new HashSet<string>();
32 +
33 + foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
34 + {
35 + var sequenceTableName = sequence.WindowsInstallerTableName();
36 + var sequenceTable = this.Output.Tables[sequenceTableName];
37 +
38 + if (null == sequenceTable)
39 + {
40 + sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]);
41 + }
42 +
43 + if (0 == sequenceTable.Rows.Count)
44 + {
45 + suppressedTableNames.Add(sequenceTableName);
46 + }
47 + }
48 +
49 + return this.SuppressedTableNames = suppressedTableNames;
50 + }
51 + }
52 +}
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+12 -12
@@ -350,11 +350,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
350 output = command.Output;
351 }
352
353 - // Modularize identifiers.
353 + IEnumerable<string> suppressedTableNames = null;
354 if (output.Type == OutputType.Module)
355 {
356 - var command = new ModularizeCommand(output, modularizationSuffix, section.Tuples.OfType<WixSuppressModularizationTuple>());
357 - command.Execute();
356 + // Modularize identifiers.
357 + var modularize = new ModularizeCommand(output, modularizationSuffix, section.Tuples.OfType<WixSuppressModularizationTuple>());
358 + modularize.Execute();
359 +
360 + // Ensure all sequence tables in place because, mergemod.dll requires them.
361 + var unsuppress = new AddBackSuppresedSequenceTablesCommand(output, tableDefinitions);
362 + suppressedTableNames = unsuppress.Execute();
363 }
364 else if (output.Type == OutputType.Patch)
365 {
@@ -486,12 +491,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
491 {
492 this.Messaging.Write(VerboseMessages.MergingModules());
493
489 - var command = new MergeModulesCommand(this.Messaging);
490 - command.FileFacades = fileFacades;
491 - command.IntermediateFolder = this.IntermediateFolder;
492 - command.Output = output;
493 - command.OutputPath = this.OutputPath;
494 - command.TableDefinitions = tableDefinitions;
494 + var command = new MergeModulesCommand(this.Messaging, fileFacades, section, suppressedTableNames, this.OutputPath, this.IntermediateFolder);
495 command.Execute();
496 }
497
@@ -591,7 +591,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
591
592 foreach (Data.WindowsInstaller.Rows.ComponentRow row in componentTable.Rows)
593 {
594 - // we don't care about unmanaged components and if there's a * GUID remaining,
594 + // We don't care about unmanaged components and if there's a * GUID remaining,
595 // there's already an error that prevented it from being replaced with a real GUID.
596 if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid)
597 {
@@ -633,13 +633,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
633 command.Execute();
634 }
635
636 - private string ResolveMedia(MediaTuple mediaRow, string mediaLayoutDirectory, string layoutDirectory)
636 + private string ResolveMedia(MediaTuple media, string mediaLayoutDirectory, string layoutDirectory)
637 {
638 string layout = null;
639
640 foreach (var extension in this.BackendExtensions)
641 {
642 - layout = extension.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
642 + layout = extension.ResolveMedia(media, mediaLayoutDirectory, layoutDirectory);
643 if (!String.IsNullOrEmpty(layout))
644 {
645 break;
src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+55 -86
@@ -6,6 +6,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.IO;
9 + using System.Linq;
10 using System.Runtime.InteropServices;
11 using System.Text;
12 using WixToolset.Core.Bind;
@@ -14,65 +15,66 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15 using WixToolset.Data;
16 using WixToolset.Data.Tuples;
17 using WixToolset.Data.WindowsInstaller;
17 - using WixToolset.Data.WindowsInstaller.Rows;
18 using WixToolset.Extensibility.Services;
19
20 /// <summary>
21 - /// Update file information.
21 + /// Merge modules into the database at output path.
22 /// </summary>
23 internal class MergeModulesCommand
24 {
25 - public MergeModulesCommand(IMessaging messaging)
25 + public MergeModulesCommand(IMessaging messaging, IEnumerable<FileFacade> fileFacades, IntermediateSection section, IEnumerable<string> suppressedTableNames, string outputPath, string intermediateFolder)
26 {
27 this.Messaging = messaging;
28 + this.FileFacades = fileFacades;
29 + this.Section = section;
30 + this.SuppressedTableNames = suppressedTableNames ?? Array.Empty<string>();
31 + this.OutputPath = outputPath;
32 + this.IntermediateFolder = intermediateFolder;
33 }
34
30 - public IEnumerable<FileFacade> FileFacades { private get; set; }
35 + private IMessaging Messaging { get; }
36
32 - public IMessaging Messaging { private get; set; }
37 + private IEnumerable<FileFacade> FileFacades { get; }
38
34 - public WindowsInstallerData Output { private get; set; }
39 + private IntermediateSection Section { get; }
40
36 - public string OutputPath { private get; set; }
41 + private IEnumerable<string> SuppressedTableNames { get; }
42
38 - public TableDefinitionCollection TableDefinitions { private get; set; }
43 + private string OutputPath { get; }
44
40 - public string IntermediateFolder { private get; set; }
45 + private string IntermediateFolder { get; }
46
47 public void Execute()
48 {
44 - Table wixMergeTable = this.Output.Tables["WixMerge"];
45 - Table wixFeatureModulesTable = this.Output.Tables["WixFeatureModules"];
46 -
47 - // check for merge rows to see if there is any work to do
48 - if (null == wixMergeTable || 0 == wixMergeTable.Rows.Count)
49 + var wixMergeTuples = this.Section.Tuples.OfType<WixMergeTuple>().ToList();
50 + if (!wixMergeTuples.Any())
51 {
52 return;
53 }
54
53 - var suppressedTableNames = this.AddBackSuppresedSequenceTables();
54 -
55 IMsmMerge2 merge = null;
56 - bool commit = true;
57 - bool logOpen = false;
58 - bool databaseOpen = false;
59 - string logPath = null;
56 + var commit = true;
57 + var logOpen = false;
58 + var databaseOpen = false;
59 + var logPath = Path.Combine(this.IntermediateFolder, "merge.log");
60 +
61 try
62 {
63 var interop = new MsmInterop();
64 merge = interop.GetMsmMerge();
65
65 - logPath = Path.Combine(this.IntermediateFolder, "merge.log");
66 merge.OpenLog(logPath);
67 logOpen = true;
68
69 merge.OpenDatabase(this.OutputPath);
70 databaseOpen = true;
71
72 + var featureModulesByMergeId = this.Section.Tuples.OfType<WixFeatureModulesTuple>().GroupBy(t => t.WixMergeRef).ToDictionary(g => g.Key);
73 +
74 // process all the merge rows
73 - foreach (WixMergeRow wixMergeRow in wixMergeTable.Rows)
75 + foreach (var wixMergeRow in wixMergeTuples)
76 {
75 - bool moduleOpen = false;
77 + var moduleOpen = false;
78
79 try
80 {
@@ -84,7 +86,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
86 }
87 catch (FormatException)
88 {
87 - this.Messaging.Write(ErrorMessages.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language));
89 + this.Messaging.Write(ErrorMessages.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.Language.ToString()));
90 continue;
91 }
92
@@ -99,20 +101,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
101 callback = new ConfigurationCallback(wixMergeRow.ConfigurationData);
102 }
103
102 - // merge the module into the database that's being built
104 + // Merge the module into the database that's being built.
105 this.Messaging.Write(VerboseMessages.MergingMergeModule(wixMergeRow.SourceFile));
104 - merge.MergeEx(wixMergeRow.Feature, wixMergeRow.Directory, callback);
106 + merge.MergeEx(wixMergeRow.FeatureRef, wixMergeRow.DirectoryRef, callback);
107
106 - // connect any non-primary features
107 - if (null != wixFeatureModulesTable)
108 + // Connect any non-primary features.
109 + if (featureModulesByMergeId.TryGetValue(wixMergeRow.Id.Id, out var featureModules))
110 {
109 - foreach (Row row in wixFeatureModulesTable.Rows)
111 + foreach (var featureModule in featureModules)
112 {
111 - if (wixMergeRow.Id == (string)row[1])
112 - {
113 - this.Messaging.Write(VerboseMessages.ConnectingMergeModule(wixMergeRow.SourceFile, (string)row[0]));
114 - merge.Connect((string)row[0]);
115 - }
113 + this.Messaging.Write(VerboseMessages.ConnectingMergeModule(wixMergeRow.SourceFile, featureModule.FeatureRef));
114 + merge.Connect(featureModule.FeatureRef);
115 }
116 }
117 }
@@ -122,17 +121,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
121 }
122 finally
123 {
125 - IMsmErrors mergeErrors = merge.Errors;
124 + var mergeErrors = merge.Errors;
125
126 // display all the errors encountered during the merge operations for this module
128 - for (int i = 1; i <= mergeErrors.Count; i++)
127 + for (var i = 1; i <= mergeErrors.Count; i++)
128 {
130 - IMsmError mergeError = mergeErrors[i];
131 - StringBuilder databaseKeys = new StringBuilder();
132 - StringBuilder moduleKeys = new StringBuilder();
129 + var mergeError = mergeErrors[i];
130 + var databaseKeys = new StringBuilder();
131 + var moduleKeys = new StringBuilder();
132
133 // build a string of the database keys
135 - for (int j = 1; j <= mergeError.DatabaseKeys.Count; j++)
134 + for (var j = 1; j <= mergeError.DatabaseKeys.Count; j++)
135 {
136 if (1 != j)
137 {
@@ -142,7 +141,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
141 }
142
143 // build a string of the module keys
145 - for (int j = 1; j <= mergeError.ModuleKeys.Count; j++)
144 + for (var j = 1; j <= mergeError.ModuleKeys.Count; j++)
145 {
146 if (1 != j)
147 {
@@ -155,10 +154,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
154 switch (mergeError.Type)
155 {
156 case MsmErrorType.msmErrorExclusion:
158 - this.Messaging.Write(ErrorMessages.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString()));
157 + this.Messaging.Write(ErrorMessages.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleKeys.ToString()));
158 break;
159 case MsmErrorType.msmErrorFeatureRequired:
161 - this.Messaging.Write(ErrorMessages.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id));
160 + this.Messaging.Write(ErrorMessages.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id.Id));
161 break;
162 case MsmErrorType.msmErrorLanguageFailed:
163 this.Messaging.Write(ErrorMessages.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile));
@@ -217,32 +216,28 @@ namespace WixToolset.Core.WindowsInstaller.Bind
216
217 using (var db = new Database(this.OutputPath, OpenDatabase.Direct))
218 {
220 - var suppressActionTable = this.Output.Tables["WixSuppressAction"];
221 -
222 - // suppress individual actions
223 - if (null != suppressActionTable)
219 + // Suppress individual actions.
220 + foreach (var suppressAction in this.Section.Tuples.OfType<WixSuppressActionTuple>())
221 {
225 - foreach (Row row in suppressActionTable.Rows)
222 + var tableName = suppressAction.SequenceTable.WindowsInstallerTableName();
223 + if (db.TableExists(tableName))
224 {
227 - if (db.TableExists((string)row[0]))
228 - {
229 - string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]);
225 + var query = $"SELECT * FROM {tableName} WHERE `Action` = '{suppressAction.Action}'";
226
231 - using (View view = db.OpenExecuteView(query))
232 - using (Record record = view.Fetch())
227 + using (var view = db.OpenExecuteView(query))
228 + using (var record = view.Fetch())
229 + {
230 + if (null != record)
231 {
234 - if (null != record)
235 - {
236 - this.Messaging.Write(WarningMessages.SuppressMergedAction((string)row[1], row[0].ToString()));
237 - view.Modify(ModifyView.Delete, record);
238 - }
232 + this.Messaging.Write(WarningMessages.SuppressMergedAction(suppressAction.Action, tableName));
233 + view.Modify(ModifyView.Delete, record);
234 }
235 }
236 }
237 }
238
244 - // query for merge module actions in suppressed sequences and drop them
245 - foreach (var tableName in suppressedTableNames)
239 + // Query for merge module actions in suppressed sequences and drop them.
240 + foreach (var tableName in this.SuppressedTableNames)
241 {
242 if (!db.TableExists(tableName))
243 {
@@ -333,31 +328,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind
328 db.Commit();
329 }
330 }
336 -
337 - private IEnumerable<string> AddBackSuppresedSequenceTables()
338 - {
339 - // Add back possibly suppressed sequence tables since all sequence tables must be present
340 - // for the merge process to work. We'll drop the suppressed sequence tables again as
341 - // necessary.
342 - var suppressedTableNames = new HashSet<string>();
343 -
344 - foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
345 - {
346 - var sequenceTableName = (sequence == SequenceTable.AdvertiseExecuteSequence) ? "AdvtExecuteSequence" : sequence.ToString();
347 - var sequenceTable = this.Output.Tables[sequenceTableName];
348 -
349 - if (null == sequenceTable)
350 - {
351 - sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]);
352 - }
353 -
354 - if (0 == sequenceTable.Rows.Count)
355 - {
356 - suppressedTableNames.Add(sequenceTableName);
357 - }
358 - }
359 -
360 - return suppressedTableNames;
361 - }
331 }
332 }