aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs25
-rw-r--r--src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs4
-rw-r--r--src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs13
-rw-r--r--src/wix/WixToolset.Core/Link/SymbolWithSection.cs25
-rw-r--r--src/wix/WixToolset.Core/Linker.cs20
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs47
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Directory/RedundantSubdirectoryInSecondSection.wxs26
7 files changed, 108 insertions, 52 deletions
diff --git a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
index 908476db..496c6178 100644
--- a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
@@ -42,13 +42,13 @@ namespace WixToolset.Core.Link
42 /// Gets the collection of redundant symbols that should not be included 42 /// Gets the collection of redundant symbols that should not be included
43 /// in the final output. 43 /// in the final output.
44 /// </summary> 44 /// </summary>
45 public ISet<IntermediateSymbol> RedundantSymbols { get; private set; } 45 public ISet<IntermediateSymbol> IdenticalDirectorySymbols { get; private set; }
46 46
47 public void Execute() 47 public void Execute()
48 { 48 {
49 var symbolsByName = new Dictionary<string, SymbolWithSection>(); 49 var symbolsByName = new Dictionary<string, SymbolWithSection>();
50 var possibleConflicts = new HashSet<SymbolWithSection>(); 50 var possibleConflicts = new HashSet<SymbolWithSection>();
51 var redundantSymbols = new HashSet<IntermediateSymbol>(); 51 var identicalDirectorySymbols = new HashSet<IntermediateSymbol>();
52 52
53 if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType)) 53 if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType))
54 { 54 {
@@ -76,28 +76,27 @@ namespace WixToolset.Core.Link
76 } 76 }
77 } 77 }
78 78
79 // Load all the symbols from the section's tables that create symbols. 79 // Load all the symbols from the section's tables that can be referenced (i.e. have an Id).
80 foreach (var symbol in section.Symbols.Where(t => t.Id != null)) 80 foreach (var symbol in section.Symbols.Where(t => t.Id != null))
81 { 81 {
82 var symbolWithSection = new SymbolWithSection(section, symbol); 82 var symbolWithSection = new SymbolWithSection(section, symbol);
83 var fullName = symbolWithSection.GetFullName();
83 84
84 if (!symbolsByName.TryGetValue(symbolWithSection.Name, out var existingSymbol)) 85 if (!symbolsByName.TryGetValue(fullName, out var existingSymbol))
85 { 86 {
86 symbolsByName.Add(symbolWithSection.Name, symbolWithSection); 87 symbolsByName.Add(fullName, symbolWithSection);
87 } 88 }
88 else // uh-oh, duplicate symbols. 89 else // uh-oh, duplicate symbols.
89 { 90 {
90 // If the duplicate symbols are both private directories, there is a chance that they 91 // If the duplicate symbols are both private directories, there is a chance that they
91 // point to identical symbols. Identical directory symbols are redundant and will not cause 92 // point to identical symbols. Identical directory symbols should be treated as redundant.
92 // conflicts. 93 // and not cause conflicts.
93 if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access && 94 if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access &&
94 SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol)) 95 SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol))
95 { 96 {
96 // Ensure identical symbol's symbol is marked redundant to ensure (should the symbol be 97 // Ensure identical symbols are tracked to ensure that only one symbol will end up in linked intermediate.
97 // referenced into the final output) it will not add duplicate primary keys during 98 identicalDirectorySymbols.Add(existingSymbol.Symbol);
98 // the .IDT importing. 99 identicalDirectorySymbols.Add(symbolWithSection.Symbol);
99 existingSymbol.AddRedundant(symbolWithSection);
100 redundantSymbols.Add(symbolWithSection.Symbol);
101 } 100 }
102 else 101 else
103 { 102 {
@@ -111,7 +110,7 @@ namespace WixToolset.Core.Link
111 110
112 this.SymbolsByName = symbolsByName; 111 this.SymbolsByName = symbolsByName;
113 this.PossibleConflicts = possibleConflicts; 112 this.PossibleConflicts = possibleConflicts;
114 this.RedundantSymbols = redundantSymbols; 113 this.IdenticalDirectorySymbols = identicalDirectorySymbols;
115 } 114 }
116 } 115 }
117} 116}
diff --git a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
index ace2e19d..3a07d366 100644
--- a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
@@ -40,7 +40,9 @@ namespace WixToolset.Core.Link
40 40
41 if (actuallyReferencedDuplicates.Any()) 41 if (actuallyReferencedDuplicates.Any())
42 { 42 {
43 this.Messaging.Write(ErrorMessages.DuplicateSymbol(referencedDuplicate.Symbol.SourceLineNumbers, referencedDuplicate.Name)); 43 var fullName = referencedDuplicate.GetFullName();
44
45 this.Messaging.Write(ErrorMessages.DuplicateSymbol(referencedDuplicate.Symbol.SourceLineNumbers, fullName));
44 46
45 foreach (var duplicate in actuallyReferencedDuplicates) 47 foreach (var duplicate in actuallyReferencedDuplicates)
46 { 48 {
diff --git a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
index efb90bb8..d95d648f 100644
--- a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
+++ b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -91,15 +91,16 @@ namespace WixToolset.Core.Link
91 else // display errors for the duplicate symbols. 91 else // display errors for the duplicate symbols.
92 { 92 {
93 var accessibleSymbol = accessible[0]; 93 var accessibleSymbol = accessible[0];
94 var accessibleFullName = accessibleSymbol.GetFullName();
94 var referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers?.ToString(); 95 var referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers?.ToString();
95 96
96 if (String.IsNullOrEmpty(referencingSourceLineNumber)) 97 if (String.IsNullOrEmpty(referencingSourceLineNumber))
97 { 98 {
98 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleSymbol.Name)); 99 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName));
99 } 100 }
100 else 101 else
101 { 102 {
102 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber)); 103 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName, referencingSourceLineNumber));
103 } 104 }
104 105
105 foreach (var accessibleDuplicate in accessible.Skip(1)) 106 foreach (var accessibleDuplicate in accessible.Skip(1))
@@ -146,14 +147,6 @@ namespace WixToolset.Core.Link
146 } 147 }
147 } 148 }
148 149
149 foreach (var dupe in symbolWithSection.Redundants)
150 {
151 if (this.AccessibleSymbol(referencingSection, dupe))
152 {
153 accessibleSymbols.Add(dupe);
154 }
155 }
156
157 return accessibleSymbols; 150 return accessibleSymbols;
158 } 151 }
159 152
diff --git a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
index 08e01077..dbaceb28 100644
--- a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
+++ b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
@@ -13,7 +13,6 @@ namespace WixToolset.Core.Link
13 internal class SymbolWithSection 13 internal class SymbolWithSection
14 { 14 {
15 private HashSet<SymbolWithSection> possibleConflicts; 15 private HashSet<SymbolWithSection> possibleConflicts;
16 private HashSet<SymbolWithSection> redundants;
17 16
18 /// <summary> 17 /// <summary>
19 /// Creates a symbol for a symbol. 18 /// Creates a symbol for a symbol.
@@ -24,7 +23,6 @@ namespace WixToolset.Core.Link
24 { 23 {
25 this.Symbol = symbol; 24 this.Symbol = symbol;
26 this.Section = section; 25 this.Section = section;
27 this.Name = String.Concat(this.Symbol.Definition.Name, ":", this.Symbol.Id.Id);
28 } 26 }
29 27
30 /// <summary> 28 /// <summary>
@@ -34,12 +32,6 @@ namespace WixToolset.Core.Link
34 public AccessModifier Access => this.Symbol.Id.Access; 32 public AccessModifier Access => this.Symbol.Id.Access;
35 33
36 /// <summary> 34 /// <summary>
37 /// Gets the name of the symbol.
38 /// </summary>
39 /// <value>Name of the symbol.</value>
40 public string Name { get; }
41
42 /// <summary>
43 /// Gets the symbol for this symbol. 35 /// Gets the symbol for this symbol.
44 /// </summary> 36 /// </summary>
45 /// <value>Symbol for this symbol.</value> 37 /// <value>Symbol for this symbol.</value>
@@ -57,11 +49,6 @@ namespace WixToolset.Core.Link
57 public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>(); 49 public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>();
58 50
59 /// <summary> 51 /// <summary>
60 /// Gets any duplicates of this symbol with sections that are redundant.
61 /// </summary>
62 public IEnumerable<SymbolWithSection> Redundants => this.redundants ?? Enumerable.Empty<SymbolWithSection>();
63
64 /// <summary>
65 /// Adds a duplicate symbol with sections that is a possible conflict. 52 /// Adds a duplicate symbol with sections that is a possible conflict.
66 /// </summary> 53 /// </summary>
67 /// <param name="symbolWithSection">Symbol with section that is a possible conflict of this symbol.</param> 54 /// <param name="symbolWithSection">Symbol with section that is a possible conflict of this symbol.</param>
@@ -76,17 +63,11 @@ namespace WixToolset.Core.Link
76 } 63 }
77 64
78 /// <summary> 65 /// <summary>
79 /// Adds a duplicate symbol that is redundant. 66 /// Gets the full name of the symbol.
80 /// </summary> 67 /// </summary>
81 /// <param name="symbolWithSection">Symbol with section that is redundant of this symbol.</param> 68 public string GetFullName()
82 public void AddRedundant(SymbolWithSection symbolWithSection)
83 { 69 {
84 if (null == this.redundants) 70 return String.Concat(this.Symbol.Definition.Name, ":", this.Symbol.Id.Id);
85 {
86 this.redundants = new HashSet<SymbolWithSection>();
87 }
88
89 this.redundants.Add(symbolWithSection);
90 } 71 }
91 } 72 }
92} 73}
diff --git a/src/wix/WixToolset.Core/Linker.cs b/src/wix/WixToolset.Core/Linker.cs
index a3d99039..5c021ca0 100644
--- a/src/wix/WixToolset.Core/Linker.cs
+++ b/src/wix/WixToolset.Core/Linker.cs
@@ -177,14 +177,20 @@ namespace WixToolset.Core
177 // metadata. 177 // metadata.
178 var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type); 178 var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type);
179 var references = new List<WixSimpleReferenceSymbol>(); 179 var references = new List<WixSimpleReferenceSymbol>();
180 var identicalDirectoryIds = new HashSet<string>(StringComparer.Ordinal);
180 181
181 foreach (var section in sections) 182 foreach (var section in sections)
182 { 183 {
183 foreach (var symbol in section.Symbols) 184 foreach (var symbol in section.Symbols)
184 { 185 {
185 if (find.RedundantSymbols.Contains(symbol)) 186 // If this symbol is an identical directory, ensure we only visit
187 // one (and skip the other identicals with the same id).
188 if (find.IdenticalDirectorySymbols.Contains(symbol))
186 { 189 {
187 continue; 190 if (!identicalDirectoryIds.Add(symbol.Id.Id))
191 {
192 continue;
193 }
188 } 194 }
189 195
190 var copySymbol = true; // by default, copy symbols. 196 var copySymbol = true; // by default, copy symbols.
@@ -351,22 +357,24 @@ namespace WixToolset.Core
351 foreach (var actionSymbol in WindowsInstallerStandard.StandardActions()) 357 foreach (var actionSymbol in WindowsInstallerStandard.StandardActions())
352 { 358 {
353 var symbolWithSection = new SymbolWithSection(null, actionSymbol); 359 var symbolWithSection = new SymbolWithSection(null, actionSymbol);
360 var fullName = symbolWithSection.GetFullName();
354 361
355 // If the action's symbol has not already been defined (i.e. overriden by the user), add it now. 362 // If the action's symbol has not already been defined (i.e. overriden by the user), add it now.
356 if (!symbolsByName.ContainsKey(symbolWithSection.Name)) 363 if (!symbolsByName.ContainsKey(fullName))
357 { 364 {
358 symbolsByName.Add(symbolWithSection.Name, symbolWithSection); 365 symbolsByName.Add(fullName, symbolWithSection);
359 } 366 }
360 } 367 }
361 368
362 foreach (var directorySymbol in WindowsInstallerStandard.StandardDirectories()) 369 foreach (var directorySymbol in WindowsInstallerStandard.StandardDirectories())
363 { 370 {
364 var symbolWithSection = new SymbolWithSection(null, directorySymbol); 371 var symbolWithSection = new SymbolWithSection(null, directorySymbol);
372 var fullName = symbolWithSection.GetFullName();
365 373
366 // If the directory's symbol has not already been defined (i.e. overriden by the user), add it now. 374 // If the directory's symbol has not already been defined (i.e. overriden by the user), add it now.
367 if (!symbolsByName.ContainsKey(symbolWithSection.Name)) 375 if (!symbolsByName.ContainsKey(fullName))
368 { 376 {
369 symbolsByName.Add(symbolWithSection.Name, symbolWithSection); 377 symbolsByName.Add(fullName, symbolWithSection);
370 } 378 }
371 } 379 }
372 } 380 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs
index cacf72bd..3f4108cb 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs
@@ -273,5 +273,52 @@ namespace WixToolsetTest.CoreIntegration
273 }, directoryRows.Select(r => String.Join('\t', r.FieldAsString(0), r.FieldAsString(1), r.FieldAsString(2))).ToArray()); 273 }, directoryRows.Select(r => String.Join('\t', r.FieldAsString(0), r.FieldAsString(1), r.FieldAsString(2))).ToArray());
274 } 274 }
275 } 275 }
276
277 [Fact]
278 public void CanFindRedundantSubdirectoryInSecondSection()
279 {
280 var folder = TestData.Get(@"TestData");
281
282 using (var fs = new DisposableFileSystem())
283 {
284 var baseFolder = fs.GetFolder();
285 var intermediateFolder = Path.Combine(baseFolder, "obj");
286 var msiPath = Path.Combine(baseFolder, "bin", "test.msi");
287 var wixpdbPath = Path.ChangeExtension(msiPath, ".wixpdb");
288
289 var result = WixRunner.Execute(new[]
290 {
291 "build",
292 Path.Combine(folder, "Directory", "RedundantSubdirectoryInSecondSection.wxs"),
293 "-bindpath", Path.Combine(folder, "SingleFile", "data"),
294 "-intermediateFolder", intermediateFolder,
295 "-o", msiPath
296 });
297
298 result.AssertSuccess();
299
300 var intermediate = Intermediate.Load(wixpdbPath);
301 var section = intermediate.Sections.Single();
302
303 var dirSymbols = section.Symbols.OfType<WixToolset.Data.Symbols.DirectorySymbol>().ToList();
304 WixAssert.CompareLineByLine(new[]
305 {
306 @"dKO7wPCF.XLmq6KnqybHHgcBBqtU:ProgramFilesFolder:a\b\c",
307 @"ProgramFilesFolder:TARGETDIR:PFiles",
308 @"TARGETDIR::SourceDir"
309 }, dirSymbols.OrderBy(d => d.Id.Id).Select(d => d.Id.Id + ":" + d.ParentDirectoryRef + ":" + d.Name).OrderBy(s => s).ToArray());
310
311 var data = WindowsInstallerData.Load(wixpdbPath);
312 var directoryRows = data.Tables["Directory"].Rows;
313 WixAssert.CompareLineByLine(new[]
314 {
315 @"d1nVb5_zcCwRCz7i2YXNAofGRmfc:ProgramFilesFolder:a",
316 @"dijlG.bNicFgvj1_DujiGg9EBGrQ:d1nVb5_zcCwRCz7i2YXNAofGRmfc:b",
317 @"dKO7wPCF.XLmq6KnqybHHgcBBqtU:dijlG.bNicFgvj1_DujiGg9EBGrQ:c",
318 "ProgramFilesFolder:TARGETDIR:PFiles",
319 "TARGETDIR::SourceDir"
320 }, directoryRows.Select(r => r.FieldAsString(0) + ":" + r.FieldAsString(1) + ":" + r.FieldAsString(2)).OrderBy(s => s).ToArray());
321 }
322 }
276 } 323 }
277} 324}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Directory/RedundantSubdirectoryInSecondSection.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Directory/RedundantSubdirectoryInSecondSection.wxs
new file mode 100644
index 00000000..fc73ee47
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Directory/RedundantSubdirectoryInSecondSection.wxs
@@ -0,0 +1,26 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="~RedundantSubdirectories" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="12E4699F-E774-4D05-8A01-5BDD41BBA127" Compressed="no">
3 <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
4
5 <Feature Id="ProductFeature">
6 <!-- NotIncludeButFirst will define the subdirectory and IncludedButSecond needs the same symbols, this tests linking order -->
7 <ComponentGroupRef Id="IncludedButSecond" />
8 </Feature>
9 </Package>
10
11 <Fragment Id="NotIncludedButFirst">
12 <ComponentGroup Id="NotIncludedButFirst">
13 <Component Directory="ProgramFilesFolder" Subdirectory="a\b\c">
14 <File Name="notincluded.txt" Source="test.txt" />
15 </Component>
16 </ComponentGroup>
17 </Fragment>
18
19 <Fragment Id="IncludedButSecond">
20 <ComponentGroup Id="IncludedButSecond">
21 <Component Directory="ProgramFilesFolder" Subdirectory="a\b\c">
22 <File Name="included.txt" Source="test.txt" />
23 </Component>
24 </ComponentGroup>
25 </Fragment>
26</Wix>