diff --git a/src/wix/WixToolset.Core/Compiler_Package.cs b/src/wix/WixToolset.Core/Compiler_Package.cs index 4b934fa9f..74b3aae26 100644 --- a/src/wix/WixToolset.Core/Compiler_Package.cs +++ b/src/wix/WixToolset.Core/Compiler_Package.cs @@ -2504,8 +2504,8 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) var exitSequence = CompilerConstants.IntegerNotSet; var sequence = CompilerConstants.IntegerNotSet; var showDialog = "Show" == actionName; - var specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName; - var specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName; + var specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName; // these actions do NOT have default sequence numbers and MUST be scheduled. + var specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName; // these standard actions have default sequence numbers so they do NOT have to be scheduled. var suppress = false; foreach (var attrib in child.Attributes()) @@ -2608,6 +2608,8 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) } } + var standardAction = WindowsInstallerStandard.IsStandardAction(actionName); + if (customAction && "Custom" == actionName) { this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Action")); @@ -2653,7 +2655,7 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) } // normal standard actions cannot be set overridable by the user (since they are overridable by default) - if (overridable && WindowsInstallerStandard.IsStandardAction(actionName) && !specialAction) + if (overridable && standardAction && !specialAction) { this.Core.Write(ErrorMessages.UnexpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Overridable")); } @@ -2688,6 +2690,10 @@ private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) { access = actionIdentifier.Access; } + else if (standardAction) + { + access = AccessModifier.Override; + } var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(access, sequenceTable, actionName)) { diff --git a/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs index 556a24d74..805dbce4d 100644 --- a/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs +++ b/src/wix/WixToolset.Core/Link/ProcessConflictingSymbolsCommand.cs @@ -131,9 +131,16 @@ public void Execute() // Ensure referenced override symbols actually overrode a virtual symbol. foreach (var referencedOverrideSymbol in this.OverrideSymbols.Where(s => this.ResolvedSections.Contains(s.Section))) { + // The easiest check is to see if the symbol overrode a virtual symbol. If not, check to see if there were any possible + // virtual symbols that could have been overridden. If not, then we have an error. if (referencedOverrideSymbol.Overrides is null) { - this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(referencedOverrideSymbol.Symbol)); + var otherVirtualsCount = referencedOverrideSymbol.PossiblyConflicts.Count(s => s.Access == AccessModifier.Virtual); + + if (otherVirtualsCount == 0) + { + this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(referencedOverrideSymbol.Symbol)); + } } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/StandardActionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/StandardActionFixture.cs new file mode 100644 index 000000000..23cf30ae4 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/StandardActionFixture.cs @@ -0,0 +1,124 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolsetTest.CoreIntegration +{ + using System.IO; + using System.Linq; + using WixInternal.Core.TestPackage; + using WixInternal.TestSupport; + using Xunit; + + public class StandardActionFixture + { + [Fact] + public void CanCompileSpecialActionWithOverride() + { + using var fs = new DisposableFileSystem(); + + var results = BuildAndQueryMsi(fs, "SpecialActionOverride.wxs"); + + WixAssert.CompareLineByLine(new[] + { + "InstallExecuteSequence:AppSearch\t\t99", + "InstallExecuteSequence:CostFinalize\t\t1000", + "InstallExecuteSequence:CostInitialize\t\t800", + "InstallExecuteSequence:CreateFolders\t\t3700", + "InstallExecuteSequence:FileCost\t\t900", + "InstallExecuteSequence:FindRelatedProducts\t\t98", + "InstallExecuteSequence:InstallFiles\t\t4000", + "InstallExecuteSequence:InstallFinalize\t\t6600", + "InstallExecuteSequence:InstallInitialize\t\t1500", + "InstallExecuteSequence:InstallValidate\t\t1400", + "InstallExecuteSequence:LaunchConditions\t\t100", + "InstallExecuteSequence:MigrateFeatureStates\t\t1200", + "InstallExecuteSequence:ProcessComponents\t\t1600", + "InstallExecuteSequence:PublishFeatures\t\t6300", + "InstallExecuteSequence:PublishProduct\t\t6400", + "InstallExecuteSequence:RegisterProduct\t\t6100", + "InstallExecuteSequence:RegisterUser\t\t6000", + "InstallExecuteSequence:RemoveExistingProducts\t\t1401", + "InstallExecuteSequence:RemoveFiles\t\t3500", + "InstallExecuteSequence:RemoveFolders\t\t3600", + "InstallExecuteSequence:UnpublishFeatures\t\t1800", + "InstallExecuteSequence:ValidateProductID\t\t700", + "InstallUISequence:CostFinalize\t\t1000", + "InstallUISequence:CostInitialize\t\t800", + "InstallUISequence:ExecuteAction\t\t1300", + "InstallUISequence:FileCost\t\t900", + "InstallUISequence:FindRelatedProducts\t\t25", + "InstallUISequence:LaunchConditions\t\t100", + "InstallUISequence:MigrateFeatureStates\t\t1200", + "InstallUISequence:ValidateProductID\t\t700", + }, results); + } + + [Fact] + public void CanCompileStandardActionWithOverride() + { + using var fs = new DisposableFileSystem(); + + var results = BuildAndQueryMsi(fs, "StandardActionOverride.wxs"); + + WixAssert.CompareLineByLine(new[] + { + "InstallExecuteSequence:CostFinalize\t\t1000", + "InstallExecuteSequence:CostInitialize\t\t800", + "InstallExecuteSequence:CreateFolders\t\t3700", + "InstallExecuteSequence:FileCost\t\t900", + "InstallExecuteSequence:FindRelatedProducts\t\t25", + "InstallExecuteSequence:InstallFiles\tTEST_CONDITION\t4000", + "InstallExecuteSequence:InstallFinalize\t\t6600", + "InstallExecuteSequence:InstallInitialize\t\t1500", + "InstallExecuteSequence:InstallValidate\t\t1400", + "InstallExecuteSequence:LaunchConditions\t\t100", + "InstallExecuteSequence:MigrateFeatureStates\t\t1200", + "InstallExecuteSequence:ProcessComponents\t\t1600", + "InstallExecuteSequence:PublishFeatures\t\t6300", + "InstallExecuteSequence:PublishProduct\t\t6400", + "InstallExecuteSequence:RegisterProduct\t\t6100", + "InstallExecuteSequence:RegisterUser\t\t6000", + "InstallExecuteSequence:RemoveExistingProducts\t\t1401", + "InstallExecuteSequence:RemoveFiles\t\t3500", + "InstallExecuteSequence:RemoveFolders\t\t3600", + "InstallExecuteSequence:UnpublishFeatures\t\t1800", + "InstallExecuteSequence:ValidateProductID\t\t700", + "InstallUISequence:CostFinalize\t\t1000", + "InstallUISequence:CostInitialize\t\t800", + "InstallUISequence:ExecuteAction\t\t1300", + "InstallUISequence:FileCost\t\t900", + "InstallUISequence:FindRelatedProducts\t\t25", + "InstallUISequence:LaunchConditions\t\t100", + "InstallUISequence:MigrateFeatureStates\t\t1200", + "InstallUISequence:ValidateProductID\t\t700", + }, results); + } + + private static string[] BuildAndQueryMsi(DisposableFileSystem fs, string sourceFile) + { + var folder = TestData.Get(@"TestData"); + + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, "bin", "test.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "StandardAction", sourceFile), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-o", msiPath + }); + + result.AssertSuccess(); + + var results = Query.QueryDatabase(msiPath, new[] + { + "InstallExecuteSequence", + "InstallUISequence" + }).ToArray(); + + return results; + } + } +} diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/SpecialActionOverride.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/SpecialActionOverride.wxs new file mode 100644 index 000000000..e10538c18 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/SpecialActionOverride.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/StandardActionOverride.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/StandardActionOverride.wxs new file mode 100644 index 000000000..2965014ea --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/StandardAction/StandardActionOverride.wxs @@ -0,0 +1,10 @@ + + + + + + + + +