aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Bind/BindBundleCommand.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/BindBundleCommand.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/BindBundleCommand.cs')
-rw-r--r--src/WixToolset.Core/Bind/BindBundleCommand.cs905
1 files changed, 0 insertions, 905 deletions
diff --git a/src/WixToolset.Core/Bind/BindBundleCommand.cs b/src/WixToolset.Core/Bind/BindBundleCommand.cs
deleted file mode 100644
index 7ea0c830..00000000
--- a/src/WixToolset.Core/Bind/BindBundleCommand.cs
+++ /dev/null
@@ -1,905 +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.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 using System.Linq;
11 using System.Reflection;
12 using WixToolset.Bind.Bundles;
13 using WixToolset.Data;
14 using WixToolset.Data.Rows;
15 using WixToolset.Extensibility;
16
17 /// <summary>
18 /// Binds a this.bundle.
19 /// </summary>
20 internal class BindBundleCommand : ICommand
21 {
22 public CompressionLevel DefaultCompressionLevel { private get; set; }
23
24 public IEnumerable<IBinderExtension> Extensions { private get; set; }
25
26 public BinderFileManagerCore FileManagerCore { private get; set; }
27
28 public IEnumerable<IBinderFileManager> FileManagers { private get; set; }
29
30 public Output Output { private get; set; }
31
32 public string OutputPath { private get; set; }
33
34 public string PdbFile { private get; set; }
35
36 public TableDefinitionCollection TableDefinitions { private get; set; }
37
38 public string TempFilesLocation { private get; set; }
39
40 public WixVariableResolver WixVariableResolver { private get; set; }
41
42 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
43
44 public IEnumerable<string> ContentFilePaths { get; private set; }
45
46 public void Execute()
47 {
48 this.FileTransfers = Enumerable.Empty<FileTransfer>();
49 this.ContentFilePaths = Enumerable.Empty<string>();
50
51 // First look for data we expect to find... Chain, WixGroups, etc.
52
53 // We shouldn't really get past the linker phase if there are
54 // no group items... that means that there's no UX, no Chain,
55 // *and* no Containers!
56 Table chainPackageTable = this.GetRequiredTable("WixBundlePackage");
57
58 Table wixGroupTable = this.GetRequiredTable("WixGroup");
59
60 // Ensure there is one and only one row in the WixBundle table.
61 // The compiler and linker behavior should have colluded to get
62 // this behavior.
63 WixBundleRow bundleRow = (WixBundleRow)this.GetSingleRowTable("WixBundle");
64
65 bundleRow.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user.
66
67 // Ensure there is one and only one row in the WixBootstrapperApplication table.
68 // The compiler and linker behavior should have colluded to get
69 // this behavior.
70 Row baRow = this.GetSingleRowTable("WixBootstrapperApplication");
71
72 // Ensure there is one and only one row in the WixChain table.
73 // The compiler and linker behavior should have colluded to get
74 // this behavior.
75 WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain");
76
77 if (Messaging.Instance.EncounteredError)
78 {
79 return;
80 }
81
82 // Localize fields, resolve wix variables, and resolve file paths.
83 ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
84
85 IEnumerable<DelayedField> delayedFields;
86 {
87 ResolveFieldsCommand command = new ResolveFieldsCommand();
88 command.Tables = this.Output.Tables;
89 command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
90 command.FileManagerCore = this.FileManagerCore;
91 command.FileManagers = this.FileManagers;
92 command.SupportDelayedResolution = true;
93 command.TempFilesLocation = this.TempFilesLocation;
94 command.WixVariableResolver = this.WixVariableResolver;
95 command.Execute();
96
97 delayedFields = command.DelayedFields;
98 }
99
100 if (Messaging.Instance.EncounteredError)
101 {
102 return;
103 }
104
105 // If there are any fields to resolve later, create the cache to populate during bind.
106 IDictionary<string, string> variableCache = null;
107 if (delayedFields.Any())
108 {
109 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
110 }
111
112 // TODO: Although the WixSearch tables are defined in the Util extension,
113 // the Bundle Binder has to know all about them. We hope to revisit all
114 // of this in the 4.0 timeframe.
115 IEnumerable<WixSearchInfo> orderedSearches = this.OrderSearches();
116
117 // Extract files that come from cabinet files (this does not extract files from merge modules).
118 {
119 ExtractEmbeddedFilesCommand extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
120 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
121 extractEmbeddedFilesCommand.Execute();
122 }
123
124 // Get the explicit payloads.
125 RowDictionary<WixBundlePayloadRow> payloads = new RowDictionary<WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]);
126
127 // Update explicitly authored payloads with their parent package and container (as appropriate)
128 // to make it easier to gather the payloads later.
129 foreach (WixGroupRow row in wixGroupTable.RowsAs<WixGroupRow>())
130 {
131 if (ComplexReferenceChildType.Payload == row.ChildType)
132 {
133 WixBundlePayloadRow payload = payloads.Get(row.ChildId);
134
135 if (ComplexReferenceParentType.Package == row.ParentType)
136 {
137 Debug.Assert(String.IsNullOrEmpty(payload.Package));
138 payload.Package = row.ParentId;
139 }
140 else if (ComplexReferenceParentType.Container == row.ParentType)
141 {
142 Debug.Assert(String.IsNullOrEmpty(payload.Container));
143 payload.Container = row.ParentId;
144 }
145 else if (ComplexReferenceParentType.Layout == row.ParentType)
146 {
147 payload.LayoutOnly = true;
148 }
149 }
150 }
151
152 List<FileTransfer> fileTransfers = new List<FileTransfer>();
153 string layoutDirectory = Path.GetDirectoryName(this.OutputPath);
154
155 // Process the explicitly authored payloads.
156 ISet<string> processedPayloads;
157 {
158 ProcessPayloadsCommand command = new ProcessPayloadsCommand();
159 command.Payloads = payloads.Values;
160 command.DefaultPackaging = bundleRow.DefaultPackagingType;
161 command.LayoutDirectory = layoutDirectory;
162 command.Execute();
163
164 fileTransfers.AddRange(command.FileTransfers);
165
166 processedPayloads = new HashSet<string>(payloads.Keys);
167 }
168
169 IDictionary<string, PackageFacade> facades;
170 {
171 GetPackageFacadesCommand command = new GetPackageFacadesCommand();
172 command.PackageTable = chainPackageTable;
173 command.ExePackageTable = this.Output.Tables["WixBundleExePackage"];
174 command.MsiPackageTable = this.Output.Tables["WixBundleMsiPackage"];
175 command.MspPackageTable = this.Output.Tables["WixBundleMspPackage"];
176 command.MsuPackageTable = this.Output.Tables["WixBundleMsuPackage"];
177 command.Execute();
178
179 facades = command.PackageFacades;
180 }
181
182 // Process each package facade. Note this is likely to add payloads and other rows to tables so
183 // note that any indexes created above may be out of date now.
184 foreach (PackageFacade facade in facades.Values)
185 {
186 switch (facade.Package.Type)
187 {
188 case WixBundlePackageType.Exe:
189 {
190 ProcessExePackageCommand command = new ProcessExePackageCommand();
191 command.AuthoredPayloads = payloads;
192 command.Facade = facade;
193 command.Execute();
194
195 // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer);
196 }
197 break;
198
199 case WixBundlePackageType.Msi:
200 {
201 ProcessMsiPackageCommand command = new ProcessMsiPackageCommand();
202 command.AuthoredPayloads = payloads;
203 command.Facade = facade;
204 command.FileManager = this.FileManagers.First();
205 command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]);
206 command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]);
207 command.PayloadTable = this.Output.Tables["WixBundlePayload"];
208 command.RelatedPackageTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleRelatedPackage"]);
209 command.Execute();
210
211 if (null != variableCache)
212 {
213 variableCache.Add(String.Concat("packageLanguage.", facade.Package.WixChainItemId), facade.MsiPackage.ProductLanguage.ToString());
214
215 if (null != facade.MsiPackage.Manufacturer)
216 {
217 variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.MsiPackage.Manufacturer);
218 }
219 }
220
221 }
222 break;
223
224 case WixBundlePackageType.Msp:
225 {
226 ProcessMspPackageCommand command = new ProcessMspPackageCommand();
227 command.AuthoredPayloads = payloads;
228 command.Facade = facade;
229 command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
230 command.Execute();
231 }
232 break;
233
234 case WixBundlePackageType.Msu:
235 {
236 ProcessMsuPackageCommand command = new ProcessMsuPackageCommand();
237 command.Facade = facade;
238 command.Execute();
239 }
240 break;
241 }
242
243 if (null != variableCache)
244 {
245 BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache);
246 }
247 }
248
249 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
250 // are present.
251 payloads = new RowDictionary<WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]);
252
253 // Process the payloads that were added by processing the packages.
254 {
255 ProcessPayloadsCommand command = new ProcessPayloadsCommand();
256 command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList();
257 command.DefaultPackaging = bundleRow.DefaultPackagingType;
258 command.LayoutDirectory = layoutDirectory;
259 command.Execute();
260
261 fileTransfers.AddRange(command.FileTransfers);
262
263 processedPayloads = null;
264 }
265
266 // Set the package metadata from the payloads now that we have the complete payload information.
267 ILookup<string, WixBundlePayloadRow> payloadsByPackage = payloads.Values.ToLookup(p => p.Package);
268
269 {
270 foreach (PackageFacade facade in facades.Values)
271 {
272 facade.Package.Size = 0;
273
274 IEnumerable<WixBundlePayloadRow> packagePayloads = payloadsByPackage[facade.Package.WixChainItemId];
275
276 foreach (WixBundlePayloadRow payload in packagePayloads)
277 {
278 facade.Package.Size += payload.FileSize;
279 }
280
281 if (!facade.Package.InstallSize.HasValue)
282 {
283 facade.Package.InstallSize = facade.Package.Size;
284
285 }
286
287 WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload];
288
289 if (String.IsNullOrEmpty(facade.Package.Description))
290 {
291 facade.Package.Description = packagePayload.Description;
292 }
293
294 if (String.IsNullOrEmpty(facade.Package.DisplayName))
295 {
296 facade.Package.DisplayName = packagePayload.DisplayName;
297 }
298 }
299 }
300
301
302 // Give the UX payloads their embedded IDs...
303 int uxPayloadIndex = 0;
304 {
305 foreach (WixBundlePayloadRow payload in payloads.Values.Where(p => Compiler.BurnUXContainerId == p.Container))
306 {
307 // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even
308 // downloaded. The current engine requires the UX to be fully present before any downloading starts,
309 // so that rules out downloading. Also, the burn engine does not currently copy external UX payloads
310 // into the temporary UX directory correctly, so we don't allow external either.
311 if (PackagingType.Embedded != payload.Packaging)
312 {
313 Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName));
314 payload.Packaging = PackagingType.Embedded;
315 }
316
317 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloadIndex);
318 ++uxPayloadIndex;
319 }
320
321 if (0 == uxPayloadIndex)
322 {
323 // If we didn't get any UX payloads, it's an error!
324 throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication"));
325 }
326
327 // Give the embedded payloads without an embedded id yet an embedded id.
328 int payloadIndex = 0;
329 foreach (WixBundlePayloadRow payload in payloads.Values)
330 {
331 Debug.Assert(PackagingType.Unknown != payload.Packaging);
332
333 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId))
334 {
335 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAttachedContainerEmbeddedIdFormat, payloadIndex);
336 ++payloadIndex;
337 }
338 }
339 }
340
341 // Determine patches to automatically slipstream.
342 {
343 AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand();
344 command.PackageFacades = facades.Values;
345 command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]);
346 command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
347 command.Execute();
348 }
349
350 // If catalog files exist, non-embedded payloads should validate with the catalogs.
351 IEnumerable<WixBundleCatalogRow> catalogs = this.Output.Tables["WixBundleCatalog"].RowsAs<WixBundleCatalogRow>();
352
353 if (catalogs.Any())
354 {
355 VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand();
356 command.Catalogs = catalogs;
357 command.Payloads = payloads.Values;
358 command.Execute();
359 }
360
361 if (Messaging.Instance.EncounteredError)
362 {
363 return;
364 }
365
366 IEnumerable<PackageFacade> orderedFacades;
367 IEnumerable<WixBundleRollbackBoundaryRow> boundaries;
368 {
369 OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand();
370 command.Boundaries = new RowDictionary<WixBundleRollbackBoundaryRow>(this.Output.Tables["WixBundleRollbackBoundary"]);
371 command.PackageFacades = facades;
372 command.WixGroupTable = wixGroupTable;
373 command.Execute();
374
375 orderedFacades = command.OrderedPackageFacades;
376 boundaries = command.UsedRollbackBoundaries;
377 }
378
379 // Resolve any delayed fields before generating the manifest.
380 if (delayedFields.Any())
381 {
382 ResolveDelayedFieldsCommand resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand();
383 resolveDelayedFieldsCommand.OutputType = this.Output.Type;
384 resolveDelayedFieldsCommand.DelayedFields = delayedFields;
385 resolveDelayedFieldsCommand.ModularizationGuid = null;
386 resolveDelayedFieldsCommand.VariableCache = variableCache;
387 resolveDelayedFieldsCommand.Execute();
388 }
389
390 // Set the overridable bundle provider key.
391 this.SetBundleProviderKey(this.Output, bundleRow);
392
393 // Import or generate dependency providers for packages in the manifest.
394 this.ProcessDependencyProviders(this.Output, facades);
395
396 // Update the bundle per-machine/per-user scope based on the chained packages.
397 this.ResolveBundleInstallScope(bundleRow, orderedFacades);
398
399 // Generate the core-defined BA manifest tables...
400 {
401 CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand();
402 command.BundleRow = bundleRow;
403 command.ChainPackages = orderedFacades;
404 command.LastUXPayloadIndex = uxPayloadIndex;
405 command.MsiFeatures = this.Output.Tables["WixBundleMsiFeature"].RowsAs<WixBundleMsiFeatureRow>();
406 command.Output = this.Output;
407 command.Payloads = payloads;
408 command.TableDefinitions = this.TableDefinitions;
409 command.TempFilesLocation = this.TempFilesLocation;
410 command.Execute();
411
412 WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
413 payloads.Add(baManifestPayload);
414 }
415
416 foreach (BinderExtension extension in this.Extensions)
417 {
418 extension.Finish(Output);
419 }
420
421 // Create all the containers except the UX container first so the manifest (that goes in the UX container)
422 // can contain all size and hash information about the non-UX containers.
423 RowDictionary<WixBundleContainerRow> containers = new RowDictionary<WixBundleContainerRow>(this.Output.Tables["WixBundleContainer"]);
424
425 ILookup<string, WixBundlePayloadRow> payloadsByContainer = payloads.Values.ToLookup(p => p.Container);
426
427 int attachedContainerIndex = 1; // count starts at one because UX container is "0".
428
429 IEnumerable<WixBundlePayloadRow> uxContainerPayloads = Enumerable.Empty<WixBundlePayloadRow>();
430
431 foreach (WixBundleContainerRow container in containers.Values)
432 {
433 IEnumerable<WixBundlePayloadRow> containerPayloads = payloadsByContainer[container.Id];
434
435 if (!containerPayloads.Any())
436 {
437 if (container.Id != Compiler.BurnDefaultAttachedContainerId)
438 {
439 // TODO: display warning that we're ignoring container that ended up with no paylods in it.
440 }
441 }
442 else if (Compiler.BurnUXContainerId == container.Id)
443 {
444 container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name);
445 container.AttachedContainerIndex = 0;
446
447 // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first
448 // in the list since that is the Payload that Burn attempts to load.
449 List<WixBundlePayloadRow> uxPayloads = new List<WixBundlePayloadRow>();
450
451 string baPayloadId = baRow.FieldAsString(0);
452
453 foreach (WixBundlePayloadRow uxPayload in containerPayloads)
454 {
455 if (uxPayload.Id == baPayloadId)
456 {
457 uxPayloads.Insert(0, uxPayload);
458 }
459 else
460 {
461 uxPayloads.Add(uxPayload);
462 }
463 }
464
465 uxContainerPayloads = uxPayloads;
466 }
467 else
468 {
469 container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name);
470
471 // Add detached containers to the list of file transfers.
472 if (ContainerType.Detached == container.Type)
473 {
474 FileTransfer transfer;
475 if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer))
476 {
477 transfer.Built = true;
478 fileTransfers.Add(transfer);
479 }
480 }
481 else // update the attached container index.
482 {
483 Debug.Assert(ContainerType.Attached == container.Type);
484
485 container.AttachedContainerIndex = attachedContainerIndex;
486 ++attachedContainerIndex;
487 }
488
489 this.CreateContainer(container, containerPayloads, null);
490 }
491 }
492
493 // Create the bundle manifest then UX container.
494 string manifestPath = Path.Combine(this.TempFilesLocation, "bundle-manifest.xml");
495 {
496 CreateBurnManifestCommand command = new CreateBurnManifestCommand();
497 command.FileManagers = this.FileManagers;
498 command.Output = this.Output;
499
500 command.BundleInfo = bundleRow;
501 command.Chain = chainRow;
502 command.Containers = containers;
503 command.Catalogs = catalogs;
504 command.ExecutableName = Path.GetFileName(this.OutputPath);
505 command.OrderedPackages = orderedFacades;
506 command.OutputPath = manifestPath;
507 command.RollbackBoundaries = boundaries;
508 command.OrderedSearches = orderedSearches;
509 command.Payloads = payloads;
510 command.UXContainerPayloads = uxContainerPayloads;
511 command.Execute();
512 }
513
514 WixBundleContainerRow uxContainer = containers[Compiler.BurnUXContainerId];
515 this.CreateContainer(uxContainer, uxContainerPayloads, manifestPath);
516
517 // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note
518 // that today, the x64 Burn uses the x86 stub.
519 string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString();
520
521 string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe");
522 string bundleTempPath = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath));
523
524 Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile));
525
526 string bundleFilename = Path.GetFileName(this.OutputPath);
527 if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase))
528 {
529 Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename));
530 }
531
532 FileTransfer bundleTransfer;
533 if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer))
534 {
535 bundleTransfer.Built = true;
536 fileTransfers.Add(bundleTransfer);
537 }
538
539 File.Copy(stubFile, bundleTempPath, true);
540 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
541
542 this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow);
543
544 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers
545 // if they should be attached.
546 using (BurnWriter writer = BurnWriter.Open(bundleTempPath))
547 {
548 FileInfo burnStubFile = new FileInfo(bundleTempPath);
549 writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId);
550
551 // Always attach the UX container first
552 writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX);
553
554 // Now append all other attached containers
555 foreach (WixBundleContainerRow container in containers.Values)
556 {
557 if (ContainerType.Attached == container.Type)
558 {
559 // The container was only created if it had payloads.
560 if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id)
561 {
562 writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached);
563 }
564 }
565 }
566 }
567
568 if (null != this.PdbFile)
569 {
570 Pdb pdb = new Pdb();
571 pdb.Output = Output;
572 pdb.Save(this.PdbFile);
573 }
574
575 this.FileTransfers = fileTransfers;
576 this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList();
577 }
578
579 private Table GetRequiredTable(string tableName)
580 {
581 Table table = this.Output.Tables[tableName];
582 if (null == table || 0 == table.Rows.Count)
583 {
584 throw new WixException(WixErrors.MissingBundleInformation(tableName));
585 }
586
587 return table;
588 }
589
590 private Row GetSingleRowTable(string tableName)
591 {
592 Table table = this.Output.Tables[tableName];
593 if (null == table || 1 != table.Rows.Count)
594 {
595 throw new WixException(WixErrors.MissingBundleInformation(tableName));
596 }
597
598 return table.Rows[0];
599 }
600
601 private List<WixSearchInfo> OrderSearches()
602 {
603 Dictionary<string, WixSearchInfo> allSearches = new Dictionary<string, WixSearchInfo>();
604 Table wixFileSearchTable = this.Output.Tables["WixFileSearch"];
605 if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count)
606 {
607 foreach (Row row in wixFileSearchTable.Rows)
608 {
609 WixFileSearchInfo fileSearchInfo = new WixFileSearchInfo(row);
610 allSearches.Add(fileSearchInfo.Id, fileSearchInfo);
611 }
612 }
613
614 Table wixRegistrySearchTable = this.Output.Tables["WixRegistrySearch"];
615 if (null != wixRegistrySearchTable && 0 < wixRegistrySearchTable.Rows.Count)
616 {
617 foreach (Row row in wixRegistrySearchTable.Rows)
618 {
619 WixRegistrySearchInfo registrySearchInfo = new WixRegistrySearchInfo(row);
620 allSearches.Add(registrySearchInfo.Id, registrySearchInfo);
621 }
622 }
623
624 Table wixComponentSearchTable = this.Output.Tables["WixComponentSearch"];
625 if (null != wixComponentSearchTable && 0 < wixComponentSearchTable.Rows.Count)
626 {
627 foreach (Row row in wixComponentSearchTable.Rows)
628 {
629 WixComponentSearchInfo componentSearchInfo = new WixComponentSearchInfo(row);
630 allSearches.Add(componentSearchInfo.Id, componentSearchInfo);
631 }
632 }
633
634 Table wixProductSearchTable = this.Output.Tables["WixProductSearch"];
635 if (null != wixProductSearchTable && 0 < wixProductSearchTable.Rows.Count)
636 {
637 foreach (Row row in wixProductSearchTable.Rows)
638 {
639 WixProductSearchInfo productSearchInfo = new WixProductSearchInfo(row);
640 allSearches.Add(productSearchInfo.Id, productSearchInfo);
641 }
642 }
643
644 // Merge in the variable/condition info and get the canonical ordering for
645 // the searches.
646 List<WixSearchInfo> orderedSearches = new List<WixSearchInfo>();
647 Table wixSearchTable = this.Output.Tables["WixSearch"];
648 if (null != wixSearchTable && 0 < wixSearchTable.Rows.Count)
649 {
650 orderedSearches.Capacity = wixSearchTable.Rows.Count;
651 foreach (Row row in wixSearchTable.Rows)
652 {
653 WixSearchInfo searchInfo = allSearches[(string)row[0]];
654 searchInfo.AddWixSearchRowInfo(row);
655 orderedSearches.Add(searchInfo);
656 }
657 }
658
659 return orderedSearches;
660 }
661
662 /// <summary>
663 /// Populates the variable cache with specific package properties.
664 /// </summary>
665 /// <param name="package">The package with properties to cache.</param>
666 /// <param name="variableCache">The property cache.</param>
667 private static void PopulatePackageVariableCache(WixBundlePackageRow package, IDictionary<string, string> variableCache)
668 {
669 string id = package.WixChainItemId;
670
671 variableCache.Add(String.Concat("packageDescription.", id), package.Description);
672 //variableCache.Add(String.Concat("packageLanguage.", id), package.Language);
673 //variableCache.Add(String.Concat("packageManufacturer.", id), package.Manufacturer);
674 variableCache.Add(String.Concat("packageName.", id), package.DisplayName);
675 variableCache.Add(String.Concat("packageVersion.", id), package.Version);
676 }
677
678 private void CreateContainer(WixBundleContainerRow container, IEnumerable<WixBundlePayloadRow> containerPayloads, string manifestFile)
679 {
680 CreateContainerCommand command = new CreateContainerCommand();
681 command.DefaultCompressionLevel = this.DefaultCompressionLevel;
682 command.Payloads = containerPayloads;
683 command.ManifestFile = manifestFile;
684 command.OutputPath = container.WorkingPath;
685 command.Execute();
686
687 container.Hash = command.Hash;
688 container.Size = command.Size;
689 }
690
691 private void ResolveBundleInstallScope(WixBundleRow bundleInfo, IEnumerable<PackageFacade> facades)
692 {
693 foreach (PackageFacade facade in facades)
694 {
695 if (bundleInfo.PerMachine && YesNoDefaultType.No == facade.Package.PerMachine)
696 {
697 Messaging.Instance.OnMessage(WixVerboses.SwitchingToPerUserPackage(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId));
698
699 bundleInfo.PerMachine = false;
700 break;
701 }
702 }
703
704 foreach (PackageFacade facade in facades)
705 {
706 // Update package scope from bundle scope if default.
707 if (YesNoDefaultType.Default == facade.Package.PerMachine)
708 {
709 facade.Package.PerMachine = bundleInfo.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
710 }
711
712 // We will only register packages in the same scope as the bundle. Warn if any packages with providers
713 // are in a different scope and not permanent (permanents typically don't need a ref-count).
714 if (!bundleInfo.PerMachine && YesNoDefaultType.Yes == facade.Package.PerMachine && !facade.Package.Permanent && 0 < facade.Provides.Count)
715 {
716 Messaging.Instance.OnMessage(WixWarnings.NoPerMachineDependencies(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId));
717 }
718 }
719 }
720
721 private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleRow bundleInfo)
722 {
723 WixToolset.Dtf.Resources.ResourceCollection resources = new WixToolset.Dtf.Resources.ResourceCollection();
724 WixToolset.Dtf.Resources.VersionResource version = new WixToolset.Dtf.Resources.VersionResource("#1", 1033);
725
726 version.Load(bundleTempPath);
727 resources.Add(version);
728
729 // Ensure the bundle info provides a full four part version.
730 Version fourPartVersion = new Version(bundleInfo.Version);
731 int major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major;
732 int minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor;
733 int build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build;
734 int revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision;
735
736 if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision)
737 {
738 throw new WixException(WixErrors.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version));
739 }
740
741 fourPartVersion = new Version(major, minor, build, revision);
742 version.FileVersion = fourPartVersion;
743 version.ProductVersion = fourPartVersion;
744
745 WixToolset.Dtf.Resources.VersionStringTable strings = version[1033];
746 strings["LegalCopyright"] = bundleInfo.Copyright;
747 strings["OriginalFilename"] = Path.GetFileName(outputPath);
748 strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
749 strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
750
751 if (!String.IsNullOrEmpty(bundleInfo.Name))
752 {
753 strings["ProductName"] = bundleInfo.Name;
754 strings["FileDescription"] = bundleInfo.Name;
755 }
756
757 if (!String.IsNullOrEmpty(bundleInfo.Publisher))
758 {
759 strings["CompanyName"] = bundleInfo.Publisher;
760 }
761 else
762 {
763 strings["CompanyName"] = String.Empty;
764 }
765
766 if (!String.IsNullOrEmpty(bundleInfo.IconPath))
767 {
768 Dtf.Resources.GroupIconResource iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033);
769 iconGroup.ReadFromFile(bundleInfo.IconPath);
770 resources.Add(iconGroup);
771
772 foreach (Dtf.Resources.Resource icon in iconGroup.Icons)
773 {
774 resources.Add(icon);
775 }
776 }
777
778 if (!String.IsNullOrEmpty(bundleInfo.SplashScreenBitmapPath))
779 {
780 Dtf.Resources.BitmapResource bitmap = new Dtf.Resources.BitmapResource("#1", 1033);
781 bitmap.ReadFromFile(bundleInfo.SplashScreenBitmapPath);
782 resources.Add(bitmap);
783 }
784
785 resources.Save(bundleTempPath);
786 }
787
788 #region DependencyExtension
789 /// <summary>
790 /// Imports authored dependency providers for each package in the manifest,
791 /// and generates dependency providers for certain package types that do not
792 /// have a provider defined.
793 /// </summary>
794 /// <param name="bundle">The <see cref="Output"/> object for the bundle.</param>
795 /// <param name="facades">An indexed collection of chained packages.</param>
796 private void ProcessDependencyProviders(Output bundle, IDictionary<string, PackageFacade> facades)
797 {
798 // First import any authored dependencies. These may merge with imported provides from MSI packages.
799 Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"];
800 if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count)
801 {
802 // Add package information for each dependency provider authored into the manifest.
803 foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows)
804 {
805 string packageId = (string)wixDependencyProviderRow[1];
806
807 PackageFacade facade = null;
808 if (facades.TryGetValue(packageId, out facade))
809 {
810 ProvidesDependency dependency = new ProvidesDependency(wixDependencyProviderRow);
811
812 if (String.IsNullOrEmpty(dependency.Key))
813 {
814 switch (facade.Package.Type)
815 {
816 // The WixDependencyExtension allows an empty Key for MSIs and MSPs.
817 case WixBundlePackageType.Msi:
818 dependency.Key = facade.MsiPackage.ProductCode;
819 break;
820 case WixBundlePackageType.Msp:
821 dependency.Key = facade.MspPackage.PatchCode;
822 break;
823 }
824 }
825
826 if (String.IsNullOrEmpty(dependency.Version))
827 {
828 dependency.Version = facade.Package.Version;
829 }
830
831 // If the version is still missing, a version could not be harvested from the package and was not authored.
832 if (String.IsNullOrEmpty(dependency.Version))
833 {
834 Messaging.Instance.OnMessage(WixErrors.MissingDependencyVersion(facade.Package.WixChainItemId));
835 }
836
837 if (String.IsNullOrEmpty(dependency.DisplayName))
838 {
839 dependency.DisplayName = facade.Package.DisplayName;
840 }
841
842 if (!facade.Provides.Merge(dependency))
843 {
844 Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId));
845 }
846 }
847 }
848 }
849
850 // Generate providers for MSI packages that still do not have providers.
851 foreach (PackageFacade facade in facades.Values)
852 {
853 string key = null;
854
855 if (WixBundlePackageType.Msi == facade.Package.Type && 0 == facade.Provides.Count)
856 {
857 key = facade.MsiPackage.ProductCode;
858 }
859 else if (WixBundlePackageType.Msp == facade.Package.Type && 0 == facade.Provides.Count)
860 {
861 key = facade.MspPackage.PatchCode;
862 }
863
864 if (!String.IsNullOrEmpty(key))
865 {
866 ProvidesDependency dependency = new ProvidesDependency(key, facade.Package.Version, facade.Package.DisplayName, 0);
867
868 if (!facade.Provides.Merge(dependency))
869 {
870 Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId));
871 }
872 }
873 }
874 }
875
876 /// <summary>
877 /// Sets the provider key for the bundle.
878 /// </summary>
879 /// <param name="bundle">The <see cref="Output"/> object for the bundle.</param>
880 /// <param name="bundleInfo">The <see cref="BundleInfo"/> containing the provider key and other information for the bundle.</param>
881 private void SetBundleProviderKey(Output bundle, WixBundleRow bundleInfo)
882 {
883 // From DependencyCommon.cs in the WixDependencyExtension.
884 const int ProvidesAttributesBundle = 0x10000;
885
886 Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"];
887 if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count)
888 {
889 // Search the WixDependencyProvider table for the single bundle provider key.
890 foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows)
891 {
892 object attributes = wixDependencyProviderRow[5];
893 if (null != attributes && 0 != (ProvidesAttributesBundle & (int)attributes))
894 {
895 bundleInfo.ProviderKey = (string)wixDependencyProviderRow[2];
896 break;
897 }
898 }
899 }
900
901 // Defaults to the bundle ID as the provider key.
902 }
903 #endregion
904 }
905}