diff --git a/src/api/wix/WixToolset.Data/Symbols/WixPackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixPackageSymbol.cs index e17200332..4a112266c 100644 --- a/src/api/wix/WixToolset.Data/Symbols/WixPackageSymbol.cs +++ b/src/api/wix/WixToolset.Data/Symbols/WixPackageSymbol.cs @@ -46,6 +46,12 @@ public enum WixPackageAttributes PerMachine = 0x1, } + public enum WixPackageUpgradeStrategy + { + None = 0x0, + MajorUpgrade = 0x1, + } + public class WixPackageSymbol : IntermediateSymbol { public WixPackageSymbol() : base(SymbolDefinitions.WixPackage, null, null) @@ -106,6 +112,12 @@ public string Codepage set => this.Set((int)WixPackageSymbolFields.Codepage, value); } + public WixPackageUpgradeStrategy UpgradeStrategy + { + get => (WixPackageUpgradeStrategy)this.Fields[(int)WixPackageSymbolFields.Attributes].AsNumber(); + set => this.Set((int)WixPackageSymbolFields.Attributes, (int)value); + } + public bool PerMachine => (this.Attributes & WixPackageAttributes.PerMachine) == WixPackageAttributes.PerMachine; } } diff --git a/src/api/wix/WixToolset.Data/WixStandardLibrary.cs b/src/api/wix/WixToolset.Data/WixStandardLibrary.cs index 3758e5b15..3f851f090 100644 --- a/src/api/wix/WixToolset.Data/WixStandardLibrary.cs +++ b/src/api/wix/WixToolset.Data/WixStandardLibrary.cs @@ -32,7 +32,17 @@ public static Intermediate Build(Platform platform) private static IEnumerable YieldLocalizations() { - var strings = new BindVariable[0]; + var sourceLineNumber = new SourceLineNumber("wixstd.wixlib"); + + var strings = new[] { + new BindVariable() + { + SourceLineNumbers = sourceLineNumber, + Id = "WixDowngradePreventedMessage", + Value = "A newer version of [ProductName] is already installed.", + Overridable = true, + }, + }; var localizedControls = new LocalizedControl[0]; diff --git a/src/api/wix/WixToolset.Data/WixStandardLibraryIdentifiers.cs b/src/api/wix/WixToolset.Data/WixStandardLibraryIdentifiers.cs index 6579a42b9..73e4245b7 100644 --- a/src/api/wix/WixToolset.Data/WixStandardLibraryIdentifiers.cs +++ b/src/api/wix/WixToolset.Data/WixStandardLibraryIdentifiers.cs @@ -21,5 +21,10 @@ public static class WixStandardLibraryIdentifiers /// Default feature name. /// public static readonly string DefaultFeatureName = "WixDefaultFeature"; + + /// + /// WiX Standard localization strings. + /// + public static readonly string WixStandardLocalizationStrings = "WixStandardLocalizationStrings"; } } diff --git a/src/wix/WixToolset.Core/Compiler_Package.cs b/src/wix/WixToolset.Core/Compiler_Package.cs index 17a6a913f..31b8e81cc 100644 --- a/src/wix/WixToolset.Core/Compiler_Package.cs +++ b/src/wix/WixToolset.Core/Compiler_Package.cs @@ -41,6 +41,7 @@ private void ParsePackageElement(XElement node) var isPackageNameSet = false; var isKeywordsSet = false; var isPackageAuthorSet = false; + var upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade; this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion); @@ -111,6 +112,21 @@ private void ParsePackageElement(XElement node) case "UpgradeCode": upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; + case "UpgradeStrategy": + var strategy = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (strategy) + { + case "majorUpgrade": + upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade; + break; + case "none": + upgradeStrategy = WixPackageUpgradeStrategy.None; + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, strategy, "majorUpgrade", "none")); + break; + } + break; case "Version": version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); break; @@ -382,6 +398,7 @@ private void ParsePackageElement(XElement node) Manufacturer = manufacturer, Attributes = isPerMachine ? WixPackageAttributes.PerMachine : WixPackageAttributes.None, Codepage = codepage, + UpgradeStrategy = upgradeStrategy, }); if (!isCommentsSet) diff --git a/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs index 0023261fe..ab0eb8c2c 100644 --- a/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs +++ b/src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs @@ -2,6 +2,7 @@ namespace WixToolset.Core.Link { + using System; using System.Collections.Generic; using System.Linq; using WixToolset.Data; @@ -19,8 +20,6 @@ public AddDefaultSymbolsCommand(FindEntrySectionAndLoadSymbolsCommand find, List this.Sections = sections; } - public IntermediateSection EntrySection { get; } - public IEnumerable Sections { get; } public FindEntrySectionAndLoadSymbolsCommand Find { get; } @@ -34,8 +33,8 @@ public void Execute() } var symbols = this.Sections.SelectMany(s => s.Symbols); - var directorySymbols = symbols.OfType(); var referenceSymbols = symbols.OfType(); + var directorySymbols = symbols.OfType(); if (referenceSymbols.Any(s => s.SymbolicName == WixStandardInstallFolderReference) && !directorySymbols.Any(d => d.Id.Id == WixStandardInstallFolder)) @@ -55,15 +54,81 @@ public void Execute() PrimaryKeys = WixStandardInstallFolderParent, }); } + + var upgradeSymbols = symbols.OfType(); + if (!upgradeSymbols.Any()) + { + var packageSymbol = this.Find.EntrySection.Symbols.OfType().FirstOrDefault(); + + if (packageSymbol?.UpgradeStrategy == WixPackageUpgradeStrategy.MajorUpgrade + && !String.IsNullOrEmpty(packageSymbol?.UpgradeCode)) + { + this.AddDefaultMajorUpgrade(packageSymbol); + } + + } + } + + private void AddDefaultMajorUpgrade(WixPackageSymbol packageSymbol) + { + this.AddSymbol(new UpgradeSymbol(packageSymbol.SourceLineNumbers) + { + UpgradeCode = packageSymbol.UpgradeCode, + MigrateFeatures = true, + ActionProperty = WixUpgradeConstants.UpgradeDetectedProperty, + VersionMax = packageSymbol.Version, + Language = packageSymbol.Language, + }); + + this.AddSymbol(new UpgradeSymbol(packageSymbol.SourceLineNumbers) + { + UpgradeCode = packageSymbol.UpgradeCode, + VersionMin = packageSymbol.Version, + Language = packageSymbol.Language, + OnlyDetect = true, + ActionProperty = WixUpgradeConstants.DowngradeDetectedProperty, + }); + + this.AddSymbol(new LaunchConditionSymbol(packageSymbol.SourceLineNumbers) + { + Condition = WixUpgradeConstants.DowngradePreventedCondition, + Description = "!(loc.WixDowngradePreventedMessage)", + }); + + this.CreateActionSymbol(this.Find.EntrySection, packageSymbol.SourceLineNumbers, SequenceTable.InstallExecuteSequence, "RemoveExistingProducts", "InstallValidate"); + } + + public WixActionSymbol CreateActionSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, SequenceTable sequence, string actionName, string afterAction) + { + var actionId = new Identifier(AccessModifier.Global, sequence, actionName); + + var actionSymbol = section.AddSymbol(new WixActionSymbol(sourceLineNumbers, actionId) + { + SequenceTable = sequence, + Action = actionName, + After = afterAction, + }); + + section.AddSymbol(new WixSimpleReferenceSymbol(sourceLineNumbers) + { + Table = SymbolDefinitions.WixAction.Name, + PrimaryKeys = $"{sequence}/{afterAction}", + }); + + return actionSymbol; } private void AddSymbol(IntermediateSymbol symbol) { this.Find.EntrySection.AddSymbol(symbol); - var symbolWithSection = new SymbolWithSection(this.Find.EntrySection, symbol); - var fullName = symbolWithSection.GetFullName(); - this.Find.SymbolsByName.Add(fullName, symbolWithSection); + if (!String.IsNullOrEmpty(symbol.Id?.Id)) + { + var symbolWithSection = new SymbolWithSection(this.Find.EntrySection, symbol); + var fullName = symbolWithSection.GetFullName(); + + this.Find.SymbolsByName.Add(fullName, symbolWithSection); + } } } } diff --git a/src/wix/WixToolset.Core/Linker.cs b/src/wix/WixToolset.Core/Linker.cs index ce6c067b6..a120f98d9 100644 --- a/src/wix/WixToolset.Core/Linker.cs +++ b/src/wix/WixToolset.Core/Linker.cs @@ -90,7 +90,7 @@ public Intermediate Link(ILinkContext context) if (library.Localizations?.Count > 0) { // Include localizations from the extension data and be sure to note that the localization came from - // an extension. It is important to remember whiche localization came from an extension when filtering + // an extension. It is important to remember which localization came from an extension when filtering // localizations during the resolve process later. localizations.AddRange(library.Localizations.Select(l => l.UpdateLocation(LocalizationLocation.Extension))); } @@ -103,6 +103,11 @@ public Intermediate Link(ILinkContext context) var stdlib = WixStandardLibrary.Build(this.Context.Platform); sections.AddRange(stdlib.Sections); + + if (stdlib.Localizations?.Count > 0) + { + localizations.AddRange(stdlib.Localizations); + } } var multipleFeatureComponents = new Hashtable(); diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgrade/DefaultMajorUpgrade.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgrade/DefaultMajorUpgrade.wxs new file mode 100644 index 000000000..ce9fd96fb --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgrade/DefaultMajorUpgrade.wxs @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgradeNone/DefaultMajorUpgradeNone.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgradeNone/DefaultMajorUpgradeNone.wxs new file mode 100644 index 000000000..63ff178bc --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgradeNone/DefaultMajorUpgradeNone.wxs @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs index 3d2aa7227..1ae742107 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs @@ -58,6 +58,31 @@ public void MajorUpgradeDowngradeMessagePopulatesRowsAsExpected() }, results); } + [Fact] + public void DefaultMajorUpgradePopulatesUpgradeRowsAsExpected() + { + var folder = TestData.Get("TestData", "DefaultMajorUpgrade"); + var build = new Builder(folder, new Type[] { }, new[] { folder }); + + var results = build.BuildAndQuery(Build, "Upgrade", "LaunchCondition"); + WixAssert.CompareLineByLine(new[] + { + "LaunchCondition:NOT WIX_DOWNGRADE_DETECTED\tA newer version of [ProductName] is already installed.", + "Upgrade:{7AB24276-C628-43DB-9E65-A184D052909B}\t\t2.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED", + "Upgrade:{7AB24276-C628-43DB-9E65-A184D052909B}\t2.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED", + }, results); + } + + [Fact] + public void UpgradeStrategyNoneDoesNotCreateDefaultMajorUpgrade() + { + var folder = TestData.Get("TestData", "DefaultMajorUpgradeNone"); + var build = new Builder(folder, new Type[] { }, new[] { folder }); + + var results = build.BuildAndQuery(Build, "Upgrade", "LaunchCondition"); + Assert.Empty(results); + } + private static void Build(string[] args) { var result = WixRunner.Execute(args);