aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Bind/BindDatabaseCommand.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
committerRob Mensching <rob@firegiant.com>2017-10-14 16:12:07 -0700
commitdbde9e7104b907bbbaea17e21247d8cafc8b3a4c (patch)
tree0f5fbbb6fe12c6b2e5e622a0e18ce4c5b4eb2b96 /src/WixToolset.Core/Bind/BindDatabaseCommand.cs
parentfbf986eb97f68396797a89fc7d40dec07b775440 (diff)
downloadwix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.gz
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.bz2
wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.zip
Massive refactoring to introduce the concept of IBackend
Diffstat (limited to 'src/WixToolset.Core/Bind/BindDatabaseCommand.cs')
-rw-r--r--src/WixToolset.Core/Bind/BindDatabaseCommand.cs1311
1 files changed, 0 insertions, 1311 deletions
diff --git a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core/Bind/BindDatabaseCommand.cs
deleted file mode 100644
index 93af2e9a..00000000
--- a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs
+++ /dev/null
@@ -1,1311 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Bind
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IO;
11 using System.Linq;
12 using WixToolset.Bind.Databases;
13 using WixToolset.Data;
14 using WixToolset.Data.Rows;
15 using WixToolset.Extensibility;
16 using WixToolset.Msi;
17
18 /// <summary>
19 /// Binds a databse.
20 /// </summary>
21 internal class BindDatabaseCommand : ICommand
22 {
23 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
24 private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
25
26 public int Codepage { private get; set; }
27
28 public int CabbingThreadCount { private get; set; }
29
30 public CompressionLevel DefaultCompressionLevel { private get; set; }
31
32 public bool DeltaBinaryPatch { get; set; }
33
34 public IEnumerable<IBinderExtension> Extensions { private get; set; }
35
36 public BinderFileManagerCore FileManagerCore { private get; set; }
37
38 public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
39
40 public IEnumerable<InspectorExtension> InspectorExtensions { private get; set; }
41
42 public Localizer Localizer { private get; set; }
43
44 public string PdbFile { private get; set; }
45
46 public Output Output { private get; set; }
47
48 public string OutputPath { private get; set; }
49
50 public bool SuppressAddingValidationRows { private get; set; }
51
52 public bool SuppressLayout { private get; set; }
53
54 public TableDefinitionCollection TableDefinitions { private get; set; }
55
56 public string TempFilesLocation { private get; set; }
57
58 public Validator Validator { private get; set; }
59
60 public WixVariableResolver WixVariableResolver { private get; set; }
61
62 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
63
64 public IEnumerable<string> ContentFilePaths { get; private set; }
65
66 public void Execute()
67 {
68 List<FileTransfer> fileTransfers = new List<FileTransfer>();
69
70 HashSet<string> suppressedTableNames = new HashSet<string>();
71
72 // Localize fields, resolve wix variables, and resolve file paths.
73 ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
74
75 IEnumerable<DelayedField> delayedFields;
76 {
77 ResolveFieldsCommand command = new ResolveFieldsCommand();
78 command.Tables = this.Output.Tables;
79 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
80 command.FileManagerCore = this.FileManagerCore;
81 command.FileManagers = this.FileManagers;
82 command.SupportDelayedResolution = true;
83 command.TempFilesLocation = this.TempFilesLocation;
84 command.WixVariableResolver = this.WixVariableResolver;
85 command.Execute();
86
87 delayedFields = command.DelayedFields;
88 }
89
90 if (OutputType.Patch == this.Output.Type)
91 {
92 foreach (SubStorage transform in this.Output.SubStorages)
93 {
94 ResolveFieldsCommand command = new ResolveFieldsCommand();
95 command.Tables = transform.Data.Tables;
96 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
97 command.FileManagerCore = this.FileManagerCore;
98 command.FileManagers = this.FileManagers;
99 command.SupportDelayedResolution = false;
100 command.TempFilesLocation = this.TempFilesLocation;
101 command.WixVariableResolver = this.WixVariableResolver;
102 command.Execute();
103 }
104 }
105
106 // If there are any fields to resolve later, create the cache to populate during bind.
107 IDictionary<string, string> variableCache = null;
108 if (delayedFields.Any())
109 {
110 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
111 }
112
113 this.LocalizeUI(this.Output.Tables);
114
115 // Process the summary information table before the other tables.
116 bool compressed;
117 bool longNames;
118 int installerVersion;
119 string modularizationGuid;
120 {
121 BindSummaryInfoCommand command = new BindSummaryInfoCommand();
122 command.Output = this.Output;
123 command.Execute();
124
125 compressed = command.Compressed;
126 longNames = command.LongNames;
127 installerVersion = command.InstallerVersion;
128 modularizationGuid = command.ModularizationGuid;
129 }
130
131 // Stop processing if an error previously occurred.
132 if (Messaging.Instance.EncounteredError)
133 {
134 return;
135 }
136
137 // Modularize identifiers and add tables with real streams to the import tables.
138 if (OutputType.Module == this.Output.Type)
139 {
140 // Gather all the suppress modularization identifiers
141 HashSet<string> suppressModularizationIdentifiers = null;
142 Table wixSuppressModularizationTable = this.Output.Tables["WixSuppressModularization"];
143 if (null != wixSuppressModularizationTable)
144 {
145 suppressModularizationIdentifiers = new HashSet<string>(wixSuppressModularizationTable.Rows.Select(row => (string)row[0]));
146 }
147
148 foreach (Table table in this.Output.Tables)
149 {
150 table.Modularize(modularizationGuid, suppressModularizationIdentifiers);
151 }
152 }
153
154 // This must occur after all variables and source paths have been resolved and after modularization.
155 List<FileFacade> fileFacades;
156 {
157 GetFileFacadesCommand command = new GetFileFacadesCommand();
158 command.FileTable = this.Output.Tables["File"];
159 command.WixFileTable = this.Output.Tables["WixFile"];
160 command.WixDeltaPatchFileTable = this.Output.Tables["WixDeltaPatchFile"];
161 command.WixDeltaPatchSymbolPathsTable = this.Output.Tables["WixDeltaPatchSymbolPaths"];
162 command.Execute();
163
164 fileFacades = command.FileFacades;
165 }
166
167 ////if (OutputType.Patch == this.Output.Type)
168 ////{
169 //// foreach (SubStorage substorage in this.Output.SubStorages)
170 //// {
171 //// Output transform = substorage.Data;
172
173 //// ResolveFieldsCommand command = new ResolveFieldsCommand();
174 //// command.Tables = transform.Tables;
175 //// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
176 //// command.FileManagerCore = this.FileManagerCore;
177 //// command.FileManagers = this.FileManagers;
178 //// command.SupportDelayedResolution = false;
179 //// command.TempFilesLocation = this.TempFilesLocation;
180 //// command.WixVariableResolver = this.WixVariableResolver;
181 //// command.Execute();
182
183 //// this.MergeUnrealTables(transform.Tables);
184 //// }
185 ////}
186
187 {
188 CreateSpecialPropertiesCommand command = new CreateSpecialPropertiesCommand();
189 command.PropertyTable = this.Output.Tables["Property"];
190 command.WixPropertyTable = this.Output.Tables["WixProperty"];
191 command.Execute();
192 }
193
194 if (Messaging.Instance.EncounteredError)
195 {
196 return;
197 }
198
199 // Add binder variables for all properties.
200 Table propertyTable = this.Output.Tables["Property"];
201 if (null != propertyTable)
202 {
203 foreach (PropertyRow propertyRow in propertyTable.Rows)
204 {
205 // Set the ProductCode if it is to be generated.
206 if (OutputType.Product == this.Output.Type && "ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
207 {
208 propertyRow.Value = Common.GenerateGuid();
209
210 // Update the target ProductCode in any instance transforms.
211 foreach (SubStorage subStorage in this.Output.SubStorages)
212 {
213 Output subStorageOutput = subStorage.Data;
214 if (OutputType.Transform != subStorageOutput.Type)
215 {
216 continue;
217 }
218
219 Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
220 foreach (Row row in instanceSummaryInformationTable.Rows)
221 {
222 if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
223 {
224 row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
225 break;
226 }
227 }
228 }
229 }
230
231 // Add the property name and value to the variableCache.
232 if (null != variableCache)
233 {
234 string key = String.Concat("property.", Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property));
235 variableCache[key] = propertyRow.Value;
236 }
237 }
238 }
239
240 // Extract files that come from cabinet files (this does not extract files from merge modules).
241 {
242 ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand();
243 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
244 command.Execute();
245 }
246
247 if (OutputType.Product == this.Output.Type)
248 {
249 // Retrieve files and their information from merge modules.
250 Table wixMergeTable = this.Output.Tables["WixMerge"];
251
252 if (null != wixMergeTable)
253 {
254 ExtractMergeModuleFilesCommand command = new ExtractMergeModuleFilesCommand();
255 command.FileFacades = fileFacades;
256 command.FileTable = this.Output.Tables["File"];
257 command.WixFileTable = this.Output.Tables["WixFile"];
258 command.WixMergeTable = wixMergeTable;
259 command.OutputInstallerVersion = installerVersion;
260 command.SuppressLayout = this.SuppressLayout;
261 command.TempFilesLocation = this.TempFilesLocation;
262 command.Execute();
263
264 fileFacades.AddRange(command.MergeModulesFileFacades);
265 }
266 }
267 else if (OutputType.Patch == this.Output.Type)
268 {
269 // Merge transform data into the output object.
270 IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output);
271
272 fileFacades.AddRange(filesFromTransform);
273 }
274
275 // stop processing if an error previously occurred
276 if (Messaging.Instance.EncounteredError)
277 {
278 return;
279 }
280
281 Messaging.Instance.OnMessage(WixVerboses.UpdatingFileInformation());
282
283 // Gather information about files that did not come from merge modules (i.e. rows with a reference to the File table).
284 {
285 UpdateFileFacadesCommand command = new UpdateFileFacadesCommand();
286 command.FileFacades = fileFacades;
287 command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
288 command.ModularizationGuid = modularizationGuid;
289 command.Output = this.Output;
290 command.OverwriteHash = true;
291 command.TableDefinitions = this.TableDefinitions;
292 command.VariableCache = variableCache;
293 command.Execute();
294 }
295
296 // Set generated component guids.
297 this.SetComponentGuids(this.Output);
298
299 // With the Component Guids set now we can create instance transforms.
300 this.CreateInstanceTransforms(this.Output);
301
302 this.ValidateComponentGuids(this.Output);
303
304 this.UpdateControlText(this.Output);
305
306 if (delayedFields.Any())
307 {
308 ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand();
309 command.OutputType = this.Output.Type;
310 command.DelayedFields = delayedFields;
311 command.ModularizationGuid = null;
312 command.VariableCache = variableCache;
313 command.Execute();
314 }
315
316 // Assign files to media.
317 RowDictionary<MediaRow> assignedMediaRows;
318 Dictionary<MediaRow, IEnumerable<FileFacade>> filesByCabinetMedia;
319 IEnumerable<FileFacade> uncompressedFiles;
320 {
321 AssignMediaCommand command = new AssignMediaCommand();
322 command.FilesCompressed = compressed;
323 command.FileFacades = fileFacades;
324 command.Output = this.Output;
325 command.TableDefinitions = this.TableDefinitions;
326 command.Execute();
327
328 assignedMediaRows = command.MediaRows;
329 filesByCabinetMedia = command.FileFacadesByCabinetMedia;
330 uncompressedFiles = command.UncompressedFileFacades;
331 }
332
333 // Update file sequence.
334 this.UpdateMediaSequences(this.Output.Type, fileFacades, assignedMediaRows);
335
336 // stop processing if an error previously occurred
337 if (Messaging.Instance.EncounteredError)
338 {
339 return;
340 }
341
342 // Extended binder extensions can be called now that fields are resolved.
343 {
344 Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);
345
346 foreach (BinderExtension extension in this.Extensions)
347 {
348 extension.AfterResolvedFields(this.Output);
349 }
350
351 List<FileFacade> updatedFileFacades = new List<FileFacade>();
352
353 foreach (Row updatedFile in updatedFiles.Rows)
354 {
355 string updatedId = updatedFile.FieldAsString(0);
356
357 FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId));
358
359 updatedFileFacades.Add(updatedFacade);
360 }
361
362 if (updatedFileFacades.Any())
363 {
364 UpdateFileFacadesCommand command = new UpdateFileFacadesCommand();
365 command.FileFacades = fileFacades;
366 command.UpdateFileFacades = updatedFileFacades;
367 command.ModularizationGuid = modularizationGuid;
368 command.Output = this.Output;
369 command.OverwriteHash = true;
370 command.TableDefinitions = this.TableDefinitions;
371 command.VariableCache = variableCache;
372 command.Execute();
373 }
374 }
375
376 // stop processing if an error previously occurred
377 if (Messaging.Instance.EncounteredError)
378 {
379 return;
380 }
381
382 Directory.CreateDirectory(this.TempFilesLocation);
383
384 if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch)
385 {
386 CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand();
387 command.FileFacades = fileFacades;
388 command.WixPatchIdTable = this.Output.Tables["WixPatchId"];
389 command.TempFilesLocation = this.TempFilesLocation;
390 command.Execute();
391 }
392
393 // create cabinet files and process uncompressed files
394 string layoutDirectory = Path.GetDirectoryName(this.OutputPath);
395 if (!this.SuppressLayout || OutputType.Module == this.Output.Type)
396 {
397 Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles());
398
399 CreateCabinetsCommand command = new CreateCabinetsCommand();
400 command.CabbingThreadCount = this.CabbingThreadCount;
401 command.DefaultCompressionLevel = this.DefaultCompressionLevel;
402 command.Output = this.Output;
403 command.FileManagers = this.FileManagers;
404 command.LayoutDirectory = layoutDirectory;
405 command.Compressed = compressed;
406 command.FileRowsByCabinet = filesByCabinetMedia;
407 command.ResolveMedia = this.ResolveMedia;
408 command.TableDefinitions = this.TableDefinitions;
409 command.TempFilesLocation = this.TempFilesLocation;
410 command.WixMediaTable = this.Output.Tables["WixMedia"];
411 command.Execute();
412
413 fileTransfers.AddRange(command.FileTransfers);
414 }
415
416 if (OutputType.Patch == this.Output.Type)
417 {
418 // copy output data back into the transforms
419 this.CopyToTransformData(this.Output);
420 }
421
422 // stop processing if an error previously occurred
423 if (Messaging.Instance.EncounteredError)
424 {
425 return;
426 }
427
428 // add back suppressed tables which must be present prior to merging in modules
429 if (OutputType.Product == this.Output.Type)
430 {
431 Table wixMergeTable = this.Output.Tables["WixMerge"];
432
433 if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count)
434 {
435 foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
436 {
437 string sequenceTableName = sequence.ToString();
438 Table sequenceTable = this.Output.Tables[sequenceTableName];
439
440 if (null == sequenceTable)
441 {
442 sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]);
443 }
444
445 if (0 == sequenceTable.Rows.Count)
446 {
447 suppressedTableNames.Add(sequenceTableName);
448 }
449 }
450 }
451 }
452
453 foreach (BinderExtension extension in this.Extensions)
454 {
455 extension.Finish(this.Output);
456 }
457
458 // generate database file
459 Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase());
460 string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath));
461 this.GenerateDatabase(this.Output, tempDatabaseFile, false, false);
462
463 FileTransfer transfer;
464 if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, this.Output.Type.ToString(), null, out transfer)) // note where this database needs to move in the future
465 {
466 transfer.Built = true;
467 fileTransfers.Add(transfer);
468 }
469
470 // stop processing if an error previously occurred
471 if (Messaging.Instance.EncounteredError)
472 {
473 return;
474 }
475
476 // Output the output to a file
477 Pdb pdb = new Pdb();
478 pdb.Output = this.Output;
479 if (!String.IsNullOrEmpty(this.PdbFile))
480 {
481 pdb.Save(this.PdbFile);
482 }
483
484 // Merge modules.
485 if (OutputType.Product == this.Output.Type)
486 {
487 Messaging.Instance.OnMessage(WixVerboses.MergingModules());
488
489 MergeModulesCommand command = new MergeModulesCommand();
490 command.FileFacades = fileFacades;
491 command.Output = this.Output;
492 command.OutputPath = tempDatabaseFile;
493 command.SuppressedTableNames = suppressedTableNames;
494 command.Execute();
495
496 // stop processing if an error previously occurred
497 if (Messaging.Instance.EncounteredError)
498 {
499 return;
500 }
501 }
502
503 // inspect the MSI prior to running ICEs
504 InspectorCore inspectorCore = new InspectorCore();
505 foreach (InspectorExtension inspectorExtension in this.InspectorExtensions)
506 {
507 inspectorExtension.Core = inspectorCore;
508 inspectorExtension.InspectDatabase(tempDatabaseFile, pdb);
509
510 inspectorExtension.Core = null; // reset.
511 }
512
513 if (Messaging.Instance.EncounteredError)
514 {
515 return;
516 }
517
518 // validate the output if there is an MSI validator
519 if (null != this.Validator)
520 {
521 Stopwatch stopwatch = Stopwatch.StartNew();
522
523 // set the output file for source line information
524 this.Validator.Output = this.Output;
525
526 Messaging.Instance.OnMessage(WixVerboses.ValidatingDatabase());
527
528 this.Validator.Validate(tempDatabaseFile);
529
530 stopwatch.Stop();
531 Messaging.Instance.OnMessage(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
532
533 // Stop processing if an error occurred.
534 if (Messaging.Instance.EncounteredError)
535 {
536 return;
537 }
538 }
539
540 // Process uncompressed files.
541 if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
542 {
543 ProcessUncompressedFilesCommand command = new ProcessUncompressedFilesCommand();
544 command.Compressed = compressed;
545 command.FileFacades = uncompressedFiles;
546 command.LayoutDirectory = layoutDirectory;
547 command.LongNamesInImage = longNames;
548 command.MediaRows = assignedMediaRows;
549 command.ResolveMedia = this.ResolveMedia;
550 command.DatabasePath = tempDatabaseFile;
551 command.WixMediaTable = this.Output.Tables["WixMedia"];
552 command.Execute();
553
554 fileTransfers.AddRange(command.FileTransfers);
555 }
556
557 this.FileTransfers = fileTransfers;
558 this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source).ToList();
559 }
560
561 /// <summary>
562 /// Localize dialogs and controls.
563 /// </summary>
564 /// <param name="tables">The tables to localize.</param>
565 private void LocalizeUI(TableIndexedCollection tables)
566 {
567 Table dialogTable = tables["Dialog"];
568 if (null != dialogTable)
569 {
570 foreach (Row row in dialogTable.Rows)
571 {
572 string dialog = (string)row[0];
573 LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, null);
574 if (null != localizedControl)
575 {
576 if (CompilerConstants.IntegerNotSet != localizedControl.X)
577 {
578 row[1] = localizedControl.X;
579 }
580
581 if (CompilerConstants.IntegerNotSet != localizedControl.Y)
582 {
583 row[2] = localizedControl.Y;
584 }
585
586 if (CompilerConstants.IntegerNotSet != localizedControl.Width)
587 {
588 row[3] = localizedControl.Width;
589 }
590
591 if (CompilerConstants.IntegerNotSet != localizedControl.Height)
592 {
593 row[4] = localizedControl.Height;
594 }
595
596 row[5] = (int)row[5] | localizedControl.Attributes;
597
598 if (!String.IsNullOrEmpty(localizedControl.Text))
599 {
600 row[6] = localizedControl.Text;
601 }
602 }
603 }
604 }
605
606 Table controlTable = tables["Control"];
607 if (null != controlTable)
608 {
609 foreach (Row row in controlTable.Rows)
610 {
611 string dialog = (string)row[0];
612 string control = (string)row[1];
613 LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, control);
614 if (null != localizedControl)
615 {
616 if (CompilerConstants.IntegerNotSet != localizedControl.X)
617 {
618 row[3] = localizedControl.X.ToString();
619 }
620
621 if (CompilerConstants.IntegerNotSet != localizedControl.Y)
622 {
623 row[4] = localizedControl.Y.ToString();
624 }
625
626 if (CompilerConstants.IntegerNotSet != localizedControl.Width)
627 {
628 row[5] = localizedControl.Width.ToString();
629 }
630
631 if (CompilerConstants.IntegerNotSet != localizedControl.Height)
632 {
633 row[6] = localizedControl.Height.ToString();
634 }
635
636 row[7] = (int)row[7] | localizedControl.Attributes;
637
638 if (!String.IsNullOrEmpty(localizedControl.Text))
639 {
640 row[9] = localizedControl.Text;
641 }
642 }
643 }
644 }
645 }
646
647 /// <summary>
648 /// Copy file data between transform substorages and the patch output object
649 /// </summary>
650 /// <param name="output">The output to bind.</param>
651 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
652 private IEnumerable<FileFacade> CopyFromTransformData(Output output)
653 {
654 CopyTransformDataCommand command = new CopyTransformDataCommand();
655 command.CopyOutFileRows = true;
656 command.FileManagerCore = this.FileManagerCore;
657 command.FileManagers = this.FileManagers;
658 command.Output = output;
659 command.TableDefinitions = this.TableDefinitions;
660 command.Execute();
661
662 return command.FileFacades;
663 }
664
665 /// <summary>
666 /// Copy file data between transform substorages and the patch output object
667 /// </summary>
668 /// <param name="output">The output to bind.</param>
669 /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param>
670 private void CopyToTransformData(Output output)
671 {
672 CopyTransformDataCommand command = new CopyTransformDataCommand();
673 command.CopyOutFileRows = false;
674 command.FileManagerCore = this.FileManagerCore;
675 command.FileManagers = this.FileManagers;
676 command.Output = output;
677 command.TableDefinitions = this.TableDefinitions;
678 command.Execute();
679 }
680
681 /// <summary>
682 /// Takes an id, and demodularizes it (if possible).
683 /// </summary>
684 /// <remarks>
685 /// If the output type is a module, returns a demodularized version of an id. Otherwise, returns the id.
686 /// </remarks>
687 /// <param name="outputType">The type of the output to bind.</param>
688 /// <param name="modularizationGuid">The modularization GUID.</param>
689 /// <param name="id">The id to demodularize.</param>
690 /// <returns>The demodularized id.</returns>
691 internal static string Demodularize(OutputType outputType, string modularizationGuid, string id)
692 {
693 if (OutputType.Module == outputType && id.EndsWith(String.Concat(".", modularizationGuid), StringComparison.Ordinal))
694 {
695 id = id.Substring(0, id.Length - 37);
696 }
697
698 return id;
699 }
700
701 private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows)
702 {
703 // Calculate sequence numbers and media disk id layout for all file media information objects.
704 if (OutputType.Module == outputType)
705 {
706 int lastSequence = 0;
707 foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI.
708 {
709 facade.File.Sequence = ++lastSequence;
710 }
711 }
712 else
713 {
714 int lastSequence = 0;
715 MediaRow mediaRow = null;
716 Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>();
717
718 // sequence the non-patch-added files
719 foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI.
720 {
721 if (null == mediaRow)
722 {
723 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
724 if (OutputType.Patch == outputType)
725 {
726 // patch Media cannot start at zero
727 lastSequence = mediaRow.LastSequence;
728 }
729 }
730 else if (mediaRow.DiskId != facade.WixFile.DiskId)
731 {
732 mediaRow.LastSequence = lastSequence;
733 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
734 }
735
736 if (0 < facade.WixFile.PatchGroup)
737 {
738 List<FileFacade> patchGroup = patchGroups[facade.WixFile.PatchGroup];
739
740 if (null == patchGroup)
741 {
742 patchGroup = new List<FileFacade>();
743 patchGroups.Add(facade.WixFile.PatchGroup, patchGroup);
744 }
745
746 patchGroup.Add(facade);
747 }
748 else
749 {
750 facade.File.Sequence = ++lastSequence;
751 }
752 }
753
754 if (null != mediaRow)
755 {
756 mediaRow.LastSequence = lastSequence;
757 mediaRow = null;
758 }
759
760 // sequence the patch-added files
761 foreach (List<FileFacade> patchGroup in patchGroups.Values)
762 {
763 foreach (FileFacade facade in patchGroup)
764 {
765 if (null == mediaRow)
766 {
767 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
768 }
769 else if (mediaRow.DiskId != facade.WixFile.DiskId)
770 {
771 mediaRow.LastSequence = lastSequence;
772 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
773 }
774
775 facade.File.Sequence = ++lastSequence;
776 }
777 }
778
779 if (null != mediaRow)
780 {
781 mediaRow.LastSequence = lastSequence;
782 }
783 }
784 }
785
786 /// <summary>
787 /// Set the guids for components with generatable guids.
788 /// </summary>
789 /// <param name="output">Internal representation of the database to operate on.</param>
790 private void SetComponentGuids(Output output)
791 {
792 Table componentTable = output.Tables["Component"];
793 if (null != componentTable)
794 {
795 Hashtable registryKeyRows = null;
796 Hashtable directories = null;
797 Hashtable componentIdGenSeeds = null;
798 Dictionary<string, List<FileRow>> fileRows = null;
799
800 // find components with generatable guids
801 foreach (ComponentRow componentRow in componentTable.Rows)
802 {
803 // component guid will be generated
804 if ("*" == componentRow.Guid)
805 {
806 if (null == componentRow.KeyPath || componentRow.IsOdbcDataSourceKeyPath)
807 {
808 Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers));
809 }
810 else if (componentRow.IsRegistryKeyPath)
811 {
812 if (null == registryKeyRows)
813 {
814 Table registryTable = output.Tables["Registry"];
815
816 registryKeyRows = new Hashtable(registryTable.Rows.Count);
817
818 foreach (Row registryRow in registryTable.Rows)
819 {
820 registryKeyRows.Add((string)registryRow[0], registryRow);
821 }
822 }
823
824 Row foundRow = registryKeyRows[componentRow.KeyPath] as Row;
825
826 string bitness = componentRow.Is64Bit ? "64" : String.Empty;
827 if (null != foundRow)
828 {
829 string regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]);
830 componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant();
831 }
832 }
833 else // must be a File KeyPath
834 {
835 // if the directory table hasn't been loaded into an indexed hash
836 // of directory ids to target names do that now.
837 if (null == directories)
838 {
839 Table directoryTable = output.Tables["Directory"];
840
841 int numDirectoryTableRows = (null != directoryTable) ? directoryTable.Rows.Count : 0;
842
843 directories = new Hashtable(numDirectoryTableRows);
844
845 // get the target paths for all directories
846 if (null != directoryTable)
847 {
848 foreach (Row row in directoryTable.Rows)
849 {
850 // if the directory Id already exists, we will skip it here since
851 // checking for duplicate primary keys is done later when importing tables
852 // into database
853 if (directories.ContainsKey(row[0]))
854 {
855 continue;
856 }
857
858 string targetName = Installer.GetName((string)row[2], false, true);
859 directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName));
860 }
861 }
862 }
863
864 // if the component id generation seeds have not been indexed
865 // from the WixDirectory table do that now.
866 if (null == componentIdGenSeeds)
867 {
868 Table wixDirectoryTable = output.Tables["WixDirectory"];
869
870 int numWixDirectoryRows = (null != wixDirectoryTable) ? wixDirectoryTable.Rows.Count : 0;
871
872 componentIdGenSeeds = new Hashtable(numWixDirectoryRows);
873
874 // if there are any WixDirectory rows, build up the Component Guid
875 // generation seeds indexed by Directory/@Id.
876 if (null != wixDirectoryTable)
877 {
878 foreach (Row row in wixDirectoryTable.Rows)
879 {
880 componentIdGenSeeds.Add(row[0], (string)row[1]);
881 }
882 }
883 }
884
885 // if the file rows have not been indexed by File.Component yet
886 // then do that now
887 if (null == fileRows)
888 {
889 Table fileTable = output.Tables["File"];
890
891 int numFileRows = (null != fileTable) ? fileTable.Rows.Count : 0;
892
893 fileRows = new Dictionary<string, List<FileRow>>(numFileRows);
894
895 if (null != fileTable)
896 {
897 foreach (FileRow file in fileTable.Rows)
898 {
899 List<FileRow> files;
900 if (!fileRows.TryGetValue(file.Component, out files))
901 {
902 files = new List<FileRow>();
903 fileRows.Add(file.Component, files);
904 }
905
906 files.Add(file);
907 }
908 }
909 }
910
911 // validate component meets all the conditions to have a generated guid
912 List<FileRow> currentComponentFiles = fileRows[componentRow.Component];
913 int numFilesInComponent = currentComponentFiles.Count;
914 string path = null;
915
916 foreach (FileRow fileRow in currentComponentFiles)
917 {
918 if (fileRow.File == componentRow.KeyPath)
919 {
920 // calculate the key file's canonical target path
921 string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true);
922 string fileName = Installer.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture);
923 path = Path.Combine(directoryPath, fileName);
924
925 // find paths that are not canonicalized
926 if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) ||
927 path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) ||
928 path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) ||
929 path.StartsWith("TARGETDIR", StringComparison.Ordinal) ||
930 path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) ||
931 path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal))
932 {
933 Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component, path));
934 }
935
936 // if component has more than one file, the key path must be versioned
937 if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version))
938 {
939 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers));
940 }
941 }
942 else
943 {
944 // not a key path, so it must be an unversioned file if component has more than one file
945 if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version))
946 {
947 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers));
948 }
949 }
950 }
951
952 // if the rules were followed, reward with a generated guid
953 if (!Messaging.Instance.EncounteredError)
954 {
955 componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant();
956 }
957 }
958 }
959 }
960 }
961 }
962
963 /// <summary>
964 /// Creates instance transform substorages in the output.
965 /// </summary>
966 /// <param name="output">Output containing instance transform definitions.</param>
967 private void CreateInstanceTransforms(Output output)
968 {
969 // Create and add substorages for instance transforms.
970 Table wixInstanceTransformsTable = output.Tables["WixInstanceTransforms"];
971 if (null != wixInstanceTransformsTable && 0 <= wixInstanceTransformsTable.Rows.Count)
972 {
973 string targetProductCode = null;
974 string targetUpgradeCode = null;
975 string targetProductVersion = null;
976
977 Table targetSummaryInformationTable = output.Tables["_SummaryInformation"];
978 Table targetPropertyTable = output.Tables["Property"];
979
980 // Get the data from target database
981 foreach (Row propertyRow in targetPropertyTable.Rows)
982 {
983 if ("ProductCode" == (string)propertyRow[0])
984 {
985 targetProductCode = (string)propertyRow[1];
986 }
987 else if ("ProductVersion" == (string)propertyRow[0])
988 {
989 targetProductVersion = (string)propertyRow[1];
990 }
991 else if ("UpgradeCode" == (string)propertyRow[0])
992 {
993 targetUpgradeCode = (string)propertyRow[1];
994 }
995 }
996
997 // Index the Instance Component Rows.
998 Dictionary<string, ComponentRow> instanceComponentGuids = new Dictionary<string, ComponentRow>();
999 Table targetInstanceComponentTable = output.Tables["WixInstanceComponent"];
1000 if (null != targetInstanceComponentTable && 0 < targetInstanceComponentTable.Rows.Count)
1001 {
1002 foreach (Row row in targetInstanceComponentTable.Rows)
1003 {
1004 // Build up all the instances, we'll get the Components rows from the real Component table.
1005 instanceComponentGuids.Add((string)row[0], null);
1006 }
1007
1008 Table targetComponentTable = output.Tables["Component"];
1009 foreach (ComponentRow componentRow in targetComponentTable.Rows)
1010 {
1011 string component = (string)componentRow[0];
1012 if (instanceComponentGuids.ContainsKey(component))
1013 {
1014 instanceComponentGuids[component] = componentRow;
1015 }
1016 }
1017 }
1018
1019 // Generate the instance transforms
1020 foreach (Row instanceRow in wixInstanceTransformsTable.Rows)
1021 {
1022 string instanceId = (string)instanceRow[0];
1023
1024 Output instanceTransform = new Output(instanceRow.SourceLineNumbers);
1025 instanceTransform.Type = OutputType.Transform;
1026 instanceTransform.Codepage = output.Codepage;
1027
1028 Table instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
1029 string targetPlatformAndLanguage = null;
1030
1031 foreach (Row summaryInformationRow in targetSummaryInformationTable.Rows)
1032 {
1033 if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE
1034 {
1035 targetPlatformAndLanguage = (string)summaryInformationRow[1];
1036 }
1037
1038 // Copy the row's data to the transform.
1039 Row copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(null);
1040 copyOfSummaryRow[0] = summaryInformationRow[0];
1041 copyOfSummaryRow[1] = summaryInformationRow[1];
1042 }
1043
1044 // Modify the appropriate properties.
1045 Table propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]);
1046
1047 // Change the ProductCode property
1048 string productCode = (string)instanceRow[2];
1049 if ("*" == productCode)
1050 {
1051 productCode = Common.GenerateGuid();
1052 }
1053
1054 Row productCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
1055 productCodeRow.Operation = RowOperation.Modify;
1056 productCodeRow.Fields[1].Modified = true;
1057 productCodeRow[0] = "ProductCode";
1058 productCodeRow[1] = productCode;
1059
1060 // Change the instance property
1061 Row instanceIdRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
1062 instanceIdRow.Operation = RowOperation.Modify;
1063 instanceIdRow.Fields[1].Modified = true;
1064 instanceIdRow[0] = (string)instanceRow[1];
1065 instanceIdRow[1] = instanceId;
1066
1067 if (null != instanceRow[3])
1068 {
1069 // Change the ProductName property
1070 Row productNameRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
1071 productNameRow.Operation = RowOperation.Modify;
1072 productNameRow.Fields[1].Modified = true;
1073 productNameRow[0] = "ProductName";
1074 productNameRow[1] = (string)instanceRow[3];
1075 }
1076
1077 if (null != instanceRow[4])
1078 {
1079 // Change the UpgradeCode property
1080 Row upgradeCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
1081 upgradeCodeRow.Operation = RowOperation.Modify;
1082 upgradeCodeRow.Fields[1].Modified = true;
1083 upgradeCodeRow[0] = "UpgradeCode";
1084 upgradeCodeRow[1] = instanceRow[4];
1085
1086 // Change the Upgrade table
1087 Table targetUpgradeTable = output.Tables["Upgrade"];
1088 if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count)
1089 {
1090 string upgradeId = (string)instanceRow[4];
1091 Table upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]);
1092 foreach (Row row in targetUpgradeTable.Rows)
1093 {
1094 // In case they are upgrading other codes to this new product, leave the ones that don't match the
1095 // Product.UpgradeCode intact.
1096 if (targetUpgradeCode == (string)row[0])
1097 {
1098 Row upgradeRow = upgradeTable.CreateRow(null);
1099 upgradeRow.Operation = RowOperation.Add;
1100 upgradeRow.Fields[0].Modified = true;
1101 // I was hoping to be able to RowOperation.Modify, but that didn't appear to function.
1102 // upgradeRow.Fields[0].PreviousData = (string)row[0];
1103
1104 // Inserting a new Upgrade record with the updated UpgradeCode
1105 upgradeRow[0] = upgradeId;
1106 upgradeRow[1] = row[1];
1107 upgradeRow[2] = row[2];
1108 upgradeRow[3] = row[3];
1109 upgradeRow[4] = row[4];
1110 upgradeRow[5] = row[5];
1111 upgradeRow[6] = row[6];
1112
1113 // Delete the old row
1114 Row upgradeRemoveRow = upgradeTable.CreateRow(null);
1115 upgradeRemoveRow.Operation = RowOperation.Delete;
1116 upgradeRemoveRow[0] = row[0];
1117 upgradeRemoveRow[1] = row[1];
1118 upgradeRemoveRow[2] = row[2];
1119 upgradeRemoveRow[3] = row[3];
1120 upgradeRemoveRow[4] = row[4];
1121 upgradeRemoveRow[5] = row[5];
1122 upgradeRemoveRow[6] = row[6];
1123 }
1124 }
1125 }
1126 }
1127
1128 // If there are instance Components generate new GUIDs for them.
1129 if (0 < instanceComponentGuids.Count)
1130 {
1131 Table componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]);
1132 foreach (ComponentRow targetComponentRow in instanceComponentGuids.Values)
1133 {
1134 string guid = targetComponentRow.Guid;
1135 if (!String.IsNullOrEmpty(guid))
1136 {
1137 Row instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers);
1138 instanceComponentRow.Operation = RowOperation.Modify;
1139 instanceComponentRow.Fields[1].Modified = true;
1140 instanceComponentRow[0] = targetComponentRow[0];
1141 instanceComponentRow[1] = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)).ToString("B").ToUpper(CultureInfo.InvariantCulture);
1142 instanceComponentRow[2] = targetComponentRow[2];
1143 instanceComponentRow[3] = targetComponentRow[3];
1144 instanceComponentRow[4] = targetComponentRow[4];
1145 instanceComponentRow[5] = targetComponentRow[5];
1146 }
1147 }
1148 }
1149
1150 // Update the summary information
1151 Hashtable summaryRows = new Hashtable(instanceSummaryInformationTable.Rows.Count);
1152 foreach (Row row in instanceSummaryInformationTable.Rows)
1153 {
1154 summaryRows[row[0]] = row;
1155
1156 if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0])
1157 {
1158 row[1] = targetPlatformAndLanguage;
1159 }
1160 else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0])
1161 {
1162 row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode);
1163 }
1164 else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0])
1165 {
1166 row[1] = 0;
1167 }
1168 else if ((int)SummaryInformation.Transform.Security == (int)row[0])
1169 {
1170 row[1] = "4";
1171 }
1172 }
1173
1174 if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage))
1175 {
1176 Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
1177 summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage;
1178 summaryRow[1] = targetPlatformAndLanguage;
1179 }
1180 else if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags))
1181 {
1182 Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
1183 summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags;
1184 summaryRow[1] = "0";
1185 }
1186 else if (!summaryRows.Contains((int)SummaryInformation.Transform.Security))
1187 {
1188 Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
1189 summaryRow[0] = (int)SummaryInformation.Transform.Security;
1190 summaryRow[1] = "4";
1191 }
1192
1193 output.SubStorages.Add(new SubStorage(instanceId, instanceTransform));
1194 }
1195 }
1196 }
1197
1198 /// <summary>
1199 /// Validate that there are no duplicate GUIDs in the output.
1200 /// </summary>
1201 /// <remarks>
1202 /// Duplicate GUIDs without conditions are an error condition; with conditions, it's a
1203 /// warning, as the conditions might be mutually exclusive.
1204 /// </remarks>
1205 private void ValidateComponentGuids(Output output)
1206 {
1207 Table componentTable = output.Tables["Component"];
1208 if (null != componentTable)
1209 {
1210 Dictionary<string, bool> componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count);
1211
1212 foreach (ComponentRow row in componentTable.Rows)
1213 {
1214 // we don't care about unmanaged components and if there's a * GUID remaining,
1215 // there's already an error that prevented it from being replaced with a real GUID.
1216 if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid)
1217 {
1218 bool thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition);
1219 bool allComponentsHaveConditions = thisComponentHasCondition;
1220
1221 if (componentGuidConditions.ContainsKey(row.Guid))
1222 {
1223 allComponentsHaveConditions = componentGuidConditions[row.Guid] && thisComponentHasCondition;
1224
1225 if (allComponentsHaveConditions)
1226 {
1227 Messaging.Instance.OnMessage(WixWarnings.DuplicateComponentGuidsMustHaveMutuallyExclusiveConditions(row.SourceLineNumbers, row.Component, row.Guid));
1228 }
1229 else
1230 {
1231 Messaging.Instance.OnMessage(WixErrors.DuplicateComponentGuids(row.SourceLineNumbers, row.Component, row.Guid));
1232 }
1233 }
1234
1235 componentGuidConditions[row.Guid] = allComponentsHaveConditions;
1236 }
1237 }
1238 }
1239 }
1240
1241 /// <summary>
1242 /// Update Control and BBControl text by reading from files when necessary.
1243 /// </summary>
1244 /// <param name="output">Internal representation of the msi database to operate upon.</param>
1245 private void UpdateControlText(Output output)
1246 {
1247 UpdateControlTextCommand command = new UpdateControlTextCommand();
1248 command.BBControlTable = output.Tables["BBControl"];
1249 command.WixBBControlTable = output.Tables["WixBBControl"];
1250 command.ControlTable = output.Tables["Control"];
1251 command.WixControlTable = output.Tables["WixControl"];
1252 command.Execute();
1253 }
1254
1255 private string ResolveMedia(MediaRow mediaRow, string mediaLayoutDirectory, string layoutDirectory)
1256 {
1257 string layout = null;
1258
1259 foreach (IBinderFileManager fileManager in this.FileManagers)
1260 {
1261 layout = fileManager.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
1262 if (!String.IsNullOrEmpty(layout))
1263 {
1264 break;
1265 }
1266 }
1267
1268 // If no binder file manager resolved the layout, do the default behavior.
1269 if (String.IsNullOrEmpty(layout))
1270 {
1271 if (String.IsNullOrEmpty(mediaLayoutDirectory))
1272 {
1273 layout = layoutDirectory;
1274 }
1275 else if (Path.IsPathRooted(mediaLayoutDirectory))
1276 {
1277 layout = mediaLayoutDirectory;
1278 }
1279 else
1280 {
1281 layout = Path.Combine(layoutDirectory, mediaLayoutDirectory);
1282 }
1283 }
1284
1285 return layout;
1286 }
1287
1288 /// <summary>
1289 /// Creates the MSI/MSM/PCP database.
1290 /// </summary>
1291 /// <param name="output">Output to create database for.</param>
1292 /// <param name="databaseFile">The database file to create.</param>
1293 /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param>
1294 /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
1295 private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
1296 {
1297 GenerateDatabaseCommand command = new GenerateDatabaseCommand();
1298 command.Extensions = this.Extensions;
1299 command.FileManagers = this.FileManagers;
1300 command.Output = output;
1301 command.OutputPath = databaseFile;
1302 command.KeepAddedColumns = keepAddedColumns;
1303 command.UseSubDirectory = useSubdirectory;
1304 command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
1305 command.TableDefinitions = this.TableDefinitions;
1306 command.TempFilesLocation = this.TempFilesLocation;
1307 command.Codepage = this.Codepage;
1308 command.Execute();
1309 }
1310 }
1311}