@joebigelow / wix / commits / 36a8f464

Support bind variables in Package and Bundle versions

Closes 6779

Rob Mensching committed Jul 7, 2022 at 17:52 UTC 36a8f464f6c9b7e553c94b29ee3ca836638eef4d
22 files changed +380 -72
src/api/wix/WixToolset.Data/ErrorMessages.cs
+1 -6
@@ -1326,12 +1326,7 @@ namespace WixToolset.Data
1326
1327 public static Message InvalidProductVersion(SourceLineNumber sourceLineNumbers, string version)
1328 {
1329 - return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.", version);
1330 - }
1331 -
1332 - public static Message InvalidProductVersion(SourceLineNumber sourceLineNumbers, string version, string packagePath)
1333 - {
1334 - return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}' in package '{1}'. When included in a bundle, all product version fields in an MSI package must be less than 65536.", version, packagePath);
1329 + return Message(sourceLineNumbers, Ids.InvalidProductVersion, "Invalid product version '{0}'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.", version);
1330 }
1331
1332 public static Message InvalidRemoveComponent(SourceLineNumber sourceLineNumbers, string component, string feature, string transformPath)
src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
+18 -1
@@ -93,7 +93,7 @@ namespace WixToolset.Extensibility.Services
93 /// <param name="value">The Filename value.</param>
94 /// <param name="source">true to get a source name; false to get a target name</param>
95 /// <param name="longName">true to get a long name; false to get a short name</param>
96 - /// <returns>The name.</returns>
96 + /// <returns>The requesed file name.</returns>
97 string GetMsiFileName(string value, bool source, bool longName);
98
99 /// <summary>
@@ -165,5 +165,22 @@ namespace WixToolset.Extensibility.Services
165 /// Thus the returned array will always be of length 4.
166 /// </remarks>
167 string[] SplitMsiFileName(string value);
168 +
169 + /// <summary>
170 + /// Tries to parse a version from the provided version string.
171 + /// </summary>
172 + /// <param name="version">The version to verify and parse.</param>
173 + /// <param name="parsedVersion">The parsed result if possible, otherwise null.</param>
174 + /// <returns>True if the version was able to parsed, otherwise false.</returns>
175 + bool TryParseFourPartVersion(string version, out string parsedVersion);
176 +
177 + /// <summary>
178 + /// Tries to parse an MSI product version from the provided version string.
179 + /// </summary>
180 + /// <param name="version">The version to verify and parse.</param>
181 + /// <param name="strict">Indicates whether to return a strict (255.255.65535) product version or any valid product version (255.255.65535.*).</param>
182 + /// <param name="parsedVersion">The parsed result if possible, otherwise null.</param>
183 + /// <returns>True if the version was able to parsed as an product version, otherwise false.</returns>
184 + bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion);
185 }
186 }
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+26 -6
@@ -227,6 +227,16 @@ namespace WixToolset.Core.Burn
227 return;
228 }
229
230 + // Resolve any delayed fields now that the variable cache is populated with package information.
231 + if (this.DelayedFields.Any())
232 + {
233 + this.BackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
234 + }
235 +
236 + // Now that delayed variables are resolved the bundle version must be valid so ensure
237 + // it is correct.
238 + this.ProcessBundleVersion(bundleSymbol);
239 +
240 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
241 // are present.
242 payloadSymbols = section.Symbols.OfType<WixBundlePayloadSymbol>().ToDictionary(t => t.Id.Id);
@@ -344,12 +354,6 @@ namespace WixToolset.Core.Burn
354 boundaries = command.UsedRollbackBoundaries;
355 }
356
347 - // Resolve any delayed fields before generating the manifest.
348 - if (this.DelayedFields.Any())
349 - {
350 - this.BackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
351 - }
352 -
357 {
358 var command = new ProcessDependencyProvidersCommand(this.ServiceProvider, section, facades);
359 command.Execute();
@@ -535,6 +539,22 @@ namespace WixToolset.Core.Burn
539 bundleSymbol.UpgradeCode = this.NormalizeBundleRelatedBundleId(bundleSymbol.SourceLineNumbers, bundleSymbol.UpgradeCode, null, null);
540 }
541
542 + private void ProcessBundleVersion(WixBundleSymbol bundleSymbol)
543 + {
544 + if (WixVersion.TryParse(bundleSymbol.Version, out var wixVersion))
545 + {
546 + // Trim the prefix from the version if it is there.
547 + if (wixVersion.Prefix.HasValue)
548 + {
549 + bundleSymbol.Version = bundleSymbol.Version.Substring(1);
550 + }
551 + }
552 + else
553 + {
554 + this.Messaging.Write(ErrorMessages.IllegalVersionValue(bundleSymbol.SourceLineNumbers, "Bundle", "Version", bundleSymbol.Version));
555 + }
556 + }
557 +
558 private string NormalizeBundleRelatedBundleId(SourceLineNumber sourceLineNumber, string relatedBundleId, string elementName, string attributeName)
559 {
560 if (Guid.TryParse(relatedBundleId, out var guid))
src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+1 -1
@@ -92,7 +92,7 @@ namespace WixToolset.Core.Burn
92
93 public static Message InvalidBundleManifest(SourceLineNumber sourceLineNumbers, string bundleExecutable, string reason)
94 {
95 - return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to harvest bundle executable '{0}'. The manifest was invalid. {1}", bundleExecutable, reason);
95 + return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to read bundle executable '{0}'. Its manifest is invalid. {1}", bundleExecutable, reason);
96 }
97
98 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+10
@@ -134,6 +134,16 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
134 return this.backendHelper.SplitMsiFileName(value);
135 }
136
137 + public bool TryParseFourPartVersion(string version, out string parsedVersion)
138 + {
139 + return this.backendHelper.TryParseFourPartVersion(version, out parsedVersion);
140 + }
141 +
142 + public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
143 + {
144 + return this.backendHelper.TryParseMsiProductVersion(version, strict, out parsedVersion);
145 + }
146 +
147 public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
148 {
149 return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+20 -11
@@ -127,6 +127,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
127 tableDefinitions = command.TableDefinitions;
128 }
129
130 + if (section.Type == SectionType.Product)
131 + {
132 + this.ProcessProductVersion(packageSymbol, section, validate: false);
133 + }
134 +
135 // Calculate codepage
136 var codepage = this.CalculateCodepage(packageSymbol, moduleSymbol, patchSymbol);
137
@@ -298,8 +303,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
303 command.Execute();
304 }
305
301 - // Now that delayed fields are processed, validate the package/module version.
302 - this.VerifyVersion(packageSymbol, moduleSymbol);
306 + // Now that delayed fields are processed, fixup the package version (if needed) and validate it
307 + // which will short circuit duplicate errors later if the ProductVersion is invalid.
308 + if (SectionType.Product == section.Type)
309 + {
310 + this.ProcessProductVersion(packageSymbol, section, validate: true);
311 + }
312
313 // Process dependency references.
314 if (SectionType.Product == section.Type || SectionType.Module == section.Type)
@@ -551,21 +560,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
560 return result;
561 }
562
554 - private void VerifyVersion(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol)
563 + private void ProcessProductVersion(WixPackageSymbol packageSymbol, IntermediateSection section, bool validate)
564 {
556 - if (packageSymbol != null)
565 + if (this.WindowsInstallerBackendHelper.TryParseMsiProductVersion(packageSymbol.Version, strict: false, out var version))
566 {
558 - if (!this.WindowsInstallerBackendHelper.IsValidMsiProductVersion(packageSymbol.Version))
567 + if (packageSymbol.Version != version)
568 {
560 - this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
569 + packageSymbol.Version = version;
570 +
571 + var productVersionProperty = section.Symbols.OfType<PropertySymbol>().FirstOrDefault(p => p.Id.Id == "ProductVersion");
572 + productVersionProperty.Value = version;
573 }
574 }
563 - else if (moduleSymbol != null)
575 + else if (validate)
576 {
565 - if (!this.WindowsInstallerBackendHelper.IsValidFourPartVersion(moduleSymbol.Version))
566 - {
567 - this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(moduleSymbol.SourceLineNumbers, moduleSymbol.Version));
568 - }
577 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
578 }
579 }
580
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+47 -4
@@ -224,6 +224,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
224 this.AddWixEnsureTableSymbol((WixEnsureTableSymbol)symbol);
225 break;
226
227 + case SymbolDefinitionType.WixModule:
228 + this.AddWixModuleSymbol((WixModuleSymbol)symbol);
229 + break;
230 +
231 case SymbolDefinitionType.WixPackage:
232 this.AddWixPackageSymbol((WixPackageSymbol)symbol);
233 break;
@@ -1033,14 +1037,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1037
1038 private void AddUpgradeSymbol(UpgradeSymbol symbol)
1039 {
1036 - if (!String.IsNullOrEmpty(symbol.VersionMin) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMin))
1040 + if (this.CheckUpgradeVersion(symbol, symbol.VersionMin, out var changedVersion))
1041 {
1038 - this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMin));
1042 + symbol.VersionMin = changedVersion;
1043 }
1044
1041 - if (!String.IsNullOrEmpty(symbol.VersionMax) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMax))
1045 + if (this.CheckUpgradeVersion(symbol, symbol.VersionMax, out changedVersion))
1046 {
1043 - this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMax));
1047 + symbol.VersionMax = changedVersion;
1048 }
1049
1050 var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade");
@@ -1248,6 +1252,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1252 }
1253 }
1254
1255 + private void AddWixModuleSymbol(WixModuleSymbol symbol)
1256 + {
1257 + if (!String.IsNullOrEmpty(symbol.Version) && this.BackendHelper.TryParseFourPartVersion(symbol.Version, out var version))
1258 + {
1259 + var row = this.CreateRow(symbol, "ModuleSignature");
1260 + row[0] = symbol.ModuleId;
1261 + row[1] = symbol.Language;
1262 + row[2] = version;
1263 + }
1264 + else
1265 + {
1266 + this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(symbol.SourceLineNumbers, symbol.Version));
1267 + }
1268 + }
1269 +
1270 private void AddWixPackageSymbol(WixPackageSymbol symbol)
1271 {
1272 // TODO: Remove the following from the compiler and do it here instead.
@@ -1584,6 +1603,30 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1603 return this.BackendHelper.CreateRow(this.Section, symbol, this.Data, tableDefinition);
1604 }
1605
1606 + private bool CheckUpgradeVersion(UpgradeSymbol symbol, string version, out string changedVersion)
1607 + {
1608 + if (String.IsNullOrEmpty(version))
1609 + {
1610 + // Null is allowed.
1611 + }
1612 + else if (this.BackendHelper.TryParseMsiProductVersion(version, strict: false, out var parsedVersionMin))
1613 + {
1614 + // If the strictly parsed value is different, update the symbol.
1615 + if (version != parsedVersionMin)
1616 + {
1617 + changedVersion = parsedVersionMin;
1618 + return true;
1619 + }
1620 + }
1621 + else
1622 + {
1623 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, version));
1624 + }
1625 +
1626 + changedVersion = null;
1627 + return false;
1628 + }
1629 +
1630 private string CreateShortName(string longName, bool keepExtension, params string[] args)
1631 {
1632 longName = longName.ToLowerInvariant();
src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessPackageSoftwareTagsCommand.cs
+25 -25
@@ -36,32 +36,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
36 {
37 var trackedFiles = new List<ITrackedFile>();
38
39 - string productName = null;
40 - string productVersion = null;
41 - string manufacturer = null;
42 - string upgradeCode = null;
43 -
39 var summaryInfo = this.Section.Symbols.OfType<SummaryInformationSymbol>().FirstOrDefault(s => s.PropertyId == SummaryInformationType.PackageCode);
40 var packageCode = NormalizeGuid(summaryInfo?.Value);
41
47 - foreach (var property in this.Section.Symbols.OfType<PropertySymbol>())
48 - {
49 - switch (property.Id.Id)
50 - {
51 - case "ProductName":
52 - productName = property.Value;
53 - break;
54 - case "ProductVersion":
55 - productVersion = property.Value;
56 - break;
57 - case "Manufacturer":
58 - manufacturer = property.Value;
59 - break;
60 - case "UpgradeCode":
61 - upgradeCode = NormalizeGuid(property.Value);
62 - break;
63 - }
64 - }
42 + var packageSymbol = this.Section.Symbols.OfType<WixPackageSymbol>().First();
43 + var upgradeCode = NormalizeGuid(packageSymbol.UpgradeCode);
44
45 var fileSymbolsById = this.Section.Symbols.OfType<FileSymbol>().Where(f => f.Id != null).ToDictionary(f => f.Id.Id);
46
@@ -83,7 +62,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
62
63 using (var fs = new FileStream(fileSymbol.Source.Path, FileMode.Create))
64 {
86 - CreateTagFile(fs, uniqueId, productName, productVersion, tagRow.Regid, manufacturer, persistentId);
65 + CreateTagFile(fs, uniqueId, packageSymbol.Name, packageSymbol.Version, tagRow.Regid, packageSymbol.Manufacturer, persistentId);
66 }
67
68 // Ensure the matching "SoftwareIdentificationTag" row exists and
@@ -113,7 +92,28 @@ namespace WixToolset.Core.WindowsInstaller.Bind
92
93 private static void CreateTagFile(Stream stream, string uniqueId, string name, string version, string regid, string manufacturer, string persistendId)
94 {
116 - var versionScheme = Version.TryParse(version, out _) ? "multipartnumeric" : "alphanumeric";
95 + var versionScheme = "alphanumeric";
96 +
97 + if (WixVersion.TryParse(version, out var parsedVersion))
98 + {
99 + if (parsedVersion.Prefix.HasValue)
100 + {
101 + version = version.Substring(1);
102 + }
103 +
104 + if (Version.TryParse(version, out _))
105 + {
106 + versionScheme = "multipartnumeric";
107 + }
108 + else if (!parsedVersion.HasRevision)
109 + {
110 + versionScheme = "semver";
111 + }
112 + else
113 + {
114 + versionScheme = "multipartnumeric+suffix";
115 + }
116 + }
117
118 using (var writer = XmlWriter.Create(stream, new XmlWriterSettings { Indent = true }))
119 {
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+10
@@ -123,6 +123,16 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
123 return this.backendHelper.SplitMsiFileName(value);
124 }
125
126 + public bool TryParseFourPartVersion(string version, out string parsedVersion)
127 + {
128 + return this.backendHelper.TryParseFourPartVersion(version, out parsedVersion);
129 + }
130 +
131 + public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
132 + {
133 + return this.backendHelper.TryParseMsiProductVersion(version, strict, out parsedVersion);
134 + }
135 +
136 public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
137 {
138 return this.backendHelper.TrackFile(path, type, sourceLineNumbers);
src/wix/WixToolset.Core/Common.cs
+1 -1
@@ -134,7 +134,7 @@ namespace WixToolset.Core
134
135 public static bool IsValidMsiProductVersion(string version)
136 {
137 - return Version.TryParse(version, out var ver) && ver.Major < 256 && ver.Minor < 256 && ver.Build < 65536;
137 + return WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 256 && wixVersion.Minor < 256 && wixVersion.Patch < 65536 && wixVersion.Labels == null && String.IsNullOrEmpty(wixVersion.Metadata);
138 }
139
140 public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
src/wix/WixToolset.Core/Compiler.cs
-1
@@ -5910,7 +5910,6 @@ namespace WixToolset.Core
5910 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Message"));
5911 }
5912
5913 -
5913 this.Core.ParseForExtensionElements(node);
5914
5915 if (!this.Core.EncounteredError)
src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+35
@@ -119,5 +119,40 @@ namespace WixToolset.Core.ExtensibilityServices
119 {
120 return Common.IsValidShortFilename(filename, allowWildcards);
121 }
122 +
123 + public bool TryParseFourPartVersion(string version, out string parsedVersion)
124 + {
125 + if (WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 65536 && wixVersion.Minor < 65536 && wixVersion.Patch < 65536 && wixVersion.Revision < 65536)
126 + {
127 + parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}.{wixVersion.Patch}.{wixVersion.Revision}";
128 + return true;
129 + }
130 +
131 + parsedVersion = null;
132 + return false;
133 + }
134 +
135 + public bool TryParseMsiProductVersion(string version, bool strict, out string parsedVersion)
136 + {
137 + if (WixVersion.TryParse(version, out var wixVersion) && wixVersion.HasMajor && wixVersion.Major < 256 && wixVersion.Minor < 256 && wixVersion.Patch < 65536 && wixVersion.Labels == null && String.IsNullOrEmpty(wixVersion.Metadata))
138 + {
139 + parsedVersion = $"{wixVersion.Major}.{wixVersion.Minor}";
140 +
141 + if (strict || wixVersion.HasPatch)
142 + {
143 + parsedVersion += $".{wixVersion.Patch}";
144 + }
145 +
146 + if (!strict && wixVersion.HasRevision)
147 + {
148 + parsedVersion += $".{wixVersion.Revision}";
149 + }
150 +
151 + return true;
152 + }
153 +
154 + parsedVersion = null;
155 + return false;
156 + }
157 }
158 }
src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+2 -3
@@ -552,10 +552,9 @@ namespace WixToolset.Core.ExtensibilityServices
552
553 if (!String.IsNullOrEmpty(value))
554 {
555 - if (WixVersion.TryParse(value, out var version))
555 + if (WixVersion.TryParse(value, out var _))
556 {
557 - // Return the attribute value sans-prefix, if present.
558 - return version.Prefix.HasValue ? value.Substring(1) : value;
557 + return value;
558 }
559
560 // Allow versions to contain binder variables.
src/wix/test/WixToolsetTest.Converters/TestData/PackageSummaryInformation/TypicalV3.wxs
+1 -1
@@ -34,4 +34,4 @@
34 </Component>
35 </ComponentGroup>
36 </Product>
37 -</Wix>
\ No newline at end of file
37 +</Wix>
src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs
+1 -1
@@ -78,7 +78,7 @@ namespace WixToolsetTest.Core
78 var attribute = CreateAttribute("v1.2.3.4");
79 var result = helper.GetAttributeVersionValue(null, attribute);
80
81 - WixAssert.StringEqual("1.2.3.4", result);
81 + WixAssert.StringEqual("v1.2.3.4", result);
82 }
83
84
src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+27 -1
@@ -8,7 +8,6 @@ namespace WixToolsetTest.CoreIntegration
8 using System.Linq;
9 using System.Text;
10 using System.Xml;
11 - using System.Xml.Linq;
11 using Example.Extension;
12 using WixBuildTools.TestSupport;
13 using WixToolset.Core.Burn;
@@ -21,6 +20,33 @@ namespace WixToolsetTest.CoreIntegration
20
21 public class BundleFixture
22 {
23 + [Fact]
24 + public void CanBuildBundleWithBindVariableVersion()
25 + {
26 + var folder = TestData.Get(@"TestData");
27 +
28 + using (var fs = new DisposableFileSystem())
29 + {
30 + var baseFolder = fs.GetFolder();
31 + var intermediateFolder = Path.Combine(baseFolder, "obj");
32 + var exePath = Path.Combine(baseFolder, @"bin\test.exe");
33 +
34 + var result = WixRunner.Execute(new[]
35 + {
36 + "build",
37 + Path.Combine(folder, "BundleBindVariables", "BindVarBundleVersion.wxs"),
38 + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
39 + "-bindpath", Path.Combine(folder, ".Data"),
40 + "-intermediateFolder", intermediateFolder,
41 + "-o", exePath,
42 + });
43 +
44 + result.AssertSuccess();
45 +
46 + Assert.True(File.Exists(exePath));
47 + }
48 + }
49 +
50 [Fact]
51 public void CanBuildMultiFileBundle()
52 {
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundleBindVariables/BindVarBundleVersion.wxs new
+14
@@ -0,0 +1,14 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Bundle Name="BindVarBundleVersion" Version="!(wix.BundleVersion)" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 + <BootstrapperApplication Id="fakeba">
5 + <BootstrapperApplicationDll SourceFile="fakeba.dll" />
6 + </BootstrapperApplication>
7 +
8 + <Chain>
9 + <MsiPackage SourceFile="test.msi" />
10 + </Chain>
11 +
12 + <WixVariable Id="BundleVersion" Value="v8.7.6.5" />
13 + </Bundle>
14 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/PackageWithBindVariableVersion.wxs new
+33
@@ -0,0 +1,33 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Package Version="!(wix.Version)"
3 + Name="MsiPackage"
4 + Manufacturer="Example Corporation"
5 + UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a"
6 + Scope="perUser">
7 + <MajorUpgrade DowngradeErrorMessage="Downgrade not allowed" />
8 + <MediaTemplate EmbedCab="true" />
9 +
10 + <Feature Id="ProductFeature" Title="Feature title">
11 + <ComponentGroupRef Id="ProductComponents" />
12 + </Feature>
13 +
14 + <WixVariable Id="Version" Value="v9.8.7.6" />
15 + </Package>
16 +
17 + <Fragment>
18 + <StandardDirectory Id="DesktopFolder">
19 + <Directory Id="INSTALLFOLDER" Name="MsiPackage !(wix.Version) and !(bind.property.ProductVersion)" />
20 + </StandardDirectory>
21 + </Fragment>
22 +
23 + <Fragment>
24 + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
25 + <Component>
26 + <File Source="test.txt" />
27 + </Component>
28 + <Component Id="Shared.dll" Shared="yes">
29 + <File Name="Shared.dll" Source="test.txt" />
30 + </Component>
31 + </ComponentGroup>
32 + </Fragment>
33 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/PackageWithReplaceableVersion.wxs renamed
src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs
+1 -1
@@ -39,7 +39,7 @@ namespace WixToolsetTest.CoreIntegration
39 .ToArray();
40 WixAssert.CompareLineByLine(new[]
41 {
42 - "Invalid product version '1.256.0'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.",
42 + "Invalid product version '1.256.0'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
43 }, errorMessages);
44 Assert.Equal(242, result.ExitCode);
45 }
src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs
+107 -5
@@ -12,6 +12,101 @@ namespace WixToolsetTest.CoreIntegration
12
13 public class VersionFixture
14 {
15 + [Fact]
16 + public void CanBuildMsiWithPrefixedVersion()
17 + {
18 + var folder = TestData.Get(@"TestData");
19 +
20 + using (var fs = new DisposableFileSystem())
21 + {
22 + var baseFolder = fs.GetFolder();
23 + var intermediateFolder = Path.Combine(baseFolder, "obj");
24 + var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
25 +
26 + var result = WixRunner.Execute(new[]
27 + {
28 + "build",
29 + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
30 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
31 + "-intermediateFolder", intermediateFolder,
32 + "-d", "Version=v4.3.2.1",
33 + "-o", msiPath
34 + });
35 +
36 + result.AssertSuccess();
37 +
38 + var productVersion = GetProductVersionFromMsi(msiPath);
39 + Assert.Equal("4.3.2.1", productVersion);
40 + }
41 + }
42 +
43 + [Fact]
44 + public void CanBuildMsiWithPrefixedVersionBindVariable()
45 + {
46 + var folder = TestData.Get(@"TestData");
47 +
48 + using (var fs = new DisposableFileSystem())
49 + {
50 + var baseFolder = fs.GetFolder();
51 + var intermediateFolder = Path.Combine(baseFolder, "obj");
52 + var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
53 +
54 + var result = WixRunner.Execute(new[]
55 + {
56 + "build",
57 + Path.Combine(folder, "Version", "PackageWithBindVariableVersion.wxs"),
58 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
59 + "-intermediateFolder", intermediateFolder,
60 + "-o", msiPath
61 + });
62 +
63 + result.AssertSuccess();
64 +
65 + var productVersion = GetProductVersionFromMsi(msiPath);
66 + Assert.Equal("9.8.7.6", productVersion);
67 +
68 + var directoryTable = Query.QueryDatabase(msiPath, new[] { "Directory" }).OrderBy(s => s).ToArray();
69 + WixAssert.CompareLineByLine(new[]
70 + {
71 + "Directory:DesktopFolder\tTARGETDIR\tDesktop",
72 + "Directory:INSTALLFOLDER\tDesktopFolder\tpja2bznq|MsiPackage v9.8.7.6 and 9.8.7.6",
73 + "Directory:TARGETDIR\t\tSourceDir"
74 + }, directoryTable);
75 + }
76 + }
77 +
78 + [Fact]
79 + public void CannotBuildMsiWithExtendedVersion()
80 + {
81 + var folder = TestData.Get(@"TestData");
82 +
83 + using (var fs = new DisposableFileSystem())
84 + {
85 + var baseFolder = fs.GetFolder();
86 + var intermediateFolder = Path.Combine(baseFolder, "obj");
87 + var msiPath = Path.Combine(baseFolder, "bin", "test1.msi");
88 +
89 + var result = WixRunner.Execute(new[]
90 + {
91 + "build",
92 + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
93 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
94 + "-intermediateFolder", intermediateFolder,
95 + "-d", "Version=v4.3.2-preview.1",
96 + "-o", msiPath
97 + });
98 +
99 + var errorMessages = result.Messages.Where(m => m.Level == MessageLevel.Error)
100 + .Select(m => m.ToString())
101 + .ToArray();
102 + WixAssert.CompareLineByLine(new[]
103 + {
104 + "Invalid product version 'v4.3.2-preview.1'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
105 + }, errorMessages);
106 + Assert.Equal(242, result.ExitCode);
107 + }
108 + }
109 +
110 [Fact]
111 public void CannotBuildMsiWithInvalidMajorVersion()
112 {
@@ -26,7 +121,7 @@ namespace WixToolsetTest.CoreIntegration
121 var result = WixRunner.Execute(new[]
122 {
123 "build",
29 - Path.Combine(folder, "Version", "Package.wxs"),
124 + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
125 "-bindpath", Path.Combine(folder, "SingleFile", "data"),
126 "-intermediateFolder", intermediateFolder,
127 "-d", "Version=257.0.0",
@@ -38,7 +133,7 @@ namespace WixToolsetTest.CoreIntegration
133 .ToArray();
134 WixAssert.CompareLineByLine(new[]
135 {
41 - "Invalid product version '257.0.0'. Product version must have a major version less than 256, a minor version less than 256, and a build version less than 65536.",
136 + "Invalid product version '257.0.0'. MSI product versions must have a major version less than 256, a minor version less than 256, and a build version less than 65536. The revision value is ignored but version labels and metadata are not allowed.",
137 }, errorMessages);
138 Assert.Equal(242, result.ExitCode);
139 }
@@ -60,7 +155,7 @@ namespace WixToolsetTest.CoreIntegration
155 var result = WixRunner.Execute(new[]
156 {
157 "build",
63 - Path.Combine(folder, "Version", "Package.wxs"),
158 + Path.Combine(folder, "Version", "PackageWithReplaceableVersion.wxs"),
159 "-bindpath", Path.Combine(folder, "SingleFile", "data"),
160 "-intermediateFolder", intermediateFolder,
161 "-d", "Version=255.255.65535",
@@ -82,8 +177,7 @@ namespace WixToolsetTest.CoreIntegration
177
178 result3.AssertSuccess();
179
85 - var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]);
86 - Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion));
180 + var productVersion = GetProductVersionFromMsi(msiPath);
181 WixAssert.StringEqual("255.255.65535", productVersion);
182
183 var extractResult = BundleExtractor.ExtractAllContainers(null, bundlePath, Path.Combine(baseFolder, "ba"), Path.Combine(baseFolder, "attached"), Path.Combine(baseFolder, "extract"));
@@ -95,5 +189,13 @@ namespace WixToolsetTest.CoreIntegration
189 WixAssert.StringEqual("2022.3.9-preview.0-build.5+0987654321abcdef1234567890", bundleVersion.Value);
190 }
191 }
192 +
193 + private static string GetProductVersionFromMsi(string msiPath)
194 + {
195 + var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]);
196 + Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion));
197 +
198 + return productVersion;
199 + }
200 }
201 }
src/wix/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
-4
@@ -14,10 +14,6 @@
14 <Content Include="TestData\**" CopyToOutputDirectory="PreserveNewest" />
15 </ItemGroup>
16
17 - <ItemGroup>
18 - <None Remove="TestData\BundleWithInvalid\BundleWithInvalidLocVariableNames.wxs" />
19 - </ItemGroup>
20 -
17 <ItemGroup>
18 <ProjectReference Include="..\..\WixToolset.Core\WixToolset.Core.csproj" />
19 <ProjectReference Include="..\..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" />