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