aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Linker.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Linker.cs')
-rw-r--r--src/WixToolset.Core/Linker.cs1164
1 files changed, 578 insertions, 586 deletions
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs
index c1c9f848..59481387 100644
--- a/src/WixToolset.Core/Linker.cs
+++ b/src/WixToolset.Core/Linker.cs
@@ -1,21 +1,18 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. 1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 2
3namespace WixToolset 3namespace WixToolset.Core
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
7 using System.Collections.Generic; 7 using System.Collections.Generic;
8 using System.Collections.Specialized;
9 using System.Diagnostics; 8 using System.Diagnostics;
10 using System.Diagnostics.CodeAnalysis;
11 using System.Globalization; 9 using System.Globalization;
12 using System.Linq; 10 using System.Linq;
13 using System.Text;
14 using WixToolset.Data; 11 using WixToolset.Data;
15 using WixToolset.Data.Rows;
16 using WixToolset.Extensibility;
17 using WixToolset.Link; 12 using WixToolset.Link;
18 using WixToolset.Core.Native; 13 using WixToolset.Core.Link;
14 using WixToolset.Data.Tuples;
15 using WixToolset.Extensibility.Services;
19 16
20 /// <summary> 17 /// <summary>
21 /// Linker core of the WiX toolset. 18 /// Linker core of the WiX toolset.
@@ -25,13 +22,10 @@ namespace WixToolset
25 private static readonly char[] colonCharacter = ":".ToCharArray(); 22 private static readonly char[] colonCharacter = ":".ToCharArray();
26 private static readonly string emptyGuid = Guid.Empty.ToString("B"); 23 private static readonly string emptyGuid = Guid.Empty.ToString("B");
27 24
28 private List<IExtensionData> extensionData;
29
30 private List<InspectorExtension> inspectorExtensions;
31 private bool sectionIdOnRows; 25 private bool sectionIdOnRows;
32 private WixActionRowCollection standardActions; 26 //private WixActionRowCollection standardActions;
33 private Output activeOutput; 27 //private Output activeOutput;
34 private TableDefinitionCollection tableDefinitions; 28 //private TableDefinitionCollection tableDefinitions;
35 29
36 /// <summary> 30 /// <summary>
37 /// Creates a linker. 31 /// Creates a linker.
@@ -40,18 +34,14 @@ namespace WixToolset
40 { 34 {
41 this.sectionIdOnRows = true; // TODO: what is the correct value for this? 35 this.sectionIdOnRows = true; // TODO: what is the correct value for this?
42 36
43 this.standardActions = WindowsInstallerStandard.GetStandardActions(); 37 //this.standardActions = WindowsInstallerStandard.GetStandardActions();
44 this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions()); 38 //this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
45 39
46 this.extensionData = new List<IExtensionData>(); 40 //this.extensionData = new List<IExtensionData>();
47 this.inspectorExtensions = new List<InspectorExtension>(); 41 //this.inspectorExtensions = new List<InspectorExtension>();
48 } 42 }
49 43
50 /// <summary> 44 private ILinkContext Context { get; set; }
51 /// Gets or sets the localizer.
52 /// </summary>
53 /// <value>The localizer.</value>
54 public Localizer Localizer { get; set; }
55 45
56 /// <summary> 46 /// <summary>
57 /// Gets or sets the path to output unreferenced symbols to. If null or empty, there is no output. 47 /// Gets or sets the path to output unreferenced symbols to. If null or empty, there is no output.
@@ -66,45 +56,36 @@ namespace WixToolset
66 public bool ShowPedanticMessages { get; set; } 56 public bool ShowPedanticMessages { get; set; }
67 57
68 /// <summary> 58 /// <summary>
69 /// Gets the table definitions used by the linker.
70 /// </summary>
71 /// <value>Table definitions used by the linker.</value>
72 public TableDefinitionCollection TableDefinitions
73 {
74 get { return this.tableDefinitions; }
75 }
76
77 /// <summary>
78 /// Gets or sets the Wix variable resolver. 59 /// Gets or sets the Wix variable resolver.
79 /// </summary> 60 /// </summary>
80 /// <value>The Wix variable resolver.</value> 61 /// <value>The Wix variable resolver.</value>
81 internal IBindVariableResolver WixVariableResolver { get; set; } 62 //internal IBindVariableResolver WixVariableResolver { get; set; }
82 63
83 /// <summary> 64 /// <summary>
84 /// Adds an extension. 65 /// Adds an extension.
85 /// </summary> 66 /// </summary>
86 /// <param name="extension">The extension to add.</param> 67 /// <param name="extension">The extension to add.</param>
87 public void AddExtensionData(IExtensionData extension) 68 //public void AddExtensionData(IExtensionData extension)
88 { 69 //{
89 if (null != extension.TableDefinitions) 70 // if (null != extension.TableDefinitions)
90 { 71 // {
91 foreach (TableDefinition tableDefinition in extension.TableDefinitions) 72 // foreach (TableDefinition tableDefinition in extension.TableDefinitions)
92 { 73 // {
93 if (!this.tableDefinitions.Contains(tableDefinition.Name)) 74 // if (!this.tableDefinitions.Contains(tableDefinition.Name))
94 { 75 // {
95 this.tableDefinitions.Add(tableDefinition); 76 // this.tableDefinitions.Add(tableDefinition);
96 } 77 // }
97 else 78 // else
98 { 79 // {
99 throw new WixException(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name)); 80 // throw new WixException(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name));
100 } 81 // }
101 } 82 // }
102 } 83 // }
103 84
104 // keep track of extension data so the libraries can be loaded from these later once all the table definitions 85 // // keep track of extension data so the libraries can be loaded from these later once all the table definitions
105 // are loaded; this will allow extensions to have cross table definition dependencies 86 // // are loaded; this will allow extensions to have cross table definition dependencies
106 this.extensionData.Add(extension); 87 // this.extensionData.Add(extension);
107 } 88 //}
108 89
109 /// <summary> 90 /// <summary>
110 /// Links a collection of sections into an output. 91 /// Links a collection of sections into an output.
@@ -112,31 +93,39 @@ namespace WixToolset
112 /// <param name="inputs">The collection of sections to link together.</param> 93 /// <param name="inputs">The collection of sections to link together.</param>
113 /// <param name="expectedOutputType">Expected output type, based on output file extension provided to the linker.</param> 94 /// <param name="expectedOutputType">Expected output type, based on output file extension provided to the linker.</param>
114 /// <returns>Output object from the linking.</returns> 95 /// <returns>Output object from the linking.</returns>
115 public Output Link(IEnumerable<Section> inputs, OutputType expectedOutputType) 96 public Intermediate Link(ILinkContext context)
116 { 97 {
117 Output output = null; 98 this.Context = context ?? throw new ArgumentNullException(nameof(context));
118 List<Section> sections = new List<Section>(inputs);
119 99
120 try 100 //IEnumerable<Section> inputs, OutputType expectedOutputType
121 { 101
102 var sections = this.Context.Intermediates.SelectMany(i => i.Sections).ToList();
103
104#if MOVE_TO_BACKEND
122 bool containsModuleSubstitution = false; 105 bool containsModuleSubstitution = false;
123 bool containsModuleConfiguration = false; 106 bool containsModuleConfiguration = false;
107#endif
124 108
125 this.activeOutput = null; 109 //this.activeOutput = null;
126 110
127 List<Row> actionRows = new List<Row>(); 111#if MOVE_TO_BACKEND
128 List<Row> suppressActionRows = new List<Row>(); 112 var actionRows = new List<IntermediateTuple>();
113 var suppressActionRows = new List<IntermediateTuple>();
114#endif
129 115
130 TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection(); 116 //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection();
131 List<Row> customRows = new List<Row>(); 117 //IntermediateTuple customRows = new List<IntermediateTuple>();
132 118
119#if MOVE_TO_BACKEND
133 StringCollection generatedShortFileNameIdentifiers = new StringCollection(); 120 StringCollection generatedShortFileNameIdentifiers = new StringCollection();
134 Hashtable generatedShortFileNames = new Hashtable(); 121 Hashtable generatedShortFileNames = new Hashtable();
122#endif
135 123
136 Hashtable multipleFeatureComponents = new Hashtable(); 124 Hashtable multipleFeatureComponents = new Hashtable();
137 125
138 Hashtable wixVariables = new Hashtable(); 126 var wixVariables = new Dictionary<string, WixVariableTuple>();
139 127
128#if MOVE_TO_BACKEND
140 // verify that modularization types match for foreign key relationships 129 // verify that modularization types match for foreign key relationships
141 foreach (TableDefinition tableDefinition in this.tableDefinitions) 130 foreach (TableDefinition tableDefinition in this.tableDefinitions)
142 { 131 {
@@ -164,7 +153,9 @@ namespace WixToolset
164 } 153 }
165 } 154 }
166 } 155 }
156#endif
167 157
158#if TODO
168 // Add sections from the extensions with data. 159 // Add sections from the extensions with data.
169 foreach (IExtensionData data in this.extensionData) 160 foreach (IExtensionData data in this.extensionData)
170 { 161 {
@@ -175,123 +166,118 @@ namespace WixToolset
175 sections.AddRange(library.Sections); 166 sections.AddRange(library.Sections);
176 } 167 }
177 } 168 }
169#endif
178 170
179 // First find the entry section and while processing all sections load all the symbols from all of the sections. 171 // First find the entry section and while processing all sections load all the symbols from all of the sections.
180 // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols); 172 // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols);
181 FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(sections); 173 var find = new FindEntrySectionAndLoadSymbolsCommand(sections);
182 find.ExpectedOutputType = expectedOutputType; 174 find.ExpectedOutputType = this.Context.ExpectedOutputType;
175 find.Execute();
183 176
184 find.Execute(); 177 // Must have found the entry section by now.
178 if (null == find.EntrySection)
179 {
180 throw new WixException(WixErrors.MissingEntrySection(this.Context.ExpectedOutputType.ToString()));
181 }
185 182
186 // Must have found the entry section by now. 183 // Now that we know where we're starting from, create the section to hold the linked content.
187 if (null == find.EntrySection) 184 var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type, find.EntrySection.Codepage);
188 { 185 var allSymbols = find.Symbols;
189 throw new WixException(WixErrors.MissingEntrySection(expectedOutputType.ToString()));
190 }
191 186
192 IDictionary<string, Symbol> allSymbols = find.Symbols; 187 // Add the missing standard action symbols.
188 this.LoadStandardActionSymbols(resolvedSection, allSymbols);
193 189
194 // Add the missing standard action symbols. 190 // Resolve the symbol references to find the set of sections we care about for linking.
195 this.LoadStandardActionSymbols(allSymbols); 191 // Of course, we start with the entry section (that's how it got its name after all).
192 var resolve = new ResolveReferencesCommand(find.EntrySection, allSymbols);
193 resolve.BuildingMergeModule = (SectionType.Module == find.EntrySection.Type);
196 194
197 // now that we know where we're starting from, create the output object 195 resolve.Execute();
198 output = new Output(null);
199 output.EntrySection = find.EntrySection; // Note: this entry section will get added to the Output.Sections collection later
200 if (null != this.Localizer && -1 != this.Localizer.Codepage)
201 {
202 output.Codepage = this.Localizer.Codepage;
203 }
204 this.activeOutput = output;
205 196
206 // Resolve the symbol references to find the set of sections we care about for linking. 197 if (Messaging.Instance.EncounteredError)
207 // Of course, we start with the entry section (that's how it got its name after all). 198 {
208 ResolveReferencesCommand resolve = new ResolveReferencesCommand(output.EntrySection, allSymbols); 199 return null;
209 resolve.BuildingMergeModule = (OutputType.Module == output.Type); 200 }
210 201
211 resolve.Execute(); 202 // Reset the sections to only those that were resolved then flatten the complex
203 // references that particpate in groups.
204 sections = resolve.ResolvedSections.ToList();
212 205
213 if (Messaging.Instance.EncounteredError) 206 this.FlattenSectionsComplexReferences(sections);
214 {
215 return null;
216 }
217 207
218 // Add the resolved sections to the output then flatten the complex 208 if (Messaging.Instance.EncounteredError)
219 // references that particpate in groups. 209 {
220 foreach (Section section in resolve.ResolvedSections) 210 return null;
221 { 211 }
222 output.Sections.Add(section);
223 }
224 212
225 this.FlattenSectionsComplexReferences(output.Sections); 213 // The hard part in linking is processing the complex references.
214 var referencedComponents = new HashSet<string>();
215 var componentsToFeatures = new ConnectToFeatureCollection();
216 var featuresToFeatures = new ConnectToFeatureCollection();
217 var modulesToFeatures = new ConnectToFeatureCollection();
218 this.ProcessComplexReferences(resolvedSection, sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures);
226 219
227 if (Messaging.Instance.EncounteredError) 220 if (Messaging.Instance.EncounteredError)
221 {
222 return null;
223 }
224
225 // Display an error message for Components that were not referenced by a Feature.
226 foreach (var symbol in resolve.ReferencedSymbols.Where(s => s.Row.Definition.Type == TupleDefinitionType.Component))
227 {
228 if (!referencedComponents.Contains(symbol.Name))
228 { 229 {
229 return null; 230 this.OnMessage(WixErrors.OrphanedComponent(symbol.Row.SourceLineNumbers, symbol.Row.Id.Id));
230 } 231 }
232 }
231 233
232 // The hard part in linking is processing the complex references. 234 // Report duplicates that would ultimately end up being primary key collisions.
233 HashSet<string> referencedComponents = new HashSet<string>(); 235 ReportConflictingSymbolsCommand reportDupes = new ReportConflictingSymbolsCommand(find.PossiblyConflictingSymbols, resolve.ResolvedSections);
234 ConnectToFeatureCollection componentsToFeatures = new ConnectToFeatureCollection(); 236 reportDupes.Execute();
235 ConnectToFeatureCollection featuresToFeatures = new ConnectToFeatureCollection();
236 ConnectToFeatureCollection modulesToFeatures = new ConnectToFeatureCollection();
237 this.ProcessComplexReferences(output, output.Sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures);
238 237
239 if (Messaging.Instance.EncounteredError) 238 if (Messaging.Instance.EncounteredError)
240 { 239 {
241 return null; 240 return null;
242 } 241 }
243 242
244 // Display an error message for Components that were not referenced by a Feature. 243 // resolve the feature to feature connects
245 foreach (Symbol symbol in resolve.ReferencedSymbols.Where(s => "Component".Equals(s.Row.TableDefinition.Name, StringComparison.Ordinal))) 244 this.ResolveFeatureToFeatureConnects(featuresToFeatures, allSymbols);
246 {
247 if (!referencedComponents.Contains(symbol.Name))
248 {
249 this.OnMessage(WixErrors.OrphanedComponent(symbol.Row.SourceLineNumbers, (string)symbol.Row[0]));
250 }
251 }
252 245
253 // Report duplicates that would ultimately end up being primary key collisions. 246 // start generating OutputTables and OutputRows for all the sections in the output
254 ReportConflictingSymbolsCommand reportDupes = new ReportConflictingSymbolsCommand(find.PossiblyConflictingSymbols, resolve.ResolvedSections); 247 var ensureTableRows = new List<IntermediateTuple>();
255 reportDupes.Execute();
256 248
257 if (Messaging.Instance.EncounteredError) 249 int sectionCount = 0;
250 foreach (var section in sections)
251 {
252 sectionCount++;
253
254 string sectionId = section.Id;
255 if (null == sectionId && this.sectionIdOnRows)
258 { 256 {
259 return null; 257 sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture);
260 } 258 }
261 259
262 // resolve the feature to feature connects 260 foreach (var tuple in section.Tuples)
263 this.ResolveFeatureToFeatureConnects(featuresToFeatures, allSymbols);
264
265 // start generating OutputTables and OutputRows for all the sections in the output
266 List<Row> ensureTableRows = new List<Row>();
267 int sectionCount = 0;
268 foreach (Section section in output.Sections)
269 { 261 {
270 sectionCount++; 262 var copyTuple = true; // by default, copy tuples.
271 string sectionId = section.Id;
272 if (null == sectionId && this.sectionIdOnRows)
273 {
274 sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture);
275 }
276 263
277 foreach (Table table in section.Tables) 264 // handle special tables
265 switch (tuple.Definition.Type)
278 { 266 {
279 bool copyRows = true; // by default, copy rows. 267#if MOVE_TO_BACKEND
280
281 // handle special tables
282 switch (table.Name)
283 {
284 case "AppSearch": 268 case "AppSearch":
285 this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]); 269 this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]);
286 break; 270 break;
271#endif
287 272
288 case "Class": 273 case TupleDefinitionType.Class:
289 if (OutputType.Product == output.Type) 274 if (SectionType.Product == resolvedSection.Type)
290 { 275 {
291 this.ResolveFeatures(table.Rows, 2, 11, componentsToFeatures, multipleFeatureComponents); 276 this.ResolveFeatures(tuple, 2, 11, componentsToFeatures, multipleFeatureComponents);
292 } 277 }
293 break; 278 break;
294 279
280#if MOVE_TO_BACKEND
295 case "CustomAction": 281 case "CustomAction":
296 if (OutputType.Module == this.activeOutput.Type) 282 if (OutputType.Module == this.activeOutput.Type)
297 { 283 {
@@ -342,29 +328,32 @@ namespace WixToolset
342 } 328 }
343 } 329 }
344 break; 330 break;
331#endif
332 case TupleDefinitionType.Extension:
333 if (SectionType.Product == resolvedSection.Type)
334 {
335 this.ResolveFeatures(tuple, 1, 4, componentsToFeatures, multipleFeatureComponents);
336 }
337 break;
345 338
346 case "Extension": 339#if MOVE_TO_BACKEND
347 if (OutputType.Product == output.Type) 340 case TupleDefinitionType.ModuleSubstitution:
348 {
349 this.ResolveFeatures(table.Rows, 1, 4, componentsToFeatures, multipleFeatureComponents);
350 }
351 break;
352
353 case "ModuleSubstitution":
354 containsModuleSubstitution = true; 341 containsModuleSubstitution = true;
355 break; 342 break;
356 343
357 case "ModuleConfiguration": 344 case TupleDefinitionType.ModuleConfiguration:
358 containsModuleConfiguration = true; 345 containsModuleConfiguration = true;
359 break; 346 break;
347#endif
360 348
361 case "MsiAssembly": 349 case TupleDefinitionType.MsiAssembly:
362 if (OutputType.Product == output.Type) 350 if (SectionType.Product == resolvedSection.Type)
363 { 351 {
364 this.ResolveFeatures(table.Rows, 0, 1, componentsToFeatures, multipleFeatureComponents); 352 this.ResolveFeatures(tuple, 0, 1, componentsToFeatures, multipleFeatureComponents);
365 } 353 }
366 break; 354 break;
367 355
356#if MOVE_TO_BACKEND
368 case "ProgId": 357 case "ProgId":
369 // the Extension table is required with a ProgId table 358 // the Extension table is required with a ProgId table
370 this.activeOutput.EnsureTable(this.tableDefinitions["Extension"]); 359 this.activeOutput.EnsureTable(this.tableDefinitions["Extension"]);
@@ -382,42 +371,46 @@ namespace WixToolset
382 } 371 }
383 } 372 }
384 break; 373 break;
374#endif
385 375
386 case "PublishComponent": 376 case TupleDefinitionType.PublishComponent:
387 if (OutputType.Product == output.Type) 377 if (SectionType.Product == resolvedSection.Type)
388 { 378 {
389 this.ResolveFeatures(table.Rows, 2, 4, componentsToFeatures, multipleFeatureComponents); 379 this.ResolveFeatures(tuple, 2, 4, componentsToFeatures, multipleFeatureComponents);
390 } 380 }
391 break; 381 break;
392
393 case "Shortcut":
394 if (OutputType.Product == output.Type)
395 {
396 this.ResolveFeatures(table.Rows, 3, 4, componentsToFeatures, multipleFeatureComponents);
397 }
398 break;
399 382
400 case "TypeLib": 383 case TupleDefinitionType.Shortcut:
401 if (OutputType.Product == output.Type) 384 if (SectionType.Product == resolvedSection.Type)
402 { 385 {
403 this.ResolveFeatures(table.Rows, 2, 6, componentsToFeatures, multipleFeatureComponents); 386 this.ResolveFeatures(tuple, 3, 4, componentsToFeatures, multipleFeatureComponents);
404 } 387 }
405 break; 388 break;
406 389
407 case "WixAction": 390 case TupleDefinitionType.TypeLib:
408 if (this.sectionIdOnRows) 391 if (SectionType.Product == resolvedSection.Type)
409 { 392 {
410 foreach (Row row in table.Rows) 393 this.ResolveFeatures(tuple, 2, 6, componentsToFeatures, multipleFeatureComponents);
411 { 394 }
412 row.SectionId = sectionId; 395 break;
413 } 396
414 } 397#if MOVE_TO_BACKEND
415 actionRows.AddRange(table.Rows); 398 case TupleDefinitionType.WixAction:
399 //if (this.sectionIdOnRows)
400 //{
401 // foreach (Row row in table.Rows)
402 // {
403 // row.SectionId = sectionId;
404 // }
405 //}
406 actionRows.Add(tuple);
416 break; 407 break;
408#endif
417 409
410#if SOLVE_CUSTOM_TABLE
418 case "WixCustomTable": 411 case "WixCustomTable":
419 this.LinkCustomTable(table, customTableDefinitions); 412 this.LinkCustomTable(table, customTableDefinitions);
420 copyRows = false; // we've created table definitions from these rows, no need to process them any longer 413 copyTuple = false; // we've created table definitions from these rows, no need to process them any longer
421 break; 414 break;
422 415
423 case "WixCustomRow": 416 case "WixCustomRow":
@@ -426,19 +419,22 @@ namespace WixToolset
426 row.SectionId = (this.sectionIdOnRows ? sectionId : null); 419 row.SectionId = (this.sectionIdOnRows ? sectionId : null);
427 customRows.Add(row); 420 customRows.Add(row);
428 } 421 }
429 copyRows = false; 422 copyTuple = false;
430 break; 423 break;
424#endif
425
426 case TupleDefinitionType.WixEnsureTable:
427 ensureTableRows.Add(tuple);
428 break;
431 429
432 case "WixEnsureTable":
433 ensureTableRows.AddRange(table.Rows);
434 break;
435 430
431#if MOVE_TO_BACKEND
436 case "WixFile": 432 case "WixFile":
437 foreach (Row row in table.Rows) 433 foreach (Row row in table.Rows)
438 { 434 {
439 // DiskId is not valid when creating a module, so set it to 435 // DiskId is not valid when creating a module, so set it to
440 // 0 for all files to ensure proper sorting in the binder 436 // 0 for all files to ensure proper sorting in the binder
441 if (OutputType.Module == this.activeOutput.Type) 437 if (SectionType.Module == entrySection.Type)
442 { 438 {
443 row[5] = 0; 439 row[5] = 0;
444 } 440 }
@@ -450,61 +446,76 @@ namespace WixToolset
450 } 446 }
451 } 447 }
452 break; 448 break;
449#endif
453 450
454 case "WixMerge": 451 case TupleDefinitionType.WixMerge:
455 if (OutputType.Product == output.Type) 452 if (SectionType.Product == resolvedSection.Type)
456 { 453 {
457 this.ResolveFeatures(table.Rows, 0, 7, modulesToFeatures, null); 454 this.ResolveFeatures(tuple, 0, 7, modulesToFeatures, null);
458 } 455 }
459 break; 456 break;
460 457
461 case "WixSuppressAction": 458#if MOVE_TO_BACKEND
462 suppressActionRows.AddRange(table.Rows); 459 case TupleDefinitionType.WixSuppressAction:
460 suppressActionRows.Add(tuple);
463 break; 461 break;
462#endif
464 463
465 case "WixVariable": 464 case TupleDefinitionType.WixComplexReference:
466 // check for colliding values and collect the wix variable rows 465 copyTuple = false;
467 foreach (WixVariableRow row in table.Rows) 466 break;
468 { 467
469 WixVariableRow collidingRow = (WixVariableRow)wixVariables[row.Id]; 468 case TupleDefinitionType.WixSimpleReference:
469 copyTuple = false;
470 break;
470 471
471 if (null == collidingRow || (collidingRow.Overridable && !row.Overridable)) 472 case TupleDefinitionType.WixVariable:
473 // check for colliding values and collect the wix variable rows
474 {
475 var row = (WixVariableTuple)tuple;
476
477 if (wixVariables.TryGetValue(row.WixVariable, out var collidingRow))
478 {
479 if (collidingRow.Overridable && !row.Overridable)
472 { 480 {
473 wixVariables[row.Id] = row; 481 wixVariables[row.WixVariable] = row;
474 } 482 }
475 else if (!row.Overridable || (collidingRow.Overridable && row.Overridable)) 483 else if (!row.Overridable || (collidingRow.Overridable && row.Overridable))
476 { 484 {
477 this.OnMessage(WixErrors.WixVariableCollision(row.SourceLineNumbers, row.Id)); 485 this.OnMessage(WixErrors.WixVariableCollision(row.SourceLineNumbers, row.WixVariable));
478 } 486 }
479 } 487 }
480 copyRows = false; 488 else
481 break; 489 {
482 } 490 wixVariables.Add(row.WixVariable, row);
491 }
492 }
483 493
484 if (copyRows) 494 copyTuple = false;
485 { 495 break;
486 Table outputTable = this.activeOutput.EnsureTable(this.tableDefinitions[table.Name]); 496 }
487 this.CopyTableRowsToOutputTable(table, outputTable, sectionId); 497
488 } 498 if (copyTuple)
499 {
500 resolvedSection.Tuples.Add(tuple);
489 } 501 }
490 } 502 }
503 }
491 504
492 // copy the module to feature connections into the output 505 // copy the module to feature connections into the output
493 if (0 < modulesToFeatures.Count) 506 foreach (ConnectToFeature connectToFeature in modulesToFeatures)
507 {
508 foreach (var feature in connectToFeature.ConnectFeatures)
494 { 509 {
495 Table wixFeatureModulesTable = this.activeOutput.EnsureTable(this.tableDefinitions["WixFeatureModules"]); 510 var row = new WixFeatureModulesTuple();
511 row.Feature_ = feature;
512 row.WixMerge_ = connectToFeature.ChildId;
496 513
497 foreach (ConnectToFeature connectToFeature in modulesToFeatures) 514 resolvedSection.Tuples.Add(row);
498 {
499 foreach (string feature in connectToFeature.ConnectFeatures)
500 {
501 Row row = wixFeatureModulesTable.CreateRow(null);
502 row[0] = feature;
503 row[1] = connectToFeature.ChildId;
504 }
505 }
506 } 515 }
516 }
507 517
518#if MOVE_TO_BACKEND
508 // ensure the creation of tables that need to exist 519 // ensure the creation of tables that need to exist
509 if (0 < ensureTableRows.Count) 520 if (0 < ensureTableRows.Count)
510 { 521 {
@@ -525,17 +536,14 @@ namespace WixToolset
525 this.activeOutput.EnsureTable(tableDef); 536 this.activeOutput.EnsureTable(tableDef);
526 } 537 }
527 } 538 }
539#endif
528 540
529 // copy all the suppress action rows to the output to suppress actions from merge modules 541#if MOVE_TO_BACKEND
530 if (0 < suppressActionRows.Count)
531 {
532 Table suppressActionTable = this.activeOutput.EnsureTable(this.tableDefinitions["WixSuppressAction"]);
533 suppressActionRows.ForEach(r => suppressActionTable.Rows.Add(r));
534 }
535
536 // sequence all the actions 542 // sequence all the actions
537 this.SequenceActions(actionRows, suppressActionRows); 543 this.SequenceActions(actionRows, suppressActionRows);
544#endif
538 545
546#if MOVE_TO_BACKEND
539 // check for missing table and add them or display an error as appropriate 547 // check for missing table and add them or display an error as appropriate
540 switch (this.activeOutput.Type) 548 switch (this.activeOutput.Type)
541 { 549 {
@@ -576,7 +584,9 @@ namespace WixToolset
576 } 584 }
577 585
578 this.CheckForIllegalTables(this.activeOutput); 586 this.CheckForIllegalTables(this.activeOutput);
587#endif
579 588
589#if SOLVE_CUSTOM_TABLE
580 // add the custom row data 590 // add the custom row data
581 foreach (Row row in customRows) 591 foreach (Row row in customRows)
582 { 592 {
@@ -649,35 +659,28 @@ namespace WixToolset
649 } 659 }
650 } 660 }
651 } 661 }
662#endif
652 663
653 //correct the section Id in FeatureComponents table 664 //correct the section Id in FeatureComponents table
654 if (this.sectionIdOnRows) 665 if (this.sectionIdOnRows)
655 { 666 {
656 Hashtable componentSectionIds = new Hashtable(); 667 //var componentSectionIds = new Dictionary<string, string>();
657 Table componentTable = output.Tables["Component"]; 668
658 669 //foreach (var componentTuple in entrySection.Tuples.OfType<ComponentTuple>())
659 if (null != componentTable) 670 //{
660 { 671 // componentSectionIds.Add(componentTuple.Id.Id, componentTuple.SectionId);
661 foreach (Row componentRow in componentTable.Rows) 672 //}
662 { 673
663 componentSectionIds.Add(componentRow.Fields[0].Data.ToString(), componentRow.SectionId); 674 //foreach (var featureComponentTuple in entrySection.Tuples.OfType<FeatureComponentsTuple>())
664 } 675 //{
665 } 676 // if (componentSectionIds.TryGetValue(featureComponentTuple.Component_, out var componentSectionId))
666 677 // {
667 Table featureComponentsTable = output.Tables["FeatureComponents"]; 678 // featureComponentTuple.SectionId = componentSectionId;
668 679 // }
669 if (null != featureComponentsTable) 680 //}
670 { 681 }
671 foreach (Row featureComponentsRow in featureComponentsTable.Rows)
672 {
673 if (componentSectionIds.Contains(featureComponentsRow.Fields[1].Data.ToString()))
674 {
675 featureComponentsRow.SectionId = (string)componentSectionIds[featureComponentsRow.Fields[1].Data.ToString()];
676 }
677 }
678 }
679 }
680 682
683#if MOVE_TO_BACKEND
681 // add the ModuleSubstitution table to the ModuleIgnoreTable 684 // add the ModuleSubstitution table to the ModuleIgnoreTable
682 if (containsModuleSubstitution) 685 if (containsModuleSubstitution)
683 { 686 {
@@ -695,7 +698,9 @@ namespace WixToolset
695 Row moduleIgnoreTableRow = moduleIgnoreTableTable.CreateRow(null); 698 Row moduleIgnoreTableRow = moduleIgnoreTableTable.CreateRow(null);
696 moduleIgnoreTableRow[0] = "ModuleConfiguration"; 699 moduleIgnoreTableRow[0] = "ModuleConfiguration";
697 } 700 }
701#endif
698 702
703#if MOVE_TO_BACKEND
699 // index all the file rows 704 // index all the file rows
700 Table fileTable = this.activeOutput.Tables["File"]; 705 Table fileTable = this.activeOutput.Tables["File"];
701 RowDictionary<FileRow> indexedFileRows = (null == fileTable) ? new RowDictionary<FileRow>() : new RowDictionary<FileRow>(fileTable); 706 RowDictionary<FileRow> indexedFileRows = (null == fileTable) ? new RowDictionary<FileRow>() : new RowDictionary<FileRow>(fileTable);
@@ -740,47 +745,32 @@ namespace WixToolset
740 } 745 }
741 } 746 }
742 } 747 }
748#endif
743 749
744 // copy the wix variable rows to the output after all overriding has been accounted for. 750 // copy the wix variable rows to the output after all overriding has been accounted for.
745 if (0 < wixVariables.Count) 751 foreach (var row in wixVariables.Values)
746 { 752 {
747 Table wixVariableTable = output.EnsureTable(this.tableDefinitions["WixVariable"]); 753 resolvedSection.Tuples.Add(row);
754 }
748 755
749 foreach (WixVariableRow row in wixVariables.Values) 756 // Bundles have groups of data that must be flattened in a way different from other types.
750 { 757 this.FlattenBundleTables(resolvedSection);
751 wixVariableTable.Rows.Add(row);
752 }
753 }
754 758
755 // Bundles have groups of data that must be flattened in a way different from other types. 759 if (Messaging.Instance.EncounteredError)
756 this.FlattenBundleTables(output); 760 {
761 return null;
762 }
757 763
758 if (Messaging.Instance.EncounteredError) 764 var output = new Intermediate(resolvedSection.Id, new[] { resolvedSection }, null, null);
759 {
760 return null;
761 }
762 765
766#if MOVE_TO_BACKEND
763 this.CheckOutputConsistency(output); 767 this.CheckOutputConsistency(output);
764 768#endif
765 // inspect the output
766 InspectorCore inspectorCore = new InspectorCore();
767 foreach (InspectorExtension inspectorExtension in this.inspectorExtensions)
768 {
769 inspectorExtension.Core = inspectorCore;
770 inspectorExtension.InspectOutput(output);
771
772 // reset
773 inspectorExtension.Core = null;
774 }
775 }
776 finally
777 {
778 this.activeOutput = null;
779 }
780 769
781 return Messaging.Instance.EncounteredError ? null : output; 770 return Messaging.Instance.EncounteredError ? null : output;
782 } 771 }
783 772
773#if SOLVE_CUSTOM_TABLE
784 /// <summary> 774 /// <summary>
785 /// Links the definition of a custom table. 775 /// Links the definition of a custom table.
786 /// </summary> 776 /// </summary>
@@ -995,7 +985,9 @@ namespace WixToolset
995 customTableDefinitions.Add(customTable); 985 customTableDefinitions.Add(customTable);
996 } 986 }
997 } 987 }
988#endif
998 989
990#if MOVE_TO_BACKEND
999 /// <summary> 991 /// <summary>
1000 /// Checks for any tables in the output which are not allowed in the output type. 992 /// Checks for any tables in the output which are not allowed in the output type.
1001 /// </summary> 993 /// </summary>
@@ -1088,7 +1080,9 @@ namespace WixToolset
1088 } 1080 }
1089 } 1081 }
1090 } 1082 }
1083#endif
1091 1084
1085#if MOVE_TO_BACKEND
1092 /// <summary> 1086 /// <summary>
1093 /// Performs various consistency checks on the output. 1087 /// Performs various consistency checks on the output.
1094 /// </summary> 1088 /// </summary>
@@ -1145,30 +1139,30 @@ namespace WixToolset
1145 } 1139 }
1146 } 1140 }
1147 } 1141 }
1148 1142#endif
1149 /// <summary> 1143 /// <summary>
1150 /// Sends a message to the message delegate if there is one. 1144 /// Sends a message to the message delegate if there is one.
1151 /// </summary> 1145 /// </summary>
1152 /// <param name="mea">Message event arguments.</param> 1146 /// <param name="mea">Message event arguments.</param>
1153 public void OnMessage(MessageEventArgs e) 1147 public void OnMessage(MessageEventArgs e)
1154 { 1148 {
1155 Messaging.Instance.OnMessage(e); 1149 this.Context.Messaging.OnMessage(e);
1156 } 1150 }
1157 1151
1158 /// <summary> 1152 /// <summary>
1159 /// Load the standard action symbols. 1153 /// Load the standard action symbols.
1160 /// </summary> 1154 /// </summary>
1161 /// <param name="allSymbols">Collection of symbols.</param> 1155 /// <param name="symbols">Collection of symbols.</param>
1162 private void LoadStandardActionSymbols(IDictionary<string, Symbol> allSymbols) 1156 private void LoadStandardActionSymbols(IntermediateSection section, IDictionary<string, Symbol> symbols)
1163 { 1157 {
1164 foreach (WixActionRow actionRow in this.standardActions) 1158 foreach (var actionRow in WindowsInstallerStandard.StandardActions())
1165 { 1159 {
1166 Symbol actionSymbol = new Symbol(actionRow); 1160 var symbol = new Symbol(section, actionRow);
1167 1161
1168 // If the action's symbol has not already been defined (i.e. overriden by the user), add it now. 1162 // If the action's symbol has not already been defined (i.e. overriden by the user), add it now.
1169 if (!allSymbols.ContainsKey(actionSymbol.Name)) 1163 if (!symbols.ContainsKey(symbol.Name))
1170 { 1164 {
1171 allSymbols.Add(actionSymbol.Name, actionSymbol); 1165 symbols.Add(symbol.Name, symbol);
1172 } 1166 }
1173 } 1167 }
1174 } 1168 }
@@ -1176,186 +1170,180 @@ namespace WixToolset
1176 /// <summary> 1170 /// <summary>
1177 /// Process the complex references. 1171 /// Process the complex references.
1178 /// </summary> 1172 /// </summary>
1179 /// <param name="output">Active output to add sections to.</param> 1173 /// <param name="resolvedSection">Active section to add tuples to.</param>
1180 /// <param name="sections">Sections that are referenced during the link process.</param> 1174 /// <param name="sections">Sections that are referenced during the link process.</param>
1181 /// <param name="referencedComponents">Collection of all components referenced by complex reference.</param> 1175 /// <param name="referencedComponents">Collection of all components referenced by complex reference.</param>
1182 /// <param name="componentsToFeatures">Component to feature complex references.</param> 1176 /// <param name="componentsToFeatures">Component to feature complex references.</param>
1183 /// <param name="featuresToFeatures">Feature to feature complex references.</param> 1177 /// <param name="featuresToFeatures">Feature to feature complex references.</param>
1184 /// <param name="modulesToFeatures">Module to feature complex references.</param> 1178 /// <param name="modulesToFeatures">Module to feature complex references.</param>
1185 private void ProcessComplexReferences(Output output, IEnumerable<Section> sections, ISet<string> referencedComponents, ConnectToFeatureCollection componentsToFeatures, ConnectToFeatureCollection featuresToFeatures, ConnectToFeatureCollection modulesToFeatures) 1179 private void ProcessComplexReferences(IntermediateSection resolvedSection, IEnumerable<IntermediateSection> sections, ISet<string> referencedComponents, ConnectToFeatureCollection componentsToFeatures, ConnectToFeatureCollection featuresToFeatures, ConnectToFeatureCollection modulesToFeatures)
1186 { 1180 {
1187 Hashtable componentsToModules = new Hashtable(); 1181 Hashtable componentsToModules = new Hashtable();
1188 1182
1189 foreach (Section section in sections) 1183 foreach (var section in sections)
1190 { 1184 {
1191 Table wixComplexReferenceTable = section.Tables["WixComplexReference"]; 1185 var featureComponents = new List<FeatureComponentsTuple>();
1192 1186
1193 if (null != wixComplexReferenceTable) 1187 foreach (var wixComplexReferenceRow in section.Tuples.OfType<WixComplexReferenceTuple>())
1194 { 1188 {
1195 foreach (WixComplexReferenceRow wixComplexReferenceRow in wixComplexReferenceTable.Rows) 1189 ConnectToFeature connection;
1190 switch (wixComplexReferenceRow.ParentType)
1196 { 1191 {
1197 ConnectToFeature connection; 1192 case ComplexReferenceParentType.Feature:
1198 switch (wixComplexReferenceRow.ParentType) 1193 switch (wixComplexReferenceRow.ChildType)
1199 { 1194 {
1200 case ComplexReferenceParentType.Feature: 1195 case ComplexReferenceChildType.Component:
1201 switch (wixComplexReferenceRow.ChildType) 1196 connection = componentsToFeatures[wixComplexReferenceRow.Child];
1202 { 1197 if (null == connection)
1203 case ComplexReferenceChildType.Component: 1198 {
1204 connection = componentsToFeatures[wixComplexReferenceRow.ChildId]; 1199 componentsToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.Child, wixComplexReferenceRow.Parent, wixComplexReferenceRow.IsPrimary));
1205 if (null == connection) 1200 }
1206 { 1201 else if (wixComplexReferenceRow.IsPrimary)
1207 componentsToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary)); 1202 {
1208 } 1203 if (connection.IsExplicitPrimaryFeature)
1209 else if (wixComplexReferenceRow.IsPrimary)
1210 { 1204 {
1211 if (connection.IsExplicitPrimaryFeature) 1205 this.OnMessage(WixErrors.MultiplePrimaryReferences(wixComplexReferenceRow.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.Child, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.Parent, (null != connection.PrimaryFeature ? "Feature" : "Product"), connection.PrimaryFeature ?? resolvedSection.Id));
1212 { 1206 continue;
1213 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1214 continue;
1215 }
1216 else
1217 {
1218 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1219 connection.PrimaryFeature = wixComplexReferenceRow.ParentId; // set the new primary feature
1220 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1221 }
1222 } 1207 }
1223 else 1208 else
1224 { 1209 {
1225 connection.ConnectFeatures.Add(wixComplexReferenceRow.ParentId); 1210 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1211 connection.PrimaryFeature = wixComplexReferenceRow.Parent; // set the new primary feature
1212 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1226 } 1213 }
1214 }
1215 else
1216 {
1217 connection.ConnectFeatures.Add(wixComplexReferenceRow.Parent);
1218 }
1227 1219
1228 // add a row to the FeatureComponents table 1220 // add a row to the FeatureComponents table
1229 Table featureComponentsTable = output.EnsureTable(this.tableDefinitions["FeatureComponents"]); 1221 var featureComponent = new FeatureComponentsTuple();
1230 Row row = featureComponentsTable.CreateRow(null); 1222 featureComponent.Feature_ = wixComplexReferenceRow.Parent;
1231 if (this.sectionIdOnRows) 1223 featureComponent.Component_ = wixComplexReferenceRow.Child;
1232 {
1233 row.SectionId = section.Id;
1234 }
1235 row[0] = wixComplexReferenceRow.ParentId;
1236 row[1] = wixComplexReferenceRow.ChildId;
1237 1224
1238 // index the component for finding orphaned records 1225 featureComponents.Add(featureComponent);
1239 string symbolName = String.Concat("Component:", wixComplexReferenceRow.ChildId);
1240 referencedComponents.Add(symbolName);
1241 1226
1242 break; 1227 // index the component for finding orphaned records
1228 var symbolName = String.Concat("Component:", wixComplexReferenceRow.Child);
1229 referencedComponents.Add(symbolName);
1243 1230
1244 case ComplexReferenceChildType.Feature: 1231 break;
1245 connection = featuresToFeatures[wixComplexReferenceRow.ChildId];
1246 if (null != connection)
1247 {
1248 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1249 continue;
1250 }
1251 1232
1252 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary)); 1233 case ComplexReferenceChildType.Feature:
1253 break; 1234 connection = featuresToFeatures[wixComplexReferenceRow.Child];
1235 if (null != connection)
1236 {
1237 this.OnMessage(WixErrors.MultiplePrimaryReferences(wixComplexReferenceRow.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.Child, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.Parent, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : resolvedSection.Id)));
1238 continue;
1239 }
1254 1240
1255 case ComplexReferenceChildType.Module: 1241 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.Child, wixComplexReferenceRow.Parent, wixComplexReferenceRow.IsPrimary));
1256 connection = modulesToFeatures[wixComplexReferenceRow.ChildId]; 1242 break;
1257 if (null == connection) 1243
1258 { 1244 case ComplexReferenceChildType.Module:
1259 modulesToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary)); 1245 connection = modulesToFeatures[wixComplexReferenceRow.Child];
1260 } 1246 if (null == connection)
1261 else if (wixComplexReferenceRow.IsPrimary) 1247 {
1248 modulesToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.Child, wixComplexReferenceRow.Parent, wixComplexReferenceRow.IsPrimary));
1249 }
1250 else if (wixComplexReferenceRow.IsPrimary)
1251 {
1252 if (connection.IsExplicitPrimaryFeature)
1262 { 1253 {
1263 if (connection.IsExplicitPrimaryFeature) 1254 this.OnMessage(WixErrors.MultiplePrimaryReferences(wixComplexReferenceRow.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.Child, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.Parent, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : resolvedSection.Id)));
1264 { 1255 continue;
1265 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1266 continue;
1267 }
1268 else
1269 {
1270 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1271 connection.PrimaryFeature = wixComplexReferenceRow.ParentId; // set the new primary feature
1272 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1273 }
1274 } 1256 }
1275 else 1257 else
1276 { 1258 {
1277 connection.ConnectFeatures.Add(wixComplexReferenceRow.ParentId); 1259 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1260 connection.PrimaryFeature = wixComplexReferenceRow.Parent; // set the new primary feature
1261 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1278 } 1262 }
1279 break; 1263 }
1264 else
1265 {
1266 connection.ConnectFeatures.Add(wixComplexReferenceRow.Parent);
1267 }
1268 break;
1280 1269
1281 default: 1270 default:
1282 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType))); 1271 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1283 } 1272 }
1284 break; 1273 break;
1285 1274
1286 case ComplexReferenceParentType.Module: 1275 case ComplexReferenceParentType.Module:
1287 switch (wixComplexReferenceRow.ChildType) 1276 switch (wixComplexReferenceRow.ChildType)
1288 { 1277 {
1289 case ComplexReferenceChildType.Component: 1278 case ComplexReferenceChildType.Component:
1290 if (componentsToModules.ContainsKey(wixComplexReferenceRow.ChildId)) 1279 if (componentsToModules.ContainsKey(wixComplexReferenceRow.Child))
1291 { 1280 {
1292 this.OnMessage(WixErrors.ComponentReferencedTwice(section.SourceLineNumbers, wixComplexReferenceRow.ChildId)); 1281 this.OnMessage(WixErrors.ComponentReferencedTwice(wixComplexReferenceRow.SourceLineNumbers, wixComplexReferenceRow.Child));
1293 continue; 1282 continue;
1294 } 1283 }
1295 else 1284 else
1296 { 1285 {
1297 componentsToModules.Add(wixComplexReferenceRow.ChildId, wixComplexReferenceRow); // should always be new 1286 componentsToModules.Add(wixComplexReferenceRow.Child, wixComplexReferenceRow); // should always be new
1298 1287
1299 // add a row to the ModuleComponents table 1288 // add a row to the ModuleComponents table
1300 Table moduleComponentsTable = output.EnsureTable(this.tableDefinitions["ModuleComponents"]); 1289 var moduleComponent = new ModuleComponentsTuple();
1301 Row row = moduleComponentsTable.CreateRow(null); 1290 moduleComponent.Component = wixComplexReferenceRow.Child;
1302 if (this.sectionIdOnRows) 1291 moduleComponent.ModuleID = wixComplexReferenceRow.Parent;
1303 { 1292 moduleComponent.Language = Convert.ToInt32(wixComplexReferenceRow.ParentLanguage);
1304 row.SectionId = section.Id; 1293 }
1305 }
1306 row[0] = wixComplexReferenceRow.ChildId;
1307 row[1] = wixComplexReferenceRow.ParentId;
1308 row[2] = wixComplexReferenceRow.ParentLanguage;
1309 }
1310 1294
1311 // index the component for finding orphaned records 1295 // index the component for finding orphaned records
1312 string componentSymbolName = String.Concat("Component:", wixComplexReferenceRow.ChildId); 1296 string componentSymbolName = String.Concat("Component:", wixComplexReferenceRow.Child);
1313 referencedComponents.Add(componentSymbolName); 1297 referencedComponents.Add(componentSymbolName);
1314 1298
1315 break; 1299 break;
1316 1300
1317 default: 1301 default:
1318 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType))); 1302 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1319 } 1303 }
1320 break; 1304 break;
1321 1305
1322 case ComplexReferenceParentType.Patch: 1306 case ComplexReferenceParentType.Patch:
1323 switch (wixComplexReferenceRow.ChildType) 1307 switch (wixComplexReferenceRow.ChildType)
1324 { 1308 {
1325 case ComplexReferenceChildType.PatchFamily: 1309 case ComplexReferenceChildType.PatchFamily:
1326 case ComplexReferenceChildType.PatchFamilyGroup: 1310 case ComplexReferenceChildType.PatchFamilyGroup:
1327 break; 1311 break;
1328 1312
1329 default: 1313 default:
1330 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType))); 1314 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1331 } 1315 }
1332 break; 1316 break;
1333 1317
1334 case ComplexReferenceParentType.Product: 1318 case ComplexReferenceParentType.Product:
1335 switch (wixComplexReferenceRow.ChildType) 1319 switch (wixComplexReferenceRow.ChildType)
1336 { 1320 {
1337 case ComplexReferenceChildType.Feature: 1321 case ComplexReferenceChildType.Feature:
1338 connection = featuresToFeatures[wixComplexReferenceRow.ChildId]; 1322 connection = featuresToFeatures[wixComplexReferenceRow.Child];
1339 if (null != connection) 1323 if (null != connection)
1340 { 1324 {
1341 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id))); 1325 this.OnMessage(WixErrors.MultiplePrimaryReferences(wixComplexReferenceRow.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.Child, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.Parent, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : resolvedSection.Id)));
1342 continue; 1326 continue;
1343 } 1327 }
1344 1328
1345 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, null, wixComplexReferenceRow.IsPrimary)); 1329 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.Child, null, wixComplexReferenceRow.IsPrimary));
1346 break; 1330 break;
1347 1331
1348 default: 1332 default:
1349 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType))); 1333 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1350 } 1334 }
1351 break; 1335 break;
1352 1336
1353 default: 1337 default:
1354 // Note: Groups have been processed before getting here so they are not handled by any case above. 1338 // Note: Groups have been processed before getting here so they are not handled by any case above.
1355 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceParentType), wixComplexReferenceRow.ParentType))); 1339 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceParentType), wixComplexReferenceRow.ParentType)));
1356 }
1357 } 1340 }
1358 } 1341 }
1342
1343 foreach (var featureComponent in featureComponents)
1344 {
1345 section.Tuples.Add(featureComponent);
1346 }
1359 } 1347 }
1360 } 1348 }
1361 1349
@@ -1363,11 +1351,11 @@ namespace WixToolset
1363 /// Flattens all complex references in all sections in the collection. 1351 /// Flattens all complex references in all sections in the collection.
1364 /// </summary> 1352 /// </summary>
1365 /// <param name="sections">Sections that are referenced during the link process.</param> 1353 /// <param name="sections">Sections that are referenced during the link process.</param>
1366 private void FlattenSectionsComplexReferences(IEnumerable<Section> sections) 1354 private void FlattenSectionsComplexReferences(IEnumerable<IntermediateSection> sections)
1367 { 1355 {
1368 Hashtable parentGroups = new Hashtable(); 1356 var parentGroups = new Dictionary<string, List<WixComplexReferenceTuple>>();
1369 Hashtable parentGroupsSections = new Hashtable(); 1357 var parentGroupsSections = new Dictionary<string, IntermediateSection>();
1370 Hashtable parentGroupsNeedingProcessing = new Hashtable(); 1358 var parentGroupsNeedingProcessing = new Dictionary<string, IntermediateSection>();
1371 1359
1372 // DisplaySectionComplexReferences("--- section's complex references before flattening ---", sections); 1360 // DisplaySectionComplexReferences("--- section's complex references before flattening ---", sections);
1373 1361
@@ -1376,69 +1364,60 @@ namespace WixToolset
1376 // parents" of Features, Modules, and, of course, Groups. These references 1364 // parents" of Features, Modules, and, of course, Groups. These references
1377 // that participate in a "grouping parent" will be removed from their section 1365 // that participate in a "grouping parent" will be removed from their section
1378 // now and after processing added back in Step 3 below. 1366 // now and after processing added back in Step 3 below.
1379 foreach (Section section in sections) 1367 foreach (var section in sections)
1380 { 1368 {
1381 Table wixComplexReferenceTable = section.Tables["WixComplexReference"]; 1369 // Count down because we'll sometimes remove items from the list.
1382 1370 for (int i = section.Tuples.Count - 1; i >= 0; --i)
1383 if (null != wixComplexReferenceTable)
1384 { 1371 {
1385 // Count down because we'll sometimes remove items from the list. 1372 // Only process the "grouping parents" such as FeatureGroup, ComponentGroup, Feature,
1386 for (int i = wixComplexReferenceTable.Rows.Count - 1; i >= 0; --i) 1373 // and Module. Non-grouping complex references are simple and
1374 // resolved during normal complex reference resolutions.
1375 if (section.Tuples[i] is WixComplexReferenceTuple wixComplexReferenceRow &&
1376 (ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType ||
1377 ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType ||
1378 ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType ||
1379 ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType ||
1380 ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType ||
1381 ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType))
1387 { 1382 {
1388 WixComplexReferenceRow wixComplexReferenceRow = (WixComplexReferenceRow)wixComplexReferenceTable.Rows[i]; 1383 var parentTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.Parent);
1389 1384
1390 // Only process the "grouping parents" such as FeatureGroup, ComponentGroup, Feature, 1385 // Group all complex references with a common parent
1391 // and Module. Non-grouping complex references are simple and 1386 // together so we can find them quickly while processing in
1392 // resolved during normal complex reference resolutions. 1387 // Step 2.
1393 if (ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType || 1388 if (!parentGroups.TryGetValue(parentTypeAndId, out var childrenComplexRefs))
1394 ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType ||
1395 ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType ||
1396 ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType ||
1397 ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType ||
1398 ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType)
1399 { 1389 {
1400 string parentTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.ParentId); 1390 childrenComplexRefs = new List<WixComplexReferenceTuple>();
1391 parentGroups.Add(parentTypeAndId, childrenComplexRefs);
1392 }
1401 1393
1402 // Group all complex references with a common parent 1394 childrenComplexRefs.Add(wixComplexReferenceRow);
1403 // together so we can find them quickly while processing in 1395 section.Tuples.RemoveAt(i);
1404 // Step 2.
1405 ArrayList childrenComplexRefs = parentGroups[parentTypeAndId] as ArrayList;
1406 if (null == childrenComplexRefs)
1407 {
1408 childrenComplexRefs = new ArrayList();
1409 parentGroups.Add(parentTypeAndId, childrenComplexRefs);
1410 }
1411 1396
1412 childrenComplexRefs.Add(wixComplexReferenceRow); 1397 // Remember the mapping from set of complex references with a common
1413 wixComplexReferenceTable.Rows.RemoveAt(i); 1398 // parent to their section. We'll need this to add them back to the
1399 // correct section in Step 3.
1400 if (!parentGroupsSections.TryGetValue(parentTypeAndId, out var parentSection))
1401 {
1402 parentGroupsSections.Add(parentTypeAndId, section);
1403 }
1414 1404
1415 // Remember the mapping from set of complex references with a common 1405 // If the child of the complex reference is another group, then in Step 2
1416 // parent to their section. We'll need this to add them back to the 1406 // we're going to have to process this complex reference again to copy
1417 // correct section in Step 3. 1407 // the child group's references into the parent group.
1418 Section parentSection = parentGroupsSections[parentTypeAndId] as Section; 1408 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1419 if (null == parentSection) 1409 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1420 { 1410 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1421 parentGroupsSections.Add(parentTypeAndId, section); 1411 {
1422 } 1412 if (!parentGroupsNeedingProcessing.ContainsKey(parentTypeAndId))
1423 // Debug.Assert(section == (Section)parentGroupsSections[parentTypeAndId]);
1424
1425 // If the child of the complex reference is another group, then in Step 2
1426 // we're going to have to process this complex reference again to copy
1427 // the child group's references into the parent group.
1428 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1429 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1430 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1431 { 1413 {
1432 if (!parentGroupsNeedingProcessing.ContainsKey(parentTypeAndId)) 1414 parentGroupsNeedingProcessing.Add(parentTypeAndId, section);
1433 {
1434 parentGroupsNeedingProcessing.Add(parentTypeAndId, section);
1435 }
1436 // Debug.Assert(section == (Section)parentGroupsNeedingProcessing[parentTypeAndId]);
1437 } 1415 }
1438 } 1416 }
1439 } 1417 }
1440 } 1418 }
1441 } 1419 }
1420
1442 Debug.Assert(parentGroups.Count == parentGroupsSections.Count); 1421 Debug.Assert(parentGroups.Count == parentGroupsSections.Count);
1443 Debug.Assert(parentGroupsNeedingProcessing.Count <= parentGroups.Count); 1422 Debug.Assert(parentGroupsNeedingProcessing.Count <= parentGroups.Count);
1444 1423
@@ -1447,14 +1426,13 @@ namespace WixToolset
1447 // Step 2: Loop through the parent groups that have nested groups removing 1426 // Step 2: Loop through the parent groups that have nested groups removing
1448 // them from the hash table as they are processed. At the end of this the 1427 // them from the hash table as they are processed. At the end of this the
1449 // complex references should all be flattened. 1428 // complex references should all be flattened.
1450 string[] keys = new string[parentGroupsNeedingProcessing.Keys.Count]; 1429 var keys = parentGroupsNeedingProcessing.Keys.ToList();
1451 parentGroupsNeedingProcessing.Keys.CopyTo(keys, 0);
1452 1430
1453 foreach (string key in keys) 1431 foreach (string key in keys)
1454 { 1432 {
1455 if (parentGroupsNeedingProcessing.Contains(key)) 1433 if (parentGroupsNeedingProcessing.ContainsKey(key))
1456 { 1434 {
1457 Stack loopDetector = new Stack(); 1435 var loopDetector = new Stack<string>();
1458 this.FlattenGroup(key, loopDetector, parentGroups, parentGroupsNeedingProcessing); 1436 this.FlattenGroup(key, loopDetector, parentGroups, parentGroupsNeedingProcessing);
1459 } 1437 }
1460 else 1438 else
@@ -1468,18 +1446,17 @@ namespace WixToolset
1468 // in Step 1 and flattened in Step 2 are added to their appropriate 1446 // in Step 1 and flattened in Step 2 are added to their appropriate
1469 // section. This is where we will toss out the final no-longer-needed 1447 // section. This is where we will toss out the final no-longer-needed
1470 // groups. 1448 // groups.
1471 foreach (string parentGroup in parentGroups.Keys) 1449 foreach (var parentGroup in parentGroups.Keys)
1472 { 1450 {
1473 Section section = (Section)parentGroupsSections[parentGroup]; 1451 var section = parentGroupsSections[parentGroup];
1474 Table wixComplexReferenceTable = section.Tables["WixComplexReference"];
1475 1452
1476 foreach (WixComplexReferenceRow wixComplexReferenceRow in (ArrayList)parentGroups[parentGroup]) 1453 foreach (var wixComplexReferenceRow in parentGroups[parentGroup])
1477 { 1454 {
1478 if ((ComplexReferenceParentType.FeatureGroup != wixComplexReferenceRow.ParentType) && 1455 if ((ComplexReferenceParentType.FeatureGroup != wixComplexReferenceRow.ParentType) &&
1479 (ComplexReferenceParentType.ComponentGroup != wixComplexReferenceRow.ParentType) && 1456 (ComplexReferenceParentType.ComponentGroup != wixComplexReferenceRow.ParentType) &&
1480 (ComplexReferenceParentType.PatchFamilyGroup != wixComplexReferenceRow.ParentType)) 1457 (ComplexReferenceParentType.PatchFamilyGroup != wixComplexReferenceRow.ParentType))
1481 { 1458 {
1482 wixComplexReferenceTable.Rows.Add(wixComplexReferenceRow); 1459 section.Tuples.Add(wixComplexReferenceRow);
1483 } 1460 }
1484 } 1461 }
1485 } 1462 }
@@ -1504,46 +1481,39 @@ namespace WixToolset
1504 /// <param name="loopDetector">Stack of groups processed thus far. Used to detect loops.</param> 1481 /// <param name="loopDetector">Stack of groups processed thus far. Used to detect loops.</param>
1505 /// <param name="parentGroups">Hash table of complex references grouped by parent id.</param> 1482 /// <param name="parentGroups">Hash table of complex references grouped by parent id.</param>
1506 /// <param name="parentGroupsNeedingProcessing">Hash table of parent groups that still have nested groups that need to be flattened.</param> 1483 /// <param name="parentGroupsNeedingProcessing">Hash table of parent groups that still have nested groups that need to be flattened.</param>
1507 private void FlattenGroup(string parentTypeAndId, Stack loopDetector, Hashtable parentGroups, Hashtable parentGroupsNeedingProcessing) 1484 private void FlattenGroup(string parentTypeAndId, Stack<string> loopDetector, Dictionary<string, List<WixComplexReferenceTuple>> parentGroups, Dictionary<string, IntermediateSection> parentGroupsNeedingProcessing)
1508 { 1485 {
1509 Debug.Assert(parentGroupsNeedingProcessing.Contains(parentTypeAndId)); 1486 Debug.Assert(parentGroupsNeedingProcessing.ContainsKey(parentTypeAndId));
1510 loopDetector.Push(parentTypeAndId); // push this complex reference parent identfier into the stack for loop verifying 1487 loopDetector.Push(parentTypeAndId); // push this complex reference parent identfier into the stack for loop verifying
1511 1488
1512 ArrayList allNewChildComplexReferences = new ArrayList(); 1489 var allNewChildComplexReferences = new List<WixComplexReferenceTuple>();
1513 ArrayList referencesToParent = (ArrayList)parentGroups[parentTypeAndId]; 1490
1514 foreach (WixComplexReferenceRow wixComplexReferenceRow in referencesToParent) 1491 var referencesToParent = parentGroups[parentTypeAndId];
1492 foreach (var wixComplexReferenceRow in referencesToParent)
1515 { 1493 {
1516 Debug.Assert(ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Patch == wixComplexReferenceRow.ParentType); 1494 Debug.Assert(ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Patch == wixComplexReferenceRow.ParentType);
1517 Debug.Assert(parentTypeAndId == CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.ParentId)); 1495 Debug.Assert(parentTypeAndId == CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.Parent));
1518 1496
1519 // We are only interested processing when the child is a group. 1497 // We are only interested processing when the child is a group.
1520 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) || 1498 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1521 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) || 1499 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1522 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType)) 1500 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1523 { 1501 {
1524 string childTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ChildType, wixComplexReferenceRow.ChildId); 1502 string childTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ChildType, wixComplexReferenceRow.Child);
1525 if (loopDetector.Contains(childTypeAndId)) 1503 if (loopDetector.Contains(childTypeAndId))
1526 { 1504 {
1527 // Create a comma delimited list of the references that participate in the 1505 // Create a comma delimited list of the references that participate in the
1528 // loop for the error message. Start at the bottom of the stack and work the 1506 // loop for the error message. Start at the bottom of the stack and work the
1529 // way up to present the loop as a directed graph. 1507 // way up to present the loop as a directed graph.
1530 object[] stack = loopDetector.ToArray(); 1508 var loop = String.Join(" -> ", loopDetector);
1531 StringBuilder loop = new StringBuilder();
1532 for (int i = stack.Length - 1; i >= 0; --i)
1533 {
1534 loop.Append((string)stack[i]);
1535 if (0 < i)
1536 {
1537 loop.Append(" -> ");
1538 }
1539 }
1540 1509
1541 this.OnMessage(WixErrors.ReferenceLoopDetected(wixComplexReferenceRow.Table.Section == null ? null : wixComplexReferenceRow.Table.Section.SourceLineNumbers, loop.ToString())); 1510 this.OnMessage(WixErrors.ReferenceLoopDetected(wixComplexReferenceRow?.SourceLineNumbers, loop));
1542 1511
1543 // Cleanup the parentGroupsNeedingProcessing and the loopDetector just like the 1512 // Cleanup the parentGroupsNeedingProcessing and the loopDetector just like the
1544 // exit of this method does at the end because we are exiting early. 1513 // exit of this method does at the end because we are exiting early.
1545 loopDetector.Pop(); 1514 loopDetector.Pop();
1546 parentGroupsNeedingProcessing.Remove(parentTypeAndId); 1515 parentGroupsNeedingProcessing.Remove(parentTypeAndId);
1516
1547 return; // bail 1517 return; // bail
1548 } 1518 }
1549 1519
@@ -1560,10 +1530,9 @@ namespace WixToolset
1560 // complex reference (because we're moving references up the tree), and finally 1530 // complex reference (because we're moving references up the tree), and finally
1561 // add the cloned child's complex reference to the list of complex references 1531 // add the cloned child's complex reference to the list of complex references
1562 // that we'll eventually add to the parent group. 1532 // that we'll eventually add to the parent group.
1563 ArrayList referencesToChild = (ArrayList)parentGroups[childTypeAndId]; 1533 if (parentGroups.TryGetValue(childTypeAndId, out var referencesToChild))
1564 if (null != referencesToChild)
1565 { 1534 {
1566 foreach (WixComplexReferenceRow crefChild in referencesToChild) 1535 foreach (var crefChild in referencesToChild)
1567 { 1536 {
1568 // Only merge up the non-group items since groups are purged 1537 // Only merge up the non-group items since groups are purged
1569 // after this part of the processing anyway (cloning them would 1538 // after this part of the processing anyway (cloning them would
@@ -1572,8 +1541,8 @@ namespace WixToolset
1572 (ComplexReferenceChildType.ComponentGroup != crefChild.ChildType) || 1541 (ComplexReferenceChildType.ComponentGroup != crefChild.ChildType) ||
1573 (ComplexReferenceChildType.PatchFamilyGroup != crefChild.ChildType)) 1542 (ComplexReferenceChildType.PatchFamilyGroup != crefChild.ChildType))
1574 { 1543 {
1575 WixComplexReferenceRow crefChildClone = crefChild.Clone(); 1544 var crefChildClone = crefChild.Clone();
1576 Debug.Assert(crefChildClone.ParentId == wixComplexReferenceRow.ChildId); 1545 Debug.Assert(crefChildClone.Parent == wixComplexReferenceRow.Child);
1577 1546
1578 crefChildClone.Reparent(wixComplexReferenceRow); 1547 crefChildClone.Reparent(wixComplexReferenceRow);
1579 allNewChildComplexReferences.Add(crefChildClone); 1548 allNewChildComplexReferences.Add(crefChildClone);
@@ -1587,10 +1556,11 @@ namespace WixToolset
1587 // group. Clean out any left over groups and quietly remove any 1556 // group. Clean out any left over groups and quietly remove any
1588 // duplicate complex references that occurred during the merge. 1557 // duplicate complex references that occurred during the merge.
1589 referencesToParent.AddRange(allNewChildComplexReferences); 1558 referencesToParent.AddRange(allNewChildComplexReferences);
1590 referencesToParent.Sort(); 1559 referencesToParent.Sort(ComplexReferenceComparision);
1591 for (int i = referencesToParent.Count - 1; i >= 0; --i) 1560 for (int i = referencesToParent.Count - 1; i >= 0; --i)
1592 { 1561 {
1593 WixComplexReferenceRow wixComplexReferenceRow = (WixComplexReferenceRow)referencesToParent[i]; 1562 var wixComplexReferenceRow = referencesToParent[i];
1563
1594 if ((ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) || 1564 if ((ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1595 (ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) || 1565 (ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1596 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType)) 1566 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
@@ -1602,7 +1572,7 @@ namespace WixToolset
1602 // Since the list is already sorted, we can find duplicates by simply 1572 // Since the list is already sorted, we can find duplicates by simply
1603 // looking at the next sibling in the list and tossing out one if they 1573 // looking at the next sibling in the list and tossing out one if they
1604 // match. 1574 // match.
1605 WixComplexReferenceRow crefCompare = (WixComplexReferenceRow)referencesToParent[i - 1]; 1575 var crefCompare = referencesToParent[i - 1];
1606 if (0 == wixComplexReferenceRow.CompareToWithoutConsideringPrimary(crefCompare)) 1576 if (0 == wixComplexReferenceRow.CompareToWithoutConsideringPrimary(crefCompare))
1607 { 1577 {
1608 referencesToParent.RemoveAt(i); 1578 referencesToParent.RemoveAt(i);
@@ -1610,6 +1580,29 @@ namespace WixToolset
1610 } 1580 }
1611 } 1581 }
1612 1582
1583 int ComplexReferenceComparision(WixComplexReferenceTuple x, WixComplexReferenceTuple y)
1584 {
1585 var comparison = x.ChildType - y.ChildType;
1586 if (0 == comparison)
1587 {
1588 comparison = String.Compare(x.Child, y.Child, StringComparison.Ordinal);
1589 if (0 == comparison)
1590 {
1591 comparison = x.ParentType - y.ParentType;
1592 if (0 == comparison)
1593 {
1594 comparison = String.Compare(x.ParentLanguage ?? String.Empty, y.ParentLanguage ?? String.Empty, StringComparison.Ordinal);
1595 if (0 == comparison)
1596 {
1597 comparison = String.Compare(x.Parent, y.Parent, StringComparison.Ordinal);
1598 }
1599 }
1600 }
1601 }
1602
1603 return comparison;
1604 }
1605
1613 loopDetector.Pop(); // pop this complex reference off the stack since we're done verify the loop here 1606 loopDetector.Pop(); // pop this complex reference off the stack since we're done verify the loop here
1614 parentGroupsNeedingProcessing.Remove(parentTypeAndId); // remove the newly processed complex reference 1607 parentGroupsNeedingProcessing.Remove(parentTypeAndId); // remove the newly processed complex reference
1615 } 1608 }
@@ -1639,9 +1632,9 @@ namespace WixToolset
1639 /// Flattens the tables used in a Bundle. 1632 /// Flattens the tables used in a Bundle.
1640 /// </summary> 1633 /// </summary>
1641 /// <param name="output">Output containing the tables to process.</param> 1634 /// <param name="output">Output containing the tables to process.</param>
1642 private void FlattenBundleTables(Output output) 1635 private void FlattenBundleTables(IntermediateSection entrySection)
1643 { 1636 {
1644 if (OutputType.Bundle != output.Type) 1637 if (SectionType.Bundle != entrySection.Type)
1645 { 1638 {
1646 return; 1639 return;
1647 } 1640 }
@@ -1651,7 +1644,7 @@ namespace WixToolset
1651 // will hold Payloads under UX, ChainPackages (references?) under Chain, 1644 // will hold Payloads under UX, ChainPackages (references?) under Chain,
1652 // and ChainPackages/Payloads under the attached and any detatched 1645 // and ChainPackages/Payloads under the attached and any detatched
1653 // Containers. 1646 // Containers.
1654 WixGroupingOrdering groups = new WixGroupingOrdering(output, this); 1647 var groups = new WixGroupingOrdering(entrySection, this);
1655 1648
1656 // Create UX payloads and Package payloads 1649 // Create UX payloads and Package payloads
1657 groups.UseTypes(new string[] { "Container", "Layout", "PackageGroup", "PayloadGroup", "Package" }, new string[] { "PackageGroup", "Package", "PayloadGroup", "Payload" }); 1650 groups.UseTypes(new string[] { "Container", "Layout", "PackageGroup", "PayloadGroup", "Package" }, new string[] { "PackageGroup", "Package", "PayloadGroup", "Payload" });
@@ -1675,21 +1668,21 @@ namespace WixToolset
1675 { 1668 {
1676 foreach (ConnectToFeature connection in featuresToFeatures) 1669 foreach (ConnectToFeature connection in featuresToFeatures)
1677 { 1670 {
1678 WixSimpleReferenceRow wixSimpleReferenceRow = new WixSimpleReferenceRow(null, this.tableDefinitions["WixSimpleReference"]); 1671 var wixSimpleReferenceRow = new WixSimpleReferenceTuple();
1679 wixSimpleReferenceRow.TableName = "Feature"; 1672 wixSimpleReferenceRow.Table = "Feature";
1680 wixSimpleReferenceRow.PrimaryKeys = connection.ChildId; 1673 wixSimpleReferenceRow.PrimaryKeys = connection.ChildId;
1681 1674
1682 Symbol symbol; 1675 if (!allSymbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out var symbol))
1683 if (!allSymbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out symbol))
1684 { 1676 {
1685 continue; 1677 continue;
1686 } 1678 }
1687 1679
1688 Row row = symbol.Row; 1680 var row = symbol.Row;
1689 row[1] = connection.PrimaryFeature; 1681 row.Set(1, connection.PrimaryFeature);
1690 } 1682 }
1691 } 1683 }
1692 1684
1685#if DEAD_CODE
1693 /// <summary> 1686 /// <summary>
1694 /// Copies a table's rows to an output table. 1687 /// Copies a table's rows to an output table.
1695 /// </summary> 1688 /// </summary>
@@ -1731,16 +1724,18 @@ namespace WixToolset
1731 outputTable.Rows.Add(row); 1724 outputTable.Rows.Add(row);
1732 } 1725 }
1733 } 1726 }
1727#endif
1734 1728
1729#if MOVE_TO_BACKEND
1735 /// <summary> 1730 /// <summary>
1736 /// Set sequence numbers for all the actions and create rows in the output object. 1731 /// Set sequence numbers for all the actions and create rows in the output object.
1737 /// </summary> 1732 /// </summary>
1738 /// <param name="actionRows">Collection of actions to schedule.</param> 1733 /// <param name="actionRows">Collection of actions to schedule.</param>
1739 /// <param name="suppressActionRows">Collection of actions to suppress.</param> 1734 /// <param name="suppressActionRows">Collection of actions to suppress.</param>
1740 private void SequenceActions(List<Row> actionRows, List<Row> suppressActionRows) 1735 private void SequenceActions(List<IntermediateTuple> actionRows, List<IntermediateTuple> suppressActionRows)
1741 { 1736 {
1742 WixActionRowCollection overridableActionRows = new WixActionRowCollection(); 1737 var overridableActionRows = new WixActionRowCollection();
1743 WixActionRowCollection requiredActionRows = new WixActionRowCollection(); 1738 var requiredActionRows = new WixActionRowCollection();
1744 ArrayList scheduledActionRows = new ArrayList(); 1739 ArrayList scheduledActionRows = new ArrayList();
1745 1740
1746 // gather the required actions for the output type 1741 // gather the required actions for the output type
@@ -2362,6 +2357,7 @@ namespace WixToolset
2362 WixActionRowCollection relatedRows = (after ? parentActionRow.NextActionRows : parentActionRow.PreviousActionRows); 2357 WixActionRowCollection relatedRows = (after ? parentActionRow.NextActionRows : parentActionRow.PreviousActionRows);
2363 relatedRows.Add(actionRow); 2358 relatedRows.Add(actionRow);
2364 } 2359 }
2360#endif
2365 2361
2366 /// <summary> 2362 /// <summary>
2367 /// Resolve features for columns that have null guid placeholders. 2363 /// Resolve features for columns that have null guid placeholders.
@@ -2371,59 +2367,55 @@ namespace WixToolset
2371 /// <param name="featureColumn">Number of the column containing the feature.</param> 2367 /// <param name="featureColumn">Number of the column containing the feature.</param>
2372 /// <param name="connectToFeatures">Connect to feature complex references.</param> 2368 /// <param name="connectToFeatures">Connect to feature complex references.</param>
2373 /// <param name="multipleFeatureComponents">Hashtable of known components under multiple features.</param> 2369 /// <param name="multipleFeatureComponents">Hashtable of known components under multiple features.</param>
2374 private void ResolveFeatures(IEnumerable<Row> rows, int connectionColumn, int featureColumn, ConnectToFeatureCollection connectToFeatures, Hashtable multipleFeatureComponents) 2370 private void ResolveFeatures(IntermediateTuple row, int connectionColumn, int featureColumn, ConnectToFeatureCollection connectToFeatures, Hashtable multipleFeatureComponents)
2375 { 2371 {
2376 foreach (Row row in rows) 2372 var connectionId = row.AsString(connectionColumn);
2373 var featureId = row.AsString(featureColumn);
2374
2375 if (emptyGuid == featureId)
2377 { 2376 {
2378 string connectionId = (string)row[connectionColumn]; 2377 ConnectToFeature connection = connectToFeatures[connectionId];
2379 string featureId = (string)row[featureColumn];
2380 2378
2381 if (emptyGuid == featureId) 2379 if (null == connection)
2382 { 2380 {
2383 ConnectToFeature connection = connectToFeatures[connectionId]; 2381 // display an error for the component or merge module as approrpriate
2384 2382 if (null != multipleFeatureComponents)
2385 if (null == connection)
2386 { 2383 {
2387 // display an error for the component or merge module as approrpriate 2384 this.OnMessage(WixErrors.ComponentExpectedFeature(row.SourceLineNumbers, connectionId, row.Definition.Name, row.Id.Id));
2388 if (null != multipleFeatureComponents)
2389 {
2390 this.OnMessage(WixErrors.ComponentExpectedFeature(row.SourceLineNumbers, connectionId, row.Table.Name, row.GetPrimaryKey('/')));
2391 }
2392 else
2393 {
2394 this.OnMessage(WixErrors.MergeModuleExpectedFeature(row.SourceLineNumbers, connectionId));
2395 }
2396 } 2385 }
2397 else 2386 else
2398 { 2387 {
2399 // check for unique, implicit, primary feature parents with multiple possible parent features 2388 this.OnMessage(WixErrors.MergeModuleExpectedFeature(row.SourceLineNumbers, connectionId));
2400 if (this.ShowPedanticMessages && 2389 }
2401 !connection.IsExplicitPrimaryFeature && 2390 }
2402 0 < connection.ConnectFeatures.Count) 2391 else
2392 {
2393 // check for unique, implicit, primary feature parents with multiple possible parent features
2394 if (this.ShowPedanticMessages &&
2395 !connection.IsExplicitPrimaryFeature &&
2396 0 < connection.ConnectFeatures.Count)
2397 {
2398 // display a warning for the component or merge module as approrpriate
2399 if (null != multipleFeatureComponents)
2403 { 2400 {
2404 // display a warning for the component or merge module as approrpriate 2401 if (!multipleFeatureComponents.Contains(connectionId))
2405 if (null != multipleFeatureComponents)
2406 { 2402 {
2407 if (!multipleFeatureComponents.Contains(connectionId)) 2403 this.OnMessage(WixWarnings.ImplicitComponentPrimaryFeature(connectionId));
2408 {
2409 this.OnMessage(WixWarnings.ImplicitComponentPrimaryFeature(connectionId));
2410 2404
2411 // remember this component so only one warning is generated for it 2405 // remember this component so only one warning is generated for it
2412 multipleFeatureComponents[connectionId] = null; 2406 multipleFeatureComponents[connectionId] = null;
2413 }
2414 }
2415 else
2416 {
2417 this.OnMessage(WixWarnings.ImplicitMergeModulePrimaryFeature(connectionId));
2418 } 2407 }
2419 } 2408 }
2420 2409 else
2421 // set the feature 2410 {
2422 row[featureColumn] = connection.PrimaryFeature; 2411 this.OnMessage(WixWarnings.ImplicitMergeModulePrimaryFeature(connectionId));
2412 }
2423 } 2413 }
2414
2415 // set the feature
2416 row.Set(featureColumn, connection.PrimaryFeature);
2424 } 2417 }
2425 } 2418 }
2426 } 2419 }
2427
2428 } 2420 }
2429} 2421}