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
}