@joebigelow / wix / commits / 02e68288

Add support for semver in bundles and dependencies

Take advantage of WixVersion/verutil functionality to support wider range of version numbers were possible in the WiX Toolset Completes 4666

Rob Mensching committed May 9, 2022 at 22:21 UTC 02e682881979cd87592ee1e8e39b7744b575829c
36 files changed +568 -116
src/api/wix/WixToolset.Data/ErrorMessages.cs
+4 -4
@@ -1076,7 +1076,7 @@ namespace WixToolset.Data
1076
1077 public static Message IllegalVersionValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value)
1078 {
1079 - return Message(sourceLineNumbers, Ids.IllegalVersionValue, "The {0}/@{1} attribute's value, '{2}', is not a valid version. Legal version values should look like 'x.x.x.x' where x is an integer from 0 to 65534.", elementName, attributeName, value);
1079 + return Message(sourceLineNumbers, Ids.IllegalVersionValue, "The {0}/@{1} attribute's value, '{2}', is not a valid version. Specify a four-part version or semantic version, such as '#.#.#.#' or '#.#.#-label.#'.", elementName, attributeName, value);
1080 }
1081
1082 public static Message IllegalWarningIdAsError(string warningId)
@@ -1279,9 +1279,9 @@ namespace WixToolset.Data
1279 return Message(sourceLineNumbers, Ids.InvalidMergeLanguage, "The Merge element '{0}' specified an invalid language '{1}'. Verify that localization tokens are being properly resolved to a numeric LCID.", mergeId, mergeLanguage);
1280 }
1281
1282 - public static Message InvalidModuleOrBundleVersion(SourceLineNumber sourceLineNumbers, string moduleOrBundle, string version)
1282 + public static Message InvalidFourPartVersion(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string version)
1283 {
1284 - return Message(sourceLineNumbers, Ids.InvalidModuleOrBundleVersion, "Invalid {0}/@Version '{1}'. {0} version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", moduleOrBundle, version);
1284 + return Message(sourceLineNumbers, Ids.InvalidFourPartVersion, "Invalid {0}/@Version '{1}'. {0} version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", elementName, version);
1285 }
1286
1287 public static Message InvalidPlatformValue(SourceLineNumber sourceLineNumbers, string value)
@@ -2678,7 +2678,7 @@ namespace WixToolset.Data
2678 SplitCabinetNameCollision = 377,
2679 SplitCabinetInsertionFailed = 378,
2680 InvalidPreprocessorFunctionAutoVersion = 379,
2681 - InvalidModuleOrBundleVersion = 380,
2681 + InvalidFourPartVersion = 380,
2682 UnsupportedPlatformForElement = 381,
2683 MissingMedia = 382,
2684 IllegalYesNoAlwaysValue = 384,
src/api/wix/WixToolset.Data/WarningMessages.cs
+6 -6
@@ -367,9 +367,9 @@ namespace WixToolset.Data
367 return Message(sourceLineNumbers, Ids.InvalidHigherInstallerVersionInModule, "Merge module '{0}' has an installer version of {1} which is greater than the product's installer version of {2}. Merging a module with a higher installer version than the product it is being merged into can result in invalid values in the resulting msi. You must set the Package/@InstallerVersion attribute to {1} or greater to merge this merge module into your product.", moduleId, moduleInstallerVersion, productInstallerVersion);
368 }
369
370 - public static Message InvalidModuleOrBundleVersion(SourceLineNumber sourceLineNumbers, string moduleOrBundle, string version)
370 + public static Message InvalidFourPartVersion(SourceLineNumber sourceLineNumbers, string elementName, string version)
371 {
372 - return Message(sourceLineNumbers, Ids.InvalidModuleOrBundleVersion, "Invalid {0}/@Version '{1}'. {0} version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", moduleOrBundle, version);
372 + return Message(sourceLineNumbers, Ids.InvalidFourPartVersion, "Invalid {0}/@Version '{1}'. {0} version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", elementName, version);
373 }
374
375 public static Message InvalidRemoveFile(SourceLineNumber sourceLineNumbers, string file, string component)
@@ -663,9 +663,9 @@ namespace WixToolset.Data
663 return Message(sourceLineNumbers, Ids.VariableDeclarationCollision, "The variable '{0}' with value '{1}' was previously declared with value '{2}'.", variableName, variableValue, variableCollidingValue);
664 }
665
666 - public static Message VersionTruncated(SourceLineNumber sourceLineNumbers, string originalVersion, string package, string truncatedVersion)
666 + public static Message InvalidMsiProductVersion(SourceLineNumber sourceLineNumbers, string version, string package)
667 {
668 - return Message(sourceLineNumbers, Ids.VersionTruncated, "Product version {0} in package '{1}' is not valid per the MSI SDK and cannot be represented in a bundle. It has been truncated to {2}.", originalVersion, package, truncatedVersion);
668 + return Message(sourceLineNumbers, Ids.InvalidMsiProductVersion, "Invalid product version '{0}' in MSI package '{1}'. Product version should have a major version less than 256, a minor version less than 256, and a build version less than 65536. The bundle may incorrectly detect upgrades of this package.", version, package);
669 }
670
671 public static Message CollidingModularizationTypes(string tableName, string columnName, string foreignTableName, int foreignColumnNumber, string modularizationType, string foreignModularizationType)
@@ -764,7 +764,7 @@ namespace WixToolset.Data
764 DeprecatedPackageCompressedAttribute = 1087,
765 DeprecatedQuestionMarksGuid = 1090,
766 PackageCodeSet = 1091,
767 - InvalidModuleOrBundleVersion = 1093,
767 + InvalidFourPartVersion = 1093,
768 InvalidRemoveFile = 1095,
769 PreprocessorWarning = 1096,
770 UpdateOfNonKeyPathFile = 1097,
@@ -816,7 +816,7 @@ namespace WixToolset.Data
816 AllChangesIncludedInPatch = 1145,
817 RelatedAttributeConditionallyIgnored = 1146,
818 BackslashTerminateInlineDirectorySyntax = 1147,
819 - VersionTruncated = 1148,
819 + InvalidMsiProductVersion = 1148,
820 ServiceConfigFamilyNotSupported = 1149,
821 SymbolNotTranslatedToOutput = 1150,
822 MsiTransactionLimitations = 1151,
src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
+16 -2
@@ -104,12 +104,26 @@ namespace WixToolset.Extensibility.Services
104 bool IsValidBinderVariable(string variable);
105
106 /// <summary>
107 - /// Verifies the given string is a valid 4-part version module or bundle version.
107 + /// Verifies the given string is a valid 4-part version.
108 /// </summary>
109 /// <param name="version">The version to verify.</param>
110 - /// <returns>True if version is a valid module or bundle version.</returns>
110 + /// <returns>True if version is a valid 4-part version.</returns>
111 bool IsValidFourPartVersion(string version);
112
113 + /// <summary>
114 + /// Verifies the given string is a valid MSI product version.
115 + /// </summary>
116 + /// <param name="version">The MSI product version to verify.</param>
117 + /// <returns>True if version is a valid MSI product version</returns>
118 + bool IsValidMsiProductVersion(string version);
119 +
120 + /// <summary>
121 + /// Verifies the given string is a valid WiX version.
122 + /// </summary>
123 + /// <param name="version">The version to verify.</param>
124 + /// <returns>True if version is a valid WiX version.</returns>
125 + bool IsValidWixVersion(string version);
126 +
127 /// <summary>
128 /// Determines if value is a valid identifier.
129 /// </summary>
src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution new
+6
@@ -0,0 +1,6 @@
1 +<SolutionConfiguration>
2 + <Settings>
3 + <AllowParallelTestExecution>True</AllowParallelTestExecution>
4 + <SolutionConfigured>True</SolutionConfigured>
5 + </Settings>
6 +</SolutionConfiguration>
\ No newline at end of file
src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs
+2 -1
@@ -17,13 +17,14 @@ namespace WixToolsetTest.Dependency
17 var folder = TestData.Get(@"TestData\UsingProvides");
18 var build = new Builder(folder, typeof(DependencyExtensionFactory), new[] { folder });
19
20 - var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider")
20 + var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider", "Wix4Dependency")
21 .Select(r => Regex.Replace(r, "{[^}]*}", "{*}"))
22 .ToArray();
23 WixAssert.CompareLineByLine(new[]
24 {
25 "CustomAction:Wix4DependencyCheck_X86\t1\tDependencyCA_X86\tWixDependencyCheck\t",
26 "CustomAction:Wix4DependencyRequire_X86\t1\tDependencyCA_X86\tWixDependencyRequire\t",
27 + "Wix4Dependency:depL8BNflcqZaN5CQEWh2U3SBHFDdg\tUsingRequires\t1.0.0-beta.9\t\t0",
28 "Wix4DependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t",
29 "Wix4DependencyProvider:depTpv28q7slcxvXPWmU4Z0GfbiI.4\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\t{*}\t\t\t",
30 }, results);
src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs
+1 -1
@@ -12,7 +12,7 @@
12 <File Name="example.txt" Source="Package.wxs" />
13 <Provides Key="UsingProvides" dep:Check="yes" />
14 <Provides>
15 - <Requires ProviderKey="UsingRequires" Minimum="1.0.0.0" dep:Enforce="yes" />
15 + <Requires ProviderKey="UsingRequires" Minimum="1.0.0-beta.9" dep:Enforce="yes" />
16 </Provides>
17 </Component>
18 </Feature>
src/libs/dutil/WixToolset.DUtil/deputil.cpp
+22 -10
@@ -121,9 +121,10 @@ DAPI_(HRESULT) DepCheckDependency(
121 HRESULT hr = S_OK;
122 LPWSTR sczKey = NULL;
123 HKEY hkKey = NULL;
124 - DWORD64 dw64Version = 0;
125 - DWORD64 dw64MinVersion = 0;
126 - DWORD64 dw64MaxVersion = 0;
124 + VERUTIL_VERSION* pVersion = NULL;
125 + VERUTIL_VERSION* pMinVersion = NULL;
126 + VERUTIL_VERSION* pMaxVersion = NULL;
127 + int nResult = 0;
128 BOOL fAllowEqual = FALSE;
129 LPWSTR sczName = NULL;
130
@@ -138,7 +139,7 @@ DAPI_(HRESULT) DepCheckDependency(
139 DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey);
140
141 // If there are no registry values, consider the key orphaned and treat it as missing.
141 - hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version);
142 + hr = RegReadWixVersion(hkKey, vcszVersionValue, &pVersion);
143 if (E_FILENOTFOUND != hr)
144 {
145 DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey);
@@ -171,11 +172,15 @@ DAPI_(HRESULT) DepCheckDependency(
172 {
173 if (*wzMinVersion)
174 {
174 - hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion);
175 - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion);
175 + hr = VerParseVersion(wzMinVersion, 0, FALSE, &pMinVersion);
176 + DepExitOnFailure(hr, "Failed to get the min version number from \"%ls\".", wzMinVersion);
177 +
178 + hr = VerCompareParsedVersions(pVersion, pMinVersion, &nResult);
179 + DepExitOnFailure(hr, "Failed to compare dependency with min version \"%ls\".", wzMinVersion);
180
181 fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive;
178 - if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version))
182 + // !(fAllowEqual && pMinVersion <= pVersion || pMinVersion < pVersion))
183 + if (!(fAllowEqual && 0 <= nResult || 0 < nResult))
184 {
185 hr = DictKeyExists(sdDependencies, wzProviderKey);
186 if (E_NOTFOUND != hr)
@@ -205,11 +210,15 @@ DAPI_(HRESULT) DepCheckDependency(
210 {
211 if (*wzMaxVersion)
212 {
208 - hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion);
209 - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion);
213 + hr = VerParseVersion(wzMaxVersion, 0, FALSE, &pMaxVersion);
214 + DepExitOnFailure(hr, "Failed to get the max version number from \"%ls\".", wzMaxVersion);
215 +
216 + hr = VerCompareParsedVersions(pMaxVersion, pVersion, &nResult);
217 + DepExitOnFailure(hr, "Failed to compare dependency with max version \"%ls\".", wzMaxVersion);
218
219 fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive;
212 - if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion))
220 + // !(fAllowEqual && pVersion <= pMaxVersion || pVersion < pMaxVersion)
221 + if (!(fAllowEqual && 0 <= nResult || 0 < nResult))
222 {
223 hr = DictKeyExists(sdDependencies, wzProviderKey);
224 if (E_NOTFOUND != hr)
@@ -235,6 +244,9 @@ DAPI_(HRESULT) DepCheckDependency(
244 }
245
246 LExit:
247 + ReleaseVerutilVersion(pMaxVersion);
248 + ReleaseVerutilVersion(pMinVersion);
249 + ReleaseVerutilVersion(pVersion);
250 ReleaseStr(sczName);
251 ReleaseRegKey(hkKey);
252 ReleaseStr(sczKey);
src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+11
@@ -1,6 +1,7 @@
1 #pragma once
2 // 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.
3
4 +#include "verutil.h"
5
6 #ifdef __cplusplus
7 extern "C" {
@@ -260,6 +261,16 @@ HRESULT DAPI RegReadVersion(
261 __out DWORD64* pdw64Version
262 );
263
264 +/********************************************************************
265 + RegReadWixVersion - reads a registry key value as a WiX version.
266 +
267 +*********************************************************************/
268 +HRESULT DAPI RegReadWixVersion(
269 + __in HKEY hk,
270 + __in_z_opt LPCWSTR wzName,
271 + __out VERUTIL_VERSION** ppVersion
272 + );
273 +
274 /********************************************************************
275 RegReadNone - reads a NONE registry key value.
276
src/libs/dutil/WixToolset.DUtil/regutil.cpp
+53
@@ -590,6 +590,7 @@ DAPI_(HRESULT) RegReadVersion(
590 {
591 ExitFunction1(hr = E_FILENOTFOUND);
592 }
593 +
594 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
595 {
596 hr = RegReadString(hk, wzName, &sczVersion);
@@ -614,6 +615,58 @@ LExit:
615 return hr;
616 }
617
618 +
619 +DAPI_(HRESULT) RegReadWixVersion(
620 + __in HKEY hk,
621 + __in_z_opt LPCWSTR wzName,
622 + __out VERUTIL_VERSION** ppVersion
623 + )
624 +{
625 + HRESULT hr = S_OK;
626 + DWORD er = ERROR_SUCCESS;
627 + DWORD dwType = 0;
628 + DWORD cb = 0;
629 + DWORD64 dw64Version = 0;
630 + LPWSTR sczVersion = NULL;
631 + VERUTIL_VERSION* pVersion = NULL;
632 +
633 + cb = sizeof(DWORD64);
634 + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast<LPBYTE>(&dw64Version), &cb);
635 + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er))
636 + {
637 + ExitFunction1(hr = E_FILENOTFOUND);
638 + }
639 +
640 + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
641 + {
642 + hr = RegReadString(hk, wzName, &sczVersion);
643 + RegExitOnFailure(hr, "Failed to read registry version as string.");
644 +
645 + hr = VerParseVersion(sczVersion, 0, FALSE, &pVersion);
646 + RegExitOnFailure(hr, "Failed to convert registry string to version.");
647 + }
648 + else if (REG_QWORD == dwType)
649 + {
650 + hr = VerVersionFromQword(dw64Version, &pVersion);
651 + RegExitOnFailure(hr, "Failed to convert registry string to version.");
652 + }
653 + else // unexpected data type
654 + {
655 + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
656 + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
657 + }
658 +
659 + *ppVersion = pVersion;
660 + pVersion = NULL;
661 +
662 +LExit:
663 + ReleaseVerutilVersion(pVersion);
664 + ReleaseStr(sczVersion);
665 +
666 + return hr;
667 +}
668 +
669 +
670 DAPI_(HRESULT) RegReadNone(
671 __in HKEY hk,
672 __in_z_opt LPCWSTR wzName
src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs
+1 -1
@@ -11,7 +11,7 @@
11 <Component Id="FileComponent2" Guid="A1866388-65B4-4215-A8FB-9A7AADBE4E8E" Directory="INSTALLFOLDER">
12 <File Source="$(sys.SOURCEFILEPATH)" />
13 <Provides>
14 - <Requires ProviderKey="WiX.$(var.TestGroupName).A,v1.0" Minimum="1.0.0.0" IncludeMinimum="yes" dep:Enforce="yes" />
14 + <Requires ProviderKey="WiX.$(var.TestGroupName).A,v1.0" Minimum="1.0.0-alpha.420" IncludeMinimum="yes" dep:Enforce="yes" />
15 </Provides>
16 </Component>
17 </Fragment>
src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+19
@@ -1086,5 +1086,24 @@ namespace WixToolsetTest.BurnE2E
1086 packageA.UninstallProduct();
1087 packageA.VerifyInstalled(false);
1088 }
1089 +
1090 + [Fact]
1091 + public void CannotInstallWhenDependencyUnsatisfied()
1092 + {
1093 + var packageA = this.CreatePackageInstaller("PackageAv1");
1094 + var packageB = this.CreatePackageInstaller("PackageB");
1095 + var bundleB = this.CreateBundleInstaller("BundleB");
1096 + var testBAController = this.CreateTestBAController();
1097 +
1098 + packageA.VerifyInstalled(false);
1099 + packageB.VerifyInstalled(false);
1100 +
1101 + // Prevent install of PackageA to cause PackageB's enforced dependency
1102 + // to fail the install.
1103 + testBAController.SetPackageRequestedState("PackageA", RequestState.None);
1104 +
1105 + var bundleBInstallLogFilePath = bundleB.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE);
1106 + Assert.True(LogVerifier.MessageInLogFileRegex(bundleBInstallLogFilePath, @"Applied execute package: PackageB, result: 0x80070643, restart: None"));
1107 + }
1108 }
1109 }
src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+28 -15
@@ -71,11 +71,11 @@ namespace WixToolset.Core.Burn.Bundles
71 FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false);
72 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
73
74 - var windowsAssemblyVersion = GetWindowsAssemblyVersion(this.BundleSymbol);
74 + var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol);
75
76 - var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, windowsAssemblyVersion);
76 + var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, fourPartVersion);
77
78 - UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, windowsAssemblyVersion, applicationManifestData);
78 + this.UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, fourPartVersion, applicationManifestData);
79
80 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers
81 // if they should be attached.
@@ -242,24 +242,37 @@ namespace WixToolset.Core.Burn.Bundles
242 }
243 }
244
245 - private static Version GetWindowsAssemblyVersion(WixBundleSymbol bundleSymbol)
245 + private Version GetFourPartVersion(WixBundleSymbol bundleSymbol)
246 {
247 - // Ensure the bundle info provides a full four part version.
248 - var fourPartVersion = new Version(bundleSymbol.Version);
249 - var major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major;
250 - var minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor;
251 - var build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build;
252 - var revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision;
247 + // Ensure the bundle info provides a full four-part version.
248 +
249 + if (!WixVersion.TryParse(bundleSymbol.Version, out var wixVersion))
250 + {
251 + // Display an error message indicating that we will require a four-part version number
252 + // not just a WixVersion.
253 + this.Messaging.Write(ErrorMessages.IllegalVersionValue(bundleSymbol.SourceLineNumbers, "Bundle", "Version", bundleSymbol.Version));
254 + return new Version(0, 0);
255 + }
256 +
257 + var major = wixVersion.Major ?? 0;
258 + var minor = wixVersion.Minor ?? 0;
259 + var build = wixVersion.Patch ?? 0;
260 + var revision = wixVersion.Revision ?? 0;
261
262 if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision)
263 {
256 - throw new WixException(ErrorMessages.InvalidModuleOrBundleVersion(bundleSymbol.SourceLineNumbers, "Bundle", bundleSymbol.Version));
264 + major = Math.Max(major, UInt16.MaxValue);
265 + minor = Math.Max(minor, UInt16.MaxValue);
266 + build = Math.Max(build, UInt16.MaxValue);
267 + revision = Math.Max(revision, UInt16.MaxValue);
268 +
269 + this.Messaging.Write(BurnBackendWarnings.CannotParseBundleVersionAsFourPartVersion(bundleSymbol.SourceLineNumbers, bundleSymbol.Version));
270 }
271
259 - return new Version(major, minor, build, revision);
272 + return new Version((int)major, (int)minor, (int)build, (int)revision);
273 }
274
262 - private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleSymbol bundleInfo, Version windowsAssemblyVersion, byte[] applicationManifestData)
275 + private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleSymbol bundleInfo, Version fourPartVersion, byte[] applicationManifestData)
276 {
277 const int burnLocale = 1033;
278 var resources = new ResourceCollection();
@@ -268,8 +281,8 @@ namespace WixToolset.Core.Burn.Bundles
281 version.Load(bundleTempPath);
282 resources.Add(version);
283
271 - version.FileVersion = windowsAssemblyVersion;
272 - version.ProductVersion = windowsAssemblyVersion;
284 + version.FileVersion = fourPartVersion;
285 + version.ProductVersion = fourPartVersion;
286
287 var strings = version[burnLocale] ?? version.Add(burnLocale);
288 strings["LegalCopyright"] = bundleInfo.Copyright;
src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+2 -23
@@ -118,30 +118,9 @@ namespace WixToolset.Core.Burn.Bundles
118 this.ChainPackage.Version = this.MsiPackage.ProductVersion;
119 }
120
121 - if (!this.BackendHelper.IsValidFourPartVersion(this.MsiPackage.ProductVersion))
121 + if (!this.BackendHelper.IsValidMsiProductVersion(this.MsiPackage.ProductVersion))
122 {
123 - // not a proper .NET version (e.g., five fields); can we get a valid four-part version number?
124 - string version = null;
125 - var versionParts = this.MsiPackage.ProductVersion.Split('.');
126 - var count = versionParts.Length;
127 - if (0 < count)
128 - {
129 - version = versionParts[0];
130 - for (var i = 1; i < 4 && i < count; ++i)
131 - {
132 - version = String.Concat(version, ".", versionParts[i]);
133 - }
134 - }
135 -
136 - if (!String.IsNullOrEmpty(version) && this.BackendHelper.IsValidFourPartVersion(version))
137 - {
138 - this.Messaging.Write(WarningMessages.VersionTruncated(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId, version));
139 - this.MsiPackage.ProductVersion = version;
140 - }
141 - else
142 - {
143 - this.Messaging.Write(ErrorMessages.InvalidProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId));
144 - }
123 + this.Messaging.Write(WarningMessages.InvalidMsiProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId));
124 }
125
126 this.SetPerMachineAppropriately(harvestedMsiPackage.AllUsers);
src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
+6
@@ -16,6 +16,11 @@ namespace WixToolset.Core.Burn
16 return Message(sourceLineNumbers, Ids.AttachedContainerPayloadCollision2, "The location of the payload related to the previous error.");
17 }
18
19 + public static Message CannotParseBundleVersionAsFourPartVersion(SourceLineNumber originalLineNumber, string version)
20 + {
21 + return Message(originalLineNumber, Ids.CannotParseBundleVersionAsFourPartVersion, "The Bundle/@Version was not be able to be used as a four-part version. A valid four-part version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", version);
22 + }
23 +
24 public static Message EmptyContainer(SourceLineNumber sourceLineNumbers, string containerId)
25 {
26 return Message(sourceLineNumbers, Ids.EmptyContainer, "The Container '{0}' is being ignored because it doesn't have any payloads.", containerId);
@@ -61,6 +66,7 @@ namespace WixToolset.Core.Burn
66 UnknownBundleRelationAction = 8505,
67 HiddenBundleNotSupported = 8506,
68 UnknownMsiPackagePlatform = 8507,
69 + CannotParseBundleVersionAsFourPartVersion = 8508,
70 } // last available is 8999. 9000 is VerboseMessages.
71 }
72 }
src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+10
@@ -104,6 +104,16 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
104 return this.backendHelper.IsValidIdentifier(id);
105 }
106
107 + public bool IsValidMsiProductVersion(string version)
108 + {
109 + return this.backendHelper.IsValidMsiProductVersion(version);
110 + }
111 +
112 + public bool IsValidWixVersion(string version)
113 + {
114 + return this.backendHelper.IsValidWixVersion(version);
115 + }
116 +
117 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
118 {
119 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
+15 -4
@@ -52,16 +52,27 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
52
53 if (null != versionInfo)
54 {
55 - // Use the fixed version info block for the file since the resource text may not be a dotted quad.
56 - var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
55 + var version = versionInfo.ProductVersion;
56
58 - if (PayloadHarvester.EmptyVersion != version)
57 + if (String.IsNullOrEmpty(version))
58 {
60 - payload.Version = version.ToString();
59 + version = versionInfo.FileVersion;
60 + }
61 +
62 + if (String.IsNullOrEmpty(version))
63 + {
64 + // Fallback to fixed version info block for the file.
65 + var fixedVersion = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
66 +
67 + if (PayloadHarvester.EmptyVersion != fixedVersion)
68 + {
69 + version = fixedVersion.ToString();
70 + }
71 }
72
73 payload.Description = versionInfo.FileDescription;
74 payload.DisplayName = versionInfo.ProductName;
75 + payload.Version = version;
76 }
77 }
78 }
src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+21
@@ -298,6 +298,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
298 command.Execute();
299 }
300
301 + // Now that delayed fields are processed, validate the package/module version.
302 + this.VerifyVersion(packageSymbol, moduleSymbol);
303 +
304 // Process dependency references.
305 if (SectionType.Product == section.Type || SectionType.Module == section.Type)
306 {
@@ -537,6 +540,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
540 return result;
541 }
542
543 + private void VerifyVersion(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol)
544 + {
545 + if (packageSymbol != null)
546 + {
547 + if (!this.WindowsInstallerBackendHelper.IsValidMsiProductVersion(packageSymbol.Version))
548 + {
549 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version));
550 + }
551 + }
552 + else if (moduleSymbol != null)
553 + {
554 + if (!this.WindowsInstallerBackendHelper.IsValidFourPartVersion(moduleSymbol.Version))
555 + {
556 + this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(moduleSymbol.SourceLineNumbers, moduleSymbol.Version));
557 + }
558 + }
559 + }
560 +
561 private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol)
562 {
563 var codepage = packageSymbol?.Codepage ?? moduleSymbol?.Codepage ?? patchSymbol?.Codepage;
src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+10
@@ -1033,6 +1033,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1033
1034 private void AddUpgradeSymbol(UpgradeSymbol symbol)
1035 {
1036 + if (!String.IsNullOrEmpty(symbol.VersionMin) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMin))
1037 + {
1038 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMin));
1039 + }
1040 +
1041 + if (!String.IsNullOrEmpty(symbol.VersionMax) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMax))
1042 + {
1043 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMax));
1044 + }
1045 +
1046 var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade");
1047 row.UpgradeCode = symbol.UpgradeCode;
1048 row.VersionMin = symbol.VersionMin;
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+10
@@ -93,6 +93,16 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
93 return this.backendHelper.IsValidIdentifier(id);
94 }
95
96 + public bool IsValidMsiProductVersion(string version)
97 + {
98 + return this.backendHelper.IsValidMsiProductVersion(version);
99 + }
100 +
101 + public bool IsValidWixVersion(string version)
102 + {
103 + return this.backendHelper.IsValidWixVersion(version);
104 + }
105 +
106 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
107 {
108 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
+6
@@ -14,6 +14,11 @@ namespace WixToolset.Core.WindowsInstaller
14 return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail);
15 }
16
17 + public static Message InvalidModuleVersion(SourceLineNumber originalLineNumber, string version)
18 + {
19 + return Message(originalLineNumber, Ids.InvalidModuleVersion, "The Module/@Version was not be able to be used as a four-part version. A valid four-part version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", version);
20 + }
21 +
22 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
23 {
24 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@@ -22,6 +27,7 @@ namespace WixToolset.Core.WindowsInstaller
27 public enum Ids
28 {
29 CannotLoadWixoutAsTransform = 7500,
30 + InvalidModuleVersion = 7501,
31 } // last available is 7999. 8000 is BurnBackendErrors.
32 }
33 }
src/wix/WixToolset.Core/Common.cs
+5
@@ -132,6 +132,11 @@ namespace WixToolset.Core
132 return true;
133 }
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;
138 + }
139 +
140 public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
141 {
142 if (String.IsNullOrEmpty(filename))
src/wix/WixToolset.Core/CompilerCore.cs
+1 -21
@@ -221,27 +221,7 @@ namespace WixToolset.Core
221 /// <returns>True if version is a valid product version</returns>
222 public static bool IsValidProductVersion(string version)
223 {
224 - if (!Common.IsValidBinderVariable(version))
225 - {
226 - Version ver = new Version(version);
227 -
228 - if (255 < ver.Major || 255 < ver.Minor || 65535 < ver.Build)
229 - {
230 - return false;
231 - }
232 - }
233 -
234 - return true;
235 - }
236 -
237 - /// <summary>
238 - /// Verifies the given string is a valid module or bundle version.
239 - /// </summary>
240 - /// <param name="version">The version to verify.</param>
241 - /// <returns>True if version is a valid module or bundle version.</returns>
242 - public static bool IsValidModuleOrBundleVersion(string version)
243 - {
244 - return Common.IsValidFourPartVersion(version);
224 + return Common.IsValidBinderVariable(version) || Common.IsValidMsiProductVersion(version);
225 }
226
227 /// <summary>
src/wix/WixToolset.Core/Compiler_Bundle.cs
-4
@@ -246,10 +246,6 @@ namespace WixToolset.Core
246 {
247 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
248 }
249 - else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
250 - {
251 - this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version));
252 - }
249
250 if (String.IsNullOrEmpty(upgradeCode))
251 {
src/wix/WixToolset.Core/Compiler_Module.cs
-4
@@ -96,10 +96,6 @@ namespace WixToolset.Core
96 {
97 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
98 }
99 - else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
100 - {
101 - this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Module", version));
102 - }
99
100 try
101 {
src/wix/WixToolset.Core/Compiler_Package.cs
+2 -10
@@ -105,12 +105,8 @@ namespace WixToolset.Core
105 case "UpgradeCode":
106 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
107 break;
108 - case "Version": // if the attribute is valid version, use the attribute value as is (so "1.0000.01.01" would *not* get translated to "1.0.1.1").
109 - var verifiedVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
110 - if (!String.IsNullOrEmpty(verifiedVersion))
111 - {
112 - version = attrib.Value;
113 - }
108 + case "Version":
109 + version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
110 break;
111 default:
112 this.Core.UnexpectedAttribute(node, attrib);
@@ -147,10 +143,6 @@ namespace WixToolset.Core
143 {
144 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
145 }
150 - else if (!CompilerCore.IsValidProductVersion(version))
151 - {
152 - this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version));
153 - }
146
147 if (compressed != YesNoDefaultType.No)
148 {
src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+10
@@ -95,6 +95,16 @@ namespace WixToolset.Core.ExtensibilityServices
95 return Common.IsValidFourPartVersion(version);
96 }
97
98 + public bool IsValidMsiProductVersion(string version)
99 + {
100 + return Common.IsValidMsiProductVersion(version);
101 + }
102 +
103 + public bool IsValidWixVersion(string version)
104 + {
105 + return WixVersion.TryParse(version, out _);
106 + }
107 +
108 public bool IsValidIdentifier(string id)
109 {
110 return Common.IsIdentifier(id);
src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+3 -2
@@ -552,9 +552,10 @@ namespace WixToolset.Core.ExtensibilityServices
552
553 if (!String.IsNullOrEmpty(value))
554 {
555 - if (Version.TryParse(value, out var version))
555 + if (WixVersion.TryParse(value, out var version))
556 {
557 - return version.ToString();
557 + // Return the attribute value sans-prefix, if present.
558 + return version.Prefix.HasValue ? value.Substring(1) : value;
559 }
560
561 // Allow versions to contain binder variables.
src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs new
+99
@@ -0,0 +1,99 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.Core
4 +{
5 + using System;
6 + using System.Xml.Linq;
7 + using WixToolset.Core;
8 + using WixToolset.Data;
9 + using WixToolset.Extensibility.Services;
10 + using Xunit;
11 +
12 + public class ParserHelperFixture
13 + {
14 + [Fact]
15 + public void CanParseFourPartAttributeVersion()
16 + {
17 + var helper = GetParserHelper();
18 +
19 + var attribute = CreateAttribute("1.2.3.4");
20 + var result = helper.GetAttributeVersionValue(null, attribute);
21 +
22 + Assert.Equal("1.2.3.4", result);
23 + }
24 +
25 + [Fact]
26 + public void CannotParseFivePartAttributeVersion()
27 + {
28 + var helper = GetParserHelper();
29 +
30 + var attribute = CreateAttribute("1.2.3.4.5");
31 + var exception = Assert.Throws<WixException>(() => { helper.GetAttributeVersionValue(null, attribute); });
32 + Assert.Equal("The Test/@Value attribute's value, '1.2.3.4.5', is not a valid version. Specify a four-part version or semantic version, such as '#.#.#.#' or '#.#.#-label.#'.", exception.Message);
33 + }
34 +
35 + [Fact]
36 + public void CannotParseVersionTooLargeAttributeVersion()
37 + {
38 + var version = "4294967296.2.3.4";
39 + AssertVersion(version);
40 + }
41 +
42 + private static void AssertVersion(string version)
43 + {
44 + var helper = GetParserHelper();
45 + var attribute = CreateAttribute(version);
46 + var exception = Assert.Throws<WixException>(() => { helper.GetAttributeVersionValue(null, attribute); });
47 + Assert.Equal($"The Test/@Value attribute's value, '{version}', is not a valid version. Specify a four-part version or semantic version, such as '#.#.#.#' or '#.#.#-label.#'.", exception.Message);
48 + }
49 +
50 + [Fact]
51 + public void CanParseSemverAttributeVersion()
52 + {
53 + var helper = GetParserHelper();
54 +
55 + var attribute = CreateAttribute("10.99.444-preview.0");
56 + var result = helper.GetAttributeVersionValue(null, attribute);
57 +
58 + Assert.Equal("10.99.444-preview.0", result);
59 + }
60 +
61 + [Fact]
62 + public void CanParseFourPartSemverAttributeVersion()
63 + {
64 + var helper = GetParserHelper();
65 +
66 + var attribute = CreateAttribute("1.2.3.4-meta.123-other.456");
67 + var result = helper.GetAttributeVersionValue(null, attribute);
68 +
69 + Assert.Equal("1.2.3.4-meta.123-other.456", result);
70 + }
71 +
72 + [Fact]
73 + public void CanParseVersionWithLeadingV()
74 + {
75 + var helper = GetParserHelper();
76 +
77 + var attribute = CreateAttribute("v1.2.3.4");
78 + var result = helper.GetAttributeVersionValue(null, attribute);
79 +
80 + Assert.Equal("1.2.3.4", result);
81 + }
82 +
83 +
84 + private static IParseHelper GetParserHelper()
85 + {
86 + var sp = WixToolsetServiceProviderFactory.CreateServiceProvider();
87 + var helper = sp.GetService<IParseHelper>();
88 + return helper;
89 + }
90 +
91 + private static XAttribute CreateAttribute(string value)
92 + {
93 + var attribute = new XAttribute("Value", value);
94 + _ = new XElement("Test", attribute);
95 +
96 + return attribute;
97 + }
98 + }
99 +}
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+3 -3
@@ -274,8 +274,8 @@ namespace WixToolsetTest.CoreIntegration
274 .ToArray();
275 WixAssert.CompareLineByLine(new string[]
276 {
277 - $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0.0' InstallSize='34' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' Version='1.0.0.0' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'>" +
278 - "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
277 + $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0-foo.55' InstallSize='34' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' Version='1.0.0-foo.55' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'>" +
278 + "<Provides Key='MyProviderKey,v1.0' Version='1.0.0-foo.55' DisplayName='BurnBundle' Imported='yes' />" +
279 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
280 "<PayloadRef Id='chain.exe' />" +
281 "</BundlePackage>",
@@ -302,7 +302,7 @@ namespace WixToolsetTest.CoreIntegration
302 .ToArray();
303 WixAssert.CompareLineByLine(new string[]
304 {
305 - "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundleDescription' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='yes' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='no' Version='1.0.0.0' Cache='keep' />",
305 + "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundleDescription' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='yes' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='no' Version='1.0.0-foo.55' Cache='keep' />",
306 }, packageElements);
307 }
308 }
src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
+4 -4
@@ -205,7 +205,7 @@ namespace WixToolsetTest.CoreIntegration
205
206 WixAssert.CompareLineByLine(new[]
207 {
208 - @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />",
208 + @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.0.1703' />",
209 }, elements);
210 }
211 }
@@ -265,7 +265,7 @@ namespace WixToolsetTest.CoreIntegration
265
266 WixAssert.CompareLineByLine(new[]
267 {
268 - @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />",
268 + @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.0.1703' />",
269 @"<Payload Name='signed_cab1.cab' CertificatePublicKey='BBD1B48A37503767C71F455624967D406A5D66C3' CertificateThumbprint='DE13B4CE635E3F63AA2394E66F95C460267BC82F' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />",
270 }, elements);
271 }
@@ -296,7 +296,7 @@ namespace WixToolsetTest.CoreIntegration
296
297 WixAssert.CompareLineByLine(new[]
298 {
299 - @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />",
299 + @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.0.1703' />",
300 @"<Payload Name='signed_cab1.cab' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />",
301 }, elements);
302 }
@@ -331,7 +331,7 @@ namespace WixToolsetTest.CoreIntegration
331
332 WixAssert.CompareLineByLine(new[]
333 {
334 - @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />",
334 + @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.0.1703' />",
335 @"<Payload Name='a.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/a.dat' Hash='D13926E5CBE5ED8B46133F9199FAF2FF25B25981C67A31AE2BC3F6C20390FACBFADCD89BD22D3445D95B989C8EACFB1E68DB634BECB5C9624865BA453BCE362A' Size='16' />",
336 @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/b.dat' Hash='5F94707BC29ADFE3B9615E6753388707FD0B8F5FD9EEEC2B17E21E72F1635FF7D7A101E7D14F614E111F263CB9AC4D0940BE1247881A7844F226D6C400293D8E' Size='37' />",
337 @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />",
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs
+1 -1
@@ -18,7 +18,7 @@
18 ProtocolVersion="1"
19 ProviderKey="MyProviderKey,v1.0"
20 UpgradeCode="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}"
21 - Version="1.0.0.0"
21 + Version="1.0.0-foo.55"
22 Win64="no"
23 />
24 </BundlePackagePayload>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs new
+12
@@ -0,0 +1,12 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Fragment>
4 + <ComponentGroup Id="ProductComponents">
5 + <ComponentGroupRef Id="MinimalComponentGroup"></ComponentGroupRef>
6 + </ComponentGroup>
7 +
8 + <Upgrade Id="B05772EA-82B8-4DE0-B7EB-45B5F0CCFE6D">
9 + <UpgradeVersion Minimum="1.256.0" Property="RELPRODFOUND"></UpgradeVersion>
10 + </Upgrade>
11 + </Fragment>
12 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs new
+11
@@ -0,0 +1,11 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Bundle Name="MsiPackage-Bundle" Version="$(Version)" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 + <BootstrapperApplication>
4 + <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 + </BootstrapperApplication>
6 +
7 + <Chain>
8 + <MsiPackage SourceFile="test1.msi" />
9 + </Chain>
10 + </Bundle>
11 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs new
+31
@@ -0,0 +1,31 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Package Version="$(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 + </Package>
14 +
15 + <Fragment>
16 + <StandardDirectory Id="DesktopFolder">
17 + <Directory Id="INSTALLFOLDER" Name="MsiPackage $(Version)v" />
18 + </StandardDirectory>
19 + </Fragment>
20 +
21 + <Fragment>
22 + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
23 + <Component>
24 + <File Source="test.txt" />
25 + </Component>
26 + <Component Id="Shared.dll" Shared="yes">
27 + <File Name="Shared.dll" Source="test.txt" />
28 + </Component>
29 + </ComponentGroup>
30 + </Fragment>
31 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs new
+43
@@ -0,0 +1,43 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.CoreIntegration
4 +{
5 + using System.IO;
6 + using System.Linq;
7 + using WixBuildTools.TestSupport;
8 + using WixToolset.Core.TestPackage;
9 + using WixToolset.Data;
10 + using Xunit;
11 +
12 + public class UpgradeFixture
13 + {
14 +
15 + [Fact]
16 + public void PopulatesInstallExecuteSequenceTable()
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\test.msi");
25 +
26 + var result = WixRunner.Execute(new[]
27 + {
28 + "build",
29 + Path.Combine(folder, "Upgrade", "UpgradeInvalidMinVersion.wxs"),
30 + Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"),
31 + Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"),
32 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
33 + "-intermediateFolder", intermediateFolder,
34 + "-o", msiPath
35 + });
36 +
37 + var message = result.Messages.Single(m => m.Level == MessageLevel.Error);
38 + Assert.Equal("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.", message.ToString());
39 + Assert.Equal(242, result.ExitCode);
40 + }
41 + }
42 + }
43 +}
src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs new
+94
@@ -0,0 +1,94 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.CoreIntegration
4 +{
5 + using System.IO;
6 + using System.Linq;
7 + using System.Xml;
8 + using WixBuildTools.TestSupport;
9 + using WixToolset.Core.TestPackage;
10 + using WixToolset.Data;
11 + using Xunit;
12 +
13 + public class VersionFixture
14 + {
15 + [Fact]
16 + public void CannotBuildMsiWithInvalidMajorVersion()
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", "Package.wxs"),
30 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
31 + "-intermediateFolder", intermediateFolder,
32 + "-d", "Version=257.0.0",
33 + "-o", msiPath
34 + });
35 +
36 + var message = result.Messages.Single(m => m.Level == MessageLevel.Error);
37 + Assert.Equal("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.", message.ToString());
38 + Assert.Equal(242, result.ExitCode);
39 + }
40 + }
41 +
42 + [Fact]
43 + public void CanBuildBundleWithSemanticVersion()
44 + {
45 + var folder = TestData.Get(@"TestData");
46 +
47 + using (var fs = new DisposableFileSystem())
48 + {
49 + var baseFolder = fs.GetFolder();
50 + var intermediateFolder = Path.Combine(baseFolder, "obj");
51 + var msiPath = Path.Combine(baseFolder, @"bin\test1.msi");
52 + var msi2Path = Path.Combine(baseFolder, @"bin\test2.msi");
53 + var bundlePath = Path.Combine(baseFolder, @"bin\bundle.exe");
54 +
55 + var result = WixRunner.Execute(new[]
56 + {
57 + "build",
58 + Path.Combine(folder, "Version", "Package.wxs"),
59 + "-bindpath", Path.Combine(folder, "SingleFile", "data"),
60 + "-intermediateFolder", intermediateFolder,
61 + "-d", "Version=255.255.65535",
62 + "-o", msiPath
63 + });
64 +
65 + result.AssertSuccess();
66 +
67 + var result3 = WixRunner.Execute(new[]
68 +{
69 + "build",
70 + Path.Combine(folder, "Version", "Bundle.wxs"),
71 + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
72 + "-bindpath", Path.Combine(baseFolder, "bin"),
73 + "-intermediateFolder", intermediateFolder,
74 + "-d", "Version=2022.3.9-preview.0-build.5+0987654321abcdef1234567890",
75 + "-o", bundlePath
76 + });
77 +
78 + result3.AssertSuccess();
79 +
80 + var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]);
81 + Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion));
82 + Assert.Equal("255.255.65535", productVersion);
83 +
84 + var extractResult = BundleExtractor.ExtractAllContainers(null, bundlePath, Path.Combine(baseFolder, "ba"), Path.Combine(baseFolder, "attached"), Path.Combine(baseFolder, "extract"));
85 + extractResult.AssertSuccess();
86 +
87 + var bundleVersion = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration/@Version")
88 + .Cast<XmlAttribute>()
89 + .Single();
90 + Assert.Equal("2022.3.9-preview.0-build.5+0987654321abcdef1234567890", bundleVersion.Value);
91 + }
92 + }
93 + }
94 +}