Skip to content

Commit 86774da

Browse files
committed
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
1 parent 575dc6b commit 86774da

File tree

7 files changed

+108
-52
lines changed

7 files changed

+108
-52
lines changed

src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ public FindEntrySectionAndLoadSymbolsCommand(IMessaging messaging, IEnumerable<I
4242
/// Gets the collection of redundant symbols that should not be included
4343
/// in the final output.
4444
/// </summary>
45-
public ISet<IntermediateSymbol> RedundantSymbols { get; private set; }
45+
public ISet<IntermediateSymbol> IdenticalDirectorySymbols { get; private set; }
4646

4747
public void Execute()
4848
{
4949
var symbolsByName = new Dictionary<string, SymbolWithSection>();
5050
var possibleConflicts = new HashSet<SymbolWithSection>();
51-
var redundantSymbols = new HashSet<IntermediateSymbol>();
51+
var identicalDirectorySymbols = new HashSet<IntermediateSymbol>();
5252

5353
if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType))
5454
{
@@ -76,28 +76,27 @@ public void Execute()
7676
}
7777
}
7878

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).
8080
foreach (var symbol in section.Symbols.Where(t => t.Id != null))
8181
{
8282
var symbolWithSection = new SymbolWithSection(section, symbol);
83+
var fullName = symbolWithSection.GetFullName();
8384

84-
if (!symbolsByName.TryGetValue(symbolWithSection.Name, out var existingSymbol))
85+
if (!symbolsByName.TryGetValue(fullName, out var existingSymbol))
8586
{
86-
symbolsByName.Add(symbolWithSection.Name, symbolWithSection);
87+
symbolsByName.Add(fullName, symbolWithSection);
8788
}
8889
else // uh-oh, duplicate symbols.
8990
{
9091
// 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-
// conflicts.
92+
// point to identical symbols. Identical directory symbols should be treated as redundant.
93+
// and not cause conflicts.
9394
if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access &&
9495
SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol))
9596
{
96-
// Ensure identical symbol's symbol is marked redundant to ensure (should the symbol be
97-
// referenced into the final output) it will not add duplicate primary keys during
98-
// the .IDT importing.
99-
existingSymbol.AddRedundant(symbolWithSection);
100-
redundantSymbols.Add(symbolWithSection.Symbol);
97+
// Ensure identical symbols are tracked to ensure that only one symbol will end up in linked intermediate.
98+
identicalDirectorySymbols.Add(existingSymbol.Symbol);
99+
identicalDirectorySymbols.Add(symbolWithSection.Symbol);
101100
}
102101
else
103102
{
@@ -111,7 +110,7 @@ public void Execute()
111110

112111
this.SymbolsByName = symbolsByName;
113112
this.PossibleConflicts = possibleConflicts;
114-
this.RedundantSymbols = redundantSymbols;
113+
this.IdenticalDirectorySymbols = identicalDirectorySymbols;
115114
}
116115
}
117116
}

src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ public void Execute()
4040

4141
if (actuallyReferencedDuplicates.Any())
4242
{
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));
4446

4547
foreach (var duplicate in actuallyReferencedDuplicates)
4648
{

src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,16 @@ private void RecursivelyResolveReferences(IntermediateSection section)
9191
else // display errors for the duplicate symbols.
9292
{
9393
var accessibleSymbol = accessible[0];
94+
var accessibleFullName = accessibleSymbol.GetFullName();
9495
var referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers?.ToString();
9596

9697
if (String.IsNullOrEmpty(referencingSourceLineNumber))
9798
{
98-
this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleSymbol.Name));
99+
this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName));
99100
}
100101
else
101102
{
102-
this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleSymbol.Name, referencingSourceLineNumber));
103+
this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName, referencingSourceLineNumber));
103104
}
104105

105106
foreach (var accessibleDuplicate in accessible.Skip(1))
@@ -146,14 +147,6 @@ private List<SymbolWithSection> DetermineAccessibleSymbols(IntermediateSection r
146147
}
147148
}
148149

149-
foreach (var dupe in symbolWithSection.Redundants)
150-
{
151-
if (this.AccessibleSymbol(referencingSection, dupe))
152-
{
153-
accessibleSymbols.Add(dupe);
154-
}
155-
}
156-
157150
return accessibleSymbols;
158151
}
159152

src/wix/WixToolset.Core/Link/SymbolWithSection.cs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace WixToolset.Core.Link
1313
internal class SymbolWithSection
1414
{
1515
private HashSet<SymbolWithSection> possibleConflicts;
16-
private HashSet<SymbolWithSection> redundants;
1716

1817
/// <summary>
1918
/// Creates a symbol for a symbol.
@@ -24,7 +23,6 @@ public SymbolWithSection(IntermediateSection section, IntermediateSymbol symbol)
2423
{
2524
this.Symbol = symbol;
2625
this.Section = section;
27-
this.Name = String.Concat(this.Symbol.Definition.Name, ":", this.Symbol.Id.Id);
2826
}
2927

3028
/// <summary>
@@ -33,12 +31,6 @@ public SymbolWithSection(IntermediateSection section, IntermediateSymbol symbol)
3331
/// <value>Accessbility of the symbol.</value>
3432
public AccessModifier Access => this.Symbol.Id.Access;
3533

36-
/// <summary>
37-
/// Gets the name of the symbol.
38-
/// </summary>
39-
/// <value>Name of the symbol.</value>
40-
public string Name { get; }
41-
4234
/// <summary>
4335
/// Gets the symbol for this symbol.
4436
/// </summary>
@@ -56,11 +48,6 @@ public SymbolWithSection(IntermediateSection section, IntermediateSymbol symbol)
5648
/// </summary>
5749
public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>();
5850

59-
/// <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-
6451
/// <summary>
6552
/// Adds a duplicate symbol with sections that is a possible conflict.
6653
/// </summary>
@@ -76,17 +63,11 @@ public void AddPossibleConflict(SymbolWithSection symbolWithSection)
7663
}
7764

7865
/// <summary>
79-
/// Adds a duplicate symbol that is redundant.
66+
/// Gets the full name of the symbol.
8067
/// </summary>
81-
/// <param name="symbolWithSection">Symbol with section that is redundant of this symbol.</param>
82-
public void AddRedundant(SymbolWithSection symbolWithSection)
68+
public string GetFullName()
8369
{
84-
if (null == this.redundants)
85-
{
86-
this.redundants = new HashSet<SymbolWithSection>();
87-
}
88-
89-
this.redundants.Add(symbolWithSection);
70+
return String.Concat(this.Symbol.Definition.Name, ":", this.Symbol.Id.Id);
9071
}
9172
}
9273
}

src/wix/WixToolset.Core/Linker.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,20 @@ public Intermediate Link(ILinkContext context)
177177
// metadata.
178178
var resolvedSection = new IntermediateSection(find.EntrySection.Id, find.EntrySection.Type);
179179
var references = new List<WixSimpleReferenceSymbol>();
180+
var identicalDirectoryIds = new HashSet<string>(StringComparer.Ordinal);
180181

181182
foreach (var section in sections)
182183
{
183184
foreach (var symbol in section.Symbols)
184185
{
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))
186189
{
187-
continue;
190+
if (!identicalDirectoryIds.Add(symbol.Id.Id))
191+
{
192+
continue;
193+
}
188194
}
189195

190196
var copySymbol = true; // by default, copy symbols.
@@ -351,22 +357,24 @@ private void LoadStandardSymbols(IDictionary<string, SymbolWithSection> symbolsB
351357
foreach (var actionSymbol in WindowsInstallerStandard.StandardActions())
352358
{
353359
var symbolWithSection = new SymbolWithSection(null, actionSymbol);
360+
var fullName = symbolWithSection.GetFullName();
354361

355362
// 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))
357364
{
358-
symbolsByName.Add(symbolWithSection.Name, symbolWithSection);
365+
symbolsByName.Add(fullName, symbolWithSection);
359366
}
360367
}
361368

362369
foreach (var directorySymbol in WindowsInstallerStandard.StandardDirectories())
363370
{
364371
var symbolWithSection = new SymbolWithSection(null, directorySymbol);
372+
var fullName = symbolWithSection.GetFullName();
365373

366374
// 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))
368376
{
369-
symbolsByName.Add(symbolWithSection.Name, symbolWithSection);
377+
symbolsByName.Add(fullName, symbolWithSection);
370378
}
371379
}
372380
}

src/wix/test/WixToolsetTest.CoreIntegration/DirectoryFixture.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,5 +273,52 @@ public void CanGetDuplicateTargetSourceName()
273273
}, directoryRows.Select(r => String.Join('\t', r.FieldAsString(0), r.FieldAsString(1), r.FieldAsString(2))).ToArray());
274274
}
275275
}
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+
}
276323
}
277324
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>

0 commit comments

Comments
 (0)