From abff61df823505abc01776cec7b207501c671bf2 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 19 Jun 2020 13:52:20 +1000 Subject: Implement BundleCustomData. Remove Unreal from CustomTable. --- .../Bind/GenerateManifestDataFromIRCommand.cs | 98 ++-- .../Bind/CreateOutputFromIRCommand.cs | 4 +- .../Bind/LoadTableDefinitionsCommand.cs | 2 +- src/WixToolset.Core/Compiler.cs | 566 +++++++++++---------- src/WixToolset.Core/Compiler_Bundle.cs | 244 +++++++++ .../BundleManifestFixture.cs | 49 +- .../CustomTableFixture.cs | 31 -- .../BundleCustomTable/BundleCustomTable.wxs | 52 +- .../TestData/CustomTable/CustomTable.wxs | 13 - 9 files changed, 680 insertions(+), 379 deletions(-) diff --git a/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs index 20ecd157..7fd510c6 100644 --- a/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs @@ -2,6 +2,7 @@ namespace WixToolset.Core.Burn.Bind { + using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -37,8 +38,8 @@ namespace WixToolset.Core.Burn.Bind public void Execute() { var tuples = this.Section.Tuples.ToList(); - var cellsByTableAndRowId = new Dictionary>(); - var customTablesById = new Dictionary(); + var cellsByCustomDataAndElementId = new Dictionary>(); + var customDataById = new Dictionary(); foreach (var kvp in this.ExtensionSearchTuplesById) { @@ -63,6 +64,7 @@ namespace WixToolset.Core.Burn.Bind case TupleDefinitionType.WixBundle: case TupleDefinitionType.WixBundleCatalog: case TupleDefinitionType.WixBundleContainer: + case TupleDefinitionType.WixBundleCustomDataAttribute: case TupleDefinitionType.WixBundleExePackage: case TupleDefinitionType.WixBundleExtension: case TupleDefinitionType.WixBundleMsiFeature: @@ -82,7 +84,6 @@ namespace WixToolset.Core.Burn.Bind case TupleDefinitionType.WixBundleVariable: case TupleDefinitionType.WixChain: case TupleDefinitionType.WixComponentSearch: - case TupleDefinitionType.WixCustomTableColumn: case TupleDefinitionType.WixDependencyProvider: case TupleDefinitionType.WixFileSearch: case TupleDefinitionType.WixGroup: @@ -95,12 +96,12 @@ namespace WixToolset.Core.Burn.Bind case TupleDefinitionType.WixUpdateRegistration: break; - case TupleDefinitionType.WixCustomTable: - unknownTuple = !this.IndexCustomTableTuple((WixCustomTableTuple)tuple, customTablesById); + case TupleDefinitionType.WixBundleCustomData: + unknownTuple = !this.IndexBundleCustomDataTuple((WixBundleCustomDataTuple)tuple, customDataById); break; - case TupleDefinitionType.WixCustomTableCell: - this.IndexCustomTableCellTuple((WixCustomTableCellTuple)tuple, cellsByTableAndRowId); + case TupleDefinitionType.WixBundleCustomDataCell: + this.IndexBundleCustomDataCellTuple((WixBundleCustomDataCellTuple)tuple, cellsByCustomDataAndElementId); break; case TupleDefinitionType.MustBeFromAnExtension: @@ -118,68 +119,87 @@ namespace WixToolset.Core.Burn.Bind } } - this.AddIndexedCellTuples(customTablesById, cellsByTableAndRowId); + this.AddIndexedCellTuples(customDataById, cellsByCustomDataAndElementId); } - private bool IndexCustomTableTuple(WixCustomTableTuple wixCustomTableTuple, Dictionary customTablesById) + private bool IndexBundleCustomDataTuple(WixBundleCustomDataTuple wixBundleCustomDataTuple, Dictionary customDataById) { - if (!wixCustomTableTuple.Unreal) + switch (wixBundleCustomDataTuple.Type) { - return false; + case WixBundleCustomDataType.BootstrapperApplication: + case WixBundleCustomDataType.BundleExtension: + break; + default: + return false; } - var tableId = wixCustomTableTuple.Id.Id; - customTablesById.Add(tableId, wixCustomTableTuple); + var customDataId = wixBundleCustomDataTuple.Id.Id; + customDataById.Add(customDataId, wixBundleCustomDataTuple); return true; } - private void IndexCustomTableCellTuple(WixCustomTableCellTuple wixCustomTableCellTuple, Dictionary> cellsByTableAndRowId) + private void IndexBundleCustomDataCellTuple(WixBundleCustomDataCellTuple wixBundleCustomDataCellTuple, Dictionary> cellsByCustomDataAndElementId) { - var tableAndRowId = wixCustomTableCellTuple.TableRef + "/" + wixCustomTableCellTuple.RowId; - if (!cellsByTableAndRowId.TryGetValue(tableAndRowId, out var cells)) + var tableAndRowId = wixBundleCustomDataCellTuple.CustomDataRef + "/" + wixBundleCustomDataCellTuple.ElementId; + if (!cellsByCustomDataAndElementId.TryGetValue(tableAndRowId, out var cells)) { - cells = new List(); - cellsByTableAndRowId.Add(tableAndRowId, cells); + cells = new List(); + cellsByCustomDataAndElementId.Add(tableAndRowId, cells); } - cells.Add(wixCustomTableCellTuple); + cells.Add(wixBundleCustomDataCellTuple); } - private void AddIndexedCellTuples(Dictionary customTablesById, Dictionary> cellsByTableAndRowId) + private void AddIndexedCellTuples(Dictionary customDataById, Dictionary> cellsByCustomDataAndElementId) { - var sb = new StringBuilder(); - using (var writer = XmlWriter.Create(sb, BurnBackendHelper.WriterSettings)) + foreach (var elementValues in cellsByCustomDataAndElementId.Values) { - foreach (var rowOfCells in cellsByTableAndRowId.Values) - { - var tableId = rowOfCells[0].TableRef; - var tableTuple = customTablesById[tableId]; - - if (!tableTuple.Unreal) - { - return; - } + var elementName = elementValues[0].CustomDataRef; + var customDataTuple = customDataById[elementName]; - var columnNames = tableTuple.ColumnNamesSeparated; + var attributeNames = customDataTuple.AttributeNamesSeparated; - var rowDataByColumn = rowOfCells.ToDictionary(t => t.ColumnRef, t => t.Data); + var elementValuesByAttribute = elementValues.ToDictionary(t => t.AttributeRef, t => t.Value); - writer.WriteStartElement(tableId, BurnCommon.BADataNamespace); + var sb = new StringBuilder(); + using (var writer = XmlWriter.Create(sb, BurnBackendHelper.WriterSettings)) + { + switch (customDataTuple.Type) + { + case WixBundleCustomDataType.BootstrapperApplication: + writer.WriteStartElement(elementName, BurnCommon.BADataNamespace); + break; + case WixBundleCustomDataType.BundleExtension: + writer.WriteStartElement(elementName, BurnCommon.BundleExtensionDataNamespace); + break; + default: + throw new NotImplementedException(); + } // Write all row data as attributes in table column order. - foreach (var column in columnNames) + foreach (var attributeName in attributeNames) { - if (rowDataByColumn.TryGetValue(column, out var data)) + if (elementValuesByAttribute.TryGetValue(attributeName, out var value)) { - writer.WriteAttributeString(column, data); + writer.WriteAttributeString(attributeName, value); } } writer.WriteEndElement(); } - } - this.BackendHelper.AddBootstrapperApplicationData(sb.ToString()); + switch (customDataTuple.Type) + { + case WixBundleCustomDataType.BootstrapperApplication: + this.BackendHelper.AddBootstrapperApplicationData(sb.ToString()); + break; + case WixBundleCustomDataType.BundleExtension: + this.BackendHelper.AddBundleExtensionData(customDataTuple.BundleExtensionRef, sb.ToString()); + break; + default: + throw new NotImplementedException(); + } + } } private bool AddTupleFromExtension(IntermediateTuple tuple) diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index daf4c96e..553b470b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs @@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Globalization; using System.Linq; using WixToolset.Data; @@ -978,7 +979,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind if (customTableDefinition.Unreal) { - return; + Debug.Assert(false, "CustomTableDefinition should never be unreal."); + continue; } var customRow = this.CreateRow(firstCellTuple, customTableDefinition); diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs index 0312ab44..cfb46ff9 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs @@ -208,7 +208,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind columns.Add(columnDefinition); } - var customTable = new TableDefinition(tuple.Id.Id, null, columns, tuple.Unreal); + var customTable = new TableDefinition(tuple.Id.Id, null, columns); return customTable; } } diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index da0806fb..bbd6b292 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs @@ -3668,7 +3668,6 @@ namespace WixToolset.Core { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string tableId = null; - var unreal = false; var columns = new List(); foreach (var attrib in node.Attributes()) @@ -3680,9 +3679,6 @@ namespace WixToolset.Core case "Id": tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; - case "Unreal": - unreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; default: this.Core.UnexpectedAttribute(node, attrib); break; @@ -3710,315 +3706,336 @@ namespace WixToolset.Core var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); switch (child.Name.LocalName) { - case "Column": - string columnName = null; - IntermediateFieldType? columnType = null; - var description = String.Empty; - int? keyColumn = null; - var keyTable = String.Empty; - var localizable = false; - long? maxValue = null; - long? minValue = null; - WixCustomTableColumnCategoryType? category = null; - var modularization = WixCustomTableColumnModularizeType.None; - var nullable = false; - var primaryKey = false; - var setValues = String.Empty; - var columnUnreal = false; - var width = 0; - - foreach (var childAttrib in child.Attributes()) - { - switch (childAttrib.Name.LocalName) + case "Column": + var column = this.ParseColumnElement(child, childSourceLineNumbers, tableId); + if (column != null) { - case "Id": - columnName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib); + columns.Add(column); + } + break; + case "Row": + this.ParseRowElement(child, childSourceLineNumbers, tableId); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (columns.Count > 0) + { + if (!columns.Where(c => c.PrimaryKey).Any()) + { + this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers)); + } + + if (!this.Core.EncounteredError) + { + var columnNames = String.Join(new string(WixCustomTableTuple.ColumnNamesSeparator, 1), columns.Select(c => c.Name)); + + this.Core.AddTuple(new WixCustomTableTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, tableId)) + { + ColumnNames = columnNames, + }); + } + } + } + + /// + /// Parses a Column element. + /// + /// Element to parse. + /// Element's SourceLineNumbers. + /// Table Id. + private WixCustomTableColumnTuple ParseColumnElement(XElement child, SourceLineNumber childSourceLineNumbers, string tableId) + { + string columnName = null; + IntermediateFieldType? columnType = null; + var description = String.Empty; + int? keyColumn = null; + var keyTable = String.Empty; + var localizable = false; + long? maxValue = null; + long? minValue = null; + WixCustomTableColumnCategoryType? category = null; + var modularization = WixCustomTableColumnModularizeType.None; + var nullable = false; + var primaryKey = false; + var setValues = String.Empty; + var columnUnreal = false; + var width = 0; + + foreach (var childAttrib in child.Attributes()) + { + switch (childAttrib.Name.LocalName) + { + case "Id": + columnName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib); + break; + case "Category": + var categoryValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + switch (categoryValue) + { + case "text": + category = WixCustomTableColumnCategoryType.Text; break; - case "Category": - var categoryValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); - switch (categoryValue) - { - case "text": - category = WixCustomTableColumnCategoryType.Text; - break; - case "upperCase": - category = WixCustomTableColumnCategoryType.UpperCase; - break; - case "lowerCase": - category = WixCustomTableColumnCategoryType.LowerCase; - break; - case "integer": - category = WixCustomTableColumnCategoryType.Integer; - break; - case "doubleInteger": - category = WixCustomTableColumnCategoryType.DoubleInteger; - break; - case "timeDate": - category = WixCustomTableColumnCategoryType.TimeDate; - break; - case "identifier": - category = WixCustomTableColumnCategoryType.Identifier; - break; - case "property": - category = WixCustomTableColumnCategoryType.Property; - break; - case "filename": - category = WixCustomTableColumnCategoryType.Filename; - break; - case "wildCardFilename": - category = WixCustomTableColumnCategoryType.WildCardFilename; - break; - case "path": - category = WixCustomTableColumnCategoryType.Path; - break; - case "paths": - category = WixCustomTableColumnCategoryType.Paths; - break; - case "anyPath": - category = WixCustomTableColumnCategoryType.AnyPath; - break; - case "defaultDir": - category = WixCustomTableColumnCategoryType.DefaultDir; - break; - case "regPath": - category = WixCustomTableColumnCategoryType.RegPath; - break; - case "formatted": - category = WixCustomTableColumnCategoryType.Formatted; - break; - case "formattedSddl": - category = WixCustomTableColumnCategoryType.FormattedSddl; - break; - case "template": - category = WixCustomTableColumnCategoryType.Template; - break; - case "condition": - category = WixCustomTableColumnCategoryType.Condition; - break; - case "guid": - category = WixCustomTableColumnCategoryType.Guid; - break; - case "version": - category = WixCustomTableColumnCategoryType.Version; - break; - case "language": - category = WixCustomTableColumnCategoryType.Language; - break; - case "binary": - category = WixCustomTableColumnCategoryType.Binary; - break; - case "customSource": - category = WixCustomTableColumnCategoryType.CustomSource; - break; - case "cabinet": - category = WixCustomTableColumnCategoryType.Cabinet; - break; - case "shortcut": - category = WixCustomTableColumnCategoryType.Shortcut; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Category", categoryValue, - "text", "upperCase", "lowerCase", "integer", "doubleInteger", "timeDate", "identifier", "property", "filename", - "wildCardFilename", "path", "paths", "anyPath", "defaultDir", "regPath", "formatted", "formattedSddl", "template", - "condition", "guid", "version", "language", "binary", "customSource", "cabinet", "shortcut")); - columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. - break; - } + case "upperCase": + category = WixCustomTableColumnCategoryType.UpperCase; break; - case "Description": - description = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + case "lowerCase": + category = WixCustomTableColumnCategoryType.LowerCase; break; - case "KeyColumn": - keyColumn = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32); + case "integer": + category = WixCustomTableColumnCategoryType.Integer; break; - case "KeyTable": - keyTable = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + case "doubleInteger": + category = WixCustomTableColumnCategoryType.DoubleInteger; break; - case "Localizable": - localizable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + case "timeDate": + category = WixCustomTableColumnCategoryType.TimeDate; break; - case "MaxValue": - maxValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); + case "identifier": + category = WixCustomTableColumnCategoryType.Identifier; break; - case "MinValue": - minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); + case "property": + category = WixCustomTableColumnCategoryType.Property; break; - case "Modularize": - var modularizeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); - switch (modularizeValue) - { - case "column": - modularization = WixCustomTableColumnModularizeType.Column; - break; - case "companionFile": - modularization = WixCustomTableColumnModularizeType.CompanionFile; - break; - case "condition": - modularization = WixCustomTableColumnModularizeType.Condition; - break; - case "controlEventArgument": - modularization = WixCustomTableColumnModularizeType.ControlEventArgument; - break; - case "controlText": - modularization = WixCustomTableColumnModularizeType.ControlText; - break; - case "icon": - modularization = WixCustomTableColumnModularizeType.Icon; - break; - case "none": - modularization = WixCustomTableColumnModularizeType.None; - break; - case "property": - modularization = WixCustomTableColumnModularizeType.Property; - break; - case "semicolonDelimited": - modularization = WixCustomTableColumnModularizeType.SemicolonDelimited; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Modularize", modularizeValue, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited")); - columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. - break; - } + case "filename": + category = WixCustomTableColumnCategoryType.Filename; break; - case "Nullable": - nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + case "wildCardFilename": + category = WixCustomTableColumnCategoryType.WildCardFilename; break; - case "PrimaryKey": - primaryKey = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + case "path": + category = WixCustomTableColumnCategoryType.Path; break; - case "Set": - setValues = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + case "paths": + category = WixCustomTableColumnCategoryType.Paths; break; - case "Type": - var typeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); - switch (typeValue) - { - case "binary": - columnType = IntermediateFieldType.Path; - break; - case "int": - columnType = IntermediateFieldType.Number; - break; - case "string": - columnType = IntermediateFieldType.String; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string")); - columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. - break; - } + case "anyPath": + category = WixCustomTableColumnCategoryType.AnyPath; + break; + case "defaultDir": + category = WixCustomTableColumnCategoryType.DefaultDir; + break; + case "regPath": + category = WixCustomTableColumnCategoryType.RegPath; + break; + case "formatted": + category = WixCustomTableColumnCategoryType.Formatted; + break; + case "formattedSddl": + category = WixCustomTableColumnCategoryType.FormattedSddl; + break; + case "template": + category = WixCustomTableColumnCategoryType.Template; + break; + case "condition": + category = WixCustomTableColumnCategoryType.Condition; + break; + case "guid": + category = WixCustomTableColumnCategoryType.Guid; break; - case "Width": - width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue); + case "version": + category = WixCustomTableColumnCategoryType.Version; break; - case "Unreal": - columnUnreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + case "language": + category = WixCustomTableColumnCategoryType.Language; + break; + case "binary": + category = WixCustomTableColumnCategoryType.Binary; + break; + case "customSource": + category = WixCustomTableColumnCategoryType.CustomSource; + break; + case "cabinet": + category = WixCustomTableColumnCategoryType.Cabinet; + break; + case "shortcut": + category = WixCustomTableColumnCategoryType.Shortcut; + break; + case "": break; default: - this.Core.UnexpectedAttribute(child, childAttrib); + this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Category", categoryValue, + "text", "upperCase", "lowerCase", "integer", "doubleInteger", "timeDate", "identifier", "property", "filename", + "wildCardFilename", "path", "paths", "anyPath", "defaultDir", "regPath", "formatted", "formattedSddl", "template", + "condition", "guid", "version", "language", "binary", "customSource", "cabinet", "shortcut")); + columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. break; - } - } - - if (null == columnName) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); - } - - if (!columnType.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type")); } - else if (columnType == IntermediateFieldType.Number) - { - if (2 != width && 4 != width) - { - this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width)); - } - } - else if (columnType == IntermediateFieldType.Path) + break; + case "Description": + description = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + break; + case "KeyColumn": + keyColumn = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32); + break; + case "KeyTable": + keyTable = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + break; + case "Localizable": + localizable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + break; + case "MaxValue": + maxValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); + break; + case "MinValue": + minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); + break; + case "Modularize": + var modularizeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + switch (modularizeValue) { - if (!category.HasValue) - { - category = WixCustomTableColumnCategoryType.Binary; - } - else if (category != WixCustomTableColumnCategoryType.Binary) - { - this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers)); - } + case "column": + modularization = WixCustomTableColumnModularizeType.Column; + break; + case "companionFile": + modularization = WixCustomTableColumnModularizeType.CompanionFile; + break; + case "condition": + modularization = WixCustomTableColumnModularizeType.Condition; + break; + case "controlEventArgument": + modularization = WixCustomTableColumnModularizeType.ControlEventArgument; + break; + case "controlText": + modularization = WixCustomTableColumnModularizeType.ControlText; + break; + case "icon": + modularization = WixCustomTableColumnModularizeType.Icon; + break; + case "none": + modularization = WixCustomTableColumnModularizeType.None; + break; + case "property": + modularization = WixCustomTableColumnModularizeType.Property; + break; + case "semicolonDelimited": + modularization = WixCustomTableColumnModularizeType.SemicolonDelimited; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Modularize", modularizeValue, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited")); + columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. + break; } - - this.Core.ParseForExtensionElements(child); - - if (!this.Core.EncounteredError) + break; + case "Nullable": + nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + break; + case "PrimaryKey": + primaryKey = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); + break; + case "Set": + setValues = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + break; + case "Type": + var typeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + switch (typeValue) { - var attributes = primaryKey ? WixCustomTableColumnTupleAttributes.PrimaryKey : WixCustomTableColumnTupleAttributes.None; - attributes |= localizable ? WixCustomTableColumnTupleAttributes.Localizable : WixCustomTableColumnTupleAttributes.None; - attributes |= nullable ? WixCustomTableColumnTupleAttributes.Nullable : WixCustomTableColumnTupleAttributes.None; - attributes |= columnUnreal ? WixCustomTableColumnTupleAttributes.Unreal : WixCustomTableColumnTupleAttributes.None; - - var column = this.Core.AddTuple(new WixCustomTableColumnTuple(childSourceLineNumbers, new Identifier(AccessModifier.Private, tableId, columnName)) - { - TableRef = tableId, - Name = columnName, - Type = columnType.Value, - Attributes = attributes, - Width = width, - Category = category, - Description = description, - KeyColumn = keyColumn, - KeyTable = keyTable, - MaxValue = maxValue, - MinValue = minValue, - Modularize = modularization, - Set = setValues, - }); - columns.Add(column); + case "binary": + columnType = IntermediateFieldType.Path; + break; + case "int": + columnType = IntermediateFieldType.Number; + break; + case "string": + columnType = IntermediateFieldType.String; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string")); + columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. + break; } break; - case "Row": - this.ParseRow(child, tableId); + case "Width": + width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue); + break; + case "Unreal": + columnUnreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); break; default: - this.Core.UnexpectedElement(node, child); + this.Core.UnexpectedAttribute(child, childAttrib); break; - } } - else + } + + if (null == columnName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); + } + + if (!columnType.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type")); + } + else if (columnType == IntermediateFieldType.Number) + { + if (2 != width && 4 != width) { - this.Core.ParseExtensionElement(node, child); + this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width)); } } - - if (columns.Count > 0) + else if (columnType == IntermediateFieldType.Path) { - if (!columns.Where(c => c.PrimaryKey).Any()) + if (!category.HasValue) { - this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers)); + category = WixCustomTableColumnCategoryType.Binary; } - - if (!this.Core.EncounteredError) + else if (category != WixCustomTableColumnCategoryType.Binary) { - var columnNames = String.Join(new string(WixCustomTableTuple.ColumnNamesSeparator, 1), columns.Select(c => c.Name)); - - this.Core.AddTuple(new WixCustomTableTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, tableId)) - { - ColumnNames = columnNames, - Unreal = unreal, - }); + this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers)); } } + + this.Core.ParseForExtensionElements(child); + + if (this.Core.EncounteredError) + { + return null; + } + + var attributes = primaryKey ? WixCustomTableColumnTupleAttributes.PrimaryKey : WixCustomTableColumnTupleAttributes.None; + attributes |= localizable ? WixCustomTableColumnTupleAttributes.Localizable : WixCustomTableColumnTupleAttributes.None; + attributes |= nullable ? WixCustomTableColumnTupleAttributes.Nullable : WixCustomTableColumnTupleAttributes.None; + attributes |= columnUnreal ? WixCustomTableColumnTupleAttributes.Unreal : WixCustomTableColumnTupleAttributes.None; + + var column = this.Core.AddTuple(new WixCustomTableColumnTuple(childSourceLineNumbers, new Identifier(AccessModifier.Private, tableId, columnName)) + { + TableRef = tableId, + Name = columnName, + Type = columnType.Value, + Attributes = attributes, + Width = width, + Category = category, + Description = description, + KeyColumn = keyColumn, + KeyTable = keyTable, + MaxValue = maxValue, + MinValue = minValue, + Modularize = modularization, + Set = setValues, + }); + return column; } - private void ParseRow(XElement node, string tableId) + /// + /// Parses a Row element. + /// + /// Element to parse. + /// Element's SourceLineNumbers. + /// Table Id. + private void ParseRowElement(XElement node, SourceLineNumber sourceLineNumbers, string tableId) { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var rowId = Guid.NewGuid().ToString("N").ToUpperInvariant(); foreach (var attrib in node.Attributes()) @@ -6204,6 +6221,9 @@ namespace WixToolset.Core case "BootstrapperApplicationRef": this.ParseBootstrapperApplicationRefElement(child); break; + case "BundleCustomData": + this.ParseBundleCustomDataElement(child); + break; case "BundleExtension": this.ParseBundleExtensionElement(child); break; diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs index 31896a42..5154a72f 100644 --- a/src/WixToolset.Core/Compiler_Bundle.cs +++ b/src/WixToolset.Core/Compiler_Bundle.cs @@ -7,6 +7,7 @@ namespace WixToolset.Core using System.Diagnostics; using System.Globalization; using System.IO; + using System.Linq; using System.Xml.Linq; using WixToolset.Data; using WixToolset.Data.Burn; @@ -276,6 +277,9 @@ namespace WixToolset.Core case "BootstrapperApplicationRef": this.ParseBootstrapperApplicationRefElement(child); break; + case "BundleCustomData": + this.ParseBundleCustomDataElement(child); + break; case "BundleExtension": this.ParseBundleExtensionElement(child); break; @@ -767,6 +771,246 @@ namespace WixToolset.Core } } + + + /// + /// Parses a BundleCustomData element. + /// + /// Element to parse. + private void ParseBundleCustomDataElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string customDataId = null; + WixBundleCustomDataType? customDataType = null; + string extensionId = null; + var attributeDefinitions = new List(); + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + customDataId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Type": + var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (typeValue) + { + case "bootstrapperApplication": + customDataType = WixBundleCustomDataType.BootstrapperApplication; + break; + case "bundleExtension": + customDataType = WixBundleCustomDataType.BundleExtension; + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "bootstrapperApplication", "bundleExtension")); + customDataType = WixBundleCustomDataType.Unknown; // set a value to prevent expected attribute error below. + break; + } + break; + case "ExtensionId": + extensionId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, TupleDefinitions.WixBundleExtension, extensionId); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == customDataId) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + var hasExtensionId = null != extensionId; + if (hasExtensionId && !customDataType.HasValue) + { + customDataType = WixBundleCustomDataType.BundleExtension; + } + + if (!customDataType.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type")); + } + else if (hasExtensionId) + { + if (customDataType.Value == WixBundleCustomDataType.BootstrapperApplication) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensonId", "Type", "bootstrapperApplication")); + } + } + else if (customDataType.Value == WixBundleCustomDataType.BundleExtension) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensionId", "Type", "bundleExtension")); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "BundleAttributeDefinition": + var attributeDefinition = this.ParseBundleAttributeDefinitionElement(child, childSourceLineNumbers, customDataId); + if (attributeDefinition != null) + { + attributeDefinitions.Add(attributeDefinition); + } + break; + case "BundleElement": + this.ParseBundleElementElement(child, childSourceLineNumbers, customDataId); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (attributeDefinitions.Count > 0) + { + if (!this.Core.EncounteredError) + { + var attributeNames = String.Join(new string(WixBundleCustomDataTuple.AttributeNamesSeparator, 1), attributeDefinitions.Select(c => c.Name)); + + this.Core.AddTuple(new WixBundleCustomDataTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, customDataId)) + { + AttributeNames = attributeNames, + Type = customDataType.Value, + BundleExtensionRef = extensionId, + }); + } + } + } + + /// + /// Parses a BundleAttributeDefinition element. + /// + /// Element to parse. + /// Element's SourceLineNumbers. + /// BundleCustomData Id. + private WixBundleCustomDataAttributeTuple ParseBundleAttributeDefinitionElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId) + { + string attributeName = null; + + foreach (var attrib in node.Attributes()) + { + switch (attrib.Name.LocalName) + { + case "Id": + attributeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + + if (null == attributeName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + this.Core.ParseForExtensionElements(node); + + if (this.Core.EncounteredError) + { + return null; + } + + var customDataAttribute = this.Core.AddTuple(new WixBundleCustomDataAttributeTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, customDataId, attributeName)) + { + CustomDataRef = customDataId, + Name = attributeName, + }); + return customDataAttribute; + } + + /// + /// Parses a BundleElement element. + /// + /// Element to parse. + /// Element's SourceLineNumbers. + /// BundleCustomData Id. + private void ParseBundleElementElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId) + { + var elementId = Guid.NewGuid().ToString("N").ToUpperInvariant(); + + foreach (var attrib in node.Attributes()) + { + this.Core.ParseExtensionAttribute(node, attrib); + } + + foreach (var child in node.Elements()) + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "BundleAttribute": + string attributeName = null; + string value = null; + foreach (var attrib in child.Attributes()) + { + switch (attrib.Name.LocalName) + { + case "Id": + attributeName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); + break; + case "Value": + value = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); + break; + default: + this.Core.ParseExtensionAttribute(child, attrib); + break; + } + } + + if (null == attributeName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); + } + + if (String.IsNullOrEmpty(value)) + { + value = Common.GetInnerText(child); + } + + if (!this.Core.EncounteredError) + { + this.Core.AddTuple(new WixBundleCustomDataCellTuple(childSourceLineNumbers, new Identifier(AccessModifier.Private, customDataId, elementId, attributeName)) + { + ElementId = elementId, + AttributeRef = attributeName, + CustomDataRef = customDataId, + Value = value, + }); + } + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + + if (!this.Core.EncounteredError) + { + this.Core.CreateSimpleReference(sourceLineNumbers, TupleDefinitions.WixBundleCustomData, customDataId); + } + } + /// /// Parse the BundleExtension element. /// diff --git a/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs b/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs index 4b1ec718..ae83150a 100644 --- a/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs @@ -13,7 +13,7 @@ namespace WixToolsetTest.CoreIntegration public class BundleManifestFixture { [Fact] - public void PopulatesBAManifestWithUnrealCustomTable() + public void PopulatesBAManifestWithBootstrapperApplicationBundleCustomData() { var folder = TestData.Get(@"TestData"); @@ -43,11 +43,50 @@ namespace WixToolsetTest.CoreIntegration var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); extractResult.AssertSuccess(); - var customElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:BundleCustomTable"); + var customElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:BundleCustomTableBA"); Assert.Equal(3, customElements.Count); - Assert.Equal("", customElements[0].GetTestXml()); - Assert.Equal("", customElements[1].GetTestXml()); - Assert.Equal("", customElements[2].GetTestXml()); + Assert.Equal("", customElements[0].GetTestXml()); + Assert.Equal("", customElements[1].GetTestXml()); + Assert.Equal("", customElements[2].GetTestXml()); + } + } + + [Fact] + public void PopulatesBEManifestWithBundleExtensionBundleCustomData() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var bundlePath = Path.Combine(baseFolder, @"bin\test.exe"); + var baFolderPath = Path.Combine(baseFolder, "ba"); + var extractFolderPath = Path.Combine(baseFolder, "extract"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "BundleCustomTable", "BundleCustomTable.wxs"), + Path.Combine(folder, "BundleWithPackageGroupRef", "MinimalPackageGroup.wxs"), + Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-intermediateFolder", intermediateFolder, + "-o", bundlePath + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(bundlePath)); + + var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); + extractResult.AssertSuccess(); + + var customElements = extractResult.SelectBundleExtensionDataNodes("/be:BundleExtensionData/be:BundleExtension[@Id='CustomTableExtension']/be:BundleCustomTableBE"); + Assert.Equal(3, customElements.Count); + Assert.Equal("", customElements[0].GetTestXml()); + Assert.Equal("", customElements[1].GetTestXml()); + Assert.Equal("", customElements[2].GetTestXml()); } } diff --git a/src/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs b/src/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs index 85a0ffae..afba1cbc 100644 --- a/src/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs @@ -3,7 +3,6 @@ namespace WixToolsetTest.CoreIntegration { using System.IO; - using Microsoft.Build.Tasks; using WixBuildTools.TestSupport; using WixToolset.Core.TestPackage; using Xunit; @@ -159,36 +158,6 @@ namespace WixToolsetTest.CoreIntegration } } - [Fact] - public void UnrealCustomTableIsNotPresentInMsi() - { - 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, "CustomTable", "CustomTable.wxs"), - Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), - Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), - "-bindpath", Path.Combine(folder, "SingleFile", "data"), - "-intermediateFolder", intermediateFolder, - "-o", msiPath - }); - - result.AssertSuccess(); - - Assert.True(File.Exists(msiPath)); - var results = Query.QueryDatabase(msiPath, new[] { "CustomTable2" }); - Assert.Empty(results); - } - } - [Fact] public void CanCompileAndDecompile() { diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/BundleCustomTable/BundleCustomTable.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/BundleCustomTable/BundleCustomTable.wxs index dacbc014..38d207ca 100644 --- a/src/test/WixToolsetTest.CoreIntegration/TestData/BundleCustomTable/BundleCustomTable.wxs +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/BundleCustomTable/BundleCustomTable.wxs @@ -5,22 +5,42 @@ - - - + + + - - one - two - - - < - > - - - 1 - 2 - - + + one + two + + + < + > + + + 1 + 2 + + + + + + + + + one + two + + + < + > + + + 1 + 2 + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable.wxs index 51aee5f2..d6a2521e 100644 --- a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable.wxs +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable.wxs @@ -17,18 +17,5 @@ test.txt - - - - - - RowA - test.txt - - - RowB - test.txt - - -- cgit v1.2.3-55-g6feb