@joebigelow / wix / commits / 1eea9c6e

Add default major upgrade.

Add Package/@UpgradeStrategy to allow `none` to suppress major upgrade. Implements https://github.com/wixtoolset/issues/issues/7605. Requires https://github.com/wixtoolset/wix/pull/435.

Bob Arnson committed Dec 23, 2023 at 21:37 UTC 1eea9c6e7b276c7c0dfde7779dfec45046ad5831
9 files changed +164 -5
src/api/wix/WixToolset.Data/Symbols/WixPackageSymbol.cs
+12
@@ -46,6 +46,12 @@ namespace WixToolset.Data.Symbols
46 PerMachine = 0x1,
47 }
48
49 + public enum WixPackageUpgradeStrategy
50 + {
51 + None = 0x0,
52 + MajorUpgrade = 0x1,
53 + }
54 +
55 public class WixPackageSymbol : IntermediateSymbol
56 {
57 public WixPackageSymbol() : base(SymbolDefinitions.WixPackage, null, null)
@@ -106,6 +112,12 @@ namespace WixToolset.Data.Symbols
112 set => this.Set((int)WixPackageSymbolFields.Codepage, value);
113 }
114
115 + public WixPackageUpgradeStrategy UpgradeStrategy
116 + {
117 + get => (WixPackageUpgradeStrategy)this.Fields[(int)WixPackageSymbolFields.Attributes].AsNumber();
118 + set => this.Set((int)WixPackageSymbolFields.Attributes, (int)value);
119 + }
120 +
121 public bool PerMachine => (this.Attributes & WixPackageAttributes.PerMachine) == WixPackageAttributes.PerMachine;
122 }
123 }
src/api/wix/WixToolset.Data/WixStandardLibrary.cs
+11 -1
@@ -32,7 +32,17 @@ namespace WixToolset.Data
32
33 private static IEnumerable<Localization> YieldLocalizations()
34 {
35 - var strings = new BindVariable[0];
35 + var sourceLineNumber = new SourceLineNumber("wixstd.wixlib");
36 +
37 + var strings = new[] {
38 + new BindVariable()
39 + {
40 + SourceLineNumbers = sourceLineNumber,
41 + Id = "WixDowngradePreventedMessage",
42 + Value = "A newer version of [ProductName] is already installed.",
43 + Overridable = true,
44 + },
45 + };
46
47 var localizedControls = new LocalizedControl[0];
48
src/api/wix/WixToolset.Data/WixStandardLibraryIdentifiers.cs
+5
@@ -21,5 +21,10 @@ namespace WixToolset.Data
21 /// Default feature name.
22 /// </summary>
23 public static readonly string DefaultFeatureName = "WixDefaultFeature";
24 +
25 + /// <summary>
26 + /// WiX Standard localization strings.
27 + /// </summary>
28 + public static readonly string WixStandardLocalizationStrings = "WixStandardLocalizationStrings";
29 }
30 }
src/wix/WixToolset.Core/Compiler_Package.cs
+17
@@ -41,6 +41,7 @@ namespace WixToolset.Core
41 var isPackageNameSet = false;
42 var isKeywordsSet = false;
43 var isPackageAuthorSet = false;
44 + var upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade;
45
46 this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion);
47
@@ -111,6 +112,21 @@ namespace WixToolset.Core
112 case "UpgradeCode":
113 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
114 break;
115 + case "UpgradeStrategy":
116 + var strategy = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
117 + switch (strategy)
118 + {
119 + case "majorUpgrade":
120 + upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade;
121 + break;
122 + case "none":
123 + upgradeStrategy = WixPackageUpgradeStrategy.None;
124 + break;
125 + default:
126 + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, strategy, "majorUpgrade", "none"));
127 + break;
128 + }
129 + break;
130 case "Version":
131 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
132 break;
@@ -382,6 +398,7 @@ namespace WixToolset.Core
398 Manufacturer = manufacturer,
399 Attributes = isPerMachine ? WixPackageAttributes.PerMachine : WixPackageAttributes.None,
400 Codepage = codepage,
401 + UpgradeStrategy = upgradeStrategy,
402 });
403
404 if (!isCommentsSet)
src/wix/WixToolset.Core/Link/AddDefaultSymbolsCommand.cs
+64 -3
@@ -2,6 +2,7 @@
2
3 namespace WixToolset.Core.Link
4 {
5 + using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
@@ -49,20 +50,80 @@ namespace WixToolset.Core.Link
50 }
51 );
52 }
53 +
54 + var symbols = this.Sections.SelectMany(section => section.Symbols);
55 + var upgradeSymbols = symbols.OfType<UpgradeSymbol>();
56 + if (!upgradeSymbols.Any())
57 + {
58 + var packageSymbol = this.Find.EntrySection.Symbols.OfType<WixPackageSymbol>().FirstOrDefault();
59 +
60 + if (packageSymbol?.UpgradeStrategy == WixPackageUpgradeStrategy.MajorUpgrade
61 + && !String.IsNullOrEmpty(packageSymbol?.UpgradeCode))
62 + {
63 + this.AddDefaultMajorUpgrade(packageSymbol);
64 + }
65 + }
66 + }
67 +
68 + private void AddDefaultMajorUpgrade(WixPackageSymbol packageSymbol)
69 + {
70 + this.AddSymbols(this.Find.EntrySection,
71 + new UpgradeSymbol(packageSymbol.SourceLineNumbers)
72 + {
73 + UpgradeCode = packageSymbol.UpgradeCode,
74 + MigrateFeatures = true,
75 + ActionProperty = WixUpgradeConstants.UpgradeDetectedProperty,
76 + VersionMax = packageSymbol.Version,
77 + Language = packageSymbol.Language,
78 + },
79 + new UpgradeSymbol(packageSymbol.SourceLineNumbers)
80 + {
81 + UpgradeCode = packageSymbol.UpgradeCode,
82 + VersionMin = packageSymbol.Version,
83 + Language = packageSymbol.Language,
84 + OnlyDetect = true,
85 + ActionProperty = WixUpgradeConstants.DowngradeDetectedProperty,
86 + },
87 + new LaunchConditionSymbol(packageSymbol.SourceLineNumbers)
88 + {
89 + Condition = WixUpgradeConstants.DowngradePreventedCondition,
90 + Description = "!(loc.WixDowngradePreventedMessage)",
91 + },
92 + new WixActionSymbol(packageSymbol.SourceLineNumbers,
93 + new Identifier(AccessModifier.Global, SequenceTable.InstallExecuteSequence, "RemoveExistingProducts"))
94 + {
95 + SequenceTable = SequenceTable.InstallExecuteSequence,
96 + Action = "RemoveExistingProducts",
97 + After = "InstallValidate",
98 + },
99 + new WixSimpleReferenceSymbol(packageSymbol.SourceLineNumbers)
100 + {
101 + Table = SymbolDefinitions.WixAction.Name,
102 + PrimaryKeys = "InstallExecuteSequence/InstallValidate",
103 + });
104 }
105
106 private void AddSymbolsToNewSection(string sectionId, params IntermediateSymbol[] symbols)
107 {
108 var section = new IntermediateSection(sectionId, SectionType.Fragment);
109 +
110 this.Sections.Add(section);
111
112 + this.AddSymbols(section, symbols);
113 + }
114 +
115 + private void AddSymbols(IntermediateSection section, params IntermediateSymbol[] symbols)
116 + {
117 foreach (var symbol in symbols)
118 {
119 section.AddSymbol(symbol);
120
63 - var symbolWithSection = new SymbolWithSection(section, symbol);
64 - var fullName = symbolWithSection.GetFullName();
65 - this.Find.SymbolsByName.Add(fullName, symbolWithSection);
121 + if (!String.IsNullOrEmpty(symbol.Id?.Id))
122 + {
123 + var symbolWithSection = new SymbolWithSection(section, symbol);
124 + var fullName = symbolWithSection.GetFullName();
125 + this.Find.SymbolsByName.Add(fullName, symbolWithSection);
126 + }
127 }
128 }
129 }
src/wix/WixToolset.Core/Linker.cs
+6 -1
@@ -90,7 +90,7 @@ namespace WixToolset.Core
90 if (library.Localizations?.Count > 0)
91 {
92 // Include localizations from the extension data and be sure to note that the localization came from
93 - // an extension. It is important to remember whiche localization came from an extension when filtering
93 + // an extension. It is important to remember which localization came from an extension when filtering
94 // localizations during the resolve process later.
95 localizations.AddRange(library.Localizations.Select(l => l.UpdateLocation(LocalizationLocation.Extension)));
96 }
@@ -103,6 +103,11 @@ namespace WixToolset.Core
103 var stdlib = WixStandardLibrary.Build(this.Context.Platform);
104
105 sections.AddRange(stdlib.Sections);
106 +
107 + if (stdlib.Localizations?.Count > 0)
108 + {
109 + localizations.AddRange(stdlib.Localizations);
110 + }
111 }
112
113 var multipleFeatureComponents = new Hashtable();
src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgrade/DefaultMajorUpgrade.wxs new
+12
@@ -0,0 +1,12 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Package Name="MajorUpgradeDowngradeMessage" Language="1033" Version="2.0.0" Manufacturer="Example Corporation" UpgradeCode="7ab24276-c628-43db-9e65-a184d052909b" Scope="perMachine">
3 + <Feature Id="ProductFeature" Title="MsiPackageTitle">
4 + </Feature>
5 + </Package>
6 +
7 + <Fragment>
8 + <StandardDirectory Id="ProgramFiles6432Folder">
9 + <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
10 + </StandardDirectory>
11 + </Fragment>
12 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/DefaultMajorUpgradeNone/DefaultMajorUpgradeNone.wxs new
+12
@@ -0,0 +1,12 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Package Name="MajorUpgradeDowngradeMessage" UpgradeStrategy="none" Language="1033" Version="2.0.0" Manufacturer="Example Corporation" UpgradeCode="7ab24276-c628-43db-9e65-a184d052909b" Scope="perMachine">
3 + <Feature Id="ProductFeature" Title="MsiPackageTitle">
4 + </Feature>
5 + </Package>
6 +
7 + <Fragment>
8 + <StandardDirectory Id="ProgramFiles6432Folder">
9 + <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
10 + </StandardDirectory>
11 + </Fragment>
12 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs
+25
@@ -58,6 +58,31 @@ namespace WixToolsetTest.CoreIntegration
58 }, results);
59 }
60
61 + [Fact]
62 + public void DefaultMajorUpgradePopulatesUpgradeRowsAsExpected()
63 + {
64 + var folder = TestData.Get("TestData", "DefaultMajorUpgrade");
65 + var build = new Builder(folder, new Type[] { }, new[] { folder });
66 +
67 + var results = build.BuildAndQuery(Build, "Upgrade", "LaunchCondition");
68 + WixAssert.CompareLineByLine(new[]
69 + {
70 + "LaunchCondition:NOT WIX_DOWNGRADE_DETECTED\tA newer version of [ProductName] is already installed.",
71 + "Upgrade:{7AB24276-C628-43DB-9E65-A184D052909B}\t\t2.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED",
72 + "Upgrade:{7AB24276-C628-43DB-9E65-A184D052909B}\t2.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED",
73 + }, results);
74 + }
75 +
76 + [Fact]
77 + public void UpgradeStrategyNoneDoesNotCreateDefaultMajorUpgrade()
78 + {
79 + var folder = TestData.Get("TestData", "DefaultMajorUpgradeNone");
80 + var build = new Builder(folder, new Type[] { }, new[] { folder });
81 +
82 + var results = build.BuildAndQuery(Build, "Upgrade", "LaunchCondition");
83 + Assert.Empty(results);
84 + }
85 +
86 private static void Build(string[] args)
87 {
88 var result = WixRunner.Execute(args);