aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Link
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-11-01 10:59:45 -0700
committerRob Mensching <rob@firegiant.com>2017-11-01 10:59:45 -0700
commit2bb37beda887d120a0ddabf874ad25357101faa1 (patch)
treec35e97b03274b86cfc9ff7fd2caeee211165a140 /src/WixToolset.Core/Link
parentdf7413aeed3aea3425dff20ae0c8b1be3a3ab525 (diff)
downloadwix-2bb37beda887d120a0ddabf874ad25357101faa1.tar.gz
wix-2bb37beda887d120a0ddabf874ad25357101faa1.tar.bz2
wix-2bb37beda887d120a0ddabf874ad25357101faa1.zip
Update to WiX Intermediate Representation
Diffstat (limited to 'src/WixToolset.Core/Link')
-rw-r--r--src/WixToolset.Core/Link/ConnectToFeature.cs50
-rw-r--r--src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs81
-rw-r--r--src/WixToolset.Core/Link/IntermediateTupleExtensions.cs26
-rw-r--r--src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs20
-rw-r--r--src/WixToolset.Core/Link/ResolveReferencesCommand.cs99
-rw-r--r--src/WixToolset.Core/Link/WixComplexReferenceTupleExtensions.cs73
-rw-r--r--src/WixToolset.Core/Link/WixGroupingOrdering.cs140
7 files changed, 277 insertions, 212 deletions
diff --git a/src/WixToolset.Core/Link/ConnectToFeature.cs b/src/WixToolset.Core/Link/ConnectToFeature.cs
index 6e046b89..bc85426a 100644
--- a/src/WixToolset.Core/Link/ConnectToFeature.cs
+++ b/src/WixToolset.Core/Link/ConnectToFeature.cs
@@ -2,7 +2,7 @@
2 2
3namespace WixToolset.Link 3namespace WixToolset.Link
4{ 4{
5 using System.Collections.Specialized; 5 using System.Collections.Generic;
6 using WixToolset.Data; 6 using WixToolset.Data;
7 7
8 /// <summary> 8 /// <summary>
@@ -10,19 +10,12 @@ namespace WixToolset.Link
10 /// </summary> 10 /// </summary>
11 public sealed class ConnectToFeature 11 public sealed class ConnectToFeature
12 { 12 {
13 private Section section;
14 private string childId;
15
16 private string primaryFeature;
17 private bool explicitPrimaryFeature;
18 private StringCollection connectFeatures;
19
20 /// <summary> 13 /// <summary>
21 /// Creates a new connect to feature. 14 /// Creates a new connect to feature.
22 /// </summary> 15 /// </summary>
23 /// <param name="section">Section this connect belongs to.</param> 16 /// <param name="section">Section this connect belongs to.</param>
24 /// <param name="childId">Id of the child.</param> 17 /// <param name="childId">Id of the child.</param>
25 public ConnectToFeature(Section section, string childId) : 18 public ConnectToFeature(IntermediateSection section, string childId) :
26 this(section, childId, null, false) 19 this(section, childId, null, false)
27 { 20 {
28 } 21 }
@@ -34,62 +27,43 @@ namespace WixToolset.Link
34 /// <param name="childId">Id of the child.</param> 27 /// <param name="childId">Id of the child.</param>
35 /// <param name="primaryFeature">Sets the primary feature for the connection.</param> 28 /// <param name="primaryFeature">Sets the primary feature for the connection.</param>
36 /// <param name="explicitPrimaryFeature">Sets if this is explicit primary.</param> 29 /// <param name="explicitPrimaryFeature">Sets if this is explicit primary.</param>
37 public ConnectToFeature(Section section, string childId, string primaryFeature, bool explicitPrimaryFeature) 30 public ConnectToFeature(IntermediateSection section, string childId, string primaryFeature, bool explicitPrimaryFeature)
38 { 31 {
39 this.section = section; 32 this.Section = section;
40 this.childId = childId; 33 this.ChildId = childId;
41 34
42 this.primaryFeature = primaryFeature; 35 this.PrimaryFeature = primaryFeature;
43 this.explicitPrimaryFeature = explicitPrimaryFeature; 36 this.IsExplicitPrimaryFeature = explicitPrimaryFeature;
44
45 this.connectFeatures = new StringCollection();
46 } 37 }
47 38
48 /// <summary> 39 /// <summary>
49 /// Gets the section. 40 /// Gets the section.
50 /// </summary> 41 /// </summary>
51 /// <value>Section.</value> 42 /// <value>Section.</value>
52 public Section Section 43 public IntermediateSection Section { get; }
53 {
54 get { return this.section; }
55 }
56 44
57 /// <summary> 45 /// <summary>
58 /// Gets the child identifier. 46 /// Gets the child identifier.
59 /// </summary> 47 /// </summary>
60 /// <value>The child identifier.</value> 48 /// <value>The child identifier.</value>
61 public string ChildId 49 public string ChildId { get; }
62 {
63 get { return this.childId; }
64 }
65 50
66 /// <summary> 51 /// <summary>
67 /// Gets or sets if the flag for if the primary feature was set explicitly. 52 /// Gets or sets if the flag for if the primary feature was set explicitly.
68 /// </summary> 53 /// </summary>
69 /// <value>The flag for if the primary feature was set explicitly.</value> 54 /// <value>The flag for if the primary feature was set explicitly.</value>
70 public bool IsExplicitPrimaryFeature 55 public bool IsExplicitPrimaryFeature { get; set; }
71 {
72 get { return this.explicitPrimaryFeature; }
73 set { this.explicitPrimaryFeature = value; }
74 }
75 56
76 /// <summary> 57 /// <summary>
77 /// Gets or sets the primary feature. 58 /// Gets or sets the primary feature.
78 /// </summary> 59 /// </summary>
79 /// <value>The primary feature.</value> 60 /// <value>The primary feature.</value>
80 public string PrimaryFeature 61 public string PrimaryFeature { get; set; }
81 {
82 get { return this.primaryFeature; }
83 set { this.primaryFeature = value; }
84 }
85 62
86 /// <summary> 63 /// <summary>
87 /// Gets the features connected to. 64 /// Gets the features connected to.
88 /// </summary> 65 /// </summary>
89 /// <value>Features connected to.</value> 66 /// <value>Features connected to.</value>
90 public StringCollection ConnectFeatures 67 public List<string> ConnectFeatures { get; } = new List<string>();
91 {
92 get { return this.connectFeatures; }
93 }
94 } 68 }
95} 69}
diff --git a/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs b/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
index effb06e4..00613ca1 100644
--- a/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
+++ b/src/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
@@ -1,6 +1,6 @@
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.Link 3namespace WixToolset.Core.Link
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,13 +9,13 @@ namespace WixToolset.Link
9 9
10 internal class FindEntrySectionAndLoadSymbolsCommand : ICommand 10 internal class FindEntrySectionAndLoadSymbolsCommand : ICommand
11 { 11 {
12 private IEnumerable<Section> sections; 12 public FindEntrySectionAndLoadSymbolsCommand(IEnumerable<IntermediateSection> sections)
13
14 public FindEntrySectionAndLoadSymbolsCommand(IEnumerable<Section> sections)
15 { 13 {
16 this.sections = sections; 14 this.Sections = sections;
17 } 15 }
18 16
17 private IEnumerable<IntermediateSection> Sections { get; }
18
19 /// <summary> 19 /// <summary>
20 /// Sets the expected entry output type, based on output file extension provided to the linker. 20 /// Sets the expected entry output type, based on output file extension provided to the linker.
21 /// </summary> 21 /// </summary>
@@ -24,13 +24,16 @@ namespace WixToolset.Link
24 /// <summary> 24 /// <summary>
25 /// Gets the located entry section after the command is executed. 25 /// Gets the located entry section after the command is executed.
26 /// </summary> 26 /// </summary>
27 public Section EntrySection { get; private set; } 27 public IntermediateSection EntrySection { get; private set; }
28 28
29 /// <summary> 29 /// <summary>
30 /// Gets the collection of loaded symbols. 30 /// Gets the collection of loaded symbols.
31 /// </summary> 31 /// </summary>
32 public IDictionary<string, Symbol> Symbols { get; private set; } 32 public IDictionary<string, Symbol> Symbols { get; private set; }
33 33
34 /// <summary>
35 /// Gets the collection of possibly conflicting symbols.
36 /// </summary>
34 public IEnumerable<Symbol> PossiblyConflictingSymbols { get; private set; } 37 public IEnumerable<Symbol> PossiblyConflictingSymbols { get; private set; }
35 38
36 public void Execute() 39 public void Execute()
@@ -38,22 +41,22 @@ namespace WixToolset.Link
38 Dictionary<string, Symbol> symbols = new Dictionary<string, Symbol>(); 41 Dictionary<string, Symbol> symbols = new Dictionary<string, Symbol>();
39 HashSet<Symbol> possibleConflicts = new HashSet<Symbol>(); 42 HashSet<Symbol> possibleConflicts = new HashSet<Symbol>();
40 43
41 SectionType expectedEntrySectionType; 44 if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType))
42 if (!Enum.TryParse<SectionType>(this.ExpectedOutputType.ToString(), out expectedEntrySectionType))
43 { 45 {
44 expectedEntrySectionType = SectionType.Unknown; 46 expectedEntrySectionType = SectionType.Unknown;
45 } 47 }
46 48
47 foreach (Section section in this.sections) 49 foreach (var section in this.Sections)
48 { 50 {
49 // Try to find the one and only entry section. 51 // Try to find the one and only entry section.
50 if (SectionType.Product == section.Type || SectionType.Module == section.Type || SectionType.PatchCreation == section.Type || SectionType.Patch == section.Type || SectionType.Bundle == section.Type) 52 if (SectionType.Product == section.Type || SectionType.Module == section.Type || SectionType.PatchCreation == section.Type || SectionType.Patch == section.Type || SectionType.Bundle == section.Type)
51 { 53 {
52 if (SectionType.Unknown != expectedEntrySectionType && section.Type != expectedEntrySectionType) 54 // TODO: remove this?
53 { 55 //if (SectionType.Unknown != expectedEntrySectionType && section.Type != expectedEntrySectionType)
54 string outputExtension = Output.GetExtension(this.ExpectedOutputType); 56 //{
55 Messaging.Instance.OnMessage(WixWarnings.UnexpectedEntrySection(section.SourceLineNumbers, section.Type.ToString(), expectedEntrySectionType.ToString(), outputExtension)); 57 // string outputExtension = Output.GetExtension(this.ExpectedOutputType);
56 } 58 // Messaging.Instance.OnMessage(WixWarnings.UnexpectedEntrySection(section.SourceLineNumbers, section.Type.ToString(), expectedEntrySectionType.ToString(), outputExtension));
59 //}
57 60
58 if (null == this.EntrySection) 61 if (null == this.EntrySection)
59 { 62 {
@@ -61,42 +64,38 @@ namespace WixToolset.Link
61 } 64 }
62 else 65 else
63 { 66 {
64 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections(this.EntrySection.SourceLineNumbers, this.EntrySection.Id, section.Id)); 67 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections(this.EntrySection.Tuples.FirstOrDefault()?.SourceLineNumbers, this.EntrySection.Id, section.Id));
65 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections2(section.SourceLineNumbers)); 68 Messaging.Instance.OnMessage(WixErrors.MultipleEntrySections2(section.Tuples.FirstOrDefault()?.SourceLineNumbers));
66 } 69 }
67 } 70 }
68 71
69 // Load all the symbols from the section's tables that create symbols. 72 // Load all the symbols from the section's tables that create symbols.
70 foreach (Table table in section.Tables.Where(t => t.Definition.CreateSymbols)) 73 foreach (var row in section.Tuples.Where(t => t.Id != null))
71 { 74 {
72 foreach (Row row in table.Rows) 75 var symbol = new Symbol(section, row);
73 {
74 Symbol symbol = new Symbol(row);
75 76
76 Symbol existingSymbol; 77 if (!symbols.TryGetValue(symbol.Name, out var existingSymbol))
77 if (!symbols.TryGetValue(symbol.Name, out existingSymbol)) 78 {
79 symbols.Add(symbol.Name, symbol);
80 }
81 else // uh-oh, duplicate symbols.
82 {
83 // If the duplicate symbols are both private directories, there is a chance that they
84 // point to identical tuples. Identical directory tuples are redundant and will not cause
85 // conflicts.
86 if (AccessModifier.Private == existingSymbol.Access && AccessModifier.Private == symbol.Access &&
87 TupleDefinitionType.Directory == existingSymbol.Row.Definition.Type && existingSymbol.Row.IsIdentical(symbol.Row))
78 { 88 {
79 symbols.Add(symbol.Name, symbol); 89 // Ensure identical symbol's tuple is marked redundant to ensure (should the tuple be
90 // referenced into the final output) it will not add duplicate primary keys during
91 // the .IDT importing.
92 //symbol.Row.Redundant = true; - TODO: remove this
93 existingSymbol.AddRedundant(symbol);
80 } 94 }
81 else // uh-oh, duplicate symbols. 95 else
82 { 96 {
83 // If the duplicate symbols are both private directories, there is a chance that they 97 existingSymbol.AddPossibleConflict(symbol);
84 // point to identical rows. Identical directory rows are redundant and will not cause 98 possibleConflicts.Add(existingSymbol);
85 // conflicts.
86 if (AccessModifier.Private == existingSymbol.Access && AccessModifier.Private == symbol.Access &&
87 "Directory" == existingSymbol.Row.Table.Name && existingSymbol.Row.IsIdentical(symbol.Row))
88 {
89 // Ensure identical symbol's row is marked redundant to ensure (should the row be
90 // referenced into the final output) it will not add duplicate primary keys during
91 // the .IDT importing.
92 symbol.Row.Redundant = true;
93 existingSymbol.AddRedundant(symbol);
94 }
95 else
96 {
97 existingSymbol.AddPossibleConflict(symbol);
98 possibleConflicts.Add(existingSymbol);
99 }
100 } 99 }
101 } 100 }
102 } 101 }
diff --git a/src/WixToolset.Core/Link/IntermediateTupleExtensions.cs b/src/WixToolset.Core/Link/IntermediateTupleExtensions.cs
new file mode 100644
index 00000000..c4c12e81
--- /dev/null
+++ b/src/WixToolset.Core/Link/IntermediateTupleExtensions.cs
@@ -0,0 +1,26 @@
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
3namespace WixToolset.Core.Link
4{
5 using WixToolset.Data;
6
7 internal static class IntermediateTupleExtensions
8 {
9 public static bool IsIdentical(this IntermediateTuple first, IntermediateTuple second)
10 {
11 var identical = (first.Definition.Type == second.Definition.Type &&
12 first.Definition.Name == second.Definition.Name &&
13 first.Definition.FieldDefinitions.Length == second.Definition.FieldDefinitions.Length);
14
15 for (int i = 0; identical && i < first.Definition.FieldDefinitions.Length; ++i)
16 {
17 var firstField = first[i];
18 var secondField = second[i];
19
20 identical = (firstField.AsString() == secondField.AsString());
21 }
22
23 return identical;
24 }
25 }
26}
diff --git a/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs b/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
index 39c3a5c2..ac0dd7ec 100644
--- a/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
+++ b/src/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
@@ -6,17 +6,18 @@ namespace WixToolset.Link
6 using System.Linq; 6 using System.Linq;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 8
9 public class ReportConflictingSymbolsCommand : ICommand 9 public class ReportConflictingSymbolsCommand
10 { 10 {
11 private IEnumerable<Symbol> possibleConflicts; 11 public ReportConflictingSymbolsCommand(IEnumerable<Symbol> possibleConflicts, IEnumerable<IntermediateSection> resolvedSections)
12 private IEnumerable<Section> resolvedSections;
13
14 public ReportConflictingSymbolsCommand(IEnumerable<Symbol> possibleConflicts, IEnumerable<Section> resolvedSections)
15 { 12 {
16 this.possibleConflicts = possibleConflicts; 13 this.PossibleConflicts = possibleConflicts;
17 this.resolvedSections = resolvedSections; 14 this.ResolvedSections = resolvedSections;
18 } 15 }
19 16
17 private IEnumerable<Symbol> PossibleConflicts { get; }
18
19 private IEnumerable<IntermediateSection> ResolvedSections { get; }
20
20 public void Execute() 21 public void Execute()
21 { 22 {
22 // Do a quick check if there are any possibly conflicting symbols that don't come from tables that allow 23 // Do a quick check if there are any possibly conflicting symbols that don't come from tables that allow
@@ -25,10 +26,11 @@ namespace WixToolset.Link
25 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate 26 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate
26 // (aka: conflicting) symbol. This should catch any rows with colliding primary keys (since symbols are based 27 // (aka: conflicting) symbol. This should catch any rows with colliding primary keys (since symbols are based
27 // on the primary keys of rows). 28 // on the primary keys of rows).
28 List<Symbol> illegalDuplicates = possibleConflicts.Where(s => "WixAction" != s.Row.Table.Name && "WixVariable" != s.Row.Table.Name).ToList(); 29 var illegalDuplicates = this.PossibleConflicts.Where(s => s.Row.Definition.Type != TupleDefinitionType.WixAction && s.Row.Definition.Type != TupleDefinitionType.WixVariable).ToList();
29 if (0 < illegalDuplicates.Count) 30 if (0 < illegalDuplicates.Count)
30 { 31 {
31 HashSet<Section> referencedSections = new HashSet<Section>(resolvedSections); 32 var referencedSections = new HashSet<IntermediateSection>(this.ResolvedSections);
33
32 foreach (Symbol referencedDuplicateSymbol in illegalDuplicates.Where(s => referencedSections.Contains(s.Section))) 34 foreach (Symbol referencedDuplicateSymbol in illegalDuplicates.Where(s => referencedSections.Contains(s.Section)))
33 { 35 {
34 List<Symbol> actuallyReferencedDuplicateSymbols = referencedDuplicateSymbol.PossiblyConflictingSymbols.Where(s => referencedSections.Contains(s.Section)).ToList(); 36 List<Symbol> actuallyReferencedDuplicateSymbols = referencedDuplicateSymbol.PossiblyConflictingSymbols.Where(s => referencedSections.Contains(s.Section)).ToList();
diff --git a/src/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
index 5a985f3f..9c3b2765 100644
--- a/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
+++ b/src/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -4,22 +4,21 @@ namespace WixToolset.Link
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Linq; 7 using System.Linq;
9 using WixToolset.Data; 8 using WixToolset.Data;
10 using WixToolset.Data.Rows; 9 using WixToolset.Data.Tuples;
11 10
12 /// <summary> 11 /// <summary>
13 /// Resolves all the simple references in a section. 12 /// Resolves all the simple references in a section.
14 /// </summary> 13 /// </summary>
15 internal class ResolveReferencesCommand : ICommand 14 internal class ResolveReferencesCommand : ICommand
16 { 15 {
17 private Section entrySection; 16 private IntermediateSection entrySection;
18 private IDictionary<string, Symbol> symbols; 17 private IDictionary<string, Symbol> symbols;
19 private HashSet<Symbol> referencedSymbols; 18 private HashSet<Symbol> referencedSymbols;
20 private HashSet<Section> resolvedSections; 19 private HashSet<IntermediateSection> resolvedSections;
21 20
22 public ResolveReferencesCommand(Section entrySection, IDictionary<string, Symbol> symbols) 21 public ResolveReferencesCommand(IntermediateSection entrySection, IDictionary<string, Symbol> symbols)
23 { 22 {
24 this.entrySection = entrySection; 23 this.entrySection = entrySection;
25 this.symbols = symbols; 24 this.symbols = symbols;
@@ -29,14 +28,14 @@ namespace WixToolset.Link
29 28
30 public IEnumerable<Symbol> ReferencedSymbols { get { return this.referencedSymbols; } } 29 public IEnumerable<Symbol> ReferencedSymbols { get { return this.referencedSymbols; } }
31 30
32 public IEnumerable<Section> ResolvedSections { get { return this.resolvedSections; } } 31 public IEnumerable<IntermediateSection> ResolvedSections { get { return this.resolvedSections; } }
33 32
34 /// <summary> 33 /// <summary>
35 /// Resolves all the simple references in a section. 34 /// Resolves all the simple references in a section.
36 /// </summary> 35 /// </summary>
37 public void Execute() 36 public void Execute()
38 { 37 {
39 this.resolvedSections = new HashSet<Section>(); 38 this.resolvedSections = new HashSet<IntermediateSection>();
40 this.referencedSymbols = new HashSet<Symbol>(); 39 this.referencedSymbols = new HashSet<Symbol>();
41 40
42 this.RecursivelyResolveReferences(this.entrySection); 41 this.RecursivelyResolveReferences(this.entrySection);
@@ -47,7 +46,7 @@ namespace WixToolset.Link
47 /// </summary> 46 /// </summary>
48 /// <param name="section">Section with references to resolve.</param> 47 /// <param name="section">Section with references to resolve.</param>
49 /// <remarks>Note: recursive function.</remarks> 48 /// <remarks>Note: recursive function.</remarks>
50 private void RecursivelyResolveReferences(Section section) 49 private void RecursivelyResolveReferences(IntermediateSection section)
51 { 50 {
52 // If we already resolved this section, move on to the next. 51 // If we already resolved this section, move on to the next.
53 if (!this.resolvedSections.Add(section)) 52 if (!this.resolvedSections.Add(section))
@@ -59,59 +58,53 @@ namespace WixToolset.Link
59 // symbols provided. Then recursively call this method to process the 58 // symbols provided. Then recursively call this method to process the
60 // located symbol's section. All in all this is a very simple depth-first 59 // located symbol's section. All in all this is a very simple depth-first
61 // search of the references per-section. 60 // search of the references per-section.
62 Table wixSimpleReferenceTable; 61 foreach (var wixSimpleReferenceRow in section.Tuples.OfType<WixSimpleReferenceTuple>())
63 if (section.Tables.TryGetTable("WixSimpleReference", out wixSimpleReferenceTable))
64 { 62 {
65 foreach (WixSimpleReferenceRow wixSimpleReferenceRow in wixSimpleReferenceTable.Rows) 63 // If we're building a Merge Module, ignore all references to the Media table
64 // because Merge Modules don't have Media tables.
65 if (this.BuildingMergeModule && wixSimpleReferenceRow.Definition.Type == TupleDefinitionType.Media)
66 { 66 {
67 Debug.Assert(wixSimpleReferenceRow.Section == section); 67 continue;
68 }
68 69
69 // If we're building a Merge Module, ignore all references to the Media table 70 if (!this.symbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out var symbol))
70 // because Merge Modules don't have Media tables. 71 {
71 if (this.BuildingMergeModule && "Media" == wixSimpleReferenceRow.TableName) 72 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName));
73 }
74 else // see if the symbol (and any of its duplicates) are appropriately accessible.
75 {
76 IList<Symbol> accessible = DetermineAccessibleSymbols(section, symbol);
77 if (!accessible.Any())
72 { 78 {
73 continue; 79 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName, symbol.Access));
74 } 80 }
75 81 else if (1 == accessible.Count)
76 Symbol symbol;
77 if (!this.symbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out symbol))
78 { 82 {
79 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName)); 83 var accessibleSymbol = accessible[0];
84 this.referencedSymbols.Add(accessibleSymbol);
85
86 if (null != accessibleSymbol.Section)
87 {
88 RecursivelyResolveReferences(accessibleSymbol.Section);
89 }
80 } 90 }
81 else // see if the symbol (and any of its duplicates) are appropriately accessible. 91 else // display errors for the duplicate symbols.
82 { 92 {
83 IList<Symbol> accessible = DetermineAccessibleSymbols(section, symbol); 93 var accessibleSymbol = accessible[0];
84 if (!accessible.Any()) 94 var referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers.ToString();
95
96 if (String.IsNullOrEmpty(referencingSourceLineNumber))
85 { 97 {
86 Messaging.Instance.OnMessage(WixErrors.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName, symbol.Access)); 98 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name));
87 } 99 }
88 else if (1 == accessible.Count) 100 else
89 { 101 {
90 Symbol accessibleSymbol = accessible[0]; 102 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber));
91 this.referencedSymbols.Add(accessibleSymbol);
92
93 if (null != accessibleSymbol.Section)
94 {
95 RecursivelyResolveReferences(accessibleSymbol.Section);
96 }
97 } 103 }
98 else // display errors for the duplicate symbols. 104
105 foreach (Symbol accessibleDuplicate in accessible.Skip(1))
99 { 106 {
100 Symbol accessibleSymbol = accessible[0]; 107 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol2(accessibleDuplicate.Row.SourceLineNumbers));
101 string referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers.ToString();
102 if (String.IsNullOrEmpty(referencingSourceLineNumber))
103 {
104 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name));
105 }
106 else
107 {
108 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol(accessibleSymbol.Row.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber));
109 }
110
111 foreach (Symbol accessibleDuplicate in accessible.Skip(1))
112 {
113 Messaging.Instance.OnMessage(WixErrors.DuplicateSymbol2(accessibleDuplicate.Row.SourceLineNumbers));
114 }
115 } 108 }
116 } 109 }
117 } 110 }
@@ -124,7 +117,7 @@ namespace WixToolset.Link
124 /// <param name="referencingSection">Section referencing the symbol.</param> 117 /// <param name="referencingSection">Section referencing the symbol.</param>
125 /// <param name="symbol">Symbol being referenced.</param> 118 /// <param name="symbol">Symbol being referenced.</param>
126 /// <returns>List of symbols accessible by referencing section.</returns> 119 /// <returns>List of symbols accessible by referencing section.</returns>
127 private IList<Symbol> DetermineAccessibleSymbols(Section referencingSection, Symbol symbol) 120 private IList<Symbol> DetermineAccessibleSymbols(IntermediateSection referencingSection, Symbol symbol)
128 { 121 {
129 List<Symbol> symbols = new List<Symbol>(); 122 List<Symbol> symbols = new List<Symbol>();
130 123
@@ -158,20 +151,20 @@ namespace WixToolset.Link
158 /// <param name="referencingSection">Section referencing the symbol.</param> 151 /// <param name="referencingSection">Section referencing the symbol.</param>
159 /// <param name="symbol">Symbol being referenced.</param> 152 /// <param name="symbol">Symbol being referenced.</param>
160 /// <returns>True if symbol is accessible.</returns> 153 /// <returns>True if symbol is accessible.</returns>
161 private bool AccessibleSymbol(Section referencingSection, Symbol symbol) 154 private bool AccessibleSymbol(IntermediateSection referencingSection, Symbol symbol)
162 { 155 {
163 switch (symbol.Access) 156 switch (symbol.Access)
164 { 157 {
165 case AccessModifier.Public: 158 case AccessModifier.Public:
166 return true; 159 return true;
167 case AccessModifier.Internal: 160 case AccessModifier.Internal:
168 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId) || (null != symbol.Row.Section.LibraryId && symbol.Row.Section.LibraryId.Equals(referencingSection.LibraryId)); 161 return symbol.Section.CompilationId.Equals(referencingSection.CompilationId) || (null != symbol.Section.LibraryId && symbol.Section.LibraryId.Equals(referencingSection.LibraryId));
169 case AccessModifier.Protected: 162 case AccessModifier.Protected:
170 return symbol.Row.Section.IntermediateId.Equals(referencingSection.IntermediateId); 163 return symbol.Section.CompilationId.Equals(referencingSection.CompilationId);
171 case AccessModifier.Private: 164 case AccessModifier.Private:
172 return referencingSection == symbol.Section; 165 return referencingSection == symbol.Section;
173 default: 166 default:
174 throw new InvalidOperationException(); 167 throw new ArgumentOutOfRangeException(nameof(symbol.Access));
175 } 168 }
176 } 169 }
177 } 170 }
diff --git a/src/WixToolset.Core/Link/WixComplexReferenceTupleExtensions.cs b/src/WixToolset.Core/Link/WixComplexReferenceTupleExtensions.cs
new file mode 100644
index 00000000..80cafa50
--- /dev/null
+++ b/src/WixToolset.Core/Link/WixComplexReferenceTupleExtensions.cs
@@ -0,0 +1,73 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.Link
4{
5 using System;
6 using WixToolset.Data.Tuples;
7
8 internal static class WixComplexReferenceTupleExtensions
9 {
10 /// <summary>
11 /// Creates a shallow copy of the ComplexReference.
12 /// </summary>
13 /// <returns>A shallow copy of the ComplexReference.</returns>
14 public static WixComplexReferenceTuple Clone(this WixComplexReferenceTuple source)
15 {
16 var clone = new WixComplexReferenceTuple(source.SourceLineNumbers, source.Id);
17 clone.ParentType = source.ParentType;
18 clone.Parent = source.Parent;
19 clone.ParentLanguage = source.ParentLanguage;
20 clone.ChildType = source.ChildType;
21 clone.Child = source.Child;
22 clone.IsPrimary = source.IsPrimary;
23
24 return clone;
25 }
26
27 /// <summary>
28 /// Compares two complex references without considering the primary bit.
29 /// </summary>
30 /// <param name="obj">Complex reference to compare to.</param>
31 /// <returns>Zero if the objects are equivalent, negative number if the provided object is less, positive if greater.</returns>
32 public static int CompareToWithoutConsideringPrimary(this WixComplexReferenceTuple tuple, WixComplexReferenceTuple other)
33 {
34 var comparison = tuple.ChildType - other.ChildType;
35 if (0 == comparison)
36 {
37 comparison = String.Compare(tuple.Child, other.Child, StringComparison.Ordinal);
38 if (0 == comparison)
39 {
40 comparison = tuple.ParentType - other.ParentType;
41 if (0 == comparison)
42 {
43 string thisParentLanguage = null == tuple.ParentLanguage ? String.Empty : tuple.ParentLanguage;
44 string otherParentLanguage = null == other.ParentLanguage ? String.Empty : other.ParentLanguage;
45 comparison = String.Compare(thisParentLanguage, otherParentLanguage, StringComparison.Ordinal);
46 if (0 == comparison)
47 {
48 comparison = String.Compare(tuple.Parent, other.Parent, StringComparison.Ordinal);
49 }
50 }
51 }
52 }
53
54 return comparison;
55 }
56
57 /// <summary>
58 /// Changes all of the parent references to point to the passed in parent reference.
59 /// </summary>
60 /// <param name="parent">New parent complex reference.</param>
61 public static void Reparent(this WixComplexReferenceTuple tuple, WixComplexReferenceTuple parent)
62 {
63 tuple.Parent = parent.Parent;
64 tuple.ParentLanguage = parent.ParentLanguage;
65 tuple.ParentType = parent.ParentType;
66
67 if (!tuple.IsPrimary)
68 {
69 tuple.IsPrimary = parent.IsPrimary;
70 }
71 }
72 }
73}
diff --git a/src/WixToolset.Core/Link/WixGroupingOrdering.cs b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
index fc0ce43b..4dd1596c 100644
--- a/src/WixToolset.Core/Link/WixGroupingOrdering.cs
+++ b/src/WixToolset.Core/Link/WixGroupingOrdering.cs
@@ -12,13 +12,13 @@ namespace WixToolset.Link
12 using System.Text; 12 using System.Text;
13 using WixToolset.Extensibility; 13 using WixToolset.Extensibility;
14 using WixToolset.Data; 14 using WixToolset.Data;
15 using WixToolset.Data.Tuples;
15 16
16 /// <summary> 17 /// <summary>
17 /// Grouping and Ordering class of the WiX toolset. 18 /// Grouping and Ordering class of the WiX toolset.
18 /// </summary> 19 /// </summary>
19 internal sealed class WixGroupingOrdering : IMessageHandler 20 internal sealed class WixGroupingOrdering : IMessageHandler
20 { 21 {
21 private Output output;
22 private IMessageHandler messageHandler; 22 private IMessageHandler messageHandler;
23 private List<string> groupTypes; 23 private List<string> groupTypes;
24 private List<string> itemTypes; 24 private List<string> itemTypes;
@@ -34,9 +34,9 @@ namespace WixToolset.Link
34 /// <param name="messageHandler">Handler for any error messages.</param> 34 /// <param name="messageHandler">Handler for any error messages.</param>
35 /// <param name="groupTypes">Group types to include.</param> 35 /// <param name="groupTypes">Group types to include.</param>
36 /// <param name="itemTypes">Item types to include.</param> 36 /// <param name="itemTypes">Item types to include.</param>
37 public WixGroupingOrdering(Output output, IMessageHandler messageHandler) 37 public WixGroupingOrdering(IntermediateSection entrySections, IMessageHandler messageHandler)
38 { 38 {
39 this.output = output; 39 this.EntrySection = entrySections;
40 this.messageHandler = messageHandler; 40 this.messageHandler = messageHandler;
41 41
42 this.rowsUsed = new List<int>(); 42 this.rowsUsed = new List<int>();
@@ -44,6 +44,8 @@ namespace WixToolset.Link
44 this.encounteredError = false; 44 this.encounteredError = false;
45 } 45 }
46 46
47 private IntermediateSection EntrySection { get; }
48
47 /// <summary> 49 /// <summary>
48 /// Switches a WixGroupingOrdering object to operate on a new set of groups/items. 50 /// Switches a WixGroupingOrdering object to operate on a new set of groups/items.
49 /// </summary> 51 /// </summary>
@@ -91,8 +93,7 @@ namespace WixToolset.Link
91 { 93 {
92 Debug.Assert(this.groupTypes.Contains(parentType)); 94 Debug.Assert(this.groupTypes.Contains(parentType));
93 95
94 List<Item> orderedItems; 96 this.CreateOrderedList(parentType, parentId, out var orderedItems);
95 this.CreateOrderedList(parentType, parentId, out orderedItems);
96 if (this.encounteredError) 97 if (this.encounteredError)
97 { 98 {
98 return; 99 return;
@@ -170,15 +171,16 @@ namespace WixToolset.Link
170 /// </summary> 171 /// </summary>
171 public void RemoveUsedGroupRows() 172 public void RemoveUsedGroupRows()
172 { 173 {
173 List<int> sortedIndexes = this.rowsUsed.Distinct().OrderByDescending(i => i).ToList(); 174 var sortedIndexes = this.rowsUsed.Distinct().OrderByDescending(i => i).ToList();
174 175
175 Table wixGroupTable = this.output.Tables["WixGroup"]; 176 //Table wixGroupTable = this.output.Tables["WixGroup"];
176 Debug.Assert(null != wixGroupTable); 177 //Debug.Assert(null != wixGroupTable);
177 Debug.Assert(sortedIndexes[0] < wixGroupTable.Rows.Count); 178 //Debug.Assert(sortedIndexes[0] < wixGroupTable.Rows.Count);
178 179
179 foreach (int rowIndex in sortedIndexes) 180 foreach (int rowIndex in sortedIndexes)
180 { 181 {
181 wixGroupTable.Rows.RemoveAt(rowIndex); 182 //wixGroupTable.Rows.RemoveAt(rowIndex);
183 this.EntrySection.Tuples.RemoveAt(rowIndex);
182 } 184 }
183 } 185 }
184 186
@@ -194,16 +196,15 @@ namespace WixToolset.Link
194 // does WiX (although they do, currently). We probably want to "upgrade" this to a new 196 // does WiX (although they do, currently). We probably want to "upgrade" this to a new
195 // table that includes a sequence number, and then change the code that uses ordered 197 // table that includes a sequence number, and then change the code that uses ordered
196 // groups to read from that table instead. 198 // groups to read from that table instead.
197 Table wixGroupTable = this.output.Tables["WixGroup"];
198 Debug.Assert(null != wixGroupTable);
199
200 foreach (Item item in orderedItems) 199 foreach (Item item in orderedItems)
201 { 200 {
202 Row row = wixGroupTable.CreateRow(item.Row.SourceLineNumbers); 201 var row = new WixGroupTuple(item.Row.SourceLineNumbers);
203 row[0] = parentId; 202 row.ParentId = parentId;
204 row[1] = parentType; 203 row.ParentType = (ComplexReferenceParentType)Enum.Parse(typeof(ComplexReferenceParentType), parentType);
205 row[2] = item.Id; 204 row.ChildId = item.Id;
206 row[3] = item.Type; 205 row.ChildType = (ComplexReferenceChildType)Enum.Parse(typeof(ComplexReferenceChildType), item.Type);
206
207 this.EntrySection.Tuples.Add(row);
207 } 208 }
208 } 209 }
209 210
@@ -254,47 +255,47 @@ namespace WixToolset.Link
254 /// </summary> 255 /// </summary>
255 private void LoadGroups() 256 private void LoadGroups()
256 { 257 {
257 Table wixGroupTable = this.output.Tables["WixGroup"]; 258 //Table wixGroupTable = this.output.Tables["WixGroup"];
258 if (null == wixGroupTable || 0 == wixGroupTable.Rows.Count) 259 //if (null == wixGroupTable || 0 == wixGroupTable.Rows.Count)
259 { 260 //{
260 // TODO: Change message name to make it *not* Bundle specific? 261 // // TODO: Change message name to make it *not* Bundle specific?
261 this.OnMessage(WixErrors.MissingBundleInformation("WixGroup")); 262 // this.OnMessage(WixErrors.MissingBundleInformation("WixGroup"));
262 } 263 //}
263 264
264 // Collect all of the groups 265 // Collect all of the groups
265 for (int rowIndex = 0; rowIndex < wixGroupTable.Rows.Count; ++rowIndex) 266 for (int rowIndex = 0; rowIndex < this.EntrySection.Tuples.Count; ++rowIndex)
266 { 267 {
267 Row row = wixGroupTable.Rows[rowIndex]; 268 if (this.EntrySection.Tuples[rowIndex] is WixGroupTuple row)
268 string rowParentName = (string)row[0];
269 string rowParentType = (string)row[1];
270 string rowChildName = (string)row[2];
271 string rowChildType = (string)row[3];
272
273 // If this row specifies a parent or child type that's not in our
274 // lists, we assume it's not a row that we're concerned about.
275 if (!this.groupTypes.Contains(rowParentType) ||
276 !this.itemTypes.Contains(rowChildType))
277 { 269 {
278 continue; 270 var rowParentName = row.ParentId;
279 } 271 var rowParentType = row.ParentType.ToString();
272 var rowChildName = row.ChildId;
273 var rowChildType = row.ChildType.ToString();
274
275 // If this row specifies a parent or child type that's not in our
276 // lists, we assume it's not a row that we're concerned about.
277 if (!this.groupTypes.Contains(rowParentType) ||
278 !this.itemTypes.Contains(rowChildType))
279 {
280 continue;
281 }
280 282
281 this.rowsUsed.Add(rowIndex); 283 this.rowsUsed.Add(rowIndex);
282 284
283 Item parentItem; 285 if (!this.items.TryGetValue(rowParentType, rowParentName, out var parentItem))
284 if (!this.items.TryGetValue(rowParentType, rowParentName, out parentItem)) 286 {
285 { 287 parentItem = new Item(row, rowParentType, rowParentName);
286 parentItem = new Item(row, rowParentType, rowParentName); 288 this.items.Add(parentItem);
287 this.items.Add(parentItem); 289 }
288 }
289 290
290 Item childItem; 291 if (!this.items.TryGetValue(rowChildType, rowChildName, out var childItem))
291 if (!this.items.TryGetValue(rowChildType, rowChildName, out childItem)) 292 {
292 { 293 childItem = new Item(row, rowChildType, rowChildName);
293 childItem = new Item(row, rowChildType, rowChildName); 294 this.items.Add(childItem);
294 this.items.Add(childItem); 295 }
295 }
296 296
297 parentItem.ChildItems.Add(childItem); 297 parentItem.ChildItems.Add(childItem);
298 }
298 } 299 }
299 } 300 }
300 301
@@ -374,19 +375,19 @@ namespace WixToolset.Link
374 /// </summary> 375 /// </summary>
375 private void LoadOrdering() 376 private void LoadOrdering()
376 { 377 {
377 Table wixOrderingTable = output.Tables["WixOrdering"]; 378 //Table wixOrderingTable = output.Tables["WixOrdering"];
378 if (null == wixOrderingTable || 0 == wixOrderingTable.Rows.Count) 379 //if (null == wixOrderingTable || 0 == wixOrderingTable.Rows.Count)
379 { 380 //{
380 // TODO: Do we need a message here? 381 // // TODO: Do we need a message here?
381 return; 382 // return;
382 } 383 //}
383 384
384 foreach (Row row in wixOrderingTable.Rows) 385 foreach (var row in this.EntrySection.Tuples.OfType<WixOrderingTuple>())
385 { 386 {
386 string rowItemType = (string)row[0]; 387 var rowItemType = row.ItemType;
387 string rowItemName = (string)row[1]; 388 var rowItemName = row.ItemId_;
388 string rowDependsOnType = (string)row[2]; 389 var rowDependsOnType = row.DependsOnType;
389 string rowDependsOnName = (string)row[3]; 390 var rowDependsOnName = row.DependsOnId_;
390 391
391 // If this row specifies some other (unknown) type in either 392 // If this row specifies some other (unknown) type in either
392 // position, we assume it's not a row that we're concerned about. 393 // position, we assume it's not a row that we're concerned about.
@@ -397,15 +398,12 @@ namespace WixToolset.Link
397 continue; 398 continue;
398 } 399 }
399 400
400 Item item = null; 401 if (!this.items.TryGetValue(rowItemType, rowItemName, out var item))
401 Item dependsOn = null;
402
403 if (!this.items.TryGetValue(rowItemType, rowItemName, out item))
404 { 402 {
405 this.OnMessage(WixErrors.IdentifierNotFound(rowItemType, rowItemName)); 403 this.OnMessage(WixErrors.IdentifierNotFound(rowItemType, rowItemName));
406 } 404 }
407 405
408 if (!this.items.TryGetValue(rowDependsOnType, rowDependsOnName, out dependsOn)) 406 if (!this.items.TryGetValue(rowDependsOnType, rowDependsOnName, out var dependsOn))
409 { 407 {
410 this.OnMessage(WixErrors.IdentifierNotFound(rowDependsOnType, rowDependsOnName)); 408 this.OnMessage(WixErrors.IdentifierNotFound(rowDependsOnType, rowDependsOnName));
411 } 409 }
@@ -540,7 +538,7 @@ namespace WixToolset.Link
540 private ItemCollection beforeItems; // for checking for circular references 538 private ItemCollection beforeItems; // for checking for circular references
541 private bool flattenedAfterItems; 539 private bool flattenedAfterItems;
542 540
543 public Item(Row row, string type, string id) 541 public Item(IntermediateTuple row, string type, string id)
544 { 542 {
545 this.Row = row; 543 this.Row = row;
546 this.Type = type; 544 this.Type = type;
@@ -553,7 +551,7 @@ namespace WixToolset.Link
553 flattenedAfterItems = false; 551 flattenedAfterItems = false;
554 } 552 }
555 553
556 public Row Row { get; private set; } 554 public IntermediateTuple Row { get; private set; }
557 public string Type { get; private set; } 555 public string Type { get; private set; }
558 public string Id { get; private set; } 556 public string Id { get; private set; }
559 public string Key { get; private set; } 557 public string Key { get; private set; }
@@ -718,7 +716,7 @@ namespace WixToolset.Link
718 return -1; 716 return -1;
719 } 717 }
720 718
721 return string.CompareOrdinal(x.Id, y.Id); 719 return String.CompareOrdinal(x.Id, y.Id);
722 } 720 }
723 } 721 }
724 } 722 }