From 02e682881979cd87592ee1e8e39b7744b575829c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 9 May 2022 22:21:16 -0700 Subject: 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 --- src/api/wix/WixToolset.Data/ErrorMessages.cs | 8 +- src/api/wix/WixToolset.Data/WarningMessages.cs | 12 +-- .../Services/IBackendHelper.cs | 18 +++- .../Dependency.wixext.v3.ncrunchsolution | 6 ++ .../DependencyExtensionFixture.cs | 3 +- .../TestData/UsingProvides/Package.wxs | 2 +- src/libs/dutil/WixToolset.DUtil/deputil.cpp | 32 ++++--- src/libs/dutil/WixToolset.DUtil/inc/regutil.h | 11 +++ src/libs/dutil/WixToolset.DUtil/regutil.cpp | 53 ++++++++++++ .../DependencyTests/PackageB/ProductComponents.wxs | 2 +- .../burn/WixToolsetTest.BurnE2E/DependencyTests.cs | 19 +++++ .../Bundles/CreateBundleExeCommand.cs | 43 ++++++---- .../Bundles/ProcessMsiPackageCommand.cs | 25 +----- .../WixToolset.Core.Burn/BurnBackendWarnings.cs | 6 ++ .../ExtensibilityServices/BurnBackendHelper.cs | 10 +++ .../ExtensibilityServices/PayloadHarvester.cs | 19 ++++- .../Bind/BindDatabaseCommand.cs | 21 +++++ .../CreateWindowsInstallerDataFromIRCommand.cs | 10 +++ .../WindowsInstallerBackendHelper.cs | 10 +++ .../WindowsInstallerBackendErrors.cs | 6 ++ src/wix/WixToolset.Core/Common.cs | 5 ++ src/wix/WixToolset.Core/CompilerCore.cs | 22 +---- src/wix/WixToolset.Core/Compiler_Bundle.cs | 4 - src/wix/WixToolset.Core/Compiler_Module.cs | 4 - src/wix/WixToolset.Core/Compiler_Package.cs | 12 +-- .../ExtensibilityServices/BackendHelper.cs | 10 +++ .../ExtensibilityServices/ParseHelper.cs | 5 +- .../WixToolsetTest.Core/ParserHelperFixture.cs | 99 ++++++++++++++++++++++ .../BundlePackageFixture.cs | 6 +- .../RemotePayloadFixture.cs | 8 +- .../TestData/BundlePackage/RemoteBundlePackage.wxs | 2 +- .../TestData/Upgrade/UpgradeInvalidMinVersion.wxs | 12 +++ .../TestData/Version/Bundle.wxs | 11 +++ .../TestData/Version/Package.wxs | 31 +++++++ .../UpgradeFixture.cs | 43 ++++++++++ .../VersionFixture.cs | 94 ++++++++++++++++++++ 36 files changed, 568 insertions(+), 116 deletions(-) create mode 100644 src/ext/Dependency/Dependency.wixext.v3.ncrunchsolution create mode 100644 src/wix/test/WixToolsetTest.Core/ParserHelperFixture.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/Upgrade/UpgradeInvalidMinVersion.wxs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Bundle.wxs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/Version/Package.wxs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/UpgradeFixture.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/VersionFixture.cs 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 public static Message IllegalVersionValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string value) { - 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); + 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); } public static Message IllegalWarningIdAsError(string warningId) @@ -1279,9 +1279,9 @@ namespace WixToolset.Data 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); } - public static Message InvalidModuleOrBundleVersion(SourceLineNumber sourceLineNumbers, string moduleOrBundle, string version) + public static Message InvalidFourPartVersion(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string version) { - 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); + 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); } public static Message InvalidPlatformValue(SourceLineNumber sourceLineNumbers, string value) @@ -2678,7 +2678,7 @@ namespace WixToolset.Data SplitCabinetNameCollision = 377, SplitCabinetInsertionFailed = 378, InvalidPreprocessorFunctionAutoVersion = 379, - InvalidModuleOrBundleVersion = 380, + InvalidFourPartVersion = 380, UnsupportedPlatformForElement = 381, MissingMedia = 382, 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 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); } - public static Message InvalidModuleOrBundleVersion(SourceLineNumber sourceLineNumbers, string moduleOrBundle, string version) + public static Message InvalidFourPartVersion(SourceLineNumber sourceLineNumbers, string elementName, string version) { - 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); + 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); } public static Message InvalidRemoveFile(SourceLineNumber sourceLineNumbers, string file, string component) @@ -663,9 +663,9 @@ namespace WixToolset.Data return Message(sourceLineNumbers, Ids.VariableDeclarationCollision, "The variable '{0}' with value '{1}' was previously declared with value '{2}'.", variableName, variableValue, variableCollidingValue); } - public static Message VersionTruncated(SourceLineNumber sourceLineNumbers, string originalVersion, string package, string truncatedVersion) + public static Message InvalidMsiProductVersion(SourceLineNumber sourceLineNumbers, string version, string package) { - 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); + 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); } public static Message CollidingModularizationTypes(string tableName, string columnName, string foreignTableName, int foreignColumnNumber, string modularizationType, string foreignModularizationType) @@ -764,7 +764,7 @@ namespace WixToolset.Data DeprecatedPackageCompressedAttribute = 1087, DeprecatedQuestionMarksGuid = 1090, PackageCodeSet = 1091, - InvalidModuleOrBundleVersion = 1093, + InvalidFourPartVersion = 1093, InvalidRemoveFile = 1095, PreprocessorWarning = 1096, UpdateOfNonKeyPathFile = 1097, @@ -816,7 +816,7 @@ namespace WixToolset.Data AllChangesIncludedInPatch = 1145, RelatedAttributeConditionallyIgnored = 1146, BackslashTerminateInlineDirectorySyntax = 1147, - VersionTruncated = 1148, + InvalidMsiProductVersion = 1148, ServiceConfigFamilyNotSupported = 1149, SymbolNotTranslatedToOutput = 1150, 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,12 +104,26 @@ namespace WixToolset.Extensibility.Services bool IsValidBinderVariable(string variable); /// - /// Verifies the given string is a valid 4-part version module or bundle version. + /// Verifies the given string is a valid 4-part version. /// /// The version to verify. - /// True if version is a valid module or bundle version. + /// True if version is a valid 4-part version. bool IsValidFourPartVersion(string version); + /// + /// Verifies the given string is a valid MSI product version. + /// + /// The MSI product version to verify. + /// True if version is a valid MSI product version + bool IsValidMsiProductVersion(string version); + + /// + /// Verifies the given string is a valid WiX version. + /// + /// The version to verify. + /// True if version is a valid WiX version. + bool IsValidWixVersion(string version); + /// /// Determines if value is a valid identifier. /// 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 @@ + + + True + True + + \ 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 var folder = TestData.Get(@"TestData\UsingProvides"); var build = new Builder(folder, typeof(DependencyExtensionFactory), new[] { folder }); - var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider") + var results = build.BuildAndQuery(Build, "CustomAction", "Wix4DependencyProvider", "Wix4Dependency") .Select(r => Regex.Replace(r, "{[^}]*}", "{*}")) .ToArray(); WixAssert.CompareLineByLine(new[] { "CustomAction:Wix4DependencyCheck_X86\t1\tDependencyCA_X86\tWixDependencyCheck\t", "CustomAction:Wix4DependencyRequire_X86\t1\tDependencyCA_X86\tWixDependencyRequire\t", + "Wix4Dependency:depL8BNflcqZaN5CQEWh2U3SBHFDdg\tUsingRequires\t1.0.0-beta.9\t\t0", "Wix4DependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t", "Wix4DependencyProvider:depTpv28q7slcxvXPWmU4Z0GfbiI.4\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\t{*}\t\t\t", }, 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 @@ - + 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( HRESULT hr = S_OK; LPWSTR sczKey = NULL; HKEY hkKey = NULL; - DWORD64 dw64Version = 0; - DWORD64 dw64MinVersion = 0; - DWORD64 dw64MaxVersion = 0; + VERUTIL_VERSION* pVersion = NULL; + VERUTIL_VERSION* pMinVersion = NULL; + VERUTIL_VERSION* pMaxVersion = NULL; + int nResult = 0; BOOL fAllowEqual = FALSE; LPWSTR sczName = NULL; @@ -138,7 +139,7 @@ DAPI_(HRESULT) DepCheckDependency( DepExitOnFailure(hr, "Failed to open the registry key for dependency \"%ls\".", wzProviderKey); // If there are no registry values, consider the key orphaned and treat it as missing. - hr = RegReadVersion(hkKey, vcszVersionValue, &dw64Version); + hr = RegReadWixVersion(hkKey, vcszVersionValue, &pVersion); if (E_FILENOTFOUND != hr) { DepExitOnFailure(hr, "Failed to read the %ls registry value for dependency \"%ls\".", vcszVersionValue, wzProviderKey); @@ -171,11 +172,15 @@ DAPI_(HRESULT) DepCheckDependency( { if (*wzMinVersion) { - hr = FileVersionFromStringEx(wzMinVersion, 0, &dw64MinVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMinVersion); + hr = VerParseVersion(wzMinVersion, 0, FALSE, &pMinVersion); + DepExitOnFailure(hr, "Failed to get the min version number from \"%ls\".", wzMinVersion); + + hr = VerCompareParsedVersions(pVersion, pMinVersion, &nResult); + DepExitOnFailure(hr, "Failed to compare dependency with min version \"%ls\".", wzMinVersion); fAllowEqual = iAttributes & RequiresAttributesMinVersionInclusive; - if (!(fAllowEqual && dw64MinVersion <= dw64Version || dw64MinVersion < dw64Version)) + // !(fAllowEqual && pMinVersion <= pVersion || pMinVersion < pVersion)) + if (!(fAllowEqual && 0 <= nResult || 0 < nResult)) { hr = DictKeyExists(sdDependencies, wzProviderKey); if (E_NOTFOUND != hr) @@ -205,11 +210,15 @@ DAPI_(HRESULT) DepCheckDependency( { if (*wzMaxVersion) { - hr = FileVersionFromStringEx(wzMaxVersion, 0, &dw64MaxVersion); - DepExitOnFailure(hr, "Failed to get the 64-bit version number from \"%ls\".", wzMaxVersion); + hr = VerParseVersion(wzMaxVersion, 0, FALSE, &pMaxVersion); + DepExitOnFailure(hr, "Failed to get the max version number from \"%ls\".", wzMaxVersion); + + hr = VerCompareParsedVersions(pMaxVersion, pVersion, &nResult); + DepExitOnFailure(hr, "Failed to compare dependency with max version \"%ls\".", wzMaxVersion); fAllowEqual = iAttributes & RequiresAttributesMaxVersionInclusive; - if (!(fAllowEqual && dw64Version <= dw64MaxVersion || dw64Version < dw64MaxVersion)) + // !(fAllowEqual && pVersion <= pMaxVersion || pVersion < pMaxVersion) + if (!(fAllowEqual && 0 <= nResult || 0 < nResult)) { hr = DictKeyExists(sdDependencies, wzProviderKey); if (E_NOTFOUND != hr) @@ -235,6 +244,9 @@ DAPI_(HRESULT) DepCheckDependency( } LExit: + ReleaseVerutilVersion(pMaxVersion); + ReleaseVerutilVersion(pMinVersion); + ReleaseVerutilVersion(pVersion); ReleaseStr(sczName); ReleaseRegKey(hkKey); 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 @@ #pragma once // 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. +#include "verutil.h" #ifdef __cplusplus extern "C" { @@ -260,6 +261,16 @@ HRESULT DAPI RegReadVersion( __out DWORD64* pdw64Version ); +/******************************************************************** + RegReadWixVersion - reads a registry key value as a WiX version. + +*********************************************************************/ +HRESULT DAPI RegReadWixVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out VERUTIL_VERSION** ppVersion + ); + /******************************************************************** RegReadNone - reads a NONE registry key value. 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( { ExitFunction1(hr = E_FILENOTFOUND); } + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) { hr = RegReadString(hk, wzName, &sczVersion); @@ -614,6 +615,58 @@ LExit: return hr; } + +DAPI_(HRESULT) RegReadWixVersion( + __in HKEY hk, + __in_z_opt LPCWSTR wzName, + __out VERUTIL_VERSION** ppVersion + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + DWORD dwType = 0; + DWORD cb = 0; + DWORD64 dw64Version = 0; + LPWSTR sczVersion = NULL; + VERUTIL_VERSION* pVersion = NULL; + + cb = sizeof(DWORD64); + er = vpfnRegQueryValueExW(hk, wzName, NULL, &dwType, reinterpret_cast(&dw64Version), &cb); + if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) + { + ExitFunction1(hr = E_FILENOTFOUND); + } + + if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) + { + hr = RegReadString(hk, wzName, &sczVersion); + RegExitOnFailure(hr, "Failed to read registry version as string."); + + hr = VerParseVersion(sczVersion, 0, FALSE, &pVersion); + RegExitOnFailure(hr, "Failed to convert registry string to version."); + } + else if (REG_QWORD == dwType) + { + hr = VerVersionFromQword(dw64Version, &pVersion); + RegExitOnFailure(hr, "Failed to convert registry string to version."); + } + else // unexpected data type + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE); + RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType); + } + + *ppVersion = pVersion; + pVersion = NULL; + +LExit: + ReleaseVerutilVersion(pVersion); + ReleaseStr(sczVersion); + + return hr; +} + + DAPI_(HRESULT) RegReadNone( __in HKEY hk, __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 @@ - + 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 packageA.UninstallProduct(); packageA.VerifyInstalled(false); } + + [Fact] + public void CannotInstallWhenDependencyUnsatisfied() + { + var packageA = this.CreatePackageInstaller("PackageAv1"); + var packageB = this.CreatePackageInstaller("PackageB"); + var bundleB = this.CreateBundleInstaller("BundleB"); + var testBAController = this.CreateTestBAController(); + + packageA.VerifyInstalled(false); + packageB.VerifyInstalled(false); + + // Prevent install of PackageA to cause PackageB's enforced dependency + // to fail the install. + testBAController.SetPackageRequestedState("PackageA", RequestState.None); + + var bundleBInstallLogFilePath = bundleB.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE); + Assert.True(LogVerifier.MessageInLogFileRegex(bundleBInstallLogFilePath, @"Applied execute package: PackageB, result: 0x80070643, restart: None")); + } } } 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 FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false); File.SetAttributes(bundleTempPath, FileAttributes.Normal); - var windowsAssemblyVersion = GetWindowsAssemblyVersion(this.BundleSymbol); + var fourPartVersion = this.GetFourPartVersion(this.BundleSymbol); - var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, windowsAssemblyVersion); + var applicationManifestData = GenerateApplicationManifest(this.BundleSymbol, this.BootstrapperApplicationDllSymbol, this.OutputPath, fourPartVersion); - UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, windowsAssemblyVersion, applicationManifestData); + this.UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleSymbol, fourPartVersion, applicationManifestData); // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers // if they should be attached. @@ -242,24 +242,37 @@ namespace WixToolset.Core.Burn.Bundles } } - private static Version GetWindowsAssemblyVersion(WixBundleSymbol bundleSymbol) + private Version GetFourPartVersion(WixBundleSymbol bundleSymbol) { - // Ensure the bundle info provides a full four part version. - var fourPartVersion = new Version(bundleSymbol.Version); - var major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major; - var minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor; - var build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build; - var revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision; + // Ensure the bundle info provides a full four-part version. + + if (!WixVersion.TryParse(bundleSymbol.Version, out var wixVersion)) + { + // Display an error message indicating that we will require a four-part version number + // not just a WixVersion. + this.Messaging.Write(ErrorMessages.IllegalVersionValue(bundleSymbol.SourceLineNumbers, "Bundle", "Version", bundleSymbol.Version)); + return new Version(0, 0); + } + + var major = wixVersion.Major ?? 0; + var minor = wixVersion.Minor ?? 0; + var build = wixVersion.Patch ?? 0; + var revision = wixVersion.Revision ?? 0; if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision) { - throw new WixException(ErrorMessages.InvalidModuleOrBundleVersion(bundleSymbol.SourceLineNumbers, "Bundle", bundleSymbol.Version)); + major = Math.Max(major, UInt16.MaxValue); + minor = Math.Max(minor, UInt16.MaxValue); + build = Math.Max(build, UInt16.MaxValue); + revision = Math.Max(revision, UInt16.MaxValue); + + this.Messaging.Write(BurnBackendWarnings.CannotParseBundleVersionAsFourPartVersion(bundleSymbol.SourceLineNumbers, bundleSymbol.Version)); } - return new Version(major, minor, build, revision); + return new Version((int)major, (int)minor, (int)build, (int)revision); } - private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleSymbol bundleInfo, Version windowsAssemblyVersion, byte[] applicationManifestData) + private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleSymbol bundleInfo, Version fourPartVersion, byte[] applicationManifestData) { const int burnLocale = 1033; var resources = new ResourceCollection(); @@ -268,8 +281,8 @@ namespace WixToolset.Core.Burn.Bundles version.Load(bundleTempPath); resources.Add(version); - version.FileVersion = windowsAssemblyVersion; - version.ProductVersion = windowsAssemblyVersion; + version.FileVersion = fourPartVersion; + version.ProductVersion = fourPartVersion; var strings = version[burnLocale] ?? version.Add(burnLocale); 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 this.ChainPackage.Version = this.MsiPackage.ProductVersion; } - if (!this.BackendHelper.IsValidFourPartVersion(this.MsiPackage.ProductVersion)) + if (!this.BackendHelper.IsValidMsiProductVersion(this.MsiPackage.ProductVersion)) { - // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? - string version = null; - var versionParts = this.MsiPackage.ProductVersion.Split('.'); - var count = versionParts.Length; - if (0 < count) - { - version = versionParts[0]; - for (var i = 1; i < 4 && i < count; ++i) - { - version = String.Concat(version, ".", versionParts[i]); - } - } - - if (!String.IsNullOrEmpty(version) && this.BackendHelper.IsValidFourPartVersion(version)) - { - this.Messaging.Write(WarningMessages.VersionTruncated(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId, version)); - this.MsiPackage.ProductVersion = version; - } - else - { - this.Messaging.Write(ErrorMessages.InvalidProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId)); - } + this.Messaging.Write(WarningMessages.InvalidMsiProductVersion(this.PackagePayload.SourceLineNumbers, this.MsiPackage.ProductVersion, this.PackageId)); } 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 return Message(sourceLineNumbers, Ids.AttachedContainerPayloadCollision2, "The location of the payload related to the previous error."); } + public static Message CannotParseBundleVersionAsFourPartVersion(SourceLineNumber originalLineNumber, string version) + { + 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); + } + public static Message EmptyContainer(SourceLineNumber sourceLineNumbers, string containerId) { 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 UnknownBundleRelationAction = 8505, HiddenBundleNotSupported = 8506, UnknownMsiPackagePlatform = 8507, + CannotParseBundleVersionAsFourPartVersion = 8508, } // last available is 8999. 9000 is VerboseMessages. } } 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 return this.backendHelper.IsValidIdentifier(id); } + public bool IsValidMsiProductVersion(string version) + { + return this.backendHelper.IsValidMsiProductVersion(version); + } + + public bool IsValidWixVersion(string version) + { + return this.backendHelper.IsValidWixVersion(version); + } + public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) { 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 if (null != versionInfo) { - // Use the fixed version info block for the file since the resource text may not be a dotted quad. - var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart); + var version = versionInfo.ProductVersion; - if (PayloadHarvester.EmptyVersion != version) + if (String.IsNullOrEmpty(version)) { - payload.Version = version.ToString(); + version = versionInfo.FileVersion; + } + + if (String.IsNullOrEmpty(version)) + { + // Fallback to fixed version info block for the file. + var fixedVersion = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart); + + if (PayloadHarvester.EmptyVersion != fixedVersion) + { + version = fixedVersion.ToString(); + } } payload.Description = versionInfo.FileDescription; payload.DisplayName = versionInfo.ProductName; + payload.Version = version; } } } 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 command.Execute(); } + // Now that delayed fields are processed, validate the package/module version. + this.VerifyVersion(packageSymbol, moduleSymbol); + // Process dependency references. if (SectionType.Product == section.Type || SectionType.Module == section.Type) { @@ -537,6 +540,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind return result; } + private void VerifyVersion(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol) + { + if (packageSymbol != null) + { + if (!this.WindowsInstallerBackendHelper.IsValidMsiProductVersion(packageSymbol.Version)) + { + this.Messaging.Write(ErrorMessages.InvalidProductVersion(packageSymbol.SourceLineNumbers, packageSymbol.Version)); + } + } + else if (moduleSymbol != null) + { + if (!this.WindowsInstallerBackendHelper.IsValidFourPartVersion(moduleSymbol.Version)) + { + this.Messaging.Write(WindowsInstallerBackendErrors.InvalidModuleVersion(moduleSymbol.SourceLineNumbers, moduleSymbol.Version)); + } + } + } + private int CalculateCodepage(WixPackageSymbol packageSymbol, WixModuleSymbol moduleSymbol, WixPatchSymbol patchSymbol) { 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 private void AddUpgradeSymbol(UpgradeSymbol symbol) { + if (!String.IsNullOrEmpty(symbol.VersionMin) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMin)) + { + this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMin)); + } + + if (!String.IsNullOrEmpty(symbol.VersionMax) && !this.BackendHelper.IsValidMsiProductVersion(symbol.VersionMax)) + { + this.Messaging.Write(ErrorMessages.InvalidProductVersion(symbol.SourceLineNumbers, symbol.VersionMax)); + } + var row = (UpgradeRow)this.CreateRow(symbol, "Upgrade"); row.UpgradeCode = symbol.UpgradeCode; 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 return this.backendHelper.IsValidIdentifier(id); } + public bool IsValidMsiProductVersion(string version) + { + return this.backendHelper.IsValidMsiProductVersion(version); + } + + public bool IsValidWixVersion(string version) + { + return this.backendHelper.IsValidWixVersion(version); + } + public bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) { 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 return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); } + public static Message InvalidModuleVersion(SourceLineNumber originalLineNumber, string version) + { + 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); + } + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) { return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); @@ -22,6 +27,7 @@ namespace WixToolset.Core.WindowsInstaller public enum Ids { CannotLoadWixoutAsTransform = 7500, + InvalidModuleVersion = 7501, } // last available is 7999. 8000 is BurnBackendErrors. } } 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 return true; } + public static bool IsValidMsiProductVersion(string version) + { + return Version.TryParse(version, out var ver) && ver.Major < 256 && ver.Minor < 256 && ver.Build < 65536; + } + public static bool IsValidLongFilename(string filename, bool allowWildcards, bool allowRelative) { 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 /// True if version is a valid product version public static bool IsValidProductVersion(string version) { - if (!Common.IsValidBinderVariable(version)) - { - Version ver = new Version(version); - - if (255 < ver.Major || 255 < ver.Minor || 65535 < ver.Build) - { - return false; - } - } - - return true; - } - - /// - /// Verifies the given string is a valid module or bundle version. - /// - /// The version to verify. - /// True if version is a valid module or bundle version. - public static bool IsValidModuleOrBundleVersion(string version) - { - return Common.IsValidFourPartVersion(version); + return Common.IsValidBinderVariable(version) || Common.IsValidMsiProductVersion(version); } /// 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 { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); } - else if (!CompilerCore.IsValidModuleOrBundleVersion(version)) - { - this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version)); - } if (String.IsNullOrEmpty(upgradeCode)) { 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 { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); } - else if (!CompilerCore.IsValidModuleOrBundleVersion(version)) - { - this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Module", version)); - } try { 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 case "UpgradeCode": upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; - 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"). - var verifiedVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); - if (!String.IsNullOrEmpty(verifiedVersion)) - { - version = attrib.Value; - } + case "Version": + version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); @@ -147,10 +143,6 @@ namespace WixToolset.Core { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); } - else if (!CompilerCore.IsValidProductVersion(version)) - { - this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version)); - } if (compressed != YesNoDefaultType.No) { 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 return Common.IsValidFourPartVersion(version); } + public bool IsValidMsiProductVersion(string version) + { + return Common.IsValidMsiProductVersion(version); + } + + public bool IsValidWixVersion(string version) + { + return WixVersion.TryParse(version, out _); + } + public bool IsValidIdentifier(string id) { 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 if (!String.IsNullOrEmpty(value)) { - if (Version.TryParse(value, out var version)) + if (WixVersion.TryParse(value, out var version)) { - return version.ToString(); + // Return the attribute value sans-prefix, if present. + return version.Prefix.HasValue ? value.Substring(1) : value; } // 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 @@ +// 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. + +namespace WixToolsetTest.Core +{ + using System; + using System.Xml.Linq; + using WixToolset.Core; + using WixToolset.Data; + using WixToolset.Extensibility.Services; + using Xunit; + + public class ParserHelperFixture + { + [Fact] + public void CanParseFourPartAttributeVersion() + { + var helper = GetParserHelper(); + + var attribute = CreateAttribute("1.2.3.4"); + var result = helper.GetAttributeVersionValue(null, attribute); + + Assert.Equal("1.2.3.4", result); + } + + [Fact] + public void CannotParseFivePartAttributeVersion() + { + var helper = GetParserHelper(); + + var attribute = CreateAttribute("1.2.3.4.5"); + var exception = Assert.Throws(() => { helper.GetAttributeVersionValue(null, attribute); }); + 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); + } + + [Fact] + public void CannotParseVersionTooLargeAttributeVersion() + { + var version = "4294967296.2.3.4"; + AssertVersion(version); + } + + private static void AssertVersion(string version) + { + var helper = GetParserHelper(); + var attribute = CreateAttribute(version); + var exception = Assert.Throws(() => { helper.GetAttributeVersionValue(null, attribute); }); + 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); + } + + [Fact] + public void CanParseSemverAttributeVersion() + { + var helper = GetParserHelper(); + + var attribute = CreateAttribute("10.99.444-preview.0"); + var result = helper.GetAttributeVersionValue(null, attribute); + + Assert.Equal("10.99.444-preview.0", result); + } + + [Fact] + public void CanParseFourPartSemverAttributeVersion() + { + var helper = GetParserHelper(); + + var attribute = CreateAttribute("1.2.3.4-meta.123-other.456"); + var result = helper.GetAttributeVersionValue(null, attribute); + + Assert.Equal("1.2.3.4-meta.123-other.456", result); + } + + [Fact] + public void CanParseVersionWithLeadingV() + { + var helper = GetParserHelper(); + + var attribute = CreateAttribute("v1.2.3.4"); + var result = helper.GetAttributeVersionValue(null, attribute); + + Assert.Equal("1.2.3.4", result); + } + + + private static IParseHelper GetParserHelper() + { + var sp = WixToolsetServiceProviderFactory.CreateServiceProvider(); + var helper = sp.GetService(); + return helper; + } + + private static XAttribute CreateAttribute(string value) + { + var attribute = new XAttribute("Value", value); + _ = new XElement("Test", attribute); + + return attribute; + } + } +} 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 .ToArray(); WixAssert.CompareLineByLine(new string[] { - $"" + - "" + + $"" + + "" + "" + "" + "", @@ -302,7 +302,7 @@ namespace WixToolsetTest.CoreIntegration .ToArray(); WixAssert.CompareLineByLine(new string[] { - "", + "", }, packageElements); } } 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 WixAssert.CompareLineByLine(new[] { - @"", + @"", }, elements); } } @@ -265,7 +265,7 @@ namespace WixToolsetTest.CoreIntegration WixAssert.CompareLineByLine(new[] { - @"", + @"", @"", }, elements); } @@ -296,7 +296,7 @@ namespace WixToolsetTest.CoreIntegration WixAssert.CompareLineByLine(new[] { - @"", + @"", @"", }, elements); } @@ -331,7 +331,7 @@ namespace WixToolsetTest.CoreIntegration WixAssert.CompareLineByLine(new[] { - @"", + @"", @"", @"", @"", 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 @@ ProtocolVersion="1" ProviderKey="MyProviderKey,v1.0" UpgradeCode="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}" - Version="1.0.0.0" + Version="1.0.0-foo.55" Win64="no" /> 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 @@ + + + + + + + + + + + + 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 @@ + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ +// 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. + +namespace WixToolsetTest.CoreIntegration +{ + using System.IO; + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Data; + using Xunit; + + public class UpgradeFixture + { + + [Fact] + public void PopulatesInstallExecuteSequenceTable() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Upgrade", "UpgradeInvalidMinVersion.wxs"), + Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), + Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-o", msiPath + }); + + var message = result.Messages.Single(m => m.Level == MessageLevel.Error); + 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()); + Assert.Equal(242, result.ExitCode); + } + } + } +} 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 @@ +// 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. + +namespace WixToolsetTest.CoreIntegration +{ + using System.IO; + using System.Linq; + using System.Xml; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Data; + using Xunit; + + public class VersionFixture + { + [Fact] + public void CannotBuildMsiWithInvalidMajorVersion() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, @"bin\test1.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Version", "Package.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-d", "Version=257.0.0", + "-o", msiPath + }); + + var message = result.Messages.Single(m => m.Level == MessageLevel.Error); + 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()); + Assert.Equal(242, result.ExitCode); + } + } + + [Fact] + public void CanBuildBundleWithSemanticVersion() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, @"bin\test1.msi"); + var msi2Path = Path.Combine(baseFolder, @"bin\test2.msi"); + var bundlePath = Path.Combine(baseFolder, @"bin\bundle.exe"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Version", "Package.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-d", "Version=255.255.65535", + "-o", msiPath + }); + + result.AssertSuccess(); + + var result3 = WixRunner.Execute(new[] +{ + "build", + Path.Combine(folder, "Version", "Bundle.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-bindpath", Path.Combine(baseFolder, "bin"), + "-intermediateFolder", intermediateFolder, + "-d", "Version=2022.3.9-preview.0-build.5+0987654321abcdef1234567890", + "-o", bundlePath + }); + + result3.AssertSuccess(); + + var propertyTable = Query.QueryDatabase(msiPath, new[] { "Property" }).Select(r => r.Split('\t')).ToDictionary(r => r[0].Substring("Property:".Length), r => r[1]); + Assert.True(propertyTable.TryGetValue("ProductVersion", out var productVersion)); + Assert.Equal("255.255.65535", productVersion); + + var extractResult = BundleExtractor.ExtractAllContainers(null, bundlePath, Path.Combine(baseFolder, "ba"), Path.Combine(baseFolder, "attached"), Path.Combine(baseFolder, "extract")); + extractResult.AssertSuccess(); + + var bundleVersion = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration/@Version") + .Cast() + .Single(); + Assert.Equal("2022.3.9-preview.0-build.5+0987654321abcdef1234567890", bundleVersion.Value); + } + } + } +} -- cgit v1.2.3-55-g6feb