Skip to content

Commit

Permalink
Initial support for virtual symbols.
Browse files Browse the repository at this point in the history
Completes 7913
  • Loading branch information
robmen committed Dec 16, 2023
1 parent 7be5d94 commit 3799263
Show file tree
Hide file tree
Showing 27 changed files with 621 additions and 200 deletions.
24 changes: 23 additions & 1 deletion src/api/wix/WixToolset.Data/AccessModifier.cs
Expand Up @@ -28,11 +28,21 @@ public enum AccessModifier
Protected = File,

/// <summary>
/// Indicates the identifiers is visible only to the section where it is defined.
/// Indicates the identifier is visible only to the section where it is defined.
/// </summary>
Section,
[Obsolete]
Private = Section,

/// <summary>
/// Indicates the identifier can be overridden by another symbol.
/// </summary>
Virtual,

/// <summary>
/// Indicates the identifier overrides a virtual symbol.
/// </summary>
Override,
}

/// <summary>
Expand Down Expand Up @@ -65,6 +75,12 @@ public static AccessModifier AsAccessModifier(this string access)
case "private":
return AccessModifier.Section;

case "virtual":
return AccessModifier.Virtual;

case "override":
return AccessModifier.Override;

default:
throw new ArgumentException($"Unknown AccessModifier: {access}", nameof(access));
}
Expand All @@ -91,6 +107,12 @@ public static string AsString(this AccessModifier access)
case AccessModifier.Section:
return "section";

case AccessModifier.Virtual:
return "virtual";

case AccessModifier.Override:
return "override";

default:
throw new ArgumentException($"Unknown AccessModifier: {access}", nameof(access));
}
Expand Down
4 changes: 2 additions & 2 deletions src/api/wix/WixToolset.Data/ErrorMessages.cs
Expand Up @@ -325,12 +325,12 @@ public static Message DuplicateSourcesForOutput(string sourceList, string output

public static Message DuplicateSymbol(SourceLineNumber sourceLineNumbers, string symbolName)
{
return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' found. This typically means that an Id is duplicated. Access modifiers (internal, protected, private) cannot prevent these conflicts. Ensure all your identifiers of a given type (File, Component, Feature) are unique.", symbolName);
return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' found. This typically means that an Id is duplicated. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", symbolName);
}

public static Message DuplicateSymbol(SourceLineNumber sourceLineNumbers, string symbolName, string referencingSourceLineNumber)
{
return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' referenced by {1}. This typically means that an Id is duplicated. Ensure all your identifiers of a given type (File, Component, Feature) are unique or use an access modifier to scope the identfier.", symbolName, referencingSourceLineNumber);
return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' referenced by {1}. This typically means that an Id is duplicated. Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.", symbolName, referencingSourceLineNumber);
}

public static Message DuplicateSymbol2(SourceLineNumber sourceLineNumbers)
Expand Down
278 changes: 139 additions & 139 deletions src/api/wix/WixToolset.Data/WindowsInstaller/WindowsInstallerStandard.cs

Large diffs are not rendered by default.

27 changes: 20 additions & 7 deletions src/api/wix/test/WixToolsetTest.Data/SerializeFixture.cs
Expand Up @@ -31,6 +31,12 @@ public void CanSaveAndLoadIntermediate()
KeyPath = null,
});

section.AddSymbol(new DirectorySymbol(sln, new Identifier(AccessModifier.Virtual, "TestFolder"))
{
ParentDirectoryRef = String.Empty,
Name = "Test Folder",
});

var intermediate = new Intermediate("TestIntermediate", IntermediateLevels.Compiled, new[] { section }, null);

intermediate.UpdateLevel(IntermediateLevels.Linked);
Expand All @@ -47,14 +53,21 @@ public void CanSaveAndLoadIntermediate()
Assert.True(loaded.HasLevel(IntermediateLevels.Linked));
Assert.True(loaded.HasLevel(IntermediateLevels.Resolved));

var symbol = (ComponentSymbol)loaded.Sections.Single().Symbols.Single();
var componentSymbol = loaded.Sections.Single().Symbols.OfType<ComponentSymbol>().Single();

Assert.Equal("TestComponent", componentSymbol.Id.Id);
Assert.Equal(AccessModifier.Global, componentSymbol.Id.Access);
Assert.Equal(String.Empty, componentSymbol.ComponentId);
Assert.Equal("TestFolder", componentSymbol.DirectoryRef);
Assert.Equal(ComponentLocation.Either, componentSymbol.Location);
Assert.Null(componentSymbol.KeyPath);

var directorySymbol = loaded.Sections.Single().Symbols.OfType<DirectorySymbol>().Single();

Assert.Equal("TestComponent", symbol.Id.Id);
Assert.Equal(AccessModifier.Global, symbol.Id.Access);
Assert.Equal(String.Empty, symbol.ComponentId);
Assert.Equal("TestFolder", symbol.DirectoryRef);
Assert.Equal(ComponentLocation.Either, symbol.Location);
Assert.Null(symbol.KeyPath);
Assert.Equal("TestFolder", directorySymbol.Id.Id);
Assert.Equal(AccessModifier.Virtual, directorySymbol.Id.Access);
Assert.Equal(String.Empty, directorySymbol.ParentDirectoryRef);
Assert.Equal("Test Folder", directorySymbol.Name);
}
finally
{
Expand Down
4 changes: 2 additions & 2 deletions src/ext/UI/wixlib/WixUI_Minimal.wxs
Expand Up @@ -58,8 +58,8 @@ Patch dialog sequence:
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />

<InstallUISequence>
<Show Dialog="WelcomeDlg" Before="WelcomeEulaDlg" Condition="Installed AND PATCH" />
<Show Dialog="WelcomeEulaDlg" Before="ProgressDlg" Condition="NOT Installed" />
<Show Dialog="override WelcomeDlg" Before="WelcomeEulaDlg" Condition="Installed AND PATCH" />
<Show Dialog="override WelcomeEulaDlg" Before="ProgressDlg" Condition="NOT Installed" />
</InstallUISequence>

<Property Id="ARPNOMODIFY" Value="1" />
Expand Down
Expand Up @@ -108,7 +108,7 @@ public void Execute()
// the binder.
if (requiredActionSymbols.TryGetValue(key, out var requiredActionSymbol))
{
if (requiredActionSymbol.Overridable)
if (requiredActionSymbol.Overridable || requiredActionSymbol.Id.Access == AccessModifier.Virtual)
{
this.Messaging.Write(WarningMessages.SuppressAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.Action, suppressActionSymbol.SequenceTable.ToString()));
if (null != requiredActionSymbol.SourceLineNumbers)
Expand Down Expand Up @@ -240,7 +240,7 @@ private List<WixActionSymbol> ScheduleActions(Dictionary<string, WixActionSymbol
// Schedule the relatively scheduled actions (by resolving the dependency trees).
var previousUsedSequence = 0;
var relativeActionSymbols = new List<WixActionSymbol>();
for (int j = 0; j < absoluteActionSymbols.Count; j++)
for (var j = 0; j < absoluteActionSymbols.Count; j++)
{
var absoluteActionSymbol = absoluteActionSymbols[j];

Expand Down
21 changes: 17 additions & 4 deletions src/wix/WixToolset.Core/Compiler_Package.cs
Expand Up @@ -2463,6 +2463,7 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable)
foreach (var child in node.Elements())
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
Identifier actionIdentifier = null;
var actionName = child.Name.LocalName;
string afterAction = null;
string beforeAction = null;
Expand All @@ -2485,8 +2486,9 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable)
case "Action":
if (customAction)
{
actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.CustomAction, actionName);
actionIdentifier = this.Core.GetAttributeIdentifier(childSourceLineNumbers, attrib);
actionName = actionIdentifier.Id;
this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.CustomAction, actionIdentifier.Id);
}
else
{
Expand Down Expand Up @@ -2521,7 +2523,8 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable)
case "Dialog":
if (showDialog)
{
actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
actionIdentifier = this.Core.GetAttributeIdentifier(childSourceLineNumbers, attrib);
actionName = actionIdentifier.Id;
this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.Dialog, actionName);
}
else
Expand Down Expand Up @@ -2645,7 +2648,17 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable)
}
else
{
var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Global, sequenceTable, actionName))
var access = AccessModifier.Global;
if (overridable)
{
access = AccessModifier.Virtual;
}
else if (actionIdentifier != null)
{
access = actionIdentifier.Access;
}

var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(access, sequenceTable, actionName))
{
SequenceTable = sequenceTable,
Action = actionName,
Expand Down
8 changes: 8 additions & 0 deletions src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
Expand Up @@ -423,6 +423,14 @@ public Identifier GetAttributeIdentifier(SourceLineNumber sourceLineNumbers, XAt
access = AccessModifier.Section;
break;

case "virtual":
access = AccessModifier.Virtual;
break;

case "override":
access = AccessModifier.Override;
break;

default:
return null;
}
Expand Down
15 changes: 10 additions & 5 deletions src/wix/WixToolset.Core/Link/AddRequiredStandardDirectories.cs
Expand Up @@ -43,12 +43,17 @@ public void Execute()

if (String.IsNullOrEmpty(parentDirectoryId))
{
if (directory.Id.Id != "TARGETDIR")
{
directory.ParentDirectoryRef = "TARGETDIR";
}
parentDirectoryId = this.GetStandardDirectoryParent(directory.Id.Id, platform);

directory.ParentDirectoryRef = parentDirectoryId;

//if (directory.Id.Id != "TARGETDIR")
//{
// directory.ParentDirectoryRef = "TARGETDIR";
//}
}
else

if (!String.IsNullOrEmpty(parentDirectoryId))
{
this.EnsureStandardDirectoryAdded(directoryIds, parentDirectoryId, directory.SourceLineNumbers, platform);
}
Expand Down
Expand Up @@ -44,11 +44,19 @@ public FindEntrySectionAndLoadSymbolsCommand(IMessaging messaging, IEnumerable<I
/// </summary>
public ISet<IntermediateSymbol> IdenticalDirectorySymbols { get; private set; }

/// <summary>
/// Gets the collection of overridden symbols that should not be included
/// in the final output.
/// </summary>
public ISet<IntermediateSymbol> OverriddenSymbols { get; private set; }

public void Execute()
{
var symbolsByName = new Dictionary<string, SymbolWithSection>();
var possibleConflicts = new HashSet<SymbolWithSection>();
var identicalDirectorySymbols = new HashSet<IntermediateSymbol>();
var overrideSymbols = new List<SymbolWithSection>();
var overriddenSymbols = new HashSet<IntermediateSymbol>();

if (!Enum.TryParse(this.ExpectedOutputType.ToString(), out SectionType expectedEntrySectionType))
{
Expand Down Expand Up @@ -84,15 +92,34 @@ public void Execute()

if (!symbolsByName.TryGetValue(fullName, out var existingSymbol))
{
if (symbolWithSection.Access == AccessModifier.Override)
{
overrideSymbols.Add(symbolWithSection);
}

symbolsByName.Add(fullName, symbolWithSection);
}
else // uh-oh, duplicate symbols.
{
if (AccessModifier.Virtual == existingSymbol.Access && AccessModifier.Override == symbolWithSection.Access)
{
symbolWithSection.OverrideVirtualSymbol(existingSymbol);
symbolsByName[fullName] = symbolWithSection; // replace the virtual symbol with the override symbol.

overrideSymbols.Add(symbolWithSection);
overriddenSymbols.Add(existingSymbol.Symbol);
}
else if (AccessModifier.Override == existingSymbol.Access && AccessModifier.Virtual == symbolWithSection.Access)
{
existingSymbol.OverrideVirtualSymbol(symbolWithSection);

overriddenSymbols.Add(symbolWithSection.Symbol);
}
// If the duplicate symbols are both private directories, there is a chance that they
// point to identical symbols. Identical directory symbols should be treated as redundant.
// and not cause conflicts.
if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access &&
SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol))
// point to identical symbols. Identical directory symbols are redundant and will not cause
// conflicts.
else if (AccessModifier.Section == existingSymbol.Access && AccessModifier.Section == symbolWithSection.Access &&
SymbolDefinitionType.Directory == existingSymbol.Symbol.Definition.Type && existingSymbol.Symbol.IsIdentical(symbolWithSection.Symbol))
{
// Ensure identical symbols are tracked to ensure that only one symbol will end up in linked intermediate.
identicalDirectorySymbols.Add(existingSymbol.Symbol);
Expand All @@ -108,9 +135,21 @@ public void Execute()
}
}

// Ensure override symbols actually overrode a virtual symbol.
foreach (var symbolWithSection in overrideSymbols)
{
if (symbolWithSection.Overrides is null)
{
var fullName = symbolWithSection.GetFullName();

this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(symbolWithSection.Symbol.SourceLineNumbers, fullName));
}
}

this.SymbolsByName = symbolsByName;
this.PossibleConflicts = possibleConflicts;
this.IdenticalDirectorySymbols = identicalDirectorySymbols;
this.OverriddenSymbols = overriddenSymbols;
}
}
}
16 changes: 9 additions & 7 deletions src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
Expand Up @@ -133,13 +133,13 @@ private List<SymbolWithSection> DetermineAccessibleSymbols(IntermediateSection r

foreach (var dupe in symbolWithSection.PossiblyConflicts)
{
// don't count overridable WixActionSymbols
var symbolAction = symbolWithSection.Symbol as WixActionSymbol;
var dupeAction = dupe.Symbol as WixActionSymbol;
if (symbolAction?.Overridable != dupeAction?.Overridable)
{
continue;
}
//// don't count overridable WixActionSymbols
//var symbolAction = symbolWithSection.Symbol as WixActionSymbol;
//var dupeAction = dupe.Symbol as WixActionSymbol;
//if (symbolAction?.Overridable != dupeAction?.Overridable)
//{
// continue;
//}

if (this.AccessibleSymbol(referencingSection, dupe))
{
Expand All @@ -161,6 +161,8 @@ private bool AccessibleSymbol(IntermediateSection referencingSection, SymbolWith
switch (symbolWithSection.Access)
{
case AccessModifier.Global:
case AccessModifier.Virtual:
case AccessModifier.Override:
return true;
case AccessModifier.Library:
return symbolWithSection.Section.CompilationId == referencingSection.CompilationId || (null != symbolWithSection.Section.LibraryId && symbolWithSection.Section.LibraryId == referencingSection.LibraryId);
Expand Down
19 changes: 19 additions & 0 deletions src/wix/WixToolset.Core/Link/SymbolWithSection.cs
Expand Up @@ -48,6 +48,11 @@ public SymbolWithSection(IntermediateSection section, IntermediateSymbol symbol)
/// </summary>
public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>();

/// <summary>
/// Gets the virtual symbol that is overridden by this symbol.
/// </summary>
public SymbolWithSection Overrides { get; private set; }

/// <summary>
/// Adds a duplicate symbol with sections that is a possible conflict.
/// </summary>
Expand All @@ -62,6 +67,20 @@ public void AddPossibleConflict(SymbolWithSection symbolWithSection)
this.possibleConflicts.Add(symbolWithSection);
}

/// <summary>
/// Override a virtual symbol.
/// </summary>
/// <param name="virtualSymbolWithSection">Virtual symbol with section that is being overridden.</param>
public void OverrideVirtualSymbol(SymbolWithSection virtualSymbolWithSection)
{
if (virtualSymbolWithSection.Access != AccessModifier.Virtual)
{
throw new InvalidOperationException("Cannot override non-virtual symbols");
}

this.Overrides = virtualSymbolWithSection;
}

/// <summary>
/// Gets the full name of the symbol.
/// </summary>
Expand Down

0 comments on commit 3799263

Please sign in to comment.