diff options
| author | Ronald Martin <cpuwzd@comcast.net> | 2021-02-12 22:37:15 -0500 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2021-02-19 14:57:41 -0800 |
| commit | 9dd02e0c1bb6463de5d79f14ce06d078ef876a32 (patch) | |
| tree | 96a3f60f31990d9c0a6ffb5c4d8d2430b277f695 /src/WixToolset.Core.WindowsInstaller | |
| parent | 0ce3a9b66da80c505fd8901df88d31b0ad43dac6 (diff) | |
| download | wix-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.cs | 78 |
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)) |
