aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2023-11-06 21:46:30 -0800
committerRob Mensching <rob@firegiant.com>2023-11-07 14:27:27 -0800
commit86774dad361714fdd55a2df23379c1a1600d1f27 (patch)
treeb88b9c7ebd124ebb79634876f656556eb3bd48b6
parent575dc6b828300aaf7fea47eda2526719eb85dc91 (diff)
downloadwix-86774dad361714fdd55a2df23379c1a1600d1f27.tar.gz
wix-86774dad361714fdd55a2df23379c1a1600d1f27.tar.bz2
wix-86774dad361714fdd55a2df23379c1a1600d1f27.zip
Include duplicated inline directory symbols referenced in subsequent sections
Due to the handling of redundant symbols, which are only used by inline directory syntax, the symbols were only defined in the first section encountered by the linker. Fix that so at most one duplicated inline directory symbol is included when referenced. Fixes 7840
-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>