private Dictionary <string, WixActionSymbol> GetRequiredStandardActions() { var overridableActionSymbols = new Dictionary <string, WixActionSymbol>(); var requiredActionIds = this.GetRequiredActionIds(); foreach (var actionId in requiredActionIds) { WindowsInstallerStandard.TryGetStandardAction(actionId, out var standardAction); overridableActionSymbols.Add(standardAction.Id.Id, standardAction); } return(overridableActionSymbols); }
/// <summary> /// Get the action symbol that is the parent of the given action symbol. /// </summary> /// <param name="actionSymbol">The given action symbol.</param> /// <param name="requiredActionSymbols">Collection of actions which must be included.</param> /// <returns>Null if there is no parent. Used for loop termination.</returns> private WixActionSymbol GetParentActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols) { if (null == actionSymbol.Before && null == actionSymbol.After) { return(null); } var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + (actionSymbol.After ?? actionSymbol.Before); if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol)) { WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol); } return(parentActionSymbol); }
/// <summary> /// Sequence an action before or after a standard action. /// </summary> /// <param name="actionSymbol">The action symbol to be sequenced.</param> /// <param name="requiredActionSymbols">Collection of actions which must be included.</param> private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols) { var after = false; if (actionSymbol.After != null) { after = true; } else if (actionSymbol.Before == null) { throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set."); } var parentActionName = (after ? actionSymbol.After : actionSymbol.Before); var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + parentActionName; if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol)) { // If the missing parent action is a standard action (with a suggested sequence number), add it. if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol)) { // Create a clone to avoid modifying the static copy of the object. // TODO: consider this: parentActionSymbol = parentActionSymbol.Clone(); requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol); } else { throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Found an action with a non-existent {0} action: {1}.", (after ? "After" : "Before"), parentActionName)); } } else if (actionSymbol == parentActionSymbol || this.ContainsChildActionSymbol(actionSymbol, parentActionSymbol)) // cycle detected { throw new WixException(ErrorMessages.ActionCircularDependency(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, parentActionSymbol.Action)); } // Add this action to the appropriate list of dependent action symbols. var relativeActions = this.GetRelativeActions(parentActionSymbol); var relatedSymbols = (after ? relativeActions.NextActions : relativeActions.PreviousActions); relatedSymbols.Add(actionSymbol); }
/// <summary> /// Sequence an action before or after a standard action. /// </summary> /// <param name="actionSymbol">The action symbol to be sequenced.</param> /// <param name="requiredActionSymbols">Collection of actions which must be included.</param> /// <param name="firstReference">A dictionary used for detecting cyclic references among action symbols.</param> private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols, Dictionary <WixActionSymbol, WixActionSymbol> firstReference) { var after = false; if (actionSymbol.After != null) { after = true; } else if (actionSymbol.Before == null) { throw new WixException($"Found action '{actionSymbol.Id.Id}' at {actionSymbol.SourceLineNumbers}' with no Sequence, Before, or After column set. The compiler should have prevented this."); } var parentActionName = (after ? actionSymbol.After : actionSymbol.Before); var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + parentActionName; if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol)) { // If the missing parent action is a standard action (with a suggested sequence number), add it. if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol)) { // Create a clone to avoid modifying the static copy of the object. // TODO: consider this: parentActionSymbol = parentActionSymbol.Clone(); requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol); } else { throw new WixException($"Found action {actionSymbol.Id.Id} with a non-existent {(after ? "After" : "Before")} action '{parentActionName}'. The linker should have prevented this."); } } this.CheckForCircularActionReference(actionSymbol, requiredActionSymbols, firstReference); // Add this action to the appropriate list of dependent action symbols. var relativeActions = this.GetRelativeActions(parentActionSymbol); var relatedSymbols = (after ? relativeActions.NextActions : relativeActions.PreviousActions); relatedSymbols.Add(actionSymbol); }
public void Execute() { var requiredActionSymbols = new Dictionary <string, WixActionSymbol>(); // Get the standard actions required based on symbols in the section. var overridableActionSymbols = this.GetRequiredStandardActions(); // Index all the action symbols and look for collisions. foreach (var actionSymbol in this.Section.Symbols.OfType <WixActionSymbol>()) { if (actionSymbol.Overridable) // overridable action { if (overridableActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol)) { this.Messaging.Write(ErrorMessages.OverridableActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action)); if (null != collidingActionSymbol.SourceLineNumbers) { this.Messaging.Write(ErrorMessages.OverridableActionCollision2(collidingActionSymbol.SourceLineNumbers)); } } else { overridableActionSymbols.Add(actionSymbol.Id.Id, actionSymbol); } } else // unsequenced or sequenced action. { // Unsequenced action (allowed for certain standard actions). if (null == actionSymbol.Before && null == actionSymbol.After && !actionSymbol.Sequence.HasValue) { if (WindowsInstallerStandard.TryGetStandardAction(actionSymbol.Id.Id, out var standardAction)) { // Populate the sequence from the standard action actionSymbol.Sequence = standardAction.Sequence; } else // not a supported unscheduled action. { throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set."); } } if (requiredActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol)) { this.Messaging.Write(ErrorMessages.ActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action)); if (null != collidingActionSymbol.SourceLineNumbers) { this.Messaging.Write(ErrorMessages.ActionCollision2(collidingActionSymbol.SourceLineNumbers)); } } else { requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol); } } } // Add the overridable action symbols that are not overridden to the required action symbols. foreach (var actionSymbol in overridableActionSymbols.Values) { if (!requiredActionSymbols.ContainsKey(actionSymbol.Id.Id)) { requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol); } } // Suppress the required actions that are overridable. foreach (var suppressActionSymbol in this.Section.Symbols.OfType <WixSuppressActionSymbol>()) { var key = suppressActionSymbol.Id.Id; // If there is an overridable symbol to suppress; suppress it. There is no warning if there // is no action to suppress because the action may be suppressed from a merge module in // the binder. if (requiredActionSymbols.TryGetValue(key, out var requiredActionSymbol)) { if (requiredActionSymbol.Overridable) { this.Messaging.Write(WarningMessages.SuppressAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.Action, suppressActionSymbol.SequenceTable.ToString())); if (null != requiredActionSymbol.SourceLineNumbers) { this.Messaging.Write(WarningMessages.SuppressAction2(requiredActionSymbol.SourceLineNumbers)); } requiredActionSymbols.Remove(key); } else // suppressing a non-overridable action symbol { this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.SequenceTable.ToString(), suppressActionSymbol.Action)); if (null != requiredActionSymbol.SourceLineNumbers) { this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction2(requiredActionSymbol.SourceLineNumbers)); } } } } // Build up dependency trees of the relatively scheduled actions. // Use ToList() to create a copy of the required action symbols so that new symbols can // be added while enumerating. foreach (var actionSymbol in requiredActionSymbols.Values.ToList()) { if (!actionSymbol.Sequence.HasValue) { // check for standard actions that don't have a sequence number in a merge module if (SectionType.Module == this.Section.Type && WindowsInstallerStandard.IsStandardAction(actionSymbol.Action)) { this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action)); } this.SequenceActionSymbol(actionSymbol, requiredActionSymbols); } 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 { this.Messaging.Write(ErrorMessages.CustomActionSequencedInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action)); } } // Look for standard actions with sequence restrictions that aren't necessarily scheduled based // on the presence of a particular table. if (requiredActionSymbols.ContainsKey("InstallExecuteSequence/DuplicateFiles") && !requiredActionSymbols.ContainsKey("InstallExecuteSequence/InstallFiles")) { WindowsInstallerStandard.TryGetStandardAction("InstallExecuteSequence/InstallFiles", out var standardAction); requiredActionSymbols.Add(standardAction.Id.Id, standardAction); } // Schedule actions. List <WixActionSymbol> scheduledActionSymbols; if (SectionType.Module == this.Section.Type) { scheduledActionSymbols = requiredActionSymbols.Values.ToList(); } else { scheduledActionSymbols = this.ScheduleActions(requiredActionSymbols); } // Remove all existing WixActionSymbols from the section then add the // scheduled actions back to the section. Note: we add the indices in // reverse order to make it easy to remove them from the list later. var removeIndices = new List <int>(); for (var i = this.Section.Symbols.Count - 1; i >= 0; --i) { var symbol = this.Section.Symbols[i]; if (symbol.Definition.Type == SymbolDefinitionType.WixAction) { removeIndices.Add(i); } } foreach (var removeIndex in removeIndices) { this.Section.Symbols.RemoveAt(removeIndex); } foreach (var action in scheduledActionSymbols) { this.Section.AddSymbol(action); } }
/// <summary> /// Adds the PatchFiles action to the sequence table if it does not already exist. /// </summary> /// <param name="table">The sequence table to check or modify.</param> /// <param name="mainTransform">The primary authoring transform.</param> /// <param name="pairedTransform">The secondary patch transform.</param> /// <param name="mainFileRow">The file row that contains information about the patched file.</param> private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow) { var tableName = table.ToString(); // Find/add PatchFiles action (also determine sequence for it). // Search mainTransform first, then pairedTransform (pairedTransform overrides). var hasPatchFilesAction = false; var installFilesSequence = 0; var duplicateFilesSequence = 0; TestSequenceTableForPatchFilesAction( mainTransform.Tables[tableName], ref hasPatchFilesAction, ref installFilesSequence, ref duplicateFilesSequence); TestSequenceTableForPatchFilesAction( pairedTransform.Tables[tableName], ref hasPatchFilesAction, ref installFilesSequence, ref duplicateFilesSequence); if (!hasPatchFilesAction) { WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionSymbol); var sequence = patchFilesActionSymbol.Sequence; // Test for default sequence value's appropriateness if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence)) { if (0 != duplicateFilesSequence) { if (duplicateFilesSequence < installFilesSequence) { throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action)); } else { sequence = (duplicateFilesSequence + installFilesSequence) / 2; if (installFilesSequence == sequence || duplicateFilesSequence == sequence) { throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action)); } } } else { sequence = installFilesSequence + 1; } } var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); if (0 == sequenceTable.Rows.Count) { sequenceTable.Operation = TableOperation.Add; } var patchAction = sequenceTable.CreateRow(null); patchAction[0] = patchFilesActionSymbol.Action; patchAction[1] = patchFilesActionSymbol.Condition; patchAction[2] = sequence; patchAction.Operation = RowOperation.Add; } }