From d1050931018d6e8dd06189202492df666dfbb594 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 12 Mar 2021 17:11:44 -0600 Subject: Add conversion for BootstrapperApplicationData to BundleCustomData. #6176 --- src/WixToolset.Converters/FixupCommandBase.cs | 18 +++ src/WixToolset.Converters/WixConverter.cs | 160 ++++++++++++++++-- .../CustomTableFixture.cs | 178 ++++++++++++++++++++- 3 files changed, 337 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/WixToolset.Converters/FixupCommandBase.cs b/src/WixToolset.Converters/FixupCommandBase.cs index 7ecce543..21282d07 100644 --- a/src/WixToolset.Converters/FixupCommandBase.cs +++ b/src/WixToolset.Converters/FixupCommandBase.cs @@ -29,6 +29,8 @@ namespace WixToolset.Converters protected bool ShowHelp { get; set; } + protected CustomTableTarget CustomTableSetting { get; set; } + protected bool DryRun { get; set; } protected HashSet ErrorsAsWarnings { get; } @@ -68,6 +70,22 @@ namespace WixToolset.Converters this.StopParsing = true; return true; + case "-custom-table": + var customTableSetting = parser.GetNextArgumentOrError(argument); + switch (customTableSetting) + { + case "bundle": + this.CustomTableSetting = CustomTableTarget.Bundle; + break; + case "msi": + this.CustomTableSetting = CustomTableTarget.Msi; + break; + default: + parser.ReportErrorArgument(argument); + break; + } + return true; + case "n": case "-dry-run": this.DryRun = true; diff --git a/src/WixToolset.Converters/WixConverter.cs b/src/WixToolset.Converters/WixConverter.cs index 3348e49c..9ada4745 100644 --- a/src/WixToolset.Converters/WixConverter.cs +++ b/src/WixToolset.Converters/WixConverter.cs @@ -14,6 +14,27 @@ namespace WixToolset.Converters using WixToolset.Data; using WixToolset.Extensibility.Services; + /// + /// How to convert CustomTable elements. + /// + public enum CustomTableTarget + { + /// + /// Ambiguous elements will be left alone. + /// + Unknown, + + /// + /// Use CustomTable, CustomTableRef, and Unreal. + /// + Msi, + + /// + /// Use BundleCustomData and BundleCustomDataRef. + /// + Bundle, + } + /// /// WiX source code converter. /// @@ -45,19 +66,25 @@ namespace WixToolset.Converters private static readonly XName BootstrapperApplicationDllElementName = WixNamespace + "BootstrapperApplicationDll"; private static readonly XName BootstrapperApplicationRefElementName = WixNamespace + "BootstrapperApplicationRef"; private static readonly XName ApprovedExeForElevationElementName = WixNamespace + "ApprovedExeForElevation"; - private static readonly XName EmbeddedChainerElementName = WixNamespace + "EmbeddedChainer"; + private static readonly XName BundleAttributeElementName = WixNamespace + "BundleAttribute"; + private static readonly XName BundleAttributeDefinitionElementName = WixNamespace + "BundleAttributeDefinition"; + private static readonly XName BundleCustomDataElementName = WixNamespace + "BundleCustomData"; + private static readonly XName BundleCustomDataRefElementName = WixNamespace + "BundleCustomDataRef"; + private static readonly XName BundleElementElementName = WixNamespace + "BundleElement"; + private static readonly XName CustomTableElementName = WixNamespace + "CustomTable"; + private static readonly XName CustomTableRefElementName = WixNamespace + "CustomTableRef"; private static readonly XName CatalogElementName = WixNamespace + "Catalog"; private static readonly XName ColumnElementName = WixNamespace + "Column"; private static readonly XName ComponentElementName = WixNamespace + "Component"; private static readonly XName ControlElementName = WixNamespace + "Control"; private static readonly XName ConditionElementName = WixNamespace + "Condition"; private static readonly XName CreateFolderElementName = WixNamespace + "CreateFolder"; - private static readonly XName CustomTableElementName = WixNamespace + "CustomTable"; private static readonly XName DataElementName = WixNamespace + "Data"; private static readonly XName OldProvidesElementName = WixDependencyNamespace + "Provides"; private static readonly XName OldRequiresElementName = WixDependencyNamespace + "Requires"; private static readonly XName OldRequiresRefElementName = WixDependencyNamespace + "RequiresRef"; private static readonly XName DirectoryElementName = WixNamespace + "Directory"; + private static readonly XName EmbeddedChainerElementName = WixNamespace + "EmbeddedChainer"; private static readonly XName ErrorElementName = WixNamespace + "Error"; private static readonly XName FeatureElementName = WixNamespace + "Feature"; private static readonly XName FileElementName = WixNamespace + "File"; @@ -85,6 +112,7 @@ namespace WixToolset.Converters private static readonly XName RemotePayloadElementName = WixNamespace + "RemotePayload"; private static readonly XName RegistrySearchElementName = WixNamespace + "RegistrySearch"; private static readonly XName RequiredPrivilegeElementName = WixNamespace + "RequiredPrivilege"; + private static readonly XName RowElementName = WixNamespace + "Row"; private static readonly XName ServiceArgumentElementName = WixNamespace + "ServiceArgument"; private static readonly XName SetDirectoryElementName = WixNamespace + "SetDirectory"; private static readonly XName SetPropertyElementName = WixNamespace + "SetProperty"; @@ -156,7 +184,8 @@ namespace WixToolset.Converters /// Indentation value to use when validating leading whitespace. /// Test errors to display as warnings. /// Test errors to ignore. - public WixConverter(IMessaging messaging, int indentationAmount, IEnumerable errorsAsWarnings = null, IEnumerable ignoreErrors = null) + /// How to convert CustomTable elements. + public WixConverter(IMessaging messaging, int indentationAmount, IEnumerable errorsAsWarnings = null, IEnumerable ignoreErrors = null, CustomTableTarget customTableTarget = CustomTableTarget.Unknown) { this.ConvertElementMapping = new Dictionary> { @@ -170,9 +199,10 @@ namespace WixToolset.Converters { WixConverter.ApprovedExeForElevationElementName, this.ConvertApprovedExeForElevationElement }, { WixConverter.CatalogElementName, this.ConvertCatalogElement }, { WixConverter.ColumnElementName, this.ConvertColumnElement }, - { WixConverter.CustomTableElementName, this.ConvertCustomTableElement }, - { WixConverter.ControlElementName, this.ConvertControlElement }, { WixConverter.ComponentElementName, this.ConvertComponentElement }, + { WixConverter.ControlElementName, this.ConvertControlElement }, + { WixConverter.CustomActionElementName, this.ConvertCustomActionElement }, + { WixConverter.CustomTableElementName, this.ConvertCustomTableElement }, { WixConverter.DataElementName, this.ConvertDataElement }, { WixConverter.DirectoryElementName, this.ConvertDirectoryElement }, { WixConverter.FeatureElementName, this.ConvertFeatureElement }, @@ -198,7 +228,6 @@ namespace WixToolset.Converters { WixConverter.RegistrySearchElementName, this.ConvertRegistrySearchElement }, { WixConverter.RemotePayloadElementName, this.ConvertRemotePayloadElement }, { WixConverter.RequiredPrivilegeElementName, this.ConvertRequiredPrivilegeElement }, - { WixConverter.CustomActionElementName, this.ConvertCustomActionElement }, { WixConverter.ServiceArgumentElementName, this.ConvertServiceArgumentElement }, { WixConverter.SetDirectoryElementName, this.ConvertSetDirectoryElement }, { WixConverter.SetPropertyElementName, this.ConvertSetPropertyElement }, @@ -224,8 +253,12 @@ namespace WixToolset.Converters this.ErrorsAsWarnings = new HashSet(this.YieldConverterTypes(errorsAsWarnings)); this.IgnoreErrors = new HashSet(this.YieldConverterTypes(ignoreErrors)); + + this.CustomTableSetting = customTableTarget; } + private CustomTableTarget CustomTableSetting { get; } + private int Errors { get; set; } private HashSet ErrorsAsWarnings { get; set; } @@ -717,11 +750,104 @@ namespace WixToolset.Converters private void ConvertCustomTableElement(XElement element) { var bootstrapperApplicationData = element.Attribute("BootstrapperApplicationData"); - if (bootstrapperApplicationData != null - && this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name)) + if (bootstrapperApplicationData?.Value == "no") + { + if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name)) + { + bootstrapperApplicationData.Remove(); + } + } + else + { + if (element.Elements(ColumnElementName).Any() || bootstrapperApplicationData != null) + { + // Table definition + if (bootstrapperApplicationData != null) + { + switch (this.CustomTableSetting) + { + case CustomTableTarget.Bundle: + if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'BundleCustomData' element for Bundles.", bootstrapperApplicationData.Name)) + { + element.Name = WixConverter.BundleCustomDataElementName; + bootstrapperApplicationData.Remove(); + this.ConvertCustomTableElementToBundle(element); + } + break; + case CustomTableTarget.Msi: + if (this.OnError(ConverterTestType.BootstrapperApplicationDataDeprecated, element, "The CustomTable element contains deprecated '{0}' attribute. Use the 'Unreal' attribute instead.", bootstrapperApplicationData.Name)) + { + element.Add(new XAttribute("Unreal", bootstrapperApplicationData.Value)); + bootstrapperApplicationData.Remove(); + } + break; + default: + this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains deprecated '{0}' attribute so can't be converted. Use the 'Unreal' attribute for MSI. Use the 'BundleCustomData' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'", bootstrapperApplicationData.Name); + break; + } + } + } + else + { + // Table ref + switch (this.CustomTableSetting) + { + case CustomTableTarget.Bundle: + if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now BundleCustomDataRef for Bundles.")) + { + element.Name = WixConverter.BundleCustomDataRefElementName; + this.ConvertCustomTableElementToBundle(element); + } + break; + case CustomTableTarget.Msi: + if (this.OnError(ConverterTestType.CustomTableRef, element, "CustomTable elements that don't contain the table definition are now CustomTableRef for MSI.")) + { + element.Name = WixConverter.CustomTableRefElementName; + } + break; + default: + this.OnError(ConverterTestType.CustomTableNotAlwaysConvertable, element, "The CustomTable element contains no 'Column' elements so can't be converted. Use the 'CustomTableRef' element for MSI. Use the 'BundleCustomDataRef' element for Bundles. Use the --custom-table argument to force conversion to 'msi' or 'bundle'"); + break; + } + } + } + } + + private void ConvertCustomTableElementToBundle(XElement element) + { + foreach (var xColumn in element.Elements(ColumnElementName)) { - element.Add(new XAttribute("Unreal", bootstrapperApplicationData.Value)); - bootstrapperApplicationData.Remove(); + xColumn.Name = WixConverter.BundleAttributeDefinitionElementName; + + foreach (var xAttribute in xColumn.Attributes().ToList()) + { + if (xAttribute.Name.LocalName != "Id" && + (xAttribute.Name.Namespace == WixConverter.Wix3Namespace || + xAttribute.Name.Namespace == WixConverter.WixNamespace || + String.IsNullOrEmpty(xAttribute.Name.Namespace.NamespaceName))) + { + xAttribute.Remove(); + } + } + } + + foreach (var xRow in element.Elements(RowElementName)) + { + xRow.Name = WixConverter.BundleElementElementName; + + foreach (var xData in xRow.Elements(DataElementName)) + { + xData.Name = WixConverter.BundleAttributeElementName; + + var xColumn = xData.Attribute("Column"); + if (xColumn != null) + { + xData.Add(new XAttribute("Id", xColumn.Value)); + xColumn.Remove(); + } + + this.ConvertInnerTextToAttribute(xData, "Value"); + } } } @@ -1165,8 +1291,6 @@ namespace WixToolset.Converters private void ConvertRequiredPrivilegeElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Name"); - private void ConvertRowElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); - private void ConvertDataElement(XElement element) => this.ConvertInnerTextToAttribute(element, "Value"); private void ConvertSequenceElement(XElement element) @@ -1854,7 +1978,7 @@ namespace WixToolset.Converters AssignDirectoryNameFromShortName, /// - /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal. + /// BootstrapperApplicationData attribute is deprecated and replaced with Unreal for MSI. Use BundleCustomData element for Bundles. /// BootstrapperApplicationDataDeprecated, @@ -2022,6 +2146,16 @@ namespace WixToolset.Converters /// The hash algorithm used for bundles changed from SHA1 to SHA512. /// BurnHashAlgorithmChanged, + + /// + /// CustomTable elements can't always be converted. + /// + CustomTableNotAlwaysConvertable, + + /// + /// CustomTable elements that don't contain the table definition are now CustomTableRef. + /// + CustomTableRef, } } } diff --git a/src/test/WixToolsetTest.Converters/CustomTableFixture.cs b/src/test/WixToolsetTest.Converters/CustomTableFixture.cs index c51d1923..2b81a863 100644 --- a/src/test/WixToolsetTest.Converters/CustomTableFixture.cs +++ b/src/test/WixToolsetTest.Converters/CustomTableFixture.cs @@ -83,7 +83,7 @@ namespace WixToolsetTest.Converters var converter = new WixConverter(messaging, 2, null, null); var errors = converter.ConvertDocument(document); - Assert.Equal(3, errors); + Assert.Equal(4, errors); var actualLines = UnformattedDocumentLines(document); WixAssert.CompareLineByLine(expected, actualLines); @@ -126,7 +126,7 @@ namespace WixToolsetTest.Converters var converter = new WixConverter(messaging, 2, null, null); var errors = converter.ConvertDocument(document); - Assert.Equal(2, errors); + Assert.Equal(3, errors); var actualLines = UnformattedDocumentLines(document); WixAssert.CompareLineByLine(expected, actualLines); @@ -162,14 +162,154 @@ namespace WixToolsetTest.Converters var converter = new WixConverter(messaging, 2, null, null); var errors = converter.ConvertDocument(document); - Assert.Equal(2, errors); + Assert.Equal(3, errors); var actualLines = UnformattedDocumentLines(document); WixAssert.CompareLineByLine(expected, actualLines); } [Fact] - public void CanConvertCustomTableBootstrapperApplicationData() + public void CanConvertBundleCustomTableBootstrapperApplicationData() + { + var parse = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " Row1", + " ", + " ", + ""); + + var expected = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " ", + " ", + " ", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(2, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertBundleCustomTableRef() + { + var parse = String.Join(Environment.NewLine, + "", + " ", + " ", + " Row1", + " ", + " ", + ""); + + var expected = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " ", + " ", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Bundle); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(2, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertMsiCustomTableBootstrapperApplicationData() + { + var parse = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " Row1", + " ", + " ", + ""); + + var expected = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " ", + " ", + " ", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(2, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanConvertMsiCustomTableRef() + { + var parse = String.Join(Environment.NewLine, + "", + " ", + " ", + " Row1", + " ", + " ", + ""); + + var expected = String.Join(Environment.NewLine, + "", + " ", + " ", + " ", + " ", + " ", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2, customTableTarget: CustomTableTarget.Msi); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(2, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanDetectAmbiguousCustomTableBootstrapperApplicationData() { var parse = String.Join(Environment.NewLine, "", @@ -178,13 +318,39 @@ namespace WixToolsetTest.Converters var expected = String.Join(Environment.NewLine, "", - " ", + " ", ""); var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); var messaging = new MockMessaging(); - var converter = new WixConverter(messaging, 2, null, null); + var converter = new WixConverter(messaging, 2); + + var errors = converter.ConvertDocument(document); + + var actual = UnformattedDocumentString(document); + + Assert.Equal(1, errors); + Assert.Equal(expected, actual); + } + + [Fact] + public void CanRemoveBootstrapperApplicationDataFromRealCustomTable() + { + var parse = String.Join(Environment.NewLine, + "", + " ", + ""); + + var expected = String.Join(Environment.NewLine, + "", + " ", + ""); + + var document = XDocument.Parse(parse, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); + + var messaging = new MockMessaging(); + var converter = new WixConverter(messaging, 2); var errors = converter.ConvertDocument(document); -- cgit v1.2.3-55-g6feb