aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-05-09 22:21:16 -0700
committerRob Mensching <rob@firegiant.com>2022-05-10 13:03:03 -0700
commit02e682881979cd87592ee1e8e39b7744b575829c (patch)
treeef0db025ca499781cf5add96a49ffd3934c0b34e
parent905a6b0c4a214a373cb437ca28ea5610b3ad7654 (diff)
downloadwix-02e682881979cd87592ee1e8e39b7744b575829c.tar.gz
wix-02e682881979cd87592ee1e8e39b7744b575829c.tar.bz2
wix-02e682881979cd87592ee1e8e39b7744b575829c.zip
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
-rw-r--r--src/api/wix/WixToolset.Data/ErrorMessages.cs8
-rw-r--r--src/api/wix/WixToolset.Data/WarningMessages.cs12
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs18
-rw-r--r--src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution6
-rw-r--r--src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs3
-rw-r--r--src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/deputil.cpp32
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/regutil.h11
-rw-r--r--src/libs/dutil/WixToolset.DUtil/regutil.cpp53
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs2
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs19
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs43
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs25
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs6
-rw-r--r--src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs10
-rw-r--r--src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs19
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs21
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs10
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs6
-rw-r--r--src/wix/WixToolset.Core/Common.cs5
-rw-r--r--src/wix/WixToolset.Core/CompilerCore.cs22
-rw-r--r--src/wix/WixToolset.Core/Compiler_Bundle.cs4
-rw-r--r--src/wix/WixToolset.Core/Compiler_Module.cs4
-rw-r--r--src/wix/WixToolset.Core/Compiler_Package.cs12
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs10
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs5
-rw-r--r--src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs99
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs8
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs12
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs11
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs31
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs43
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs94
36 files changed, 568 insertions, 116 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs
index 186acd29..d3d2932d 100644
--- a/src/api/wix/WixToolset.Data/ErrorMessages.cs
+++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs
@@ -1076,7 +1076,7 @@ namespace WixToolset.Data
1076 1076
1077 public static Message IllegalVersionValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value) 1077 public static Message IllegalVersionValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value)
1078 { 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 } 1080 }
1081 1081
1082 public static Message IllegalWarningIdAsError(string warningId) 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); 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 } 1280 }
1281 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 { 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 } 1285 }
1286 1286
1287 public static Message InvalidPlatformValue(SourceLineNumber sourceLineNumbers, string value) 1287 public static Message InvalidPlatformValue(SourceLineNumber sourceLineNumbers, string value)
@@ -2678,7 +2678,7 @@ namespace WixToolset.Data
2678 SplitCabinetNameCollision = 377, 2678 SplitCabinetNameCollision = 377,
2679 SplitCabinetInsertionFailed = 378, 2679 SplitCabinetInsertionFailed = 378,
2680 InvalidPreprocessorFunctionAutoVersion = 379, 2680 InvalidPreprocessorFunctionAutoVersion = 379,
2681 InvalidModuleOrBundleVersion = 380, 2681 InvalidFourPartVersion = 380,
2682 UnsupportedPlatformForElement = 381, 2682 UnsupportedPlatformForElement = 381,
2683 MissingMedia = 382, 2683 MissingMedia = 382,
2684 IllegalYesNoAlwaysValue = 384, 2684 IllegalYesNoAlwaysValue = 384,
diff --git a/src/api/wix/WixToolset.Data/WarningMessages.cs b/src/api/wix/WixToolset.Data/WarningMessages.cs
index ecd76392..5a5d1e79 100644
--- a/src/api/wix/WixToolset.Data/WarningMessages.cs
+++ b/src/api/wix/WixToolset.Data/WarningMessages.cs
@@ -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); 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 } 368 }
369 369
370 public static Message InvalidModuleOrBundleVersion(SourceLineNumber sourceLineNumbers, string moduleOrBundle, string version) 370 public static Message InvalidFourPartVersion(SourceLineNumber sourceLineNumbers, string elementName, string version)
371 { 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 } 373 }
374 374
375 public static Message InvalidRemoveFile(SourceLineNumber sourceLineNumbers, string file, string component) 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); 663 return Message(sourceLineNumbers, Ids.VariableDeclarationCollision, "The variable '{0}' with value '{1}' was previously declared with value '{2}'.", variableName, variableValue, variableCollidingValue);
664 } 664 }
665 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 { 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 } 669 }
670 670
671 public static Message CollidingModularizationTypes(string tableName, string columnName, string foreignTableName, int foreignColumnNumber, string modularizationType, string foreignModularizationType) 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, 764 DeprecatedPackageCompressedAttribute = 1087,
765 DeprecatedQuestionMarksGuid = 1090, 765 DeprecatedQuestionMarksGuid = 1090,
766 PackageCodeSet = 1091, 766 PackageCodeSet = 1091,
767 InvalidModuleOrBundleVersion = 1093, 767 InvalidFourPartVersion = 1093,
768 InvalidRemoveFile = 1095, 768 InvalidRemoveFile = 1095,
769 PreprocessorWarning = 1096, 769 PreprocessorWarning = 1096,
770 UpdateOfNonKeyPathFile = 1097, 770 UpdateOfNonKeyPathFile = 1097,
@@ -816,7 +816,7 @@ namespace WixToolset.Data
816 AllChangesIncludedInPatch = 1145, 816 AllChangesIncludedInPatch = 1145,
817 RelatedAttributeConditionallyIgnored = 1146, 817 RelatedAttributeConditionallyIgnored = 1146,
818 BackslashTerminateInlineDirectorySyntax = 1147, 818 BackslashTerminateInlineDirectorySyntax = 1147,
819 VersionTruncated = 1148, 819 InvalidMsiProductVersion = 1148,
820 ServiceConfigFamilyNotSupported = 1149, 820 ServiceConfigFamilyNotSupported = 1149,
821 SymbolNotTranslatedToOutput = 1150, 821 SymbolNotTranslatedToOutput = 1150,
822 MsiTransactionLimitations = 1151, 822 MsiTransactionLimitations = 1151,
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs b/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
index 23ad44f5..1c92303e 100644
--- a/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
+++ b/src/api/wix/WixToolset.Extensibility/Services/IBackendHelper.cs
@@ -104,13 +104,27 @@ namespace WixToolset.Extensibility.Services
104 bool IsValidBinderVariable(string variable); 104 bool IsValidBinderVariable(string variable);
105 105
106 /// <summary> 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> 108 /// </summary>
109 /// <param name="version">The version to verify.</param> 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); 111 bool IsValidFourPartVersion(string version);
112 112
113 /// <summary> 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>
114 /// Determines if value is a valid identifier. 128 /// Determines if value is a valid identifier.
115 /// </summary> 129 /// </summary>
116 /// <param name="id">Identifier to validate.</param> 130 /// <param name="id">Identifier to validate.</param>
diff --git a/src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution b/src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution
new file mode 100644
index 00000000..10420ac9
--- /dev/null
+++ b/src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution
@@ -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
diff --git a/src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs b/src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs
index bce128e8..9e837cff 100644
--- a/src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs
+++ b/src/ext/Dependency/test/WixToolsetTest.Dependency/DependencyExtensionFixture.cs
@@ -17,13 +17,14 @@ namespace WixToolsetTest.Dependency
17 var folder = TestData.Get(@"TestData\UsingProvides"); 17 var folder = TestData.Get(@"TestData\UsingProvides");
18 var build = new Builder(folder, typeof(DependencyExtensionFactory), new[] { folder }); 18 var build = new Builder(folder, typeof(DependencyExtensionFactory), new[] { folder });
19 19
20 var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider") 20 var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider", "Wix4Dependency")
21 .Select(r => Regex.Replace(r, "{[^}]*}", "{*}")) 21 .Select(r => Regex.Replace(r, "{[^}]*}", "{*}"))
22 .ToArray(); 22 .ToArray();
23 WixAssert.CompareLineByLine(new[] 23 WixAssert.CompareLineByLine(new[]
24 { 24 {
25 "CustomAction:Wix4DependencyCheck_X86\t1\tDependencyCA_X86\tWixDependencyCheck\t", 25 "CustomAction:Wix4DependencyCheck_X86\t1\tDependencyCA_X86\tWixDependencyCheck\t",
26 "CustomAction:Wix4DependencyRequire_X86\t1\tDependencyCA_X86\tWixDependencyRequire\t", 26 "CustomAction:Wix4DependencyRequire_X86\t1\tDependencyCA_X86\tWixDependencyRequire\t",
27 "Wix4Dependency:depL8BNflcqZaN5CQEWh2U3SBHFDdg\tUsingRequires\t1.0.0-beta.9\t\t0",
27 "Wix4DependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t", 28 "Wix4DependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t",
28 "Wix4DependencyProvider:depTpv28q7slcxvXPWmU4Z0GfbiI.4\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\t{*}\t\t\t", 29 "Wix4DependencyProvider:depTpv28q7slcxvXPWmU4Z0GfbiI.4\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\t{*}\t\t\t",
29 }, results); 30 }, results);
diff --git a/src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs b/src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs
index 65984395..990d984e 100644
--- a/src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs
+++ b/src/ext/Dependency/test/WixToolsetTest.Dependency/TestData/UsingProvides/Package.wxs
@@ -12,7 +12,7 @@
12 <File Name="example.txt" Source="Package.wxs" /> 12 <File Name="example.txt" Source="Package.wxs" />
13 <Provides Key="UsingProvides" dep:Check="yes" /> 13 <Provides Key="UsingProvides" dep:Check="yes" />
14 <Provides> 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> 16 </Provides>
17 </Component> 17 </Component>
18 </Feature> 18 </Feature>
diff --git a/src/libs/dutil/WixToolset.DUtil/deputil.cpp b/src/libs/dutil/WixToolset.DUtil/deputil.cpp
index 1a480263..4de85199 100644
--- a/src/libs/dutil/WixToolset.DUtil/deputil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/deputil.cpp
@@ -121,9 +121,10 @@ DAPI_(HRESULT) DepCheckDependency(
121 HRESULT hr = S_OK; 121 HRESULT hr = S_OK;
122 LPWSTR sczKey = NULL; 122 LPWSTR sczKey = NULL;
123 HKEY hkKey = NULL; 123 HKEY hkKey = NULL;
124 DWORD64 dw64Version = 0; 124 VERUTIL_VERSION* pVersion = NULL;
125 DWORD64 dw64MinVersion = 0; 125 VERUTIL_VERSION* pMinVersion = NULL;
126 DWORD64 dw64MaxVersion = 0; 126 VERUTIL_VERSION* pMaxVersion = NULL;
127 int nResult = 0;
127 BOOL fAllowEqual = FALSE; 128 BOOL fAllowEqual = FALSE;
128 LPWSTR sczName = NULL; 129 LPWSTR sczName = NULL;
129 130
@@ -138,7 +139,7 @@ DAPI_(HRESULT) DepCheckDependency(
138 DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); 139 DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey);
139 140
140 // If there are no registry values, consider the key orphaned and treat it as missing. 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);
142 if (E_FILENOTFOUND != hr) 143 if (E_FILENOTFOUND != hr)
143 { 144 {
144 DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); 145 DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey);
@@ -171,11 +172,15 @@ DAPI_(HRESULT) DepCheckDependency(
171 { 172 {
172 if (*wzMinVersion) 173 if (*wzMinVersion)
173 { 174 {
174 hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); 175 hr = VerParseVersion(wzMinVersion, 0, FALSE, &pMinVersion);
175 DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); 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);
176 180
177 fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; 181 fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive;
178 if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) 182 // !(fAllowEqual && pMinVersion <= pVersion || pMinVersion < pVersion))
183 if (!(fAllowEqual && 0 <= nResult || 0 < nResult))
179 { 184 {
180 hr = DictKeyExists(sdDependencies, wzProviderKey); 185 hr = DictKeyExists(sdDependencies, wzProviderKey);
181 if (E_NOTFOUND != hr) 186 if (E_NOTFOUND != hr)
@@ -205,11 +210,15 @@ DAPI_(HRESULT) DepCheckDependency(
205 { 210 {
206 if (*wzMaxVersion) 211 if (*wzMaxVersion)
207 { 212 {
208 hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); 213 hr = VerParseVersion(wzMaxVersion, 0, FALSE, &pMaxVersion);
209 DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); 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);
210 218
211 fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; 219 fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive;
212 if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) 220 // !(fAllowEqual && pVersion <= pMaxVersion || pVersion < pMaxVersion)
221 if (!(fAllowEqual && 0 <= nResult || 0 < nResult))
213 { 222 {
214 hr = DictKeyExists(sdDependencies, wzProviderKey); 223 hr = DictKeyExists(sdDependencies, wzProviderKey);
215 if (E_NOTFOUND != hr) 224 if (E_NOTFOUND != hr)
@@ -235,6 +244,9 @@ DAPI_(HRESULT) DepCheckDependency(
235 } 244 }
236 245
237LExit: 246LExit:
247 ReleaseVerutilVersion(pMaxVersion);
248 ReleaseVerutilVersion(pMinVersion);
249 ReleaseVerutilVersion(pVersion);
238 ReleaseStr(sczName); 250 ReleaseStr(sczName);
239 ReleaseRegKey(hkKey); 251 ReleaseRegKey(hkKey);
240 ReleaseStr(sczKey); 252 ReleaseStr(sczKey);
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
index 3cbb53b0..76d2d7cb 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
@@ -1,6 +1,7 @@
1#pragma once 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. 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 3
4#include "verutil.h"
4 5
5#ifdef __cplusplus 6#ifdef __cplusplus
6extern "C" { 7extern "C" {
@@ -261,6 +262,16 @@ HRESULT DAPI RegReadVersion(
261 ); 262 );
262 263
263/******************************************************************** 264/********************************************************************
265 RegReadWixVersion - reads a registry key value as a WiX version.
266
267*********************************************************************/
268HRESULT DAPI RegReadWixVersion(
269 __in HKEY hk,
270 __in_z_opt LPCWSTR wzName,
271 __out VERUTIL_VERSION** ppVersion
272 );
273
274/********************************************************************
264 RegReadNone - reads a NONE registry key value. 275 RegReadNone - reads a NONE registry key value.
265 276
266*********************************************************************/ 277*********************************************************************/
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
index 78507ade..64224d42 100644
--- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -590,6 +590,7 @@ DAPI_(HRESULT) RegReadVersion(
590 { 590 {
591 ExitFunction1(hr = E_FILENOTFOUND); 591 ExitFunction1(hr = E_FILENOTFOUND);
592 } 592 }
593
593 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) 594 if (REG_SZ == dwType || REG_EXPAND_SZ == dwType)
594 { 595 {
595 hr = RegReadString(hk, wzName, &sczVersion); 596 hr = RegReadString(hk, wzName, &sczVersion);
@@ -614,6 +615,58 @@ LExit:
614 return hr; 615 return hr;
615} 616}
616 617
618
619DAPI_(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
662LExit:
663 ReleaseVerutilVersion(pVersion);
664 ReleaseStr(sczVersion);
665
666 return hr;
667}
668
669
617DAPI_(HRESULT) RegReadNone( 670DAPI_(HRESULT) RegReadNone(
618 __in HKEY hk, 671 __in HKEY hk,
619 __in_z_opt LPCWSTR wzName 672 __in_z_opt LPCWSTR wzName
diff --git a/src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs b/src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs
index b59e53e1..65ae1e44 100644
--- a/src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs
+++ b/src/test/burn/TestData/DependencyTests/PackageB/ProductComponents.wxs
@@ -11,7 +11,7 @@
11 <Component Id="FileComponent2" Guid="A1866388-65B4-4215-A8FB-9A7AADBE4E8E" Directory="INSTALLFOLDER"> 11 <Component Id="FileComponent2" Guid="A1866388-65B4-4215-A8FB-9A7AADBE4E8E" Directory="INSTALLFOLDER">
12 <File Source="$(sys.SOURCEFILEPATH)" /> 12 <File Source="$(sys.SOURCEFILEPATH)" />
13 <Provides> 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> 15 </Provides>
16 </Component> 16 </Component>
17 </Fragment> 17 </Fragment>
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
index 3437bf00..f4bc6ba9 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
@@ -1086,5 +1086,24 @@ namespace WixToolsetTest.BurnE2E
1086 packageA.UninstallProduct(); 1086 packageA.UninstallProduct();
1087 packageA.VerifyInstalled(false); 1087 packageA.VerifyInstalled(false);
1088 } 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 }
1089 } 1108 }
1090} 1109}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index 8211bf83..e63bf65c 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -71,11 +71,11 @@ namespace WixToolset.Core.Burn.Bundles
71 FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false); 71 FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false);
72 File.SetAttributes(bundleTempPath, FileAttributes.Normal); 72 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
73 73
74 var windowsAssemblyVersion = GetWindowsAssemblyVersion(this.BundleSymbol); 74 var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol);
75 75
76 var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, windowsAssemblyVersion); 76 var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, fourPartVersion);
77 77
78 UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, windowsAssemblyVersion, applicationManifestData); 78 this.UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, fourPartVersion, applicationManifestData);
79 79
80 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers 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. 81 // if they should be attached.
@@ -242,24 +242,37 @@ namespace WixToolset.Core.Burn.Bundles
242 } 242 }
243 } 243 }
244 244
245 private static Version GetWindowsAssemblyVersion(WixBundleSymbol bundleSymbol) 245 private Version GetFourPartVersion(WixBundleSymbol bundleSymbol)
246 { 246 {
247 // Ensure the bundle info provides a full four part version. 247 // Ensure the bundle info provides a full four-part version.
248 var fourPartVersion = new Version(bundleSymbol.Version); 248
249 var major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major; 249 if (!WixVersion.TryParse(bundleSymbol.Version, out var wixVersion))
250 var minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor; 250 {
251 var build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build; 251 // Display an error message indicating that we will require a four-part version number
252 var revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision; 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;
253 261
254 if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision) 262 if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision)
255 { 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));
257 } 270 }
258 271
259 return new Version(major, minor, build, revision); 272 return new Version((int)major, (int)minor, (int)build, (int)revision);
260 } 273 }
261 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)
263 { 276 {
264 const int burnLocale = 1033; 277 const int burnLocale = 1033;
265 var resources = new ResourceCollection(); 278 var resources = new ResourceCollection();
@@ -268,8 +281,8 @@ namespace WixToolset.Core.Burn.Bundles
268 version.Load(bundleTempPath); 281 version.Load(bundleTempPath);
269 resources.Add(version); 282 resources.Add(version);
270 283
271 version.FileVersion = windowsAssemblyVersion; 284 version.FileVersion = fourPartVersion;
272 version.ProductVersion = windowsAssemblyVersion; 285 version.ProductVersion = fourPartVersion;
273 286
274 var strings = version[burnLocale] ?? version.Add(burnLocale); 287 var strings = version[burnLocale] ?? version.Add(burnLocale);
275 strings["LegalCopyright"] = bundleInfo.Copyright; 288 strings["LegalCopyright"] = bundleInfo.Copyright;
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
index d640e85d..87528a48 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
@@ -118,30 +118,9 @@ namespace WixToolset.Core.Burn.Bundles
118 this.ChainPackage.Version = this.MsiPackage.ProductVersion; 118 this.ChainPackage.Version = this.MsiPackage.ProductVersion;
119 } 119 }
120 120
121 if (!this.BackendHelper.IsValidFourPartVersion(this.MsiPackage.ProductVersion)) 121 if (!this.BackendHelper.IsValidMsiProductVersion(this.MsiPackage.ProductVersion))
122 { 122 {
123 // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? 123 this.Messaging.Write(WarningMessages.InvalidMsiProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId));
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 }
145 } 124 }
146 125
147 this.SetPerMachineAppropriately(harvestedMsiPackage.AllUsers); 126 this.SetPerMachineAppropriately(harvestedMsiPackage.AllUsers);
diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
index 0c305331..69cd8fa6 100644
--- a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
@@ -16,6 +16,11 @@ namespace WixToolset.Core.Burn
16 return Message(sourceLineNumbers, Ids.AttachedContainerPayloadCollision2, "The location of the payload related to the previous error."); 16 return Message(sourceLineNumbers, Ids.AttachedContainerPayloadCollision2, "The location of the payload related to the previous error.");
17 } 17 }
18 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
19 public static Message EmptyContainer(SourceLineNumber sourceLineNumbers, string containerId) 24 public static Message EmptyContainer(SourceLineNumber sourceLineNumbers, string containerId)
20 { 25 {
21 return Message(sourceLineNumbers, Ids.EmptyContainer, "The Container '{0}' is being ignored because it doesn't have any payloads.", containerId); 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
61 UnknownBundleRelationAction = 8505, 66 UnknownBundleRelationAction = 8505,
62 HiddenBundleNotSupported = 8506, 67 HiddenBundleNotSupported = 8506,
63 UnknownMsiPackagePlatform = 8507, 68 UnknownMsiPackagePlatform = 8507,
69 CannotParseBundleVersionAsFourPartVersion = 8508,
64 } // last available is 8999. 9000 is VerboseMessages. 70 } // last available is 8999. 9000 is VerboseMessages.
65 } 71 }
66} 72}
diff --git a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
index ed662b7f..909c9b60 100644
--- a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
+++ b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/BurnBackendHelper.cs
@@ -104,6 +104,16 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
104 return this.backendHelper.IsValidIdentifier(id); 104 return this.backendHelper.IsValidIdentifier(id);
105 } 105 }
106 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
107 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) 117 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
108 { 118 {
109 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative); 119 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
diff --git a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
index 9ef91028..3022ba76 100644
--- a/src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
+++ b/src/wix/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
@@ -52,16 +52,27 @@ namespace WixToolset.Core.Burn.ExtensibilityServices
52 52
53 if (null != versionInfo) 53 if (null != versionInfo)
54 { 54 {
55 // Use the fixed version info block for the file since the resource text may not be a dotted quad. 55 var version = versionInfo.ProductVersion;
56 var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
57 56
58 if (PayloadHarvester.EmptyVersion != version) 57 if (String.IsNullOrEmpty(version))
59 { 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 }
61 } 71 }
62 72
63 payload.Description = versionInfo.FileDescription; 73 payload.Description = versionInfo.FileDescription;
64 payload.DisplayName = versionInfo.ProductName; 74 payload.DisplayName = versionInfo.ProductName;
75 payload.Version = version;
65 } 76 }
66 } 77 }
67 } 78 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 69a954eb..28b95810 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -298,6 +298,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
298 command.Execute(); 298 command.Execute();
299 } 299 }
300 300
301 // Now that delayed fields are processed, validate the package/module version.
302 this.VerifyVersion(packageSymbol, moduleSymbol);
303
301 // Process dependency references. 304 // Process dependency references.
302 if (SectionType.Product == section.Type || SectionType.Module == section.Type) 305 if (SectionType.Product == section.Type || SectionType.Module == section.Type)
303 { 306 {
@@ -537,6 +540,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
537 return result; 540 return result;
538 } 541 }
539 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
540 private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol) 561 private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol)
541 { 562 {
542 var codepage = packageSymbol?.Codepage ?? moduleSymbol?.Codepage ?? patchSymbol?.Codepage; 563 var codepage = packageSymbol?.Codepage ?? moduleSymbol?.Codepage ?? patchSymbol?.Codepage;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
index 5b44e765..e5966920 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateWindowsInstallerDataFromIRCommand.cs
@@ -1033,6 +1033,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1033 1033
1034 private void AddUpgradeSymbol(UpgradeSymbol symbol) 1034 private void AddUpgradeSymbol(UpgradeSymbol symbol)
1035 { 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
1036 var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade"); 1046 var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade");
1037 row.UpgradeCode = symbol.UpgradeCode; 1047 row.UpgradeCode = symbol.UpgradeCode;
1038 row.VersionMin = symbol.VersionMin; 1048 row.VersionMin = symbol.VersionMin;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
index b3b4421a..30e167f5 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs
@@ -93,6 +93,16 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices
93 return this.backendHelper.IsValidIdentifier(id); 93 return this.backendHelper.IsValidIdentifier(id);
94 } 94 }
95 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
96 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) 106 public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
97 { 107 {
98 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative); 108 return this.backendHelper.IsValidLongFilename(filename, allowWildcards, allowRelative);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
index 2efb06f1..57b7181b 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs
@@ -14,6 +14,11 @@ namespace WixToolset.Core.WindowsInstaller
14 return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); 14 return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail);
15 } 15 }
16 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
17 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) 22 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
18 { 23 {
19 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); 24 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@@ -22,6 +27,7 @@ namespace WixToolset.Core.WindowsInstaller
22 public enum Ids 27 public enum Ids
23 { 28 {
24 CannotLoadWixoutAsTransform = 7500, 29 CannotLoadWixoutAsTransform = 7500,
30 InvalidModuleVersion = 7501,
25 } // last available is 7999. 8000 is BurnBackendErrors. 31 } // last available is 7999. 8000 is BurnBackendErrors.
26 } 32 }
27} 33}
diff --git a/src/wix/WixToolset.Core/Common.cs b/src/wix/WixToolset.Core/Common.cs
index 8e341c52..7a7a654c 100644
--- a/src/wix/WixToolset.Core/Common.cs
+++ b/src/wix/WixToolset.Core/Common.cs
@@ -132,6 +132,11 @@ namespace WixToolset.Core
132 return true; 132 return true;
133 } 133 }
134 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
135 public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) 140 public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative)
136 { 141 {
137 if (String.IsNullOrEmpty(filename)) 142 if (String.IsNullOrEmpty(filename))
diff --git a/src/wix/WixToolset.Core/CompilerCore.cs b/src/wix/WixToolset.Core/CompilerCore.cs
index 0fd302e5..8c676f51 100644
--- a/src/wix/WixToolset.Core/CompilerCore.cs
+++ b/src/wix/WixToolset.Core/CompilerCore.cs
@@ -221,27 +221,7 @@ namespace WixToolset.Core
221 /// <returns>True if version is a valid product version</returns> 221 /// <returns>True if version is a valid product version</returns>
222 public static bool IsValidProductVersion(string version) 222 public static bool IsValidProductVersion(string version)
223 { 223 {
224 if (!Common.IsValidBinderVariable(version)) 224 return Common.IsValidBinderVariable(version) || Common.IsValidMsiProductVersion(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);
245 } 225 }
246 226
247 /// <summary> 227 /// <summary>
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs
index 3039a205..36194882 100644
--- a/src/wix/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs
@@ -246,10 +246,6 @@ namespace WixToolset.Core
246 { 246 {
247 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); 247 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
248 } 248 }
249 else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
250 {
251 this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version));
252 }
253 249
254 if (String.IsNullOrEmpty(upgradeCode)) 250 if (String.IsNullOrEmpty(upgradeCode))
255 { 251 {
diff --git a/src/wix/WixToolset.Core/Compiler_Module.cs b/src/wix/WixToolset.Core/Compiler_Module.cs
index 7b708981..7e3a2a0e 100644
--- a/src/wix/WixToolset.Core/Compiler_Module.cs
+++ b/src/wix/WixToolset.Core/Compiler_Module.cs
@@ -96,10 +96,6 @@ namespace WixToolset.Core
96 { 96 {
97 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); 97 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
98 } 98 }
99 else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
100 {
101 this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Module", version));
102 }
103 99
104 try 100 try
105 { 101 {
diff --git a/src/wix/WixToolset.Core/Compiler_Package.cs b/src/wix/WixToolset.Core/Compiler_Package.cs
index d969decb..6ef3f241 100644
--- a/src/wix/WixToolset.Core/Compiler_Package.cs
+++ b/src/wix/WixToolset.Core/Compiler_Package.cs
@@ -105,12 +105,8 @@ namespace WixToolset.Core
105 case "UpgradeCode": 105 case "UpgradeCode":
106 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); 106 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
107 break; 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"). 108 case "Version":
109 var verifiedVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); 109 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
110 if (!String.IsNullOrEmpty(verifiedVersion))
111 {
112 version = attrib.Value;
113 }
114 break; 110 break;
115 default: 111 default:
116 this.Core.UnexpectedAttribute(node, attrib); 112 this.Core.UnexpectedAttribute(node, attrib);
@@ -147,10 +143,6 @@ namespace WixToolset.Core
147 { 143 {
148 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); 144 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
149 } 145 }
150 else if (!CompilerCore.IsValidProductVersion(version))
151 {
152 this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version));
153 }
154 146
155 if (compressed != YesNoDefaultType.No) 147 if (compressed != YesNoDefaultType.No)
156 { 148 {
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
index 3348ad0b..f3e526a9 100644
--- a/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+++ b/src/wix/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
@@ -95,6 +95,16 @@ namespace WixToolset.Core.ExtensibilityServices
95 return Common.IsValidFourPartVersion(version); 95 return Common.IsValidFourPartVersion(version);
96 } 96 }
97 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
98 public bool IsValidIdentifier(string id) 108 public bool IsValidIdentifier(string id)
99 { 109 {
100 return Common.IsIdentifier(id); 110 return Common.IsIdentifier(id);
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs b/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
index bab1dd34..4c6702b8 100644
--- a/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+++ b/src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
@@ -552,9 +552,10 @@ namespace WixToolset.Core.ExtensibilityServices
552 552
553 if (!String.IsNullOrEmpty(value)) 553 if (!String.IsNullOrEmpty(value))
554 { 554 {
555 if (Version.TryParse(value, out var version)) 555 if (WixVersion.TryParse(value, out var version))
556 { 556 {
557 return version.ToString(); 557 // Return the attribute value sans-prefix, if present.
558 return version.Prefix.HasValue ? value.Substring(1) : value;
558 } 559 }
559 560
560 // Allow versions to contain binder variables. 561 // Allow versions to contain binder variables.
diff --git a/src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs b/src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs
new file mode 100644
index 00000000..95433ab3
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs
@@ -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
3namespace 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}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
index eb5cf211..bf9e330c 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
@@ -274,8 +274,8 @@ namespace WixToolsetTest.CoreIntegration
274 .ToArray(); 274 .ToArray();
275 WixAssert.CompareLineByLine(new string[] 275 WixAssert.CompareLineByLine(new string[]
276 { 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'>" + 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.0' DisplayName='BurnBundle' Imported='yes' />" + 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' />" + 279 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
280 "<PayloadRef Id='chain.exe' />" + 280 "<PayloadRef Id='chain.exe' />" +
281 "</BundlePackage>", 281 "</BundlePackage>",
@@ -302,7 +302,7 @@ namespace WixToolsetTest.CoreIntegration
302 .ToArray(); 302 .ToArray();
303 WixAssert.CompareLineByLine(new string[] 303 WixAssert.CompareLineByLine(new string[]
304 { 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); 306 }, packageElements);
307 } 307 }
308 } 308 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
index 7126b0d5..9336a635 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
@@ -205,7 +205,7 @@ namespace WixToolsetTest.CoreIntegration
205 205
206 WixAssert.CompareLineByLine(new[] 206 WixAssert.CompareLineByLine(new[]
207 { 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); 209 }, elements);
210 } 210 }
211 } 211 }
@@ -265,7 +265,7 @@ namespace WixToolsetTest.CoreIntegration
265 265
266 WixAssert.CompareLineByLine(new[] 266 WixAssert.CompareLineByLine(new[]
267 { 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' />", 269 @"<Payload Name='signed_cab1.cab' CertificatePublicKey='BBD1B48A37503767C71F455624967D406A5D66C3' CertificateThumbprint='DE13B4CE635E3F63AA2394E66F95C460267BC82F' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />",
270 }, elements); 270 }, elements);
271 } 271 }
@@ -296,7 +296,7 @@ namespace WixToolsetTest.CoreIntegration
296 296
297 WixAssert.CompareLineByLine(new[] 297 WixAssert.CompareLineByLine(new[]
298 { 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' />", 300 @"<Payload Name='signed_cab1.cab' Hash='D8D3842403710E1F6036A62543224855CADF546853933C2B17BA99D789D4347B36717687C022678A9D3DE749DFC1482DAAB92B997B62BB32A8A6828B9D04C414' Size='1585' />",
301 }, elements); 301 }, elements);
302 } 302 }
@@ -331,7 +331,7 @@ namespace WixToolsetTest.CoreIntegration
331 331
332 WixAssert.CompareLineByLine(new[] 332 WixAssert.CompareLineByLine(new[]
333 { 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' />", 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' />", 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' />", 337 @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />",
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs
index 1a2b17f5..8800ea1b 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs
@@ -18,7 +18,7 @@
18 ProtocolVersion="1" 18 ProtocolVersion="1"
19 ProviderKey="MyProviderKey,v1.0" 19 ProviderKey="MyProviderKey,v1.0"
20 UpgradeCode="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}" 20 UpgradeCode="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}"
21 Version="1.0.0.0" 21 Version="1.0.0-foo.55"
22 Win64="no" 22 Win64="no"
23 /> 23 />
24 </BundlePackagePayload> 24 </BundlePackagePayload>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs
new file mode 100644
index 00000000..41c93bec
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs
@@ -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>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs
new file mode 100644
index 00000000..01c56be2
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs
@@ -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>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs
new file mode 100644
index 00000000..1a9e102f
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs
@@ -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>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs
new file mode 100644
index 00000000..39ccc683
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs
@@ -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
3namespace 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}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs
new file mode 100644
index 00000000..c3758c7e
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs
@@ -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
3namespace 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}