aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2024-03-08 13:58:11 -0800
committerRob Mensching <rob@firegiant.com>2024-03-08 15:05:00 -0800
commit6a3a695b5ac3758fbf4a7836f65d7f85fdd121e0 (patch)
tree8e21f7eab15c0fe2ad91bc0ec06a5c147d9f4c2d
parentd8c4f61e080a3950faf8fb46b0a5c849988fec8e (diff)
downloadwix-6a3a695b5ac3758fbf4a7836f65d7f85fdd121e0.tar.gz
wix-6a3a695b5ac3758fbf4a7836f65d7f85fdd121e0.tar.bz2
wix-6a3a695b5ac3758fbf4a7836f65d7f85fdd121e0.zip
Ensure virtual symbols are included when overridden but not referenced
-rw-r--r--src/ext/UI/test/WixToolsetTest.UI/UIExtensionFixture.cs12
-rw-r--r--src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs47
-rw-r--r--src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs159
-rw-r--r--src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs92
-rw-r--r--src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs63
-rw-r--r--src/wix/WixToolset.Core/Linker.cs9
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs25
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs15
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs7
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicateCrossFragmentReference.wxs4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/OverrideVirtualSymbolWithFragments.wxs13
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolThatDoesNotGetOverridden.wxs30
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Variables/Package.wxs2
13 files changed, 322 insertions, 156 deletions
diff --git a/src/ext/UI/test/WixToolsetTest.UI/UIExtensionFixture.cs b/src/ext/UI/test/WixToolsetTest.UI/UIExtensionFixture.cs
index 610fd5ed..bcd7654f 100644
--- a/src/ext/UI/test/WixToolsetTest.UI/UIExtensionFixture.cs
+++ b/src/ext/UI/test/WixToolsetTest.UI/UIExtensionFixture.cs
@@ -125,7 +125,7 @@ namespace WixToolsetTest.UI
125 }, results.Where(result => result.StartsWith("ControlEvent:") && result.Contains("DoAction")).ToArray()); 125 }, results.Where(result => result.StartsWith("ControlEvent:") && result.Contains("DoAction")).ToArray());
126 } 126 }
127 127
128 [Fact(Skip = "Linker problem")] 128 [Fact]
129 public void CanBuildUsingWixUIFeatureTree() 129 public void CanBuildUsingWixUIFeatureTree()
130 { 130 {
131 var folder = TestData.Get(@"TestData", "WixUI_FeatureTree"); 131 var folder = TestData.Get(@"TestData", "WixUI_FeatureTree");
@@ -161,7 +161,7 @@ namespace WixToolsetTest.UI
161 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray()); 161 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray());
162 } 162 }
163 163
164 [Fact(Skip = "Linker problem")] 164 [Fact]
165 public void CanBuildWithWixUIInstallDirWithCustomizedEula() 165 public void CanBuildWithWixUIInstallDirWithCustomizedEula()
166 { 166 {
167 var folder = TestData.Get(@"TestData", "WixUI_InstallDir"); 167 var folder = TestData.Get(@"TestData", "WixUI_InstallDir");
@@ -274,7 +274,7 @@ namespace WixToolsetTest.UI
274 } 274 }
275 } 275 }
276 276
277 [Fact(Skip = "Linker problem")] 277 [Fact]
278 public void CanBuildUsingWixUIMondo() 278 public void CanBuildUsingWixUIMondo()
279 { 279 {
280 var folder = TestData.Get(@"TestData", "WixUI_Mondo"); 280 var folder = TestData.Get(@"TestData", "WixUI_Mondo");
@@ -307,7 +307,7 @@ namespace WixToolsetTest.UI
307 }, results.Where(result => result.StartsWith("ControlEvent:") && result.Contains("DoAction")).ToArray()); 307 }, results.Where(result => result.StartsWith("ControlEvent:") && result.Contains("DoAction")).ToArray());
308 WixAssert.CompareLineByLine(new[] 308 WixAssert.CompareLineByLine(new[]
309 { 309 {
310 "InstallUISequence:WelcomeDlg\tInstalled AND PATCH\t1295", 310 "InstallUISequence:WelcomeDlg\tNOT Installed OR PATCH\t1297",
311 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray()); 311 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray());
312 } 312 }
313 313
@@ -325,7 +325,7 @@ namespace WixToolsetTest.UI
325 }, results.Where(s => s.StartsWith("Control:ErrorDlg\tY")).Select(s => s.Split('\t')[9]).ToArray()); 325 }, results.Where(s => s.StartsWith("Control:ErrorDlg\tY")).Select(s => s.Split('\t')[9]).ToArray());
326 } 326 }
327 327
328 [Fact(Skip = "Linker problem")] 328 [Fact]
329 public void CanBuildWithInstallDirAndRemovedDialog() 329 public void CanBuildWithInstallDirAndRemovedDialog()
330 { 330 {
331 var folder = TestData.Get(@"TestData", "InstallDir_NoLicense"); 331 var folder = TestData.Get(@"TestData", "InstallDir_NoLicense");
@@ -366,7 +366,7 @@ namespace WixToolsetTest.UI
366 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray()); 366 }, results.Where(r => r.StartsWith("InstallUISequence:AdvancedWelcome") || r.StartsWith("InstallUISequence:Welcome")).ToArray());
367 } 367 }
368 368
369 [Fact(Skip = "Linker problem")] 369 [Fact]
370 public void CanBuildWithInstallDirAndAddedDialog() 370 public void CanBuildWithInstallDirAndAddedDialog()
371 { 371 {
372 var folder = TestData.Get(@"TestData", "InstallDir_SpecialDlg"); 372 var folder = TestData.Get(@"TestData", "InstallDir_SpecialDlg");
diff --git a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
index f9d6ab59..8eaac27d 100644
--- a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
@@ -45,10 +45,9 @@ namespace WixToolset.Core.Link
45 public ISet<IntermediateSymbol> IdenticalDirectorySymbols { get; private set; } 45 public ISet<IntermediateSymbol> IdenticalDirectorySymbols { get; private set; }
46 46
47 /// <summary> 47 /// <summary>
48 /// Gets the collection of overridden symbols that should not be included 48 /// Gets the collection of symbols that are marked as overrides.
49 /// in the final output.
50 /// </summary> 49 /// </summary>
51 public ISet<IntermediateSymbol> OverriddenSymbols { get; private set; } 50 public IReadOnlyCollection<SymbolWithSection> OverrideSymbols { get; private set; }
52 51
53 public void Execute() 52 public void Execute()
54 { 53 {
@@ -56,7 +55,6 @@ namespace WixToolset.Core.Link
56 var possibleConflicts = new HashSet<SymbolWithSection>(); 55 var possibleConflicts = new HashSet<SymbolWithSection>();
57 var identicalDirectorySymbols = new HashSet<IntermediateSymbol>(); 56 var identicalDirectorySymbols = new HashSet<IntermediateSymbol>();
58 var overrideSymbols = new List<SymbolWithSection>(); 57 var overrideSymbols = new List<SymbolWithSection>();
59 var overriddenSymbols = new HashSet<IntermediateSymbol>();
60 58
61 if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType)) 59 if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType))
62 { 60 {
@@ -92,34 +90,15 @@ namespace WixToolset.Core.Link
92 90
93 if (!symbolsByName.TryGetValue(fullName, out var existingSymbol)) 91 if (!symbolsByName.TryGetValue(fullName, out var existingSymbol))
94 { 92 {
95 if (symbolWithSection.Access == AccessModifier.Override)
96 {
97 overrideSymbols.Add(symbolWithSection);
98 }
99
100 symbolsByName.Add(fullName, symbolWithSection); 93 symbolsByName.Add(fullName, symbolWithSection);
101 } 94 }
102 else // uh-oh, duplicate symbols. 95 else // duplicate symbol ids MAY be a problem, but not always (e.g. identical directories and virtual symbols that get overridden) so we do NOT report errors here.
103 { 96 {
104 if (AccessModifier.Virtual == existingSymbol.Access && AccessModifier.Override == symbolWithSection.Access)
105 {
106 symbolWithSection.OverrideVirtualSymbol(existingSymbol);
107 symbolsByName[fullName] = symbolWithSection; // replace the virtual symbol with the override symbol.
108
109 overrideSymbols.Add(symbolWithSection);
110 overriddenSymbols.Add(existingSymbol.Symbol);
111 }
112 else if (AccessModifier.Override == existingSymbol.Access && AccessModifier.Virtual == symbolWithSection.Access)
113 {
114 existingSymbol.OverrideVirtualSymbol(symbolWithSection);
115
116 overriddenSymbols.Add(symbolWithSection.Symbol);
117 }
118 // If the duplicate symbols are both private directories, there is a chance that they 97 // If the duplicate symbols are both private directories, there is a chance that they
119 // point to identical symbols. Identical directory symbols are redundant and will not cause 98 // point to identical symbols. Identical directory symbols are redundant and will not cause
120 // conflicts. 99 // conflicts.
121 else if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access && 100 if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access &&
122 SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol)) 101 SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol))
123 { 102 {
124 // Ensure identical symbols are tracked to ensure that only one symbol will end up in linked intermediate. 103 // Ensure identical symbols are tracked to ensure that only one symbol will end up in linked intermediate.
125 identicalDirectorySymbols.Add(existingSymbol.Symbol); 104 identicalDirectorySymbols.Add(existingSymbol.Symbol);
@@ -129,25 +108,21 @@ namespace WixToolset.Core.Link
129 { 108 {
130 symbolWithSection.AddPossibleConflict(existingSymbol); 109 symbolWithSection.AddPossibleConflict(existingSymbol);
131 existingSymbol.AddPossibleConflict(symbolWithSection); 110 existingSymbol.AddPossibleConflict(symbolWithSection);
132 possibleConflicts.Add(symbolWithSection); 111 possibleConflicts.Add(existingSymbol);
133 } 112 }
134 } 113 }
135 }
136 }
137 114
138 // Ensure override symbols actually overrode a virtual symbol. 115 if (symbolWithSection.Access == AccessModifier.Override)
139 foreach (var symbolWithSection in overrideSymbols) 116 {
140 { 117 overrideSymbols.Add(symbolWithSection);
141 if (symbolWithSection.Overrides is null) 118 }
142 {
143 this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(symbolWithSection.Symbol));
144 } 119 }
145 } 120 }
146 121
147 this.SymbolsByName = symbolsByName; 122 this.SymbolsByName = symbolsByName;
148 this.PossibleConflicts = possibleConflicts; 123 this.PossibleConflicts = possibleConflicts;
149 this.IdenticalDirectorySymbols = identicalDirectorySymbols; 124 this.IdenticalDirectorySymbols = identicalDirectorySymbols;
150 this.OverriddenSymbols = overriddenSymbols; 125 this.OverrideSymbols = overrideSymbols;
151 } 126 }
152 } 127 }
153} 128}
diff --git a/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs
new file mode 100644
index 00000000..556a24d7
--- /dev/null
+++ b/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs
@@ -0,0 +1,159 @@
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.Collections.Generic;
6 using System.Linq;
7 using WixToolset.Data;
8 using WixToolset.Data.Symbols;
9 using WixToolset.Extensibility.Services;
10
11 internal class ProcessConflictingSymbolsCommand
12 {
13 public ProcessConflictingSymbolsCommand(IMessaging messaging, IReadOnlyCollection<SymbolWithSection> possibleConflicts, IReadOnlyCollection<SymbolWithSection> overrideSymbols, ISet<IntermediateSection> resolvedSections)
14 {
15 this.Messaging = messaging;
16 this.PossibleConflicts = possibleConflicts;
17 this.OverrideSymbols = overrideSymbols;
18 this.ResolvedSections = resolvedSections;
19 }
20
21 private IMessaging Messaging { get; }
22
23 private IReadOnlyCollection<SymbolWithSection> PossibleConflicts { get; }
24
25 private ISet<IntermediateSection> ResolvedSections { get; }
26
27 private IReadOnlyCollection<SymbolWithSection> OverrideSymbols { get; }
28
29 /// <summary>
30 /// Gets the collection of overridden symbols that should not be included
31 /// in the final output.
32 /// </summary>
33 public ISet<IntermediateSymbol> OverriddenSymbols { get; private set; }
34
35 public void Execute()
36 {
37 var overriddenSymbols = new HashSet<IntermediateSymbol>();
38
39 foreach (var symbolWithConflicts in this.PossibleConflicts)
40 {
41 var conflicts = YieldReferencedConflicts(symbolWithConflicts, this.ResolvedSections).ToList();
42
43 if (conflicts.Count > 1)
44 {
45 IEnumerable<SymbolWithSection> reportDuplicates;
46
47 var virtualConflicts = conflicts.Where(s => s.Access == AccessModifier.Virtual).ToList();
48
49 // No virtual symbols, just plain old duplicate errors. This is the easy case.
50 if (virtualConflicts.Count == 0)
51 {
52 var first = conflicts[0];
53 reportDuplicates = conflicts.Skip(1);
54
55 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
56
57 this.Messaging.Write(LinkerErrors.DuplicateSymbol(first.Symbol, referencingSourceLineNumber));
58 }
59 else // there are virtual symbols, which complicates conflict resolution and may not be an error at all.
60 {
61 var firstVirtualSymbol = virtualConflicts[0];
62 var overrideConflicts = conflicts.Where(s => s.Access == AccessModifier.Override).ToList();
63
64 // If there is a single virtual symbol, there may be a single override symbol to make this a success case.
65 // All other scenarios are errors.
66 if (virtualConflicts.Count == 1)
67 {
68 var otherConflicts = conflicts.Where(s => s.Access != AccessModifier.Virtual && s.Access != AccessModifier.Override).ToList();
69
70 if (otherConflicts.Count > 0)
71 {
72 var first = otherConflicts[0];
73 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
74
75 reportDuplicates = virtualConflicts;
76
77 switch (first.Symbol)
78 {
79 case WixActionSymbol action:
80 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(action));
81 break;
82 default:
83 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(first.Symbol, referencingSourceLineNumber));
84 break;
85 }
86 }
87 else if (overrideConflicts.Count > 1) // multiple overrides report as normal duplicates.
88 {
89 var first = overrideConflicts[0];
90 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
91
92 reportDuplicates = overrideConflicts.Skip(1);
93
94 this.Messaging.Write(LinkerErrors.DuplicateSymbol(first.Symbol, referencingSourceLineNumber));
95 }
96 else // the single virtual symbol is overridden by a single override symbol. This is a success case.
97 {
98 var overrideSymbol = overrideConflicts[0];
99
100 overriddenSymbols.Add(firstVirtualSymbol.Symbol);
101
102 reportDuplicates = Enumerable.Empty<SymbolWithSection>();
103 }
104 }
105 else // multiple symbols are virtual, use the duplicate virtual symbol message.
106 {
107 var first = virtualConflicts[0];
108 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
109
110 reportDuplicates = virtualConflicts.Skip(1);
111
112 this.Messaging.Write(LinkerErrors.DuplicateVirtualSymbol(first.Symbol, referencingSourceLineNumber));
113 }
114
115 // Always point the override symbols at the first virtual symbol to prevent error being reported about missing overrides.
116 // There may have been errors reported above, but there was at least one virtual symbol to satisfy the overrides so we
117 // don't want extra errors in this case.
118 foreach (var overrideSymbol in overrideConflicts)
119 {
120 overrideSymbol.OverrideVirtualSymbol(firstVirtualSymbol);
121 }
122 }
123
124 foreach (var duplicate in reportDuplicates)
125 {
126 this.Messaging.Write(LinkerErrors.DuplicateSymbol2(duplicate.Symbol));
127 }
128 }
129 }
130
131 // Ensure referenced override symbols actually overrode a virtual symbol.
132 foreach (var referencedOverrideSymbol in this.OverrideSymbols.Where(s => this.ResolvedSections.Contains(s.Section)))
133 {
134 if (referencedOverrideSymbol.Overrides is null)
135 {
136 this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(referencedOverrideSymbol.Symbol));
137 }
138 }
139
140 this.OverriddenSymbols = overriddenSymbols;
141 }
142
143 private static IEnumerable<SymbolWithSection> YieldReferencedConflicts(SymbolWithSection symbolWithConflicts, ISet<IntermediateSection> resolvedSections)
144 {
145 if (resolvedSections.Contains(symbolWithConflicts.Section))
146 {
147 yield return symbolWithConflicts;
148 }
149
150 foreach (var possibleConflict in symbolWithConflicts.PossiblyConflicts)
151 {
152 if (resolvedSections.Contains(possibleConflict.Section))
153 {
154 yield return possibleConflict;
155 }
156 }
157 }
158 }
159}
diff --git a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
deleted file mode 100644
index b1db46a2..00000000
--- a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
+++ /dev/null
@@ -1,92 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.Link
4{
5 using System.Collections.Generic;
6 using System.Linq;
7 using WixToolset.Data;
8 using WixToolset.Data.Symbols;
9 using WixToolset.Extensibility.Services;
10
11 internal class ReportConflictingSymbolsCommand
12 {
13 public ReportConflictingSymbolsCommand(IMessaging messaging, IReadOnlyCollection<SymbolWithSection> possibleConflicts, ISet<IntermediateSection> resolvedSections)
14 {
15 this.Messaging = messaging;
16 this.PossibleConflicts = possibleConflicts;
17 this.ResolvedSections = resolvedSections;
18 }
19
20 private IMessaging Messaging { get; }
21
22 private IReadOnlyCollection<SymbolWithSection> PossibleConflicts { get; }
23
24 private ISet<IntermediateSection> ResolvedSections { get; }
25
26 public void Execute()
27 {
28 // Do a quick check if there are any possibly conflicting symbols. Hopefully the symbols with possible conflicts
29 // list is a very short list (empty should be the most common).
30 //
31 // If we have conflicts then we'll do a more costly check to see if the possible conflicting
32 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate
33 // (aka: conflicting) symbol.
34 if (0 < this.PossibleConflicts.Count)
35 {
36 foreach (var referencedDuplicate in this.PossibleConflicts.Where(s => this.ResolvedSections.Contains(s.Section)))
37 {
38 var actuallyReferencedDuplicates = referencedDuplicate.PossiblyConflicts.Where(s => this.ResolvedSections.Contains(s.Section)).ToList();
39
40 if (actuallyReferencedDuplicates.Count > 0)
41 {
42 var conflicts = actuallyReferencedDuplicates.Append(referencedDuplicate).ToList();
43 var virtualConflicts = conflicts.Where(s => s.Access == AccessModifier.Virtual).ToList();
44 var overrideConflicts = conflicts.Where(s => s.Access == AccessModifier.Override).ToList();
45 var otherConflicts = conflicts.Where(s => s.Access != AccessModifier.Virtual && s.Access != AccessModifier.Override).ToList();
46
47 IEnumerable<SymbolWithSection> reportDuplicates = actuallyReferencedDuplicates;
48
49 // If multiple symbols are virtual, use the duplicate virtual symbol message.
50 if (virtualConflicts.Count > 1)
51 {
52 var first = virtualConflicts[0];
53 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
54
55 reportDuplicates = virtualConflicts.Skip(1);
56
57 this.Messaging.Write(LinkerErrors.DuplicateVirtualSymbol(first.Symbol, referencingSourceLineNumber));
58 }
59 else if (virtualConflicts.Count == 1 && otherConflicts.Count > 0)
60 {
61 var first = otherConflicts[0];
62 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
63
64 reportDuplicates = virtualConflicts;
65
66 switch (first.Symbol)
67 {
68 case WixActionSymbol action:
69 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(action));
70 break;
71 default:
72 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(first.Symbol, referencingSourceLineNumber));
73 break;
74 }
75 }
76 else
77 {
78 var referencingSourceLineNumber = referencedDuplicate.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
79
80 this.Messaging.Write(LinkerErrors.DuplicateSymbol(referencedDuplicate.Symbol, referencingSourceLineNumber));
81 }
82
83 foreach (var duplicate in reportDuplicates)
84 {
85 this.Messaging.Write(LinkerErrors.DuplicateSymbol2(duplicate.Symbol));
86 }
87 }
88 }
89 }
90 }
91 }
92}
diff --git a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
index 0e7a7a52..7a3c502a 100644
--- a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
+++ b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -76,6 +76,7 @@ namespace WixToolset.Core.Link
76 if (this.symbolsWithSections.TryGetValue(reference.SymbolicName, out var symbolWithSection)) 76 if (this.symbolsWithSections.TryGetValue(reference.SymbolicName, out var symbolWithSection))
77 { 77 {
78 var accessible = this.DetermineAccessibleSymbols(section, symbolWithSection); 78 var accessible = this.DetermineAccessibleSymbols(section, symbolWithSection);
79
79 if (accessible.Count == 1) 80 if (accessible.Count == 1)
80 { 81 {
81 var accessibleSymbol = accessible[0]; 82 var accessibleSymbol = accessible[0];
@@ -91,7 +92,7 @@ namespace WixToolset.Core.Link
91 { 92 {
92 this.Messaging.Write(ErrorMessages.UnresolvedReference(reference.SourceLineNumbers, reference.SymbolicName, symbolWithSection.Access)); 93 this.Messaging.Write(ErrorMessages.UnresolvedReference(reference.SourceLineNumbers, reference.SymbolicName, symbolWithSection.Access));
93 } 94 }
94 else // multiple symbols referenced creates conflicting symbols. 95 else // multiple accessible symbols referenced creates conflicting symbols.
95 { 96 {
96 // Remember the direct reference to the symbol for the error reporting later, 97 // Remember the direct reference to the symbol for the error reporting later,
97 // but do NOT continue resolving references found in these conflicting symbols. 98 // but do NOT continue resolving references found in these conflicting symbols.
@@ -122,7 +123,7 @@ namespace WixToolset.Core.Link
122 } 123 }
123 124
124 /// <summary> 125 /// <summary>
125 /// Determine if the symbol and any of its duplicates are accessbile by referencing section. 126 /// Determine if the symbol and any of its duplicates are accessbile by the referencing section.
126 /// </summary> 127 /// </summary>
127 /// <param name="referencingSection">Section referencing the symbol.</param> 128 /// <param name="referencingSection">Section referencing the symbol.</param>
128 /// <param name="symbolWithSection">Symbol being referenced.</param> 129 /// <param name="symbolWithSection">Symbol being referenced.</param>
@@ -130,17 +131,48 @@ namespace WixToolset.Core.Link
130 private List<SymbolWithSection> DetermineAccessibleSymbols(IntermediateSection referencingSection, SymbolWithSection symbolWithSection) 131 private List<SymbolWithSection> DetermineAccessibleSymbols(IntermediateSection referencingSection, SymbolWithSection symbolWithSection)
131 { 132 {
132 var accessibleSymbols = new List<SymbolWithSection>(); 133 var accessibleSymbols = new List<SymbolWithSection>();
134 List<SymbolWithSection> virtualSymbols = null;
133 135
134 if (this.AccessibleSymbol(referencingSection, symbolWithSection)) 136 if (this.AccessibleSymbol(referencingSection, symbolWithSection))
135 { 137 {
136 accessibleSymbols.Add(symbolWithSection); 138 AddSymbolToCorrectCollection(symbolWithSection, accessibleSymbols, ref virtualSymbols);
137 } 139 }
138 140
139 foreach (var dupe in symbolWithSection.PossiblyConflicts) 141 foreach (var dupe in symbolWithSection.PossiblyConflicts)
140 { 142 {
141 if (this.AccessibleSymbol(referencingSection, dupe)) 143 if (this.AccessibleSymbol(referencingSection, dupe))
142 { 144 {
143 accessibleSymbols.Add(dupe); 145 AddSymbolToCorrectCollection(dupe, accessibleSymbols, ref virtualSymbols);
146 }
147 }
148
149 // If a virtual symbol is accessible we have some extra work to do.
150 if (virtualSymbols != null)
151 {
152 // If there are multiple virtual symbols accessible that's an error case so add them all and
153 // they'll be reported as conflicts later.
154 if (virtualSymbols.Count > 1)
155 {
156 accessibleSymbols.AddRange(virtualSymbols);
157 }
158 else
159 {
160 var overrode = false;
161
162 // If there are any override symbols, skip adding the virtual symbols because they are "overridden".
163 foreach (var overrideSymbol in accessibleSymbols.Where(symbol => symbol.Access == AccessModifier.Override))
164 {
165 overrideSymbol.OverrideVirtualSymbol(virtualSymbols[0]);
166
167 overrode = true;
168 }
169
170 // If there are no overriding symbols, add the virtual symbol (there is only one but we'll add the collection just in case)
171 // so it will be reported as a conflict later.
172 if (!overrode)
173 {
174 accessibleSymbols.AddRange(virtualSymbols);
175 }
144 } 176 }
145 } 177 }
146 178
@@ -148,6 +180,29 @@ namespace WixToolset.Core.Link
148 } 180 }
149 181
150 /// <summary> 182 /// <summary>
183 /// Add a symbol to the correct collection based on its access.
184 /// </summary>
185 /// <param name="symbolWithSection"></param>
186 /// <param name="accessibleSymbols">Collection of non-virtual symbol that was accessible.</param>
187 /// <param name="virtualSymbols">Collection of virtual symbol that was accessible</param>
188 private static void AddSymbolToCorrectCollection(SymbolWithSection symbolWithSection, List<SymbolWithSection> accessibleSymbols, ref List<SymbolWithSection> virtualSymbols)
189 {
190 if (symbolWithSection.Access == AccessModifier.Virtual)
191 {
192 if (virtualSymbols == null)
193 {
194 virtualSymbols = new List<SymbolWithSection>();
195 }
196
197 virtualSymbols.Add(symbolWithSection);
198 }
199 else
200 {
201 accessibleSymbols.Add(symbolWithSection);
202 }
203 }
204
205 /// <summary>
151 /// Determine if a single symbol is accessible by the referencing section. 206 /// Determine if a single symbol is accessible by the referencing section.
152 /// </summary> 207 /// </summary>
153 /// <param name="referencingSection">Section referencing the symbol.</param> 208 /// <param name="referencingSection">Section referencing the symbol.</param>
diff --git a/src/wix/WixToolset.Core/Linker.cs b/src/wix/WixToolset.Core/Linker.cs
index a536c049..43a41eac 100644
--- a/src/wix/WixToolset.Core/Linker.cs
+++ b/src/wix/WixToolset.Core/Linker.cs
@@ -190,10 +190,13 @@ namespace WixToolset.Core
190 } 190 }
191 } 191 }
192 192
193 // Report duplicates that would ultimately end up being primary key collisions. 193 // Process conflicts that may be overridden virtual symbols (that's okay) or end up as primary key collisions (those need to be reported as errors).
194 ISet<IntermediateSymbol> overriddenSymbols;
194 { 195 {
195 var reportDupes = new ReportConflictingSymbolsCommand(this.Messaging, find.PossibleConflicts, resolve.ResolvedSections); 196 var reportDupes = new ProcessConflictingSymbolsCommand(this.Messaging, find.PossibleConflicts, find.OverrideSymbols, resolve.ResolvedSections);
196 reportDupes.Execute(); 197 reportDupes.Execute();
198
199 overriddenSymbols = reportDupes.OverriddenSymbols;
197 } 200 }
198 201
199 if (this.Messaging.EncounteredError) 202 if (this.Messaging.EncounteredError)
@@ -222,7 +225,7 @@ namespace WixToolset.Core
222 continue; 225 continue;
223 } 226 }
224 } 227 }
225 else if (find.OverriddenSymbols.Contains(symbol)) 228 else if (overriddenSymbols.Contains(symbol))
226 { 229 {
227 // Skip the symbols that were overridden. 230 // Skip the symbols that were overridden.
228 continue; 231 continue;
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
index 5e40114f..13eb15be 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
@@ -63,6 +63,18 @@ namespace WixToolsetTest.CoreIntegration
63 } 63 }
64 64
65 [Fact] 65 [Fact]
66 public void CanCompileVirtualSymbolThatDoesNotGetOverridden()
67 {
68 var dirSymbols = BuildToGetDirectorySymbols("TestData", "AccessModifier", "VirtualSymbolThatDoesNotGetOverridden.wxs");
69 WixAssert.CompareLineByLine(new[]
70 {
71 "virtual:ProgramFilesFolder:TARGETDIR:PFiles",
72 "virtual:TARGETDIR::SourceDir",
73 "virtual:TestFolder:ProgramFilesFolder:Used",
74 }, dirSymbols.OrderBy(d => d.Id.Id).Select(d => d.Id.Access.AsString() + ":" + d.Id.Id + ":" + d.ParentDirectoryRef + ":" + d.Name).ToArray());
75 }
76
77 [Fact]
66 public void CannotCompileInvalidCrossFragmentReference() 78 public void CannotCompileInvalidCrossFragmentReference()
67 { 79 {
68 var errors = BuildForFailure("TestData", "AccessModifier", "InvalidCrossFragmentReference.wxs"); 80 var errors = BuildForFailure("TestData", "AccessModifier", "InvalidCrossFragmentReference.wxs");
@@ -78,8 +90,9 @@ namespace WixToolsetTest.CoreIntegration
78 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicateCrossFragmentReference.wxs"); 90 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicateCrossFragmentReference.wxs");
79 WixAssert.CompareLineByLine(new[] 91 WixAssert.CompareLineByLine(new[]
80 { 92 {
81 "ln 12: Duplicate Directory with identifier 'TestFolder' referenced by <sourceFolder>\\DuplicateCrossFragmentReference.wxs(4). Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.", 93 "ln 8: Duplicate Directory with identifier 'TestFolder' referenced by <sourceFolder>\\DuplicateCrossFragmentReference.wxs(4). Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.",
82 "ln 8: Location of symbol related to previous error." 94 "ln 12: Location of symbol related to previous error.",
95 "ln 16: Location of symbol related to previous error."
83 }, errors); 96 }, errors);
84 } 97 }
85 98
@@ -99,8 +112,8 @@ namespace WixToolsetTest.CoreIntegration
99 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatedOverrideVirtualSymbol.wxs"); 112 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatedOverrideVirtualSymbol.wxs");
100 WixAssert.CompareLineByLine(new[] 113 WixAssert.CompareLineByLine(new[]
101 { 114 {
102 "ln 14: Duplicate Directory with identifier 'TestFolder' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 115 "ln 6: Duplicate Directory with identifier 'TestFolder' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
103 "ln 6: Location of symbol related to previous error." 116 "ln 14: Location of symbol related to previous error."
104 }, errors); 117 }, errors);
105 } 118 }
106 119
@@ -132,8 +145,8 @@ namespace WixToolsetTest.CoreIntegration
132 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatePublicOverrideVirtualSymbol.wxs"); 145 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatePublicOverrideVirtualSymbol.wxs");
133 WixAssert.CompareLineByLine(new[] 146 WixAssert.CompareLineByLine(new[]
134 { 147 {
135 "ln 14: Duplicate Directory with identifier 'TestFolder' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 148 "ln 14: The Directory symbol 'TestFolder' conflicts with a virtual symbol. Use the 'override' access modifier to override the virtual symbol or use a different Id to avoid the conflict.",
136 "ln 6: Location of symbol related to previous error." 149 "ln 10: Location of symbol related to previous error."
137 }, errors); 150 }, errors);
138 } 151 }
139 152
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs
index f735b53a..17c8db95 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs
@@ -4,9 +4,9 @@ namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System.IO; 5 using System.IO;
6 using System.Linq; 6 using System.Linq;
7 using WixInternal.Core.TestPackage;
7 using WixInternal.TestSupport; 8 using WixInternal.TestSupport;
8 using WixToolset.Core; 9 using WixToolset.Core;
9 using WixInternal.Core.TestPackage;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Symbols; 11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility.Data; 12 using WixToolset.Extensibility.Data;
@@ -101,7 +101,7 @@ namespace WixToolsetTest.CoreIntegration
101 [Fact] 101 [Fact]
102 public void VariableRedefinitionIsAWarning() 102 public void VariableRedefinitionIsAWarning()
103 { 103 {
104 var folder = TestData.Get(@"TestData\Variables"); 104 var folder = TestData.Get(@"TestData", "Variables");
105 105
106 using (var fs = new DisposableFileSystem()) 106 using (var fs = new DisposableFileSystem())
107 { 107 {
@@ -121,8 +121,15 @@ namespace WixToolsetTest.CoreIntegration
121 121
122 result.AssertSuccess(); 122 result.AssertSuccess();
123 123
124 var warning = result.Messages.Where(message => message.Id == (int)WarningMessages.Ids.VariableDeclarationCollision); 124 var warning = result.Messages.Where(m => m.Level == MessageLevel.Warning || m.Level == MessageLevel.Error)
125 Assert.Single(warning); 125 .Select(m => $"ln {m.SourceLineNumbers.LineNumber}: {m.Level.ToString().ToLowerInvariant()}: {m}".Replace(baseFolder, "<baseFolder>"))
126 .ToArray();
127 WixAssert.CompareLineByLine(new[]
128 {
129 "ln 5: warning: The variable 'Bar' with value 'Baz' was previously declared with value 'Bar'.",
130 "ln 24: warning: It is no longer necessary to define the standard directory 'TARGETDIR'. Use the StandardDirectory element instead.",
131 "ln 25: warning: It is no longer necessary to define the standard directory 'ProgramFilesFolder'. Use the StandardDirectory element instead."
132 }, warning);
126 } 133 }
127 } 134 }
128 135
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
index 9931a45a..5b357994 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
@@ -102,10 +102,9 @@ namespace WixToolsetTest.CoreIntegration
102 .ToArray(); 102 .ToArray();
103 WixAssert.CompareLineByLine(new[] 103 WixAssert.CompareLineByLine(new[]
104 { 104 {
105 "ln 8: Duplicate Registry with identifier 'regJnkjRU9YGaMJhQOqKmivWKf_VdY' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 105 "ln 7: Duplicate Registry with identifier 'regJnkjRU9YGaMJhQOqKmivWKf_VdY' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
106 "ln 7: Location of symbol related to previous error.", 106 "ln 8: Location of symbol related to previous error.",
107 "ln 9: Duplicate Registry with identifier 'regJnkjRU9YGaMJhQOqKmivWKf_VdY' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 107 "ln 9: Location of symbol related to previous error."
108 "ln 7: Location of symbol related to previous error."
109 }, errors); 108 }, errors);
110 } 109 }
111 } 110 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicateCrossFragmentReference.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicateCrossFragmentReference.wxs
index f987fe13..73678cb6 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicateCrossFragmentReference.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicateCrossFragmentReference.wxs
@@ -11,4 +11,8 @@
11 <Fragment> 11 <Fragment>
12 <Directory Id="TestFolder" Name="Dupe 2" /> 12 <Directory Id="TestFolder" Name="Dupe 2" />
13 </Fragment> 13 </Fragment>
14
15 <Fragment>
16 <Directory Id="TestFolder" Name="Dupe 3" />
17 </Fragment>
14</Wix> 18</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/OverrideVirtualSymbolWithFragments.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/OverrideVirtualSymbolWithFragments.wxs
index 13f03040..9afec003 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/OverrideVirtualSymbolWithFragments.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/OverrideVirtualSymbolWithFragments.wxs
@@ -2,9 +2,22 @@
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Package Name="Override Virtual Symbol With Fragments" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a"> 3 <Package Name="Override Virtual Symbol With Fragments" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 <DirectoryRef Id="TestFolder" /> 4 <DirectoryRef Id="TestFolder" />
5
6 <PropertyRef Id="A" />
7 <PropertyRef Id="B" />
5 </Package> 8 </Package>
6 9
7 <Fragment> 10 <Fragment>
11 <Property Id="A" Secure="true"/>
12 <DirectoryRef Id="TestFolder" />
13 </Fragment>
14
15 <Fragment>
16 <Property Id="B" Secure="true"/>
17 <DirectoryRef Id="TestFolder" />
18 </Fragment>
19
20 <Fragment>
8 <StandardDirectory Id="ProgramFilesFolder"> 21 <StandardDirectory Id="ProgramFilesFolder">
9 <Directory Id="override TestFolder" Name="Override Test Folder Includes Another" /> 22 <Directory Id="override TestFolder" Name="Override Test Folder Includes Another" />
10 <Directory Id="AlsoIncluded" Name="Also Included" /> 23 <Directory Id="AlsoIncluded" Name="Also Included" />
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolThatDoesNotGetOverridden.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolThatDoesNotGetOverridden.wxs
new file mode 100644
index 00000000..123b856d
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolThatDoesNotGetOverridden.wxs
@@ -0,0 +1,30 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Package Name="Override Without Virtual Symbol" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 <PropertyRef Id="DoesGetReferenced" />
5 </Package>
6
7 <Fragment>
8 <Property Id="DoesNotGetReferenced1" Value="Does Not Get Referenced" />
9
10 <StandardDirectory Id="ProgramFilesFolder">
11 <Directory Id="override TestFolder" Name="IsNotUsed1" />
12 </StandardDirectory>
13 </Fragment>
14
15 <Fragment>
16 <Property Id="DoesNotGetReferenced2" Value="Does Not Get Referenced" />
17
18 <StandardDirectory Id="ProgramFilesFolder">
19 <Directory Id="override TestFolder" Name="IsNotUsed2" />
20 </StandardDirectory>
21 </Fragment>
22
23 <Fragment>
24 <Property Id="DoesGetReferenced" Value="Is Referenced" />
25
26 <StandardDirectory Id="ProgramFilesFolder">
27 <Directory Id="virtual TestFolder" Name="Used" />
28 </StandardDirectory>
29 </Fragment>
30</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Variables/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Variables/Package.wxs
index bcbdb00f..d22676d4 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Variables/Package.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Variables/Package.wxs
@@ -22,7 +22,7 @@
22 22
23 <Fragment> 23 <Fragment>
24 <Directory Id="override TARGETDIR" Name="SourceDir"> 24 <Directory Id="override TARGETDIR" Name="SourceDir">
25 <Directory Id="override ProgramFilesFolder"> 25 <Directory Id="ProgramFilesFolder">
26 <Directory Id="INSTALLFOLDER" Name="MsiPackage" /> 26 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
27 </Directory> 27 </Directory>
28 </Directory> 28 </Directory>