diff options
Diffstat (limited to 'src')
15 files changed, 731 insertions, 515 deletions
diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs index 2a230a90..dba2a9ba 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs | |||
@@ -281,20 +281,20 @@ namespace WixToolset.Core.Burn.Bundles | |||
281 | } | 281 | } |
282 | 282 | ||
283 | var dataTablesById = this.Section.Tuples.OfType<WixCustomTableTuple>() | 283 | var dataTablesById = this.Section.Tuples.OfType<WixCustomTableTuple>() |
284 | .Where(t => t.Unreal && t.Id != null) | 284 | .Where(t => t.Unreal && t.Id != null) |
285 | .ToDictionary(t => t.Id.Id); | 285 | .ToDictionary(t => t.Id.Id); |
286 | var dataRowsByTable = this.Section.Tuples.OfType<WixCustomRowTuple>() | 286 | var cellsByTable = this.Section.Tuples.OfType<WixCustomTableCellTuple>() |
287 | .GroupBy(t => t.Table); | 287 | .GroupBy(t => t.TableRef); |
288 | foreach (var tableDataRows in dataRowsByTable) | 288 | foreach (var tableCells in cellsByTable) |
289 | { | 289 | { |
290 | var tableName = tableDataRows.Key; | 290 | var tableName = tableCells.Key; |
291 | if (!dataTablesById.TryGetValue(tableName, out var tableTuple)) | 291 | if (!dataTablesById.TryGetValue(tableName, out var tableTuple)) |
292 | { | 292 | { |
293 | // This should have been a linker error. | 293 | // This should have been a linker error. |
294 | continue; | 294 | continue; |
295 | } | 295 | } |
296 | 296 | ||
297 | var columnNames = tableTuple.ColumnNames.Split('\t'); | 297 | var columnNames = tableTuple.ColumnNamesSeparated; |
298 | 298 | ||
299 | // We simply assert that the table (and field) name is valid, because | 299 | // We simply assert that the table (and field) name is valid, because |
300 | // this is up to the extension developer to get right. An author will | 300 | // this is up to the extension developer to get right. An author will |
@@ -307,17 +307,18 @@ namespace WixToolset.Core.Burn.Bundles | |||
307 | } | 307 | } |
308 | #endif // DEBUG | 308 | #endif // DEBUG |
309 | 309 | ||
310 | foreach (var rowTuple in tableDataRows) | 310 | foreach (var rowCells in tableCells.GroupBy(t => t.RowId)) |
311 | { | 311 | { |
312 | var rowDataByColumn = rowCells.ToDictionary(t => t.ColumnRef, t => t.Data); | ||
313 | |||
312 | writer.WriteStartElement(tableName); | 314 | writer.WriteStartElement(tableName); |
313 | 315 | ||
314 | //var rowFields = rowTuple.FieldDataSeparated; | 316 | // Write all row data as attributes in table column order. |
315 | foreach (var field in rowTuple.FieldDataSeparated) | 317 | foreach (var column in columnNames) |
316 | { | 318 | { |
317 | var splitField = field.Split(ColonCharacter, 2); | 319 | if (rowDataByColumn.TryGetValue(column, out var data)) |
318 | if (splitField.Length == 2) | ||
319 | { | 320 | { |
320 | writer.WriteAttributeString(splitField[0], splitField[1]); | 321 | writer.WriteAttributeString(column, data); |
321 | } | 322 | } |
322 | } | 323 | } |
323 | 324 | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index ffc4e84d..0c1aa312 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs | |||
@@ -18,8 +18,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
18 | private const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB | 18 | private const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB |
19 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | 19 | private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) |
20 | 20 | ||
21 | private static readonly char[] ColonCharacter = new[] { ':' }; | ||
22 | |||
23 | public CreateOutputFromIRCommand(IMessaging messaging, IntermediateSection section, TableDefinitionCollection tableDefinitions, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IWindowsInstallerBackendHelper backendHelper) | 21 | public CreateOutputFromIRCommand(IMessaging messaging, IntermediateSection section, TableDefinitionCollection tableDefinitions, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IWindowsInstallerBackendHelper backendHelper) |
24 | { | 22 | { |
25 | this.Messaging = messaging; | 23 | this.Messaging = messaging; |
@@ -54,171 +52,174 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
54 | 52 | ||
55 | private void AddSectionToOutput() | 53 | private void AddSectionToOutput() |
56 | { | 54 | { |
55 | var cellsByTableAndRowId = new Dictionary<string, List<WixCustomTableCellTuple>>(); | ||
56 | |||
57 | foreach (var tuple in this.Section.Tuples) | 57 | foreach (var tuple in this.Section.Tuples) |
58 | { | 58 | { |
59 | var unknownTuple = false; | 59 | var unknownTuple = false; |
60 | switch (tuple.Definition.Type) | 60 | switch (tuple.Definition.Type) |
61 | { | 61 | { |
62 | case TupleDefinitionType.AppSearch: | 62 | case TupleDefinitionType.AppSearch: |
63 | this.AddTupleDefaultly(tuple); | 63 | this.AddTupleDefaultly(tuple); |
64 | this.Output.EnsureTable(this.TableDefinitions["Signature"]); | 64 | this.Output.EnsureTable(this.TableDefinitions["Signature"]); |
65 | break; | 65 | break; |
66 | 66 | ||
67 | case TupleDefinitionType.Assembly: | 67 | case TupleDefinitionType.Assembly: |
68 | this.AddAssemblyTuple((AssemblyTuple)tuple); | 68 | this.AddAssemblyTuple((AssemblyTuple)tuple); |
69 | break; | 69 | break; |
70 | 70 | ||
71 | case TupleDefinitionType.BBControl: | 71 | case TupleDefinitionType.BBControl: |
72 | this.AddBBControlTuple((BBControlTuple)tuple); | 72 | this.AddBBControlTuple((BBControlTuple)tuple); |
73 | break; | 73 | break; |
74 | 74 | ||
75 | case TupleDefinitionType.Class: | 75 | case TupleDefinitionType.Class: |
76 | this.AddClassTuple((ClassTuple)tuple); | 76 | this.AddClassTuple((ClassTuple)tuple); |
77 | break; | 77 | break; |
78 | 78 | ||
79 | case TupleDefinitionType.Control: | 79 | case TupleDefinitionType.Control: |
80 | this.AddControlTuple((ControlTuple)tuple); | 80 | this.AddControlTuple((ControlTuple)tuple); |
81 | break; | 81 | break; |
82 | 82 | ||
83 | case TupleDefinitionType.Component: | 83 | case TupleDefinitionType.Component: |
84 | this.AddComponentTuple((ComponentTuple)tuple); | 84 | this.AddComponentTuple((ComponentTuple)tuple); |
85 | break; | 85 | break; |
86 | 86 | ||
87 | case TupleDefinitionType.CustomAction: | 87 | case TupleDefinitionType.CustomAction: |
88 | this.AddCustomActionTuple((CustomActionTuple)tuple); | 88 | this.AddCustomActionTuple((CustomActionTuple)tuple); |
89 | break; | 89 | break; |
90 | 90 | ||
91 | case TupleDefinitionType.Dialog: | 91 | case TupleDefinitionType.Dialog: |
92 | this.AddDialogTuple((DialogTuple)tuple); | 92 | this.AddDialogTuple((DialogTuple)tuple); |
93 | break; | 93 | break; |
94 | 94 | ||
95 | case TupleDefinitionType.Directory: | 95 | case TupleDefinitionType.Directory: |
96 | this.AddDirectoryTuple((DirectoryTuple)tuple); | 96 | this.AddDirectoryTuple((DirectoryTuple)tuple); |
97 | break; | 97 | break; |
98 | 98 | ||
99 | case TupleDefinitionType.Environment: | 99 | case TupleDefinitionType.Environment: |
100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple); | 100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple); |
101 | break; | 101 | break; |
102 | 102 | ||
103 | case TupleDefinitionType.Error: | 103 | case TupleDefinitionType.Error: |
104 | this.AddErrorTuple((ErrorTuple)tuple); | 104 | this.AddErrorTuple((ErrorTuple)tuple); |
105 | break; | 105 | break; |
106 | 106 | ||
107 | case TupleDefinitionType.Feature: | 107 | case TupleDefinitionType.Feature: |
108 | this.AddFeatureTuple((FeatureTuple)tuple); | 108 | this.AddFeatureTuple((FeatureTuple)tuple); |
109 | break; | 109 | break; |
110 | 110 | ||
111 | case TupleDefinitionType.File: | 111 | case TupleDefinitionType.File: |
112 | this.AddFileTuple((FileTuple)tuple); | 112 | this.AddFileTuple((FileTuple)tuple); |
113 | break; | 113 | break; |
114 | 114 | ||
115 | case TupleDefinitionType.IniFile: | 115 | case TupleDefinitionType.IniFile: |
116 | this.AddIniFileTuple((IniFileTuple)tuple); | 116 | this.AddIniFileTuple((IniFileTuple)tuple); |
117 | break; | 117 | break; |
118 | 118 | ||
119 | case TupleDefinitionType.Media: | 119 | case TupleDefinitionType.Media: |
120 | this.AddMediaTuple((MediaTuple)tuple); | 120 | this.AddMediaTuple((MediaTuple)tuple); |
121 | break; | 121 | break; |
122 | 122 | ||
123 | case TupleDefinitionType.ModuleConfiguration: | 123 | case TupleDefinitionType.ModuleConfiguration: |
124 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple); | 124 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple); |
125 | break; | 125 | break; |
126 | 126 | ||
127 | case TupleDefinitionType.MsiEmbeddedUI: | 127 | case TupleDefinitionType.MsiEmbeddedUI: |
128 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple); | 128 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple); |
129 | break; | 129 | break; |
130 | 130 | ||
131 | case TupleDefinitionType.MsiServiceConfig: | 131 | case TupleDefinitionType.MsiServiceConfig: |
132 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple); | 132 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple); |
133 | break; | 133 | break; |
134 | 134 | ||
135 | case TupleDefinitionType.MsiServiceConfigFailureActions: | 135 | case TupleDefinitionType.MsiServiceConfigFailureActions: |
136 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple); | 136 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple); |
137 | break; | 137 | break; |
138 | 138 | ||
139 | case TupleDefinitionType.MoveFile: | 139 | case TupleDefinitionType.MoveFile: |
140 | this.AddMoveFileTuple((MoveFileTuple)tuple); | 140 | this.AddMoveFileTuple((MoveFileTuple)tuple); |
141 | break; | 141 | break; |
142 | 142 | ||
143 | case TupleDefinitionType.ProgId: | 143 | case TupleDefinitionType.ProgId: |
144 | this.AddTupleDefaultly(tuple); | 144 | this.AddTupleDefaultly(tuple); |
145 | this.Output.EnsureTable(this.TableDefinitions["Extension"]); | 145 | this.Output.EnsureTable(this.TableDefinitions["Extension"]); |
146 | break; | 146 | break; |
147 | 147 | ||
148 | case TupleDefinitionType.Property: | 148 | case TupleDefinitionType.Property: |
149 | this.AddPropertyTuple((PropertyTuple)tuple); | 149 | this.AddPropertyTuple((PropertyTuple)tuple); |
150 | break; | 150 | break; |
151 | 151 | ||
152 | case TupleDefinitionType.RemoveFile: | 152 | case TupleDefinitionType.RemoveFile: |
153 | this.AddRemoveFileTuple((RemoveFileTuple)tuple); | 153 | this.AddRemoveFileTuple((RemoveFileTuple)tuple); |
154 | break; | 154 | break; |
155 | 155 | ||
156 | case TupleDefinitionType.Registry: | 156 | case TupleDefinitionType.Registry: |
157 | this.AddRegistryTuple((RegistryTuple)tuple); | 157 | this.AddRegistryTuple((RegistryTuple)tuple); |
158 | break; | 158 | break; |
159 | 159 | ||
160 | case TupleDefinitionType.RegLocator: | 160 | case TupleDefinitionType.RegLocator: |
161 | this.AddRegLocatorTuple((RegLocatorTuple)tuple); | 161 | this.AddRegLocatorTuple((RegLocatorTuple)tuple); |
162 | break; | 162 | break; |
163 | 163 | ||
164 | case TupleDefinitionType.RemoveRegistry: | 164 | case TupleDefinitionType.RemoveRegistry: |
165 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple); | 165 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple); |
166 | break; | 166 | break; |
167 | 167 | ||
168 | case TupleDefinitionType.ServiceControl: | 168 | case TupleDefinitionType.ServiceControl: |
169 | this.AddServiceControlTuple((ServiceControlTuple)tuple); | 169 | this.AddServiceControlTuple((ServiceControlTuple)tuple); |
170 | break; | 170 | break; |
171 | 171 | ||
172 | case TupleDefinitionType.ServiceInstall: | 172 | case TupleDefinitionType.ServiceInstall: |
173 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple); | 173 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple); |
174 | break; | 174 | break; |
175 | 175 | ||
176 | case TupleDefinitionType.Shortcut: | 176 | case TupleDefinitionType.Shortcut: |
177 | this.AddShortcutTuple((ShortcutTuple)tuple); | 177 | this.AddShortcutTuple((ShortcutTuple)tuple); |
178 | break; | 178 | break; |
179 | 179 | ||
180 | case TupleDefinitionType.TextStyle: | 180 | case TupleDefinitionType.TextStyle: |
181 | this.AddTextStyleTuple((TextStyleTuple)tuple); | 181 | this.AddTextStyleTuple((TextStyleTuple)tuple); |
182 | break; | 182 | break; |
183 | 183 | ||
184 | case TupleDefinitionType.Upgrade: | 184 | case TupleDefinitionType.Upgrade: |
185 | this.AddUpgradeTuple((UpgradeTuple)tuple); | 185 | this.AddUpgradeTuple((UpgradeTuple)tuple); |
186 | break; | 186 | break; |
187 | 187 | ||
188 | case TupleDefinitionType.WixAction: | 188 | case TupleDefinitionType.WixAction: |
189 | this.AddWixActionTuple((WixActionTuple)tuple); | 189 | this.AddWixActionTuple((WixActionTuple)tuple); |
190 | break; | 190 | break; |
191 | 191 | ||
192 | case TupleDefinitionType.WixMediaTemplate: | 192 | case TupleDefinitionType.WixMediaTemplate: |
193 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple); | 193 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple); |
194 | break; | 194 | break; |
195 | 195 | ||
196 | case TupleDefinitionType.WixCustomRow: | 196 | case TupleDefinitionType.WixCustomTableCell: |
197 | this.AddWixCustomRowTuple((WixCustomRowTuple)tuple); | 197 | this.IndexCustomTableCellTuple((WixCustomTableCellTuple)tuple, cellsByTableAndRowId); |
198 | break; | 198 | break; |
199 | 199 | ||
200 | case TupleDefinitionType.WixEnsureTable: | 200 | case TupleDefinitionType.WixEnsureTable: |
201 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple); | 201 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple); |
202 | break; | 202 | break; |
203 | 203 | ||
204 | // ignored. | 204 | // ignored. |
205 | case TupleDefinitionType.WixComponentGroup: | 205 | case TupleDefinitionType.WixComponentGroup: |
206 | case TupleDefinitionType.WixDeltaPatchFile: | 206 | case TupleDefinitionType.WixDeltaPatchFile: |
207 | case TupleDefinitionType.WixFeatureGroup: | 207 | case TupleDefinitionType.WixFeatureGroup: |
208 | case TupleDefinitionType.WixPatchBaseline: | 208 | case TupleDefinitionType.WixPatchBaseline: |
209 | break; | 209 | break; |
210 | 210 | ||
211 | // Already processed. | 211 | // Already processed by LoadTableDefinitions. |
212 | case TupleDefinitionType.WixCustomTable: | 212 | case TupleDefinitionType.WixCustomTable: |
213 | break; | 213 | case TupleDefinitionType.WixCustomTableColumn: |
214 | break; | ||
214 | 215 | ||
215 | case TupleDefinitionType.MustBeFromAnExtension: | 216 | case TupleDefinitionType.MustBeFromAnExtension: |
216 | unknownTuple = !this.AddTupleFromExtension(tuple); | 217 | unknownTuple = !this.AddTupleFromExtension(tuple); |
217 | break; | 218 | break; |
218 | 219 | ||
219 | default: | 220 | default: |
220 | unknownTuple = !this.AddTupleDefaultly(tuple); | 221 | unknownTuple = !this.AddTupleDefaultly(tuple); |
221 | break; | 222 | break; |
222 | } | 223 | } |
223 | 224 | ||
224 | if (unknownTuple) | 225 | if (unknownTuple) |
@@ -226,6 +227,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
226 | this.Messaging.Write(WarningMessages.TupleNotTranslatedToOutput(tuple)); | 227 | this.Messaging.Write(WarningMessages.TupleNotTranslatedToOutput(tuple)); |
227 | } | 228 | } |
228 | } | 229 | } |
230 | |||
231 | this.AddIndexedCellTuples(cellsByTableAndRowId); | ||
229 | } | 232 | } |
230 | 233 | ||
231 | private void AddAssemblyTuple(AssemblyTuple tuple) | 234 | private void AddAssemblyTuple(AssemblyTuple tuple) |
@@ -383,16 +386,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
383 | private void AddDialogTuple(DialogTuple tuple) | 386 | private void AddDialogTuple(DialogTuple tuple) |
384 | { | 387 | { |
385 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; | 388 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; |
386 | attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; | 389 | attributes |= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; |
387 | attributes|= tuple.Minimize ? WindowsInstallerConstants.MsidbDialogAttributesMinimize : 0; | 390 | attributes |= tuple.Minimize ? WindowsInstallerConstants.MsidbDialogAttributesMinimize : 0; |
388 | attributes|= tuple.CustomPalette ? WindowsInstallerConstants.MsidbDialogAttributesUseCustomPalette: 0; | 391 | attributes |= tuple.CustomPalette ? WindowsInstallerConstants.MsidbDialogAttributesUseCustomPalette : 0; |
389 | attributes|= tuple.ErrorDialog ? WindowsInstallerConstants.MsidbDialogAttributesError : 0; | 392 | attributes |= tuple.ErrorDialog ? WindowsInstallerConstants.MsidbDialogAttributesError : 0; |
390 | attributes|= tuple.LeftScroll ? WindowsInstallerConstants.MsidbDialogAttributesLeftScroll : 0; | 393 | attributes |= tuple.LeftScroll ? WindowsInstallerConstants.MsidbDialogAttributesLeftScroll : 0; |
391 | attributes|= tuple.KeepModeless ? WindowsInstallerConstants.MsidbDialogAttributesKeepModeless : 0; | 394 | attributes |= tuple.KeepModeless ? WindowsInstallerConstants.MsidbDialogAttributesKeepModeless : 0; |
392 | attributes|= tuple.RightAligned ? WindowsInstallerConstants.MsidbDialogAttributesRightAligned : 0; | 395 | attributes |= tuple.RightAligned ? WindowsInstallerConstants.MsidbDialogAttributesRightAligned : 0; |
393 | attributes|= tuple.RightToLeft ? WindowsInstallerConstants.MsidbDialogAttributesRTLRO : 0; | 396 | attributes |= tuple.RightToLeft ? WindowsInstallerConstants.MsidbDialogAttributesRTLRO : 0; |
394 | attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; | 397 | attributes |= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; |
395 | attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; | 398 | attributes |= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; |
396 | 399 | ||
397 | var row = this.CreateRow(tuple, "Dialog"); | 400 | var row = this.CreateRow(tuple, "Dialog"); |
398 | row[0] = tuple.Id.Id; | 401 | row[0] = tuple.Id.Id; |
@@ -419,7 +422,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
419 | targetName = "."; | 422 | targetName = "."; |
420 | } | 423 | } |
421 | 424 | ||
422 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ; | 425 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName; |
423 | 426 | ||
424 | var row = this.CreateRow(tuple, "Directory"); | 427 | var row = this.CreateRow(tuple, "Directory"); |
425 | row[0] = tuple.Id.Id; | 428 | row[0] = tuple.Id.Id; |
@@ -436,25 +439,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
436 | 439 | ||
437 | switch (tuple.Action) | 440 | switch (tuple.Action) |
438 | { | 441 | { |
439 | case EnvironmentActionType.Create: | 442 | case EnvironmentActionType.Create: |
440 | action = "+"; | 443 | action = "+"; |
441 | break; | 444 | break; |
442 | case EnvironmentActionType.Set: | 445 | case EnvironmentActionType.Set: |
443 | action = "="; | 446 | action = "="; |
444 | break; | 447 | break; |
445 | case EnvironmentActionType.Remove: | 448 | case EnvironmentActionType.Remove: |
446 | action = "!"; | 449 | action = "!"; |
447 | break; | 450 | break; |
448 | } | 451 | } |
449 | 452 | ||
450 | switch (tuple.Part) | 453 | switch (tuple.Part) |
451 | { | 454 | { |
452 | case EnvironmentPartType.First: | 455 | case EnvironmentPartType.First: |
453 | value = String.Concat(value, tuple.Separator, "[~]"); | 456 | value = String.Concat(value, tuple.Separator, "[~]"); |
454 | break; | 457 | break; |
455 | case EnvironmentPartType.Last: | 458 | case EnvironmentPartType.Last: |
456 | value = String.Concat("[~]", tuple.Separator, value); | 459 | value = String.Concat("[~]", tuple.Separator, value); |
457 | break; | 460 | break; |
458 | } | 461 | } |
459 | 462 | ||
460 | var row = this.CreateRow(tuple, "Environment"); | 463 | var row = this.CreateRow(tuple, "Environment"); |
@@ -661,40 +664,40 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
661 | 664 | ||
662 | switch (tuple.ValueType) | 665 | switch (tuple.ValueType) |
663 | { | 666 | { |
664 | case RegistryValueType.Binary: | 667 | case RegistryValueType.Binary: |
665 | value = String.Concat("#x", value); | 668 | value = String.Concat("#x", value); |
666 | break; | ||
667 | case RegistryValueType.Expandable: | ||
668 | value = String.Concat("#%", value); | ||
669 | break; | ||
670 | case RegistryValueType.Integer: | ||
671 | value = String.Concat("#", value); | ||
672 | break; | ||
673 | case RegistryValueType.MultiString: | ||
674 | switch (tuple.ValueAction) | ||
675 | { | ||
676 | case RegistryValueActionType.Append: | ||
677 | value = String.Concat("[~]", value); | ||
678 | break; | 669 | break; |
679 | case RegistryValueActionType.Prepend: | 670 | case RegistryValueType.Expandable: |
680 | value = String.Concat(value, "[~]"); | 671 | value = String.Concat("#%", value); |
681 | break; | 672 | break; |
682 | case RegistryValueActionType.Write: | 673 | case RegistryValueType.Integer: |
683 | default: | 674 | value = String.Concat("#", value); |
684 | if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) | 675 | break; |
676 | case RegistryValueType.MultiString: | ||
677 | switch (tuple.ValueAction) | ||
685 | { | 678 | { |
686 | value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value); | 679 | case RegistryValueActionType.Append: |
680 | value = String.Concat("[~]", value); | ||
681 | break; | ||
682 | case RegistryValueActionType.Prepend: | ||
683 | value = String.Concat(value, "[~]"); | ||
684 | break; | ||
685 | case RegistryValueActionType.Write: | ||
686 | default: | ||
687 | if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) | ||
688 | { | ||
689 | value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value); | ||
690 | } | ||
691 | break; | ||
692 | } | ||
693 | break; | ||
694 | case RegistryValueType.String: | ||
695 | // escape the leading '#' character for string registry keys | ||
696 | if (null != value && value.StartsWith("#", StringComparison.Ordinal)) | ||
697 | { | ||
698 | value = String.Concat("#", value); | ||
687 | } | 699 | } |
688 | break; | 700 | break; |
689 | } | ||
690 | break; | ||
691 | case RegistryValueType.String: | ||
692 | // escape the leading '#' character for string registry keys | ||
693 | if (null != value && value.StartsWith("#", StringComparison.Ordinal)) | ||
694 | { | ||
695 | value = String.Concat("#", value); | ||
696 | } | ||
697 | break; | ||
698 | } | 701 | } |
699 | 702 | ||
700 | var row = this.CreateRow(tuple, "Registry"); | 703 | var row = this.CreateRow(tuple, "Registry"); |
@@ -757,7 +760,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
757 | row[3] = tuple.Arguments; | 760 | row[3] = tuple.Arguments; |
758 | if (tuple.Wait.HasValue) | 761 | if (tuple.Wait.HasValue) |
759 | { | 762 | { |
760 | row[4] = tuple.Wait.Value ? 1 : 0; | 763 | row[4] = tuple.Wait.Value ? 1 : 0; |
761 | } | 764 | } |
762 | row[5] = tuple.ComponentRef; | 765 | row[5] = tuple.ComponentRef; |
763 | } | 766 | } |
@@ -938,83 +941,89 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
938 | row[2] = tuple.Sequence; | 941 | row[2] = tuple.Sequence; |
939 | } | 942 | } |
940 | } | 943 | } |
941 | |||
942 | private void AddWixCustomRowTuple(WixCustomRowTuple tuple) | ||
943 | { | ||
944 | var customTableDefinition = this.TableDefinitions[tuple.Table]; | ||
945 | 944 | ||
946 | if (customTableDefinition.Unreal) | 945 | private void IndexCustomTableCellTuple(WixCustomTableCellTuple wixCustomTableCellTuple, Dictionary<string, List<WixCustomTableCellTuple>> cellsByTableAndRowId) |
946 | { | ||
947 | var tableAndRowId = wixCustomTableCellTuple.TableRef + "/" + wixCustomTableCellTuple.RowId; | ||
948 | if (!cellsByTableAndRowId.TryGetValue(tableAndRowId, out var cells)) | ||
947 | { | 949 | { |
948 | 950 | cells = new List<WixCustomTableCellTuple>(); | |
949 | return; | 951 | cellsByTableAndRowId.Add(tableAndRowId, cells); |
950 | } | 952 | } |
951 | 953 | ||
952 | var customRow = this.CreateRow(tuple, customTableDefinition); | 954 | cells.Add(wixCustomTableCellTuple); |
955 | } | ||
953 | 956 | ||
954 | #if TODO // SectionId seems like a good thing to preserve. | 957 | private void AddIndexedCellTuples(Dictionary<string, List<WixCustomTableCellTuple>> cellsByTableAndRowId) |
955 | customRow.SectionId = tuple.SectionId; | 958 | { |
956 | #endif | 959 | foreach (var rowOfCells in cellsByTableAndRowId.Values) |
960 | { | ||
961 | var firstCellTuple = rowOfCells[0]; | ||
962 | var customTableDefinition = this.TableDefinitions[firstCellTuple.TableRef]; | ||
957 | 963 | ||
958 | var data = tuple.FieldDataSeparated; | 964 | if (customTableDefinition.Unreal) |
965 | { | ||
966 | return; | ||
967 | } | ||
959 | 968 | ||
960 | for (var i = 0; i < data.Length; ++i) | 969 | var customRow = this.CreateRow(firstCellTuple, customTableDefinition); |
961 | { | 970 | var customRowFieldsByColumnName = customRow.Fields.ToDictionary(f => f.Column.Name); |
962 | var foundColumn = false; | ||
963 | var item = data[i].Split(ColonCharacter, 2); | ||
964 | 971 | ||
965 | for (var j = 0; j < customRow.Fields.Length; ++j) | 972 | #if TODO // SectionId seems like a good thing to preserve. |
973 | customRow.SectionId = tuple.SectionId; | ||
974 | #endif | ||
975 | foreach (var cell in rowOfCells) | ||
966 | { | 976 | { |
967 | if (customRow.Fields[j].Column.Name == item[0]) | 977 | var data = cell.Data; |
978 | |||
979 | if (customRowFieldsByColumnName.TryGetValue(cell.ColumnRef, out var rowField)) | ||
968 | { | 980 | { |
969 | if (0 < item[1].Length) | 981 | if (!String.IsNullOrEmpty(data)) |
970 | { | 982 | { |
971 | if (ColumnType.Number == customRow.Fields[j].Column.Type) | 983 | if (rowField.Column.Type == ColumnType.Number) |
972 | { | 984 | { |
973 | try | 985 | try |
974 | { | 986 | { |
975 | customRow.Fields[j].Data = Convert.ToInt32(item[1], CultureInfo.InvariantCulture); | 987 | rowField.Data = Convert.ToInt32(data, CultureInfo.InvariantCulture); |
976 | } | 988 | } |
977 | catch (FormatException) | 989 | catch (FormatException) |
978 | { | 990 | { |
979 | this.Messaging.Write(ErrorMessages.IllegalIntegerValue(tuple.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name, item[1])); | 991 | this.Messaging.Write(ErrorMessages.IllegalIntegerValue(cell.SourceLineNumbers, rowField.Column.Name, customTableDefinition.Name, data)); |
980 | } | 992 | } |
981 | catch (OverflowException) | 993 | catch (OverflowException) |
982 | { | 994 | { |
983 | this.Messaging.Write(ErrorMessages.IllegalIntegerValue(tuple.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name, item[1])); | 995 | this.Messaging.Write(ErrorMessages.IllegalIntegerValue(cell.SourceLineNumbers, rowField.Column.Name, customTableDefinition.Name, data)); |
984 | } | 996 | } |
985 | } | 997 | } |
986 | else if (ColumnCategory.Identifier == customRow.Fields[j].Column.Category) | 998 | else if (rowField.Column.Category == ColumnCategory.Identifier) |
987 | { | 999 | { |
988 | if (Common.IsIdentifier(item[1]) || Common.IsValidBinderVariable(item[1]) || ColumnCategory.Formatted == customRow.Fields[j].Column.Category) | 1000 | if (Common.IsIdentifier(data) || Common.IsValidBinderVariable(data) || ColumnCategory.Formatted == rowField.Column.Category) |
989 | { | 1001 | { |
990 | customRow.Fields[j].Data = item[1]; | 1002 | rowField.Data = data; |
991 | } | 1003 | } |
992 | else | 1004 | else |
993 | { | 1005 | { |
994 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(tuple.SourceLineNumbers, "Data", item[1])); | 1006 | this.Messaging.Write(ErrorMessages.IllegalIdentifier(cell.SourceLineNumbers, "Data", data)); |
995 | } | 1007 | } |
996 | } | 1008 | } |
997 | else | 1009 | else |
998 | { | 1010 | { |
999 | customRow.Fields[j].Data = item[1]; | 1011 | rowField.Data = data; |
1000 | } | 1012 | } |
1001 | } | 1013 | } |
1002 | foundColumn = true; | 1014 | } |
1003 | break; | 1015 | else |
1016 | { | ||
1017 | this.Messaging.Write(ErrorMessages.UnexpectedCustomTableColumn(cell.SourceLineNumbers, cell.ColumnRef)); | ||
1004 | } | 1018 | } |
1005 | } | 1019 | } |
1006 | 1020 | ||
1007 | if (!foundColumn) | 1021 | for (var i = 0; i < customTableDefinition.Columns.Length; ++i) |
1008 | { | ||
1009 | this.Messaging.Write(ErrorMessages.UnexpectedCustomTableColumn(tuple.SourceLineNumbers, item[0])); | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | for (var i = 0; i < customTableDefinition.Columns.Length; ++i) | ||
1014 | { | ||
1015 | if (!customTableDefinition.Columns[i].Nullable && (null == customRow.Fields[i].Data || 0 == customRow.Fields[i].Data.ToString().Length)) | ||
1016 | { | 1022 | { |
1017 | this.Messaging.Write(ErrorMessages.NoDataForColumn(tuple.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name)); | 1023 | if (!customTableDefinition.Columns[i].Nullable && (null == customRow.Fields[i].Data || 0 == customRow.Fields[i].Data.ToString().Length)) |
1024 | { | ||
1025 | this.Messaging.Write(ErrorMessages.NoDataForColumn(firstCellTuple.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name)); | ||
1026 | } | ||
1018 | } | 1027 | } |
1019 | } | 1028 | } |
1020 | } | 1029 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index eff94e80..6edbdd1c 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | |||
@@ -314,18 +314,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
314 | break; | 314 | break; |
315 | 315 | ||
316 | case ColumnType.Object: | 316 | case ColumnType.Object: |
317 | if (null != row[i]) | 317 | var path = row.FieldAsString(i); |
318 | if (null != path) | ||
318 | { | 319 | { |
319 | needStream = true; | 320 | needStream = true; |
320 | try | 321 | try |
321 | { | 322 | { |
322 | record.SetStream(i + 1, row.FieldAsString(i)); | 323 | record.SetStream(i + 1, path); |
323 | } | 324 | } |
324 | catch (Win32Exception e) | 325 | catch (Win32Exception e) |
325 | { | 326 | { |
326 | if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME | 327 | if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME |
327 | { | 328 | { |
328 | throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, row.FieldAsString(i))); | 329 | throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, path)); |
329 | } | 330 | } |
330 | else | 331 | else |
331 | { | 332 | { |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs index eba7bdbe..d7809034 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs | |||
@@ -32,11 +32,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
32 | public TableDefinitionCollection Execute() | 32 | public TableDefinitionCollection Execute() |
33 | { | 33 | { |
34 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); | 34 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); |
35 | var customColumnsById = this.Section.Tuples.OfType<WixCustomTableColumnTuple>().ToDictionary(t => t.Id.Id); | ||
35 | 36 | ||
36 | foreach (var tuple in this.Section.Tuples.OfType<WixCustomTableTuple>()) | 37 | if (customColumnsById.Any()) |
37 | { | 38 | { |
38 | var customTableDefinition = this.CreateCustomTable(tuple); | 39 | foreach (var tuple in this.Section.Tuples.OfType<WixCustomTableTuple>()) |
39 | tableDefinitions.Add(customTableDefinition); | 40 | { |
41 | var customTableDefinition = this.CreateCustomTable(tuple, customColumnsById); | ||
42 | tableDefinitions.Add(customTableDefinition); | ||
43 | } | ||
40 | } | 44 | } |
41 | 45 | ||
42 | foreach (var backendExtension in this.BackendExtensions) | 46 | foreach (var backendExtension in this.BackendExtensions) |
@@ -56,177 +60,150 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
56 | return this.TableDefinitions; | 60 | return this.TableDefinitions; |
57 | } | 61 | } |
58 | 62 | ||
59 | private TableDefinition CreateCustomTable(WixCustomTableTuple tuple) | 63 | private TableDefinition CreateCustomTable(WixCustomTableTuple tuple, Dictionary<string, WixCustomTableColumnTuple> customColumnsById) |
60 | { | 64 | { |
61 | var columnNames = tuple.ColumnNames.Split('\t'); | 65 | var columnNames = tuple.ColumnNamesSeparated; |
62 | var columnTypes = tuple.ColumnTypes.Split('\t'); | ||
63 | var primaryKeys = tuple.PrimaryKeys.Split('\t'); | ||
64 | var minValues = tuple.MinValues?.Split('\t'); | ||
65 | var maxValues = tuple.MaxValues?.Split('\t'); | ||
66 | var keyTables = tuple.KeyTables?.Split('\t'); | ||
67 | var keyColumns = tuple.KeyColumns?.Split('\t'); | ||
68 | var categories = tuple.Categories?.Split('\t'); | ||
69 | var sets = tuple.Sets?.Split('\t'); | ||
70 | var descriptions = tuple.Descriptions?.Split('\t'); | ||
71 | var modularizations = tuple.Modularizations?.Split('\t'); | ||
72 | |||
73 | var currentPrimaryKey = 0; | ||
74 | |||
75 | var columns = new List<ColumnDefinition>(columnNames.Length); | 66 | var columns = new List<ColumnDefinition>(columnNames.Length); |
76 | for (var i = 0; i < columnNames.Length; ++i) | 67 | |
68 | foreach (var name in columnNames) | ||
77 | { | 69 | { |
78 | var name = columnNames[i]; | 70 | var column = customColumnsById[tuple.Id.Id + "/" + name]; |
71 | |||
79 | var type = ColumnType.Unknown; | 72 | var type = ColumnType.Unknown; |
80 | 73 | ||
81 | if (columnTypes[i].StartsWith("s", StringComparison.OrdinalIgnoreCase)) | 74 | if (column.Type == IntermediateFieldType.String) |
82 | { | ||
83 | type = ColumnType.String; | ||
84 | } | ||
85 | else if (columnTypes[i].StartsWith("l", StringComparison.OrdinalIgnoreCase)) | ||
86 | { | 75 | { |
87 | type = ColumnType.Localized; | 76 | type = column.Localizable ? ColumnType.Localized : ColumnType.String; |
88 | } | 77 | } |
89 | else if (columnTypes[i].StartsWith("i", StringComparison.OrdinalIgnoreCase)) | 78 | else if (column.Type == IntermediateFieldType.Number) |
90 | { | 79 | { |
91 | type = ColumnType.Number; | 80 | type = ColumnType.Number; |
92 | } | 81 | } |
93 | else if (columnTypes[i].StartsWith("v", StringComparison.OrdinalIgnoreCase)) | 82 | else if (column.Type == IntermediateFieldType.Path) |
94 | { | 83 | { |
95 | type = ColumnType.Object; | 84 | type = ColumnType.Object; |
96 | } | 85 | } |
97 | 86 | ||
98 | var nullable = columnTypes[i].Substring(0, 1) == columnTypes[i].Substring(0, 1).ToUpperInvariant(); | ||
99 | var length = Convert.ToInt32(columnTypes[i].Substring(1), CultureInfo.InvariantCulture); | ||
100 | |||
101 | var primaryKey = false; | ||
102 | if (currentPrimaryKey < primaryKeys.Length && primaryKeys[currentPrimaryKey] == columnNames[i]) | ||
103 | { | ||
104 | primaryKey = true; | ||
105 | currentPrimaryKey++; | ||
106 | } | ||
107 | |||
108 | var minValue = String.IsNullOrEmpty(minValues?[i]) ? (int?)null : Convert.ToInt32(minValues[i], CultureInfo.InvariantCulture); | ||
109 | var maxValue = String.IsNullOrEmpty(maxValues?[i]) ? (int?)null : Convert.ToInt32(maxValues[i], CultureInfo.InvariantCulture); | ||
110 | var keyColumn = String.IsNullOrEmpty(keyColumns?[i]) ? (int?)null : Convert.ToInt32(keyColumns[i], CultureInfo.InvariantCulture); | ||
111 | |||
112 | var category = ColumnCategory.Unknown; | 87 | var category = ColumnCategory.Unknown; |
113 | if (null != categories && null != categories[i] && 0 < categories[i].Length) | 88 | switch (column.Category) |
114 | { | 89 | { |
115 | switch (categories[i]) | 90 | case "Text": |
116 | { | 91 | category = ColumnCategory.Text; |
117 | case "Text": | 92 | break; |
118 | category = ColumnCategory.Text; | 93 | case "UpperCase": |
119 | break; | 94 | category = ColumnCategory.UpperCase; |
120 | case "UpperCase": | 95 | break; |
121 | category = ColumnCategory.UpperCase; | 96 | case "LowerCase": |
122 | break; | 97 | category = ColumnCategory.LowerCase; |
123 | case "LowerCase": | 98 | break; |
124 | category = ColumnCategory.LowerCase; | 99 | case "Integer": |
125 | break; | 100 | category = ColumnCategory.Integer; |
126 | case "Integer": | 101 | break; |
127 | category = ColumnCategory.Integer; | 102 | case "DoubleInteger": |
128 | break; | 103 | category = ColumnCategory.DoubleInteger; |
129 | case "DoubleInteger": | 104 | break; |
130 | category = ColumnCategory.DoubleInteger; | 105 | case "TimeDate": |
131 | break; | 106 | category = ColumnCategory.TimeDate; |
132 | case "TimeDate": | 107 | break; |
133 | category = ColumnCategory.TimeDate; | 108 | case "Identifier": |
134 | break; | 109 | category = ColumnCategory.Identifier; |
135 | case "Identifier": | 110 | break; |
136 | category = ColumnCategory.Identifier; | 111 | case "Property": |
137 | break; | 112 | category = ColumnCategory.Property; |
138 | case "Property": | 113 | break; |
139 | category = ColumnCategory.Property; | 114 | case "Filename": |
140 | break; | 115 | category = ColumnCategory.Filename; |
141 | case "Filename": | 116 | break; |
142 | category = ColumnCategory.Filename; | 117 | case "WildCardFilename": |
143 | break; | 118 | category = ColumnCategory.WildCardFilename; |
144 | case "WildCardFilename": | 119 | break; |
145 | category = ColumnCategory.WildCardFilename; | 120 | case "Path": |
146 | break; | 121 | category = ColumnCategory.Path; |
147 | case "Path": | 122 | break; |
148 | category = ColumnCategory.Path; | 123 | case "Paths": |
149 | break; | 124 | category = ColumnCategory.Paths; |
150 | case "Paths": | 125 | break; |
151 | category = ColumnCategory.Paths; | 126 | case "AnyPath": |
152 | break; | 127 | category = ColumnCategory.AnyPath; |
153 | case "AnyPath": | 128 | break; |
154 | category = ColumnCategory.AnyPath; | 129 | case "DefaultDir": |
155 | break; | 130 | category = ColumnCategory.DefaultDir; |
156 | case "DefaultDir": | 131 | break; |
157 | category = ColumnCategory.DefaultDir; | 132 | case "RegPath": |
158 | break; | 133 | category = ColumnCategory.RegPath; |
159 | case "RegPath": | 134 | break; |
160 | category = ColumnCategory.RegPath; | 135 | case "Formatted": |
161 | break; | 136 | category = ColumnCategory.Formatted; |
162 | case "Formatted": | 137 | break; |
163 | category = ColumnCategory.Formatted; | 138 | case "FormattedSddl": |
164 | break; | 139 | category = ColumnCategory.FormattedSDDLText; |
165 | case "FormattedSddl": | 140 | break; |
166 | category = ColumnCategory.FormattedSDDLText; | 141 | case "Template": |
167 | break; | 142 | category = ColumnCategory.Template; |
168 | case "Template": | 143 | break; |
169 | category = ColumnCategory.Template; | 144 | case "Condition": |
170 | break; | 145 | category = ColumnCategory.Condition; |
171 | case "Condition": | 146 | break; |
172 | category = ColumnCategory.Condition; | 147 | case "Guid": |
173 | break; | 148 | category = ColumnCategory.Guid; |
174 | case "Guid": | 149 | break; |
175 | category = ColumnCategory.Guid; | 150 | case "Version": |
176 | break; | 151 | category = ColumnCategory.Version; |
177 | case "Version": | 152 | break; |
178 | category = ColumnCategory.Version; | 153 | case "Language": |
179 | break; | 154 | category = ColumnCategory.Language; |
180 | case "Language": | 155 | break; |
181 | category = ColumnCategory.Language; | 156 | case "Binary": |
182 | break; | 157 | category = ColumnCategory.Binary; |
183 | case "Binary": | 158 | break; |
184 | category = ColumnCategory.Binary; | 159 | case "CustomSource": |
185 | break; | 160 | category = ColumnCategory.CustomSource; |
186 | case "CustomSource": | 161 | break; |
187 | category = ColumnCategory.CustomSource; | 162 | case "Cabinet": |
188 | break; | 163 | category = ColumnCategory.Cabinet; |
189 | case "Cabinet": | 164 | break; |
190 | category = ColumnCategory.Cabinet; | 165 | case "Shortcut": |
191 | break; | 166 | category = ColumnCategory.Shortcut; |
192 | case "Shortcut": | 167 | break; |
193 | category = ColumnCategory.Shortcut; | 168 | default: |
194 | break; | 169 | break; |
195 | default: | ||
196 | break; | ||
197 | } | ||
198 | } | 170 | } |
199 | 171 | ||
200 | var keyTable = keyTables?[i]; | ||
201 | var setValue = sets?[i]; | ||
202 | var description = descriptions?[i]; | ||
203 | var modString = modularizations?[i]; | ||
204 | var modularization = ColumnModularizeType.None; | 172 | var modularization = ColumnModularizeType.None; |
205 | 173 | ||
206 | switch (modString) | 174 | switch (column.Modularize) |
207 | { | 175 | { |
208 | case null: | 176 | case null: |
209 | case "None": | 177 | case WixCustomTableColumnModularizeType.None: |
210 | modularization = ColumnModularizeType.None; | 178 | modularization = ColumnModularizeType.None; |
211 | break; | 179 | break; |
212 | case "Column": | 180 | case WixCustomTableColumnModularizeType.Column: |
213 | modularization = ColumnModularizeType.Column; | 181 | modularization = ColumnModularizeType.Column; |
214 | break; | 182 | break; |
215 | case "Property": | 183 | case WixCustomTableColumnModularizeType.CompanionFile: |
216 | modularization = ColumnModularizeType.Property; | 184 | modularization = ColumnModularizeType.CompanionFile; |
217 | break; | 185 | break; |
218 | case "Condition": | 186 | case WixCustomTableColumnModularizeType.Condition: |
219 | modularization = ColumnModularizeType.Condition; | 187 | modularization = ColumnModularizeType.Condition; |
220 | break; | 188 | break; |
221 | case "CompanionFile": | 189 | case WixCustomTableColumnModularizeType.ControlEventArgument: |
222 | modularization = ColumnModularizeType.CompanionFile; | 190 | modularization = ColumnModularizeType.ControlEventArgument; |
191 | break; | ||
192 | case WixCustomTableColumnModularizeType.ControlText: | ||
193 | modularization = ColumnModularizeType.ControlText; | ||
194 | break; | ||
195 | case WixCustomTableColumnModularizeType.Icon: | ||
196 | modularization = ColumnModularizeType.Icon; | ||
197 | break; | ||
198 | case WixCustomTableColumnModularizeType.Property: | ||
199 | modularization = ColumnModularizeType.Property; | ||
223 | break; | 200 | break; |
224 | case "SemicolonDelimited": | 201 | case WixCustomTableColumnModularizeType.SemicolonDelimited: |
225 | modularization = ColumnModularizeType.SemicolonDelimited; | 202 | modularization = ColumnModularizeType.SemicolonDelimited; |
226 | break; | 203 | break; |
227 | } | 204 | } |
228 | 205 | ||
229 | var columnDefinition = new ColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, setValue, description, modularization, ColumnType.Localized == type, true); | 206 | var columnDefinition = new ColumnDefinition(name, type, column.Width, column.PrimaryKey, column.Nullable, category, column.MinValue, column.MaxValue, column.KeyTable, column.KeyColumn, column.Set, column.Description, modularization, ColumnType.Localized == type, useCData: true, column.Unreal); |
230 | columns.Add(columnDefinition); | 207 | columns.Add(columnDefinition); |
231 | } | 208 | } |
232 | 209 | ||
diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 3e680a98..af7e262a 100644 --- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | |||
@@ -4,7 +4,9 @@ namespace WixToolset.Core.Bind | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.Linq; | ||
7 | using WixToolset.Data; | 8 | using WixToolset.Data; |
9 | using WixToolset.Data.Tuples; | ||
8 | using WixToolset.Extensibility; | 10 | using WixToolset.Extensibility; |
9 | using WixToolset.Extensibility.Data; | 11 | using WixToolset.Extensibility.Data; |
10 | using WixToolset.Extensibility.Services; | 12 | using WixToolset.Extensibility.Services; |
@@ -42,6 +44,9 @@ namespace WixToolset.Core.Bind | |||
42 | 44 | ||
43 | var fileResolver = new FileResolver(this.BindPaths, this.Extensions); | 45 | var fileResolver = new FileResolver(this.BindPaths, this.Extensions); |
44 | 46 | ||
47 | // Build the column lookup only when needed. | ||
48 | Dictionary<string, WixCustomTableColumnTuple> customColumnsById = null; | ||
49 | |||
45 | foreach (var sections in this.Intermediate.Sections) | 50 | foreach (var sections in this.Intermediate.Sections) |
46 | { | 51 | { |
47 | foreach (var tuple in sections.Tuples) | 52 | foreach (var tuple in sections.Tuples) |
@@ -53,13 +58,37 @@ namespace WixToolset.Core.Bind | |||
53 | continue; | 58 | continue; |
54 | } | 59 | } |
55 | 60 | ||
61 | var fieldType = field.Type; | ||
62 | |||
63 | // Custom table cells require an extra look up to the column definition as the | ||
64 | // cell's data type is always a string (because strings can store anything) but | ||
65 | // the column definition may be more specific. | ||
66 | if (tuple.Definition.Type == TupleDefinitionType.WixCustomTableCell) | ||
67 | { | ||
68 | // We only care about the Data in a CustomTable cell. | ||
69 | if (field.Name != nameof(WixCustomTableCellTupleFields.Data)) | ||
70 | { | ||
71 | continue; | ||
72 | } | ||
73 | |||
74 | if (customColumnsById == null) | ||
75 | { | ||
76 | customColumnsById = this.Intermediate.Sections.SelectMany(s => s.Tuples.OfType<WixCustomTableColumnTuple>()).ToDictionary(t => t.Id.Id); | ||
77 | } | ||
78 | |||
79 | if (customColumnsById.TryGetValue(tuple.Fields[(int)WixCustomTableCellTupleFields.TableRef].AsString() + "/" + tuple.Fields[(int)WixCustomTableCellTupleFields.ColumnRef].AsString(), out var customColumn)) | ||
80 | { | ||
81 | fieldType = customColumn.Type; | ||
82 | } | ||
83 | } | ||
84 | |||
56 | var isDefault = true; | 85 | var isDefault = true; |
57 | 86 | ||
58 | // Check to make sure we're in a scenario where we can handle variable resolution. | 87 | // Check to make sure we're in a scenario where we can handle variable resolution. |
59 | if (null != delayedFields) | 88 | if (null != delayedFields) |
60 | { | 89 | { |
61 | // resolve localization and wix variables | 90 | // resolve localization and wix variables |
62 | if (field.Type == IntermediateFieldType.String) | 91 | if (fieldType == IntermediateFieldType.String) |
63 | { | 92 | { |
64 | var original = field.AsString(); | 93 | var original = field.AsString(); |
65 | if (!String.IsNullOrEmpty(original)) | 94 | if (!String.IsNullOrEmpty(original)) |
@@ -87,7 +116,7 @@ namespace WixToolset.Core.Bind | |||
87 | } | 116 | } |
88 | 117 | ||
89 | // Resolve file paths | 118 | // Resolve file paths |
90 | if (field.Type == IntermediateFieldType.Path) | 119 | if (fieldType == IntermediateFieldType.Path) |
91 | { | 120 | { |
92 | var objectField = field.AsPath(); | 121 | var objectField = field.AsPath(); |
93 | 122 | ||
@@ -226,29 +255,5 @@ namespace WixToolset.Core.Bind | |||
226 | 255 | ||
227 | this.DelayedFields = delayedFields; | 256 | this.DelayedFields = delayedFields; |
228 | } | 257 | } |
229 | |||
230 | #if false | ||
231 | private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage = BindStage.Normal) | ||
232 | { | ||
233 | string path = null; | ||
234 | foreach (var extension in this.Extensions) | ||
235 | { | ||
236 | path = extension.ResolveFile(source, type, sourceLineNumbers, bindStage); | ||
237 | if (null != path) | ||
238 | { | ||
239 | break; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | throw new NotImplementedException(); // need to do default binder stuff | ||
244 | |||
245 | //if (null == path) | ||
246 | //{ | ||
247 | // throw new WixFileNotFoundException(sourceLineNumbers, source, type); | ||
248 | //} | ||
249 | |||
250 | //return path; | ||
251 | } | ||
252 | #endif | ||
253 | } | 258 | } |
254 | } | 259 | } |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 32e9b9d6..35a73e00 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
@@ -8,6 +8,7 @@ namespace WixToolset.Core | |||
8 | using System.Diagnostics.CodeAnalysis; | 8 | using System.Diagnostics.CodeAnalysis; |
9 | using System.Globalization; | 9 | using System.Globalization; |
10 | using System.IO; | 10 | using System.IO; |
11 | using System.Linq; | ||
11 | using System.Text.RegularExpressions; | 12 | using System.Text.RegularExpressions; |
12 | using System.Xml.Linq; | 13 | using System.Xml.Linq; |
13 | using WixToolset.Data; | 14 | using WixToolset.Data; |
@@ -3667,20 +3668,8 @@ namespace WixToolset.Core | |||
3667 | { | 3668 | { |
3668 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | 3669 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); |
3669 | string tableId = null; | 3670 | string tableId = null; |
3670 | 3671 | var unreal = false; | |
3671 | string categories = null; | 3672 | var columns = new List<WixCustomTableColumnTuple>(); |
3672 | var columnCount = 0; | ||
3673 | string columnNames = null; | ||
3674 | string columnTypes = null; | ||
3675 | string descriptions = null; | ||
3676 | string keyColumns = null; | ||
3677 | string keyTables = null; | ||
3678 | string maxValues = null; | ||
3679 | string minValues = null; | ||
3680 | string modularizations = null; | ||
3681 | string primaryKeys = null; | ||
3682 | string sets = null; | ||
3683 | var bootstrapperApplicationData = false; | ||
3684 | 3673 | ||
3685 | foreach (var attrib in node.Attributes()) | 3674 | foreach (var attrib in node.Attributes()) |
3686 | { | 3675 | { |
@@ -3692,7 +3681,7 @@ namespace WixToolset.Core | |||
3692 | tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | 3681 | tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); |
3693 | break; | 3682 | break; |
3694 | case "Unreal": | 3683 | case "Unreal": |
3695 | bootstrapperApplicationData = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | 3684 | unreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); |
3696 | break; | 3685 | break; |
3697 | default: | 3686 | default: |
3698 | this.Core.UnexpectedAttribute(node, attrib); | 3687 | this.Core.UnexpectedAttribute(node, attrib); |
@@ -3722,22 +3711,20 @@ namespace WixToolset.Core | |||
3722 | switch (child.Name.LocalName) | 3711 | switch (child.Name.LocalName) |
3723 | { | 3712 | { |
3724 | case "Column": | 3713 | case "Column": |
3725 | ++columnCount; | ||
3726 | |||
3727 | var category = String.Empty; | ||
3728 | string columnName = null; | 3714 | string columnName = null; |
3729 | string columnType = null; | 3715 | var category = String.Empty; |
3716 | IntermediateFieldType? columnType = null; | ||
3730 | var description = String.Empty; | 3717 | var description = String.Empty; |
3731 | var keyColumn = CompilerConstants.IntegerNotSet; | 3718 | int? keyColumn = null; |
3732 | var keyTable = String.Empty; | 3719 | var keyTable = String.Empty; |
3733 | var localizable = false; | 3720 | var localizable = false; |
3734 | var maxValue = CompilerConstants.LongNotSet; | 3721 | long? maxValue = null; |
3735 | var minValue = CompilerConstants.LongNotSet; | 3722 | long? minValue = null; |
3736 | var modularization = "None"; | 3723 | var modularization = WixCustomTableColumnModularizeType.None; |
3737 | var nullable = false; | 3724 | var nullable = false; |
3738 | var primaryKey = false; | 3725 | var primaryKey = false; |
3739 | var setValues = String.Empty; | 3726 | var setValues = String.Empty; |
3740 | string typeName = null; | 3727 | var columnUnreal = false; |
3741 | var width = 0; | 3728 | var width = 0; |
3742 | 3729 | ||
3743 | foreach (var childAttrib in child.Attributes()) | 3730 | foreach (var childAttrib in child.Attributes()) |
@@ -3769,7 +3756,43 @@ namespace WixToolset.Core | |||
3769 | minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); | 3756 | minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); |
3770 | break; | 3757 | break; |
3771 | case "Modularize": | 3758 | case "Modularize": |
3772 | modularization = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); | 3759 | var modularizeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); |
3760 | switch (modularizeValue) | ||
3761 | { | ||
3762 | case "column": | ||
3763 | modularization = WixCustomTableColumnModularizeType.Column; | ||
3764 | break; | ||
3765 | case "companionFile": | ||
3766 | modularization = WixCustomTableColumnModularizeType.CompanionFile; | ||
3767 | break; | ||
3768 | case "condition": | ||
3769 | modularization = WixCustomTableColumnModularizeType.Condition; | ||
3770 | break; | ||
3771 | case "controlEventArgument": | ||
3772 | modularization = WixCustomTableColumnModularizeType.ControlEventArgument; | ||
3773 | break; | ||
3774 | case "controlText": | ||
3775 | modularization = WixCustomTableColumnModularizeType.ControlText; | ||
3776 | break; | ||
3777 | case "icon": | ||
3778 | modularization = WixCustomTableColumnModularizeType.Icon; | ||
3779 | break; | ||
3780 | case "none": | ||
3781 | modularization = WixCustomTableColumnModularizeType.None; | ||
3782 | break; | ||
3783 | case "property": | ||
3784 | modularization = WixCustomTableColumnModularizeType.Property; | ||
3785 | break; | ||
3786 | case "semicolonDelimited": | ||
3787 | modularization = WixCustomTableColumnModularizeType.SemicolonDelimited; | ||
3788 | break; | ||
3789 | case "": | ||
3790 | break; | ||
3791 | default: | ||
3792 | this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Modularize", modularizeValue, "column", "companionFile", "condition", "controlEventArgument", "controlText", "icon", "property", "semicolonDelimited")); | ||
3793 | columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. | ||
3794 | break; | ||
3795 | } | ||
3773 | break; | 3796 | break; |
3774 | case "Nullable": | 3797 | case "Nullable": |
3775 | nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); | 3798 | nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); |
@@ -3785,24 +3808,28 @@ namespace WixToolset.Core | |||
3785 | switch (typeValue) | 3808 | switch (typeValue) |
3786 | { | 3809 | { |
3787 | case "binary": | 3810 | case "binary": |
3788 | typeName = "OBJECT"; | 3811 | columnType = IntermediateFieldType.Path; |
3789 | break; | 3812 | break; |
3790 | case "int": | 3813 | case "int": |
3791 | typeName = "SHORT"; | 3814 | columnType = IntermediateFieldType.Number; |
3792 | break; | 3815 | break; |
3793 | case "string": | 3816 | case "string": |
3794 | typeName = "CHAR"; | 3817 | columnType = IntermediateFieldType.String; |
3795 | break; | 3818 | break; |
3796 | case "": | 3819 | case "": |
3797 | break; | 3820 | break; |
3798 | default: | 3821 | default: |
3799 | this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string")); | 3822 | this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string")); |
3823 | columnType = IntermediateFieldType.String; // set a value to prevent expected attribute error below. | ||
3800 | break; | 3824 | break; |
3801 | } | 3825 | } |
3802 | break; | 3826 | break; |
3803 | case "Width": | 3827 | case "Width": |
3804 | width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue); | 3828 | width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue); |
3805 | break; | 3829 | break; |
3830 | case "Unreal": | ||
3831 | columnUnreal = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); | ||
3832 | break; | ||
3806 | default: | 3833 | default: |
3807 | this.Core.UnexpectedAttribute(child, childAttrib); | 3834 | this.Core.UnexpectedAttribute(child, childAttrib); |
3808 | break; | 3835 | break; |
@@ -3814,100 +3841,59 @@ namespace WixToolset.Core | |||
3814 | this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); | 3841 | this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); |
3815 | } | 3842 | } |
3816 | 3843 | ||
3817 | if (null == typeName) | 3844 | if (!columnType.HasValue) |
3818 | { | 3845 | { |
3819 | this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type")); | 3846 | this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type")); |
3820 | } | 3847 | } |
3821 | else if ("SHORT" == typeName) | 3848 | else if (columnType == IntermediateFieldType.Number) |
3822 | { | 3849 | { |
3823 | if (2 != width && 4 != width) | 3850 | if (2 != width && 4 != width) |
3824 | { | 3851 | { |
3825 | this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width)); | 3852 | this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width)); |
3826 | } | 3853 | } |
3827 | columnType = String.Concat(nullable ? "I" : "i", width); | ||
3828 | } | 3854 | } |
3829 | else if ("CHAR" == typeName) | 3855 | else if (columnType == IntermediateFieldType.Path) |
3830 | { | 3856 | { |
3831 | var typeChar = localizable ? "l" : "s"; | 3857 | if (String.IsNullOrEmpty(category)) |
3832 | columnType = String.Concat(nullable ? typeChar.ToUpper(CultureInfo.InvariantCulture) : typeChar.ToLower(CultureInfo.InvariantCulture), width); | ||
3833 | } | ||
3834 | else if ("OBJECT" == typeName) | ||
3835 | { | ||
3836 | if ("Binary" != category) | ||
3837 | { | 3858 | { |
3838 | this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers)); | 3859 | category = "Binary"; |
3839 | } | 3860 | } |
3840 | columnType = String.Concat(nullable ? "V" : "v", width); | 3861 | else if (category != "Binary") |
3841 | } | ||
3842 | |||
3843 | this.Core.ParseForExtensionElements(child); | ||
3844 | |||
3845 | columnNames = String.Concat(columnNames, null == columnNames ? String.Empty : "\t", columnName); | ||
3846 | columnTypes = String.Concat(columnTypes, null == columnTypes ? String.Empty : "\t", columnType); | ||
3847 | if (primaryKey) | ||
3848 | { | ||
3849 | primaryKeys = String.Concat(primaryKeys, null == primaryKeys ? String.Empty : "\t", columnName); | ||
3850 | } | ||
3851 | |||
3852 | minValues = String.Concat(minValues, null == minValues ? String.Empty : "\t", CompilerConstants.LongNotSet != minValue ? minValue.ToString(CultureInfo.InvariantCulture) : String.Empty); | ||
3853 | maxValues = String.Concat(maxValues, null == maxValues ? String.Empty : "\t", CompilerConstants.LongNotSet != maxValue ? maxValue.ToString(CultureInfo.InvariantCulture) : String.Empty); | ||
3854 | keyTables = String.Concat(keyTables, null == keyTables ? String.Empty : "\t", keyTable); | ||
3855 | keyColumns = String.Concat(keyColumns, null == keyColumns ? String.Empty : "\t", CompilerConstants.IntegerNotSet != keyColumn ? keyColumn.ToString(CultureInfo.InvariantCulture) : String.Empty); | ||
3856 | categories = String.Concat(categories, null == categories ? String.Empty : "\t", category); | ||
3857 | sets = String.Concat(sets, null == sets ? String.Empty : "\t", setValues); | ||
3858 | descriptions = String.Concat(descriptions, null == descriptions ? String.Empty : "\t", description); | ||
3859 | modularizations = String.Concat(modularizations, null == modularizations ? String.Empty : "\t", modularization); | ||
3860 | |||
3861 | break; | ||
3862 | case "Row": | ||
3863 | string dataValue = null; | ||
3864 | |||
3865 | foreach (var childAttrib in child.Attributes()) | ||
3866 | { | ||
3867 | this.Core.ParseExtensionAttribute(child, childAttrib); | ||
3868 | } | ||
3869 | |||
3870 | foreach (var data in child.Elements()) | ||
3871 | { | ||
3872 | var dataSourceLineNumbers = Preprocessor.GetSourceLineNumbers(data); | ||
3873 | switch (data.Name.LocalName) | ||
3874 | { | 3862 | { |
3875 | case "Data": | 3863 | this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers)); |
3876 | columnName = null; | ||
3877 | foreach (var dataAttrib in data.Attributes()) | ||
3878 | { | ||
3879 | switch (dataAttrib.Name.LocalName) | ||
3880 | { | ||
3881 | case "Column": | ||
3882 | columnName = this.Core.GetAttributeValue(dataSourceLineNumbers, dataAttrib); | ||
3883 | break; | ||
3884 | default: | ||
3885 | this.Core.UnexpectedAttribute(data, dataAttrib); | ||
3886 | break; | ||
3887 | } | ||
3888 | } | ||
3889 | |||
3890 | if (null == columnName) | ||
3891 | { | ||
3892 | this.Core.Write(ErrorMessages.ExpectedAttribute(dataSourceLineNumbers, data.Name.LocalName, "Column")); | ||
3893 | } | ||
3894 | |||
3895 | dataValue = String.Concat(dataValue, null == dataValue ? String.Empty : WixCustomRowTuple.FieldSeparator.ToString(), columnName, ":", Common.GetInnerText(data)); | ||
3896 | break; | ||
3897 | } | 3864 | } |
3898 | } | 3865 | } |
3899 | 3866 | ||
3900 | this.Core.CreateSimpleReference(sourceLineNumbers, TupleDefinitions.WixCustomTable, tableId); | 3867 | this.Core.ParseForExtensionElements(child); |
3901 | 3868 | ||
3902 | if (!this.Core.EncounteredError) | 3869 | if (!this.Core.EncounteredError) |
3903 | { | 3870 | { |
3904 | this.Core.AddTuple(new WixCustomRowTuple(childSourceLineNumbers) | 3871 | var attributes = primaryKey ? WixCustomTableColumnTupleAttributes.PrimaryKey : WixCustomTableColumnTupleAttributes.None; |
3872 | attributes |= localizable ? WixCustomTableColumnTupleAttributes.Localizable : WixCustomTableColumnTupleAttributes.None; | ||
3873 | attributes |= nullable ? WixCustomTableColumnTupleAttributes.Nullable : WixCustomTableColumnTupleAttributes.None; | ||
3874 | attributes |= columnUnreal ? WixCustomTableColumnTupleAttributes.Unreal : WixCustomTableColumnTupleAttributes.None; | ||
3875 | |||
3876 | columns.Add(new WixCustomTableColumnTuple(childSourceLineNumbers, new Identifier(AccessModifier.Private, tableId, columnName)) | ||
3905 | { | 3877 | { |
3906 | Table = tableId, | 3878 | TableRef = tableId, |
3907 | FieldData = dataValue, | 3879 | Name = columnName, |
3880 | Type = columnType.Value, | ||
3881 | Attributes = attributes, | ||
3882 | Width = width, | ||
3883 | Category = category, | ||
3884 | Description = description, | ||
3885 | KeyColumn = keyColumn, | ||
3886 | KeyTable = keyTable, | ||
3887 | MaxValue = maxValue, | ||
3888 | MinValue = minValue, | ||
3889 | Modularize = modularization, | ||
3890 | Set = setValues, | ||
3908 | }); | 3891 | }); |
3909 | } | 3892 | } |
3910 | break; | 3893 | break; |
3894 | case "Row": | ||
3895 | this.ParseRow(child, tableId); | ||
3896 | break; | ||
3911 | default: | 3897 | default: |
3912 | this.Core.UnexpectedElement(node, child); | 3898 | this.Core.UnexpectedElement(node, child); |
3913 | break; | 3899 | break; |
@@ -3919,35 +3905,98 @@ namespace WixToolset.Core | |||
3919 | } | 3905 | } |
3920 | } | 3906 | } |
3921 | 3907 | ||
3922 | if (0 < columnCount) | 3908 | if (columns.Count > 0) |
3923 | { | 3909 | { |
3924 | if (null == primaryKeys || 0 == primaryKeys.Length) | 3910 | if (!columns.Where(c => c.PrimaryKey).Any()) |
3925 | { | 3911 | { |
3926 | this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers)); | 3912 | this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers)); |
3927 | } | 3913 | } |
3928 | 3914 | ||
3929 | if (!this.Core.EncounteredError) | 3915 | if (!this.Core.EncounteredError) |
3930 | { | 3916 | { |
3917 | var columnNames = String.Join(new string(WixCustomTableTuple.ColumnNamesSeparator, 1), columns.Select(c => c.Name)); | ||
3918 | |||
3931 | this.Core.AddTuple(new WixCustomTableTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, tableId)) | 3919 | this.Core.AddTuple(new WixCustomTableTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, tableId)) |
3932 | { | 3920 | { |
3933 | ColumnCount = columnCount, | ||
3934 | ColumnNames = columnNames, | 3921 | ColumnNames = columnNames, |
3935 | ColumnTypes = columnTypes, | 3922 | Unreal = unreal, |
3936 | PrimaryKeys = primaryKeys, | ||
3937 | MinValues = minValues, | ||
3938 | MaxValues = maxValues, | ||
3939 | KeyTables = keyTables, | ||
3940 | KeyColumns = keyColumns, | ||
3941 | Categories = categories, | ||
3942 | Sets = sets, | ||
3943 | Descriptions = descriptions, | ||
3944 | Modularizations = modularizations, | ||
3945 | Unreal = bootstrapperApplicationData, | ||
3946 | }); | 3923 | }); |
3924 | |||
3925 | foreach (var column in columns) | ||
3926 | { | ||
3927 | this.Core.AddTuple(column); | ||
3928 | } | ||
3947 | } | 3929 | } |
3948 | } | 3930 | } |
3949 | } | 3931 | } |
3950 | 3932 | ||
3933 | private void ParseRow(XElement node, string tableId) | ||
3934 | { | ||
3935 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
3936 | var rowId = Guid.NewGuid().ToString("N").ToUpperInvariant(); | ||
3937 | |||
3938 | foreach (var attrib in node.Attributes()) | ||
3939 | { | ||
3940 | this.Core.ParseExtensionAttribute(node, attrib); | ||
3941 | } | ||
3942 | |||
3943 | foreach (var child in node.Elements()) | ||
3944 | { | ||
3945 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
3946 | switch (child.Name.LocalName) | ||
3947 | { | ||
3948 | case "Data": | ||
3949 | string columnName = null; | ||
3950 | string data = null; | ||
3951 | foreach (var attrib in child.Attributes()) | ||
3952 | { | ||
3953 | switch (attrib.Name.LocalName) | ||
3954 | { | ||
3955 | case "Column": | ||
3956 | columnName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); | ||
3957 | break; | ||
3958 | case "Value": | ||
3959 | data = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); | ||
3960 | break; | ||
3961 | default: | ||
3962 | this.Core.ParseExtensionAttribute(child, attrib); | ||
3963 | break; | ||
3964 | } | ||
3965 | } | ||
3966 | |||
3967 | if (null == columnName) | ||
3968 | { | ||
3969 | this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Column")); | ||
3970 | } | ||
3971 | |||
3972 | if (String.IsNullOrEmpty(data)) | ||
3973 | { | ||
3974 | data = Common.GetInnerText(child); | ||
3975 | } | ||
3976 | |||
3977 | if (!this.Core.EncounteredError) | ||
3978 | { | ||
3979 | this.Core.AddTuple(new WixCustomTableCellTuple(childSourceLineNumbers, new Identifier(AccessModifier.Private, tableId, rowId, columnName)) | ||
3980 | { | ||
3981 | RowId = rowId, | ||
3982 | ColumnRef = columnName, | ||
3983 | TableRef = tableId, | ||
3984 | Data = data | ||
3985 | }); | ||
3986 | } | ||
3987 | break; | ||
3988 | default: | ||
3989 | this.Core.UnexpectedElement(node, child); | ||
3990 | break; | ||
3991 | } | ||
3992 | } | ||
3993 | |||
3994 | if (!this.Core.EncounteredError) | ||
3995 | { | ||
3996 | this.Core.CreateSimpleReference(sourceLineNumbers, TupleDefinitions.WixCustomTable, tableId); | ||
3997 | } | ||
3998 | } | ||
3999 | |||
3951 | /// <summary> | 4000 | /// <summary> |
3952 | /// Parses a directory element. | 4001 | /// Parses a directory element. |
3953 | /// </summary> | 4002 | /// </summary> |
diff --git a/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs b/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs index 53036919..80c00ef1 100644 --- a/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/BundleManifestFixture.cs | |||
@@ -46,7 +46,7 @@ namespace WixToolsetTest.CoreIntegration | |||
46 | var customElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:BundleCustomTable"); | 46 | var customElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:BundleCustomTable"); |
47 | Assert.Equal(3, customElements.Count); | 47 | Assert.Equal(3, customElements.Count); |
48 | Assert.Equal("<BundleCustomTable Id='one' Column2='two' />", customElements[0].GetTestXml()); | 48 | Assert.Equal("<BundleCustomTable Id='one' Column2='two' />", customElements[0].GetTestXml()); |
49 | Assert.Equal("<BundleCustomTable Column2='<' Id='>' />", customElements[1].GetTestXml()); | 49 | Assert.Equal("<BundleCustomTable Id='>' Column2='<' />", customElements[1].GetTestXml()); |
50 | Assert.Equal("<BundleCustomTable Id='1' Column2='2' />", customElements[2].GetTestXml()); | 50 | Assert.Equal("<BundleCustomTable Id='1' Column2='2' />", customElements[2].GetTestXml()); |
51 | } | 51 | } |
52 | } | 52 | } |
diff --git a/src/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs b/src/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs index aa8a0a0d..78a8f0a4 100644 --- a/src/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/MsiQueryFixture.cs | |||
@@ -473,6 +473,121 @@ namespace WixToolsetTest.CoreIntegration | |||
473 | } | 473 | } |
474 | 474 | ||
475 | [Fact] | 475 | [Fact] |
476 | public void PopulatesCustomTableWithLocalization() | ||
477 | { | ||
478 | var folder = TestData.Get(@"TestData"); | ||
479 | |||
480 | using (var fs = new DisposableFileSystem()) | ||
481 | { | ||
482 | var baseFolder = fs.GetFolder(); | ||
483 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
484 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); | ||
485 | |||
486 | var result = WixRunner.Execute(new[] | ||
487 | { | ||
488 | "build", | ||
489 | Path.Combine(folder, "CustomTable", "LocalizedCustomTable.wxs"), | ||
490 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), | ||
491 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), | ||
492 | "-loc", Path.Combine(folder, "CustomTable", "LocalizedCustomTable.en-us.wxl"), | ||
493 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), | ||
494 | "-intermediateFolder", intermediateFolder, | ||
495 | "-o", msiPath | ||
496 | }); | ||
497 | |||
498 | result.AssertSuccess(); | ||
499 | |||
500 | Assert.True(File.Exists(msiPath)); | ||
501 | var results = Query.QueryDatabase(msiPath, new[] { "CustomTableLocalized" }); | ||
502 | Assert.Equal(new[] | ||
503 | { | ||
504 | "CustomTableLocalized:Row1\tThis is row one", | ||
505 | "CustomTableLocalized:Row2\tThis is row two", | ||
506 | }, results); | ||
507 | } | ||
508 | } | ||
509 | |||
510 | [Fact] | ||
511 | public void PopulatesCustomTableWithFilePath() | ||
512 | { | ||
513 | var folder = TestData.Get(@"TestData"); | ||
514 | |||
515 | using (var fs = new DisposableFileSystem()) | ||
516 | { | ||
517 | var baseFolder = fs.GetFolder(); | ||
518 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
519 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); | ||
520 | |||
521 | var result = WixRunner.Execute(new[] | ||
522 | { | ||
523 | "build", | ||
524 | Path.Combine(folder, "CustomTable", "CustomTableWithFile.wxs"), | ||
525 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), | ||
526 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), | ||
527 | "-bindpath", Path.Combine(folder, "CustomTable", "data"), | ||
528 | "-intermediateFolder", intermediateFolder, | ||
529 | "-o", msiPath | ||
530 | }); | ||
531 | |||
532 | result.AssertSuccess(); | ||
533 | |||
534 | Assert.True(File.Exists(msiPath)); | ||
535 | var results = Query.QueryDatabase(msiPath, new[] { "CustomTableWithFile" }); | ||
536 | Assert.Equal(new[] | ||
537 | { | ||
538 | "CustomTableWithFile:Row1\t[Binary data]", | ||
539 | "CustomTableWithFile:Row2\t[Binary data]", | ||
540 | }, results); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | [Fact] | ||
545 | public void PopulatesCustomTableWithFilePathSerialized() | ||
546 | { | ||
547 | var folder = TestData.Get(@"TestData"); | ||
548 | |||
549 | using (var fs = new DisposableFileSystem()) | ||
550 | { | ||
551 | var baseFolder = fs.GetFolder(); | ||
552 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
553 | var wixlibPath = Path.Combine(baseFolder, @"bin\test.wixlib"); | ||
554 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); | ||
555 | |||
556 | var result = WixRunner.Execute(new[] | ||
557 | { | ||
558 | "build", | ||
559 | Path.Combine(folder, "CustomTable", "CustomTableWithFile.wxs"), | ||
560 | "-bindpath", Path.Combine(folder, "CustomTable", "data"), | ||
561 | "-intermediateFolder", intermediateFolder, | ||
562 | "-o", wixlibPath | ||
563 | }); | ||
564 | |||
565 | result.AssertSuccess(); | ||
566 | |||
567 | result = WixRunner.Execute(new[] | ||
568 | { | ||
569 | "build", | ||
570 | Path.Combine(folder, "ProductWithComponentGroupRef", "MinimalComponentGroup.wxs"), | ||
571 | Path.Combine(folder, "ProductWithComponentGroupRef", "Product.wxs"), | ||
572 | "-lib", wixlibPath, | ||
573 | "-bindpath", Path.Combine(folder, "CustomTable", "data"), | ||
574 | "-intermediateFolder", intermediateFolder, | ||
575 | "-o", msiPath | ||
576 | }); | ||
577 | |||
578 | result.AssertSuccess(); | ||
579 | |||
580 | Assert.True(File.Exists(msiPath)); | ||
581 | var results = Query.QueryDatabase(msiPath, new[] { "CustomTableWithFile" }); | ||
582 | Assert.Equal(new[] | ||
583 | { | ||
584 | "CustomTableWithFile:Row1\t[Binary data]", | ||
585 | "CustomTableWithFile:Row2\t[Binary data]", | ||
586 | }, results); | ||
587 | } | ||
588 | } | ||
589 | |||
590 | [Fact] | ||
476 | public void UnrealCustomTableIsNotPresentInMsi() | 591 | public void UnrealCustomTableIsNotPresentInMsi() |
477 | { | 592 | { |
478 | var folder = TestData.Get(@"TestData"); | 593 | var folder = TestData.Get(@"TestData"); |
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTableWithFile.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTableWithFile.wxs new file mode 100644 index 00000000..ad5ed233 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTableWithFile.wxs | |||
@@ -0,0 +1,22 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" ?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <ComponentGroup Id="ProductComponents"> | ||
5 | <ComponentGroupRef Id="MinimalComponentGroup" /> | ||
6 | </ComponentGroup> | ||
7 | |||
8 | <CustomTable Id="CustomTableWithFile"> | ||
9 | <Column Id="Column1" Type="string" PrimaryKey="yes" /> | ||
10 | <Column Id="Source" Type="binary" Width="0" /> | ||
11 | <Row> | ||
12 | <Data Column="Column1">Row1</Data> | ||
13 | <Data Column="Source">file1.txt</Data> | ||
14 | </Row> | ||
15 | <Row> | ||
16 | <Data Column="Source">SourceDir\file2.txt</Data> | ||
17 | <Data Column="Column1">Row2</Data> | ||
18 | </Row> | ||
19 | </CustomTable> | ||
20 | |||
21 | </Fragment> | ||
22 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.en-us.wxl new file mode 100644 index 00000000..bc2ccf04 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.en-us.wxl | |||
@@ -0,0 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US"> | ||
3 | |||
4 | <String Id="Loc1">This is row one</String> | ||
5 | <String Id="Loc2">This is row two</String> | ||
6 | |||
7 | </WixLocalization> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.wxs new file mode 100644 index 00000000..e1da74f8 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/LocalizedCustomTable.wxs | |||
@@ -0,0 +1,21 @@ | |||
1 | <?xml version="1.0" encoding="utf-8" ?> | ||
2 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
3 | <Fragment> | ||
4 | <ComponentGroup Id="ProductComponents"> | ||
5 | <ComponentGroupRef Id="MinimalComponentGroup" /> | ||
6 | </ComponentGroup> | ||
7 | |||
8 | <CustomTable Id="CustomTableLocalized"> | ||
9 | <Column Id="Column1" Type="string" PrimaryKey="yes" /> | ||
10 | <Column Id="DataColumn" Type="string" Localizable="yes" Width="255" /> | ||
11 | <Row> | ||
12 | <Data Column="Column1" Value="Row1" /> | ||
13 | <Data Column="DataColumn" Value="!(loc.Loc1)" /> | ||
14 | </Row> | ||
15 | <Row> | ||
16 | <Data Column="Column1" Value="Row2" /> | ||
17 | <Data Column="DataColumn" Value="!(loc.Loc2)" /> | ||
18 | </Row> | ||
19 | </CustomTable> | ||
20 | </Fragment> | ||
21 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file1.txt new file mode 100644 index 00000000..97f701ce --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file1.txt | |||
@@ -0,0 +1 @@ | |||
This is file1.txt \ No newline at end of file | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file2.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file2.txt new file mode 100644 index 00000000..46493186 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/file2.txt | |||
@@ -0,0 +1 @@ | |||
This is file2.txt \ No newline at end of file | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/test.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/test.txt new file mode 100644 index 00000000..cd0db0e1 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/data/test.txt | |||
@@ -0,0 +1 @@ | |||
This is test.txt. \ No newline at end of file | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj index f9f1ba44..51775cd0 100644 --- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj +++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj | |||
@@ -34,7 +34,13 @@ | |||
34 | <Content Include="TestData\Class\IconIndex0.wxs" CopyToOutputDirectory="PreserveNewest" /> | 34 | <Content Include="TestData\Class\IconIndex0.wxs" CopyToOutputDirectory="PreserveNewest" /> |
35 | <Content Include="TestData\Class\OldClassTableDef.msi" CopyToOutputDirectory="PreserveNewest" /> | 35 | <Content Include="TestData\Class\OldClassTableDef.msi" CopyToOutputDirectory="PreserveNewest" /> |
36 | <Content Include="TestData\CustomAction\UnscheduledCustomAction.wxs" CopyToOutputDirectory="PreserveNewest" /> | 36 | <Content Include="TestData\CustomAction\UnscheduledCustomAction.wxs" CopyToOutputDirectory="PreserveNewest" /> |
37 | <Content Include="TestData\CustomTable\CustomTableWithFile.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
38 | <Content Include="TestData\CustomTable\data\file1.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
39 | <Content Include="TestData\CustomTable\data\test.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
40 | <Content Include="TestData\CustomTable\data\file2.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
41 | <Content Include="TestData\CustomTable\LocalizedCustomTable.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
37 | <Content Include="TestData\CustomTable\CustomTable.wxs" CopyToOutputDirectory="PreserveNewest" /> | 42 | <Content Include="TestData\CustomTable\CustomTable.wxs" CopyToOutputDirectory="PreserveNewest" /> |
43 | <Content Include="TestData\CustomTable\LocalizedCustomTable.en-us.wxl" CopyToOutputDirectory="PreserveNewest" /> | ||
38 | <Content Include="TestData\DefaultDir\DefaultDir.wxs" CopyToOutputDirectory="PreserveNewest" /> | 44 | <Content Include="TestData\DefaultDir\DefaultDir.wxs" CopyToOutputDirectory="PreserveNewest" /> |
39 | <Content Include="TestData\DialogsInInstallUISequence\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" /> | 45 | <Content Include="TestData\DialogsInInstallUISequence\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" /> |
40 | <Content Include="TestData\EnsureTable\EnsureTable.wxs" CopyToOutputDirectory="PreserveNewest" /> | 46 | <Content Include="TestData\EnsureTable\EnsureTable.wxs" CopyToOutputDirectory="PreserveNewest" /> |