aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller
diff options
context:
space:
mode:
authorRonald Martin <cpuwzd@comcast.net>2021-02-12 22:37:15 -0500
committerRob Mensching <rob@firegiant.com>2021-02-19 14:57:41 -0800
commit9dd02e0c1bb6463de5d79f14ce06d078ef876a32 (patch)
tree96a3f60f31990d9c0a6ffb5c4d8d2430b277f695 /src/WixToolset.Core.WindowsInstaller
parent0ce3a9b66da80c505fd8901df88d31b0ad43dac6 (diff)
downloadwix-9dd02e0c1bb6463de5d79f14ce06d078ef876a32.tar.gz
wix-9dd02e0c1bb6463de5d79f14ce06d078ef876a32.tar.bz2
wix-9dd02e0c1bb6463de5d79f14ce06d078ef876a32.zip
Checkks all custom actions for cycles. Version 3. Fixes wixtoolset/issues#6201
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs78
1 files changed, 63 insertions, 15 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
index d33836c3..056b129b 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
@@ -67,7 +67,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
67 } 67 }
68 else // not a supported unscheduled action. 68 else // not a supported unscheduled action.
69 { 69 {
70 throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set."); 70 throw new InvalidOperationException($"Found an action [{actionSymbol.Id.Id}] at [{actionSymbol.SourceLineNumbers}] with no Sequence, Before, or After column set.");
71 } 71 }
72 } 72 }
73 73
@@ -129,6 +129,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
129 } 129 }
130 } 130 }
131 131
132 // A dictionary used for detecting cyclic references among action symbols.
133 var firstReference = new Dictionary<WixActionSymbol, WixActionSymbol>();
134
132 // Build up dependency trees of the relatively scheduled actions. 135 // Build up dependency trees of the relatively scheduled actions.
133 // Use ToList() to create a copy of the required action symbols so that new symbols can 136 // Use ToList() to create a copy of the required action symbols so that new symbols can
134 // be added while enumerating. 137 // be added while enumerating.
@@ -142,7 +145,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
142 this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action)); 145 this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
143 } 146 }
144 147
145 this.SequenceActionSymbol(actionSymbol, requiredActionSymbols); 148 this.SequenceActionSymbol(actionSymbol, requiredActionSymbols, firstReference);
146 } 149 }
147 else if (SectionType.Module == this.Section.Type && 0 < actionSymbol.Sequence && !WindowsInstallerStandard.IsStandardAction(actionSymbol.Action)) // check for custom actions and dialogs that have a sequence number 150 else if (SectionType.Module == this.Section.Type && 0 < actionSymbol.Sequence && !WindowsInstallerStandard.IsStandardAction(actionSymbol.Action)) // check for custom actions and dialogs that have a sequence number
148 { 151 {
@@ -566,7 +569,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
566 /// </summary> 569 /// </summary>
567 /// <param name="actionSymbol">The action symbol to be sequenced.</param> 570 /// <param name="actionSymbol">The action symbol to be sequenced.</param>
568 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param> 571 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
569 private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols) 572 private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols, Dictionary<WixActionSymbol, WixActionSymbol> firstReference)
570 { 573 {
571 var after = false; 574 var after = false;
572 575
@@ -576,7 +579,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
576 } 579 }
577 else if (actionSymbol.Before == null) 580 else if (actionSymbol.Before == null)
578 { 581 {
579 throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set."); 582 throw new InvalidOperationException($"Found an action [{actionSymbol.Id.Id}] at [{actionSymbol.SourceLineNumbers}] with no Sequence, Before, or After column set.");
580 } 583 }
581 584
582 var parentActionName = (after ? actionSymbol.After : actionSymbol.Before); 585 var parentActionName = (after ? actionSymbol.After : actionSymbol.Before);
@@ -597,10 +600,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
597 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Found an action with a non-existent {0} action: {1}.", (after ? "After" : "Before"), parentActionName)); 600 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Found an action with a non-existent {0} action: {1}.", (after ? "After" : "Before"), parentActionName));
598 } 601 }
599 } 602 }
600 else if (actionSymbol == parentActionSymbol || this.ContainsChildActionSymbol(actionSymbol, parentActionSymbol)) // cycle detected 603
601 { 604 this.CheckForCircularActionReference(actionSymbol, requiredActionSymbols, firstReference);
602 throw new WixException(ErrorMessages.ActionCircularDependency(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, parentActionSymbol.Action));
603 }
604 605
605 // Add this action to the appropriate list of dependent action symbols. 606 // Add this action to the appropriate list of dependent action symbols.
606 var relativeActions = this.GetRelativeActions(parentActionSymbol); 607 var relativeActions = this.GetRelativeActions(parentActionSymbol);
@@ -608,19 +609,66 @@ namespace WixToolset.Core.WindowsInstaller.Bind
608 relatedSymbols.Add(actionSymbol); 609 relatedSymbols.Add(actionSymbol);
609 } 610 }
610 611
611 private bool ContainsChildActionSymbol(WixActionSymbol childSymbol, WixActionSymbol parentSymbol) 612 /// <summary>
613 /// Check the specified action symbol to see if it leads to a cycle.
614 /// </summary>
615 /// <para> Use the provided dictionary to note the initial action symbol that first led to each action
616 /// symbol. Any action symbol encountered that has already been encountered starting from a different
617 /// initial action symbol inherits the loop characteristics of that initial action symbol, and thus is
618 /// also not part of a cycle. However, any action symbol encountered that has already been encountered
619 /// starting from the same initial action symbol is an indication that the current action symbol is
620 /// part of a cycle.
621 /// </para>
622 /// <param name="actionSymbol">The action symbol to be checked.</param>
623 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
624 /// <param name="firstReference">The first encountered action symbol that led to each action symbol.</param>
625 private void CheckForCircularActionReference(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols, Dictionary<WixActionSymbol, WixActionSymbol> firstReference)
612 { 626 {
613 var result = false; 627 WixActionSymbol currentActionSymbol = null;
628 var parentActionSymbol = actionSymbol;
614 629
615 if (this.RelativeActionsForActions.TryGetValue(childSymbol.Id.Id, out var relativeActions)) 630 do
616 { 631 {
617 result = relativeActions.NextActions.Any(a => a.SequenceTable == parentSymbol.SequenceTable && a.Id.Id == parentSymbol.Id.Id) || 632 var previousActionSymbol = currentActionSymbol ?? parentActionSymbol;
618 relativeActions.PreviousActions.Any(a => a.SequenceTable == parentSymbol.SequenceTable && a.Id.Id == parentSymbol.Id.Id); 633 currentActionSymbol = parentActionSymbol;
619 }
620 634
621 return result; 635 if (!firstReference.TryGetValue(currentActionSymbol, out var existingInitialActionSymbol))
636 {
637 firstReference[currentActionSymbol] = actionSymbol;
638 }
639 else if (existingInitialActionSymbol == actionSymbol)
640 {
641 throw new WixException(ErrorMessages.ActionCircularDependency(currentActionSymbol.SourceLineNumbers, currentActionSymbol.SequenceTable.ToString(), currentActionSymbol.Action, previousActionSymbol.Action));
642 }
643
644 parentActionSymbol = this.GetParentActionSymbol(currentActionSymbol, requiredActionSymbols);
645 } while (null != parentActionSymbol);
622 } 646 }
623 647
648 /// <summary>
649 /// Get the action symbol that is the parent of the given action symbol.
650 /// </summary>
651 /// <param name="actionSymbol">The given action symbol.</param>
652 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
653 /// <returns>Null if there is no parent. Used for loop termination.</returns>
654 private WixActionSymbol GetParentActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols)
655 {
656 if (null == actionSymbol.Before && null == actionSymbol.After)
657 {
658 return null;
659 }
660
661 var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + (actionSymbol.After ?? actionSymbol.Before);
662
663 if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
664 {
665 WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol);
666 }
667
668 return parentActionSymbol;
669 }
670
671
624 private RelativeActions GetRelativeActions(WixActionSymbol action) 672 private RelativeActions GetRelativeActions(WixActionSymbol action)
625 { 673 {
626 if (!this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var relativeActions)) 674 if (!this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var relativeActions))