From eab59ecbfd254bb8707615bc31b840339a6356d6 Mon Sep 17 00:00:00 2001 From: Sean Hall <r.sean.hall@gmail.com> Date: Sun, 11 Dec 2022 21:07:48 -0600 Subject: Try to convert the MBA prereq magic variables to the new attributes. Fixes 7026 --- .../test/WixToolsetTest.Bal/BalExtensionFixture.cs | 2 +- src/ext/Bal/wixext/BalErrors.cs | 4 +- src/wix/WixToolset.Converters/ConversionState.cs | 9 ++ src/wix/WixToolset.Converters/WixConverter.cs | 127 +++++++++++++++++---- .../PrereqPackageFixture.cs | 94 +++++++++++++++ 5 files changed, 210 insertions(+), 26 deletions(-) create mode 100644 src/wix/test/WixToolsetTest.Converters/PrereqPackageFixture.cs diff --git a/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs b/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs index 329722a4..32ff42b5 100644 --- a/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs +++ b/src/ext/Bal/test/WixToolsetTest.Bal/BalExtensionFixture.cs @@ -177,7 +177,7 @@ namespace WixToolsetTest.Bal "-o", bundleFile, }); Assert.Equal(6802, compileResult.ExitCode); - WixAssert.StringEqual("There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups.", compileResult.Messages[0].ToString()); + WixAssert.StringEqual("There must be at least one package with bal:PrereqPackage=\"yes\" when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups.", compileResult.Messages[0].ToString()); Assert.False(File.Exists(bundleFile)); Assert.False(File.Exists(Path.Combine(intermediateFolder, "test.exe"))); diff --git a/src/ext/Bal/wixext/BalErrors.cs b/src/ext/Bal/wixext/BalErrors.cs index cc4c6d41..cde37143 100644 --- a/src/ext/Bal/wixext/BalErrors.cs +++ b/src/ext/Bal/wixext/BalErrors.cs @@ -45,7 +45,7 @@ namespace WixToolset.Bal public static Message MissingDNCPrereq() { - return Message(null, Ids.MissingDNCPrereq, "There must be at least one PrereqPackage when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\"."); + return Message(null, Ids.MissingDNCPrereq, "There must be at least one package with bal:PrereqPackage=\"yes\" when using the DotNetCoreBootstrapperApplicationHost with SelfContainedDeployment set to \"no\"."); } public static Message MissingIUIPrimaryPackage() @@ -55,7 +55,7 @@ namespace WixToolset.Bal public static Message MissingMBAPrereq() { - return Message(null, Ids.MissingMBAPrereq, "There must be at least one PrereqPackage when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups."); + return Message(null, Ids.MissingMBAPrereq, "There must be at least one package with bal:PrereqPackage=\"yes\" when using the ManagedBootstrapperApplicationHost.\nThis is typically done by using the WixNetFxExtension and referencing one of the NetFxAsPrereq package groups."); } public static Message MultipleBAFunctions(SourceLineNumber sourceLineNumbers) diff --git a/src/wix/WixToolset.Converters/ConversionState.cs b/src/wix/WixToolset.Converters/ConversionState.cs index cfb065f2..a959fbff 100644 --- a/src/wix/WixToolset.Converters/ConversionState.cs +++ b/src/wix/WixToolset.Converters/ConversionState.cs @@ -21,6 +21,9 @@ namespace WixToolset.Converters public ConversionState(ConvertOperation operation, string sourceFile) { this.ConversionMessages = new List<Message>(); + this.ChainPackageElementsById = new Dictionary<string, List<XElement>>(); + this.WixMbaPrereqPackageIdElements = new List<XElement>(); + this.WixMbaPrereqLicenseUrlElements = new List<XElement>(); this.Operation = operation; this.SourceFile = sourceFile; this.SourceVersion = 0; @@ -28,6 +31,12 @@ namespace WixToolset.Converters public List<Message> ConversionMessages { get; } + public Dictionary<string, List<XElement>> ChainPackageElementsById { get; } + + public List<XElement> WixMbaPrereqPackageIdElements { get; } + + public List<XElement> WixMbaPrereqLicenseUrlElements { get; } + public ConvertOperation Operation { get; } public string SourceFile { get; } diff --git a/src/wix/WixToolset.Converters/WixConverter.cs b/src/wix/WixToolset.Converters/WixConverter.cs index 133d8876..1b51ef30 100644 --- a/src/wix/WixToolset.Converters/WixConverter.cs +++ b/src/wix/WixToolset.Converters/WixConverter.cs @@ -131,6 +131,8 @@ namespace WixToolset.Converters private static readonly XName UITextElementName = WixNamespace + "UIText"; private static readonly XName VariableElementName = WixNamespace + "Variable"; private static readonly XName VerbElementName = WixNamespace + "Verb"; + private static readonly XName BalPrereqLicenseUrlAttributeName = WixBalNamespace + "PrereqLicenseUrl"; + private static readonly XName BalPrereqPackageAttributeName = WixBalNamespace + "PrereqPackage"; private static readonly XName BalUseUILanguagesName = WixBalNamespace + "UseUILanguages"; private static readonly XName BalStandardBootstrapperApplicationName = WixBalNamespace + "WixStandardBootstrapperApplication"; private static readonly XName BalManagedBootstrapperApplicationHostName = WixBalNamespace + "WixManagedBootstrapperApplicationHost"; @@ -147,6 +149,7 @@ namespace WixToolset.Converters private static readonly XName Wix4ElementName = WixNamespace + "Wix"; private static readonly XName Wix3ElementName = Wix3Namespace + "Wix"; private static readonly XName WixElementWithoutNamespaceName = XNamespace.None + "Wix"; + private static readonly XName WixVariableElementName = WixNamespace + "WixVariable"; private static readonly XName Include4ElementName = WixNamespace + "Include"; private static readonly XName Include3ElementName = Wix3Namespace + "Include"; private static readonly XName IncludeElementWithoutNamespaceName = XNamespace.None + "Include"; @@ -164,11 +167,11 @@ namespace WixToolset.Converters private static readonly Dictionary<string, XNamespace> OldToNewNamespaceMapping = new Dictionary<string, XNamespace>() { - { "http://schemas.microsoft.com/wix/BalExtension", "http://wixtoolset.org/schemas/v4/wxs/bal" }, + { "http://schemas.microsoft.com/wix/BalExtension", WixBalNamespace }, { "http://schemas.microsoft.com/wix/ComPlusExtension", "http://wixtoolset.org/schemas/v4/wxs/complus" }, { "http://schemas.microsoft.com/wix/DependencyExtension", WixDependencyNamespace }, { "http://schemas.microsoft.com/wix/DifxAppExtension", "http://wixtoolset.org/schemas/v4/wxs/difxapp" }, - { "http://schemas.microsoft.com/wix/FirewallExtension", "http://wixtoolset.org/schemas/v4/wxs/firewall" }, + { "http://schemas.microsoft.com/wix/FirewallExtension", WixFirewallNamespace }, { "http://schemas.microsoft.com/wix/HttpExtension", "http://wixtoolset.org/schemas/v4/wxs/http" }, { "http://schemas.microsoft.com/wix/IIsExtension", "http://wixtoolset.org/schemas/v4/wxs/iis" }, { "http://schemas.microsoft.com/wix/MsmqExtension", "http://wixtoolset.org/schemas/v4/wxs/msmq" }, @@ -177,7 +180,7 @@ namespace WixToolset.Converters { "http://schemas.microsoft.com/wix/SqlExtension", "http://wixtoolset.org/schemas/v4/wxs/sql" }, { "http://schemas.microsoft.com/wix/TagExtension", XNamespace.None }, { "http://schemas.microsoft.com/wix/UtilExtension", WixUtilNamespace }, - { "http://schemas.microsoft.com/wix/VSExtension", "http://wixtoolset.org/schemas/v4/wxs/vs" }, + { "http://schemas.microsoft.com/wix/VSExtension", WixVSNamespace }, { "http://wixtoolset.org/schemas/thmutil/2010", "http://wixtoolset.org/schemas/v4/thmutil" }, { "http://schemas.microsoft.com/wix/2009/Lux", "http://wixtoolset.org/schemas/v4/lux" }, { "http://schemas.microsoft.com/wix/2006/wi", "http://wixtoolset.org/schemas/v4/wxs" }, @@ -394,7 +397,7 @@ namespace WixToolset.Converters if (this.TryOpenSourceFile(ConvertOperation.Convert, sourceFile)) { - this.Convert(this.State.XDocument); + this.DoIt(this.State.XDocument); // Fix Messages if requested and necessary. if (saveConvertedFile && 0 < this.ConversionMessages.Count) @@ -416,7 +419,7 @@ namespace WixToolset.Converters { this.State = new ConversionState(ConvertOperation.Convert, sourceFile); this.State.Initialize(document); - this.Convert(document); + this.DoIt(document); return this.ReportMessages(document, false); } @@ -455,28 +458,12 @@ namespace WixToolset.Converters { this.State = new ConversionState(ConvertOperation.Format, sourceFile); this.State.Initialize(document); - this.Format(document); + this.DoIt(document); return this.ReportMessages(document, false); } - private void Convert(XDocument document) - { - // Remove the declaration. - if (null != document.Declaration - && this.OnInformation(ConverterTestType.DeclarationPresent, document, "This file contains an XML declaration on the first line.")) - { - document.Declaration = null; - TrimLeadingText(document); - } - - // Start converting the nodes at the top. - this.ConvertNodes(document.Nodes(), 0); - this.RemoveUnusedNamespaces(document.Root); - this.MoveNamespacesToRoot(document.Root); - } - - private void Format(XDocument document) + private void DoIt(XDocument document) { // Remove the declaration. if (null != document.Declaration @@ -488,6 +475,7 @@ namespace WixToolset.Converters // Start converting the nodes at the top. this.ConvertNodes(document.Nodes(), 0); + this.ConvertMbaPrereqVariables(); this.RemoveUnusedNamespaces(document.Root); this.MoveNamespacesToRoot(document.Root); } @@ -2309,6 +2297,89 @@ namespace WixToolset.Converters whitespace.Value = value.Append(' ', level * indentationAmount).ToString(); } + private void ConvertMbaPrereqVariables() + { + XElement root = this.XRoot; + VisitElement(root, x => + { + if (x is XElement e && e.Attribute("Id") is XAttribute a) + { + if (e.Name == ExePackageElementName || e.Name == MsiPackageElementName || + e.Name == MspPackageElementName || e.Name == MsuPackageElementName) + { + if (!this.State.ChainPackageElementsById.TryGetValue(a.Value, out var elements)) + { + elements = new List<XElement>(); + this.State.ChainPackageElementsById.Add(a.Value, elements); + } + + elements.Add(e); + } + else if (e.Name == WixVariableElementName && e.Attribute("Value") != null) + { + switch (a.Value) + { + case "WixMbaPrereqPackageId": + this.State.WixMbaPrereqPackageIdElements.Add(e); + break; + case "WixMbaPrereqLicenseUrl": + this.State.WixMbaPrereqLicenseUrlElements.Add(e); + break; + } + } + } + return true; + }); + + if (this.State.WixMbaPrereqPackageIdElements.Count == 1 && this.State.WixMbaPrereqLicenseUrlElements.Count < 2) + { + var wixMbaPrereqPackageIdElement = this.State.WixMbaPrereqPackageIdElements[0]; + var packageId = wixMbaPrereqPackageIdElement.Attribute("Value")?.Value; + if (this.State.ChainPackageElementsById.TryGetValue(packageId, out var packageElements) && packageElements.Count == 1) + { + var packageElement = packageElements[0]; + + if (this.OnInformation(ConverterTestType.WixMbaPrereqPackageIdDeprecated, wixMbaPrereqPackageIdElement, "The magic WixVariable 'WixMbaPrereqPackageId' has been removed. Add bal:PrereqPackage=\"yes\" to the target package instead.")) + { + packageElement.Add(new XAttribute(BalPrereqPackageAttributeName, "yes")); + + using (var lab = new ConversionLab(wixMbaPrereqPackageIdElement)) + { + lab.RemoveTargetElement(); + } + + this.State.WixMbaPrereqPackageIdElements.Clear(); + } + + if (this.State.WixMbaPrereqLicenseUrlElements.Count == 1) + { + var wixMbaPrereqLicenseUrlElement = this.State.WixMbaPrereqLicenseUrlElements[0]; + if (this.OnInformation(ConverterTestType.WixMbaPrereqLicenseUrlDeprecated, wixMbaPrereqLicenseUrlElement, "The magic WixVariable 'WixMbaPrereqLicenseUrl' has been removed. Add bal:PrereqLicenseUrl=\"yes\" to a prereq package instead.")) + { + var licenseUrl = wixMbaPrereqLicenseUrlElement.Attribute("Value")?.Value; + packageElement.Add(new XAttribute(BalPrereqLicenseUrlAttributeName, licenseUrl)); + using (var lab = new ConversionLab(wixMbaPrereqLicenseUrlElement)) + { + lab.RemoveTargetElement(); + } + + this.State.WixMbaPrereqLicenseUrlElements.Clear(); + } + } + } + } + + foreach (var element in this.State.WixMbaPrereqPackageIdElements) + { + this.OnError(ConverterTestType.WixMbaPrereqPackageIdDeprecated, element, "The magic WixVariable 'WixMbaPrereqPackageId' has been removed. Add bal:PrereqPackage=\"yes\" to the target package instead."); + } + + foreach (var element in this.State.WixMbaPrereqLicenseUrlElements) + { + this.OnError(ConverterTestType.WixMbaPrereqLicenseUrlDeprecated, element, "The magic WixVariable 'WixMbaPrereqLicenseUrl' has been removed. Add bal:PrereqLicenseUrl=\"yes\" to a prereq package instead."); + } + } + /// <summary> /// Removes unused namespaces from the element and its children. /// </summary> @@ -3203,6 +3274,16 @@ namespace WixToolset.Converters /// Referencing '{0}' directory directly is no longer supported. The DirectoryRef will not be removed but you will probably need to reference a more specific directory. /// </summary> EmptyStandardDirectoryRefNotConvertable, + + /// <summary> + /// The magic WixVariable 'WixMbaPrereqLicenseUrl' has been removed. Add bal:PrereqLicenseUrl="yes" to a prereq package instead. + /// </summary> + WixMbaPrereqLicenseUrlDeprecated, + + /// <summary> + /// The magic WixVariable 'WixMbaPrereqPackageId' has been removed. Add bal:PrereqPackage="yes" to the target package instead. + /// </summary> + WixMbaPrereqPackageIdDeprecated, } } } diff --git a/src/wix/test/WixToolsetTest.Converters/PrereqPackageFixture.cs b/src/wix/test/WixToolsetTest.Converters/PrereqPackageFixture.cs new file mode 100644 index 00000000..b18dbfd3 --- /dev/null +++ b/src/wix/test/WixToolsetTest.Converters/PrereqPackageFixture.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.Converters +{ + using System; + using System.Linq; + using System.Xml.Linq; + using WixInternal.TestSupport; + using WixToolset.Converters; + using WixToolsetTest.Converters.Mocks; + using Xunit; + + public class PrereqPackageFixture : BaseConverterFixture + { + [Fact] + public void CanConvertWixMbaPrereqPackageIdToPrereqPackage() + { + var parse = String.Join(Environment.NewLine, + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs' xmlns:bal='http://wixtoolset.org/schemas/v4/wxs/bal'>", + " <Fragment>", + " <WixVariable Id='WixMbaPrereqPackageId' Value='NetFx452Web' />", + " <WixVariable Id='WixMbaPrereqLicenseUrl' Value='$(var.NetFx452EulaLink)' Overridable='yes' />", + " <PackageGroup Id='NetFx452Web'>", + " <ExePackage Id='NetFx452Web' />", + " </PackageGroup>", + " </Fragment>", + "</Wix>"); + + var expected = new[] + { + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\" xmlns:bal=\"http://wixtoolset.org/schemas/v4/wxs/bal\">", + " <Fragment><PackageGroup Id=\"NetFx452Web\">", + " <ExePackage Id=\"NetFx452Web\" bal:PrereqPackage=\"yes\" bal:PrereqLicenseUrl=\"$(var.NetFx452EulaLink)\" />", + " </PackageGroup>", + " </Fragment>", + "</Wix>" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(2, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(new[] + { + "[Converted] The magic WixVariable 'WixMbaPrereqPackageId' has been removed. Add bal:PrereqPackage=\"yes\" to the target package instead. (WixMbaPrereqPackageIdDeprecated)", + "[Converted] The magic WixVariable 'WixMbaPrereqLicenseUrl' has been removed. Add bal:PrereqLicenseUrl=\"yes\" to a prereq package instead. (WixMbaPrereqLicenseUrlDeprecated)", + }, messaging.Messages.Select(m => m.ToString()).ToArray()); + WixAssert.CompareLineByLine(expected, actualLines); + } + + [Fact] + public void CanWarnAboutOrphanWixMbaPrereqPackageId() + { + var parse = String.Join(Environment.NewLine, + "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>", + " <Fragment>", + " <WixVariable Id='WixMbaPrereqPackageId' Value='NetFx452Web' />", + " <WixVariable Id='WixMbaPrereqLicenseUrl' Value='$(var.NetFx452EulaLink)' Overridable='yes' />", + " </Fragment>", + "</Wix>"); + + var expected = new[] + { + "<Wix xmlns=\"http://wixtoolset.org/schemas/v4/wxs\">", + " <Fragment>", + " <WixVariable Id=\"WixMbaPrereqPackageId\" Value=\"NetFx452Web\" />", + " <WixVariable Id=\"WixMbaPrereqLicenseUrl\" Value=\"$(var.NetFx452EulaLink)\" Overridable=\"yes\" />", + " </Fragment>", + "</Wix>" + }; + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, null, null); + + var errors = converter.ConvertDocument(document); + Assert.Equal(2, errors); + + var actualLines = UnformattedDocumentLines(document); + WixAssert.CompareLineByLine(new[] + { + "The magic WixVariable 'WixMbaPrereqPackageId' has been removed. Add bal:PrereqPackage=\"yes\" to the target package instead. (WixMbaPrereqPackageIdDeprecated)", + "The magic WixVariable 'WixMbaPrereqLicenseUrl' has been removed. Add bal:PrereqLicenseUrl=\"yes\" to a prereq package instead. (WixMbaPrereqLicenseUrlDeprecated)", + }, messaging.Messages.Select(m => m.ToString()).ToArray()); + WixAssert.CompareLineByLine(expected, actualLines); + } + } +} -- cgit v1.2.3-55-g6feb