main
cs 695 lines 29.2 KB
Raw
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
3 namespace 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 WixToolset.Core.Burn.Bind;
12 using WixToolset.Core.Burn.Bundles;
13 using WixToolset.Core.Burn.Interfaces;
14 using WixToolset.Data;
15 using WixToolset.Data.Burn;
16 using WixToolset.Data.Symbols;
17 using WixToolset.Extensibility;
18 using WixToolset.Extensibility.Data;
19 using WixToolset.Extensibility.Services;
20 using WixToolset.Versioning;
21
22 /// <summary>
23 /// Binds a this.bundle.
24 /// </summary>
25 internal class BindBundleCommand
26 {
27 public BindBundleCommand(IBindContext context, IEnumerable<IBurnBackendBinderExtension> backedExtensions)
28 {
29 this.ServiceProvider = context.ServiceProvider;
30
31 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
32 this.FileSystem = context.ServiceProvider.GetService<IFileSystem>();
33
34 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>();
35 this.InternalBurnBackendHelper = context.ServiceProvider.GetService<IInternalBurnBackendHelper>();
36 this.PayloadHarvester = context.ServiceProvider.GetService<IPayloadHarvester>();
37
38 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
39 this.DelayedFields = context.DelayedFields;
40 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
41 this.IntermediateFolder = context.IntermediateFolder;
42 this.Output = context.IntermediateRepresentation;
43 this.OutputPath = context.OutputPath;
44 this.OutputPdbPath = context.PdbPath;
45
46 this.BackendExtensions = backedExtensions;
47 }
48
49 private IServiceProvider ServiceProvider { get; }
50
51 private IMessaging Messaging { get; }
52
53 private IFileSystem FileSystem { get; }
54
55 private IBackendHelper BackendHelper { get; }
56
57 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; }
58
59 private IPayloadHarvester PayloadHarvester { get; }
60
61 private CompressionLevel? DefaultCompressionLevel { get; }
62
63 public IEnumerable<IDelayedField> DelayedFields { get; }
64
65 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; }
66
67 private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
68
69 private Intermediate Output { get; }
70
71 private string OutputPath { get; }
72
73 private string OutputPdbPath { get; }
74
75 private string IntermediateFolder { get; }
76
77 public IReadOnlyCollection<IFileTransfer> FileTransfers { get; private set; }
78
79 public IReadOnlyCollection<ITrackedFile> TrackedFiles { get; private set; }
80
81 public WixOutput Wixout { get; private set; }
82
83 public void Execute()
84 {
85 var section = this.Output.Sections.Single();
86
87 var fileTransfers = new List<IFileTransfer>();
88 var trackedFiles = new List<ITrackedFile>();
89
90 // First look for data we expect to find... Chain, WixGroups, etc.
91
92 // We shouldn't really get past the linker phase if there are
93 // no group items... that means that there's no UX, no Chain,
94 // *and* no Containers!
95 var chainPackageSymbols = this.GetRequiredSymbols<WixBundlePackageSymbol>();
96
97 var wixGroupSymbols = this.GetRequiredSymbols<WixGroupSymbol>();
98
99 // Ensure there is one and only one WixBundleSymbol.
100 var bundleSymbol = this.GetSingleSymbol<WixBundleSymbol>("bundle");
101
102 bundleSymbol.ProviderKey = bundleSymbol.BundleId = Guid.NewGuid().ToString("B").ToUpperInvariant();
103
104 bundleSymbol.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user.
105
106 {
107 var command = new NormalizeRelatedBundlesCommand(this.Messaging, bundleSymbol, section);
108 command.Execute();
109 }
110
111 // Find the primary bootstrapper application and optional secondary.
112 WixBootstrapperApplicationSymbol primaryBootstrapperApplicationSymbol = null;
113 WixBootstrapperApplicationSymbol secondaryBootstrapperApplicationSymbol = null;
114 {
115 var command = new GetBootstrapperApplicationSymbolsCommand(this.Messaging, section);
116 command.Execute();
117
118 primaryBootstrapperApplicationSymbol = command.Primary;
119 secondaryBootstrapperApplicationSymbol = command.Secondary;
120 }
121
122 // Ensure there is one and only one WixChainSymbol.
123 var chainSymbol = this.GetSingleSymbol<WixChainSymbol>("package chain");
124
125 if (this.Messaging.EncounteredError)
126 {
127 return;
128 }
129
130 // If there are any fields to resolve later, create the cache to populate during bind.
131 var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
132
133 IEnumerable<ISearchFacade> orderedSearches;
134 IDictionary<string, IEnumerable<IntermediateSymbol>> extensionSearchSymbolsById;
135 {
136 var orderSearchesCommand = new OrderSearchesCommand(this.Messaging, section);
137 orderSearchesCommand.Execute();
138
139 orderedSearches = orderSearchesCommand.OrderedSearchFacades;
140 extensionSearchSymbolsById = orderSearchesCommand.ExtensionSearchSymbolsByExtensionId;
141 }
142
143 // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
144 {
145 var extractedFiles = this.BackendHelper.ExtractEmbeddedFiles(this.ExpectedEmbeddedFiles);
146
147 trackedFiles.AddRange(extractedFiles);
148 }
149
150 // Get the explicit payloads.
151 var payloadSymbols = section.Symbols.OfType<WixBundlePayloadSymbol>().ToDictionary(t => t.Id.Id);
152 var packagesPayloads = RecalculatePackagesPayloads(payloadSymbols, wixGroupSymbols);
153
154 var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
155
156 // Process the explicitly authored payloads.
157 ISet<string> processedPayloads;
158 {
159 var command = new ProcessPayloadsCommand(this.InternalBurnBackendHelper, this.PayloadHarvester, payloadSymbols.Values, bundleSymbol.DefaultPackagingType, layoutDirectory);
160 command.Execute();
161
162 fileTransfers.AddRange(command.FileTransfers);
163 trackedFiles.AddRange(command.TrackedFiles);
164
165 processedPayloads = new HashSet<string>(payloadSymbols.Keys);
166 }
167
168 PackageFacades facades;
169 {
170 var command = new GetPackageFacadesCommand(this.Messaging, chainPackageSymbols, section);
171 command.Execute();
172
173 facades = command.PackageFacades;
174 }
175
176 if (this.Messaging.EncounteredError)
177 {
178 return;
179 }
180
181 // Process each package facade. Note this is likely to add payloads and other symbols so
182 // note that any indexes created above may be out of date now.
183 foreach (var facade in facades.Values)
184 {
185 switch (facade.PackageSymbol.Type)
186 {
187 case WixBundlePackageType.Bundle:
188 {
189 var command = new ProcessBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder);
190 command.Execute();
191
192 trackedFiles.AddRange(command.TrackedFiles);
193 }
194 break;
195
196 case WixBundlePackageType.Exe:
197 {
198 var command = new ProcessExePackageCommand(this.Messaging, facade, payloadSymbols);
199 command.Execute();
200 }
201 break;
202
203 case WixBundlePackageType.Msi:
204 {
205 var command = new ProcessMsiPackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, packagesPayloads[facade.PackageId]);
206 command.Execute();
207 }
208 break;
209
210 case WixBundlePackageType.Msp:
211 {
212 var command = new ProcessMspPackageCommand(this.Messaging, section, facade, payloadSymbols);
213 command.Execute();
214 }
215 break;
216
217 case WixBundlePackageType.Msu:
218 {
219 var command = new ProcessMsuPackageCommand(this.Messaging, facade, payloadSymbols);
220 command.Execute();
221 }
222 break;
223 }
224
225 if (null != variableCache)
226 {
227 BindBundleCommand.PopulatePackageVariableCache(facade, variableCache);
228 }
229 }
230
231 if (this.Messaging.EncounteredError)
232 {
233 return;
234 }
235
236 // Resolve any delayed fields now that the variable cache is populated with package information.
237 if (this.DelayedFields.Any())
238 {
239 this.BackendHelper.ResolveDelayedFields(this.DelayedFields, variableCache);
240 }
241
242 // Now that delayed variables are resolved the bundle version must be valid so ensure
243 // it is correct.
244 this.ProcessBundleVersion(bundleSymbol);
245
246 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
247 // are present.
248 payloadSymbols = section.Symbols.OfType<WixBundlePayloadSymbol>().ToDictionary(t => t.Id.Id);
249 wixGroupSymbols = this.GetRequiredSymbols<WixGroupSymbol>();
250 packagesPayloads = RecalculatePackagesPayloads(payloadSymbols, wixGroupSymbols);
251
252 // Process the payloads that were added by processing the packages.
253 {
254 var toProcess = payloadSymbols.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList();
255
256 var command = new ProcessPayloadsCommand(this.InternalBurnBackendHelper, this.PayloadHarvester, toProcess, bundleSymbol.DefaultPackagingType, layoutDirectory);
257 command.Execute();
258
259 fileTransfers.AddRange(command.FileTransfers);
260 trackedFiles.AddRange(command.TrackedFiles);
261
262 processedPayloads = null;
263 }
264
265 // Set the package metadata from the payloads now that we have the complete payload information.
266 {
267 foreach (var facade in facades.Values)
268 {
269 // Use temporary variable to avoid excessive number of PreviousValues.
270 long packageSize = 0;
271
272 var packagePayloads = packagesPayloads[facade.PackageId];
273
274 foreach (var payload in packagePayloads.Values)
275 {
276 packageSize += payload.FileSize.Value;
277 }
278
279 facade.PackageSymbol.Size = packageSize;
280
281 if (!facade.PackageSymbol.InstallSize.HasValue)
282 {
283 facade.PackageSymbol.InstallSize = facade.PackageSymbol.Size;
284 }
285
286 var packagePayload = payloadSymbols[facade.PackageSymbol.PayloadRef];
287
288 if (String.IsNullOrEmpty(facade.PackageSymbol.Description))
289 {
290 facade.PackageSymbol.Description = packagePayload.Description;
291 }
292
293 if (String.IsNullOrEmpty(facade.PackageSymbol.DisplayName))
294 {
295 facade.PackageSymbol.DisplayName = packagePayload.DisplayName;
296 }
297 }
298 }
299
300 // Give the UX payloads their embedded IDs...
301 var uxPayloadIndex = 0;
302 {
303 foreach (var payload in payloadSymbols.Values.Where(p => BurnConstants.BurnUXContainerName == p.ContainerRef))
304 {
305 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloadIndex);
306 ++uxPayloadIndex;
307 }
308
309 if (0 == uxPayloadIndex)
310 {
311 // If we didn't get any UX payloads, it's an error!
312 throw new WixException(ErrorMessages.MissingBundleInformation("bootstrapper application"));
313 }
314
315 // Give the embedded payloads without an embedded id yet an embedded id.
316 var payloadIndex = 0;
317 foreach (var payload in payloadSymbols.Values)
318 {
319 Debug.Assert(PackagingType.Unknown != payload.Packaging);
320
321 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId))
322 {
323 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAuthoredContainerEmbeddedIdFormat, payloadIndex);
324 ++payloadIndex;
325 }
326 }
327 }
328
329 if (this.Messaging.EncounteredError)
330 {
331 return;
332 }
333
334 // Determine patches to automatically slipstream.
335 {
336 var command = new AutomaticallySlipstreamPatchesCommand(this.Messaging, section, facades);
337 command.Execute();
338 }
339
340 if (this.Messaging.EncounteredError)
341 {
342 return;
343 }
344
345 IEnumerable<WixBundleRollbackBoundarySymbol> boundaries;
346 {
347 var command = new OrderPackagesAndRollbackBoundariesCommand(this.Messaging, section, facades);
348 command.Execute();
349
350 boundaries = command.UsedRollbackBoundaries;
351 }
352
353 {
354 var command = new ProcessDependencyProvidersCommand(this.ServiceProvider, section, facades);
355 command.Execute();
356
357 if (!String.IsNullOrEmpty(command.BundleProviderKey))
358 {
359 bundleSymbol.ProviderKey = command.BundleProviderKey; // set the overridable bundle provider key.
360 }
361 }
362
363 // Update the bundle per-machine/per-user scope based on the chained packages.
364 this.ResolveBundleInstallScope(section, bundleSymbol, facades.OrderedValues);
365
366 var softwareTags = section.Symbols.OfType<WixBundleTagSymbol>().ToList();
367 if (softwareTags.Any())
368 {
369 var command = new ProcessBundleSoftwareTagsCommand(section, softwareTags);
370 command.Execute();
371 }
372
373 this.DetectDuplicateCacheIds(facades.Values);
374
375 if (this.Messaging.EncounteredError)
376 {
377 return;
378 }
379
380 // Give the extension one last hook before generating the output files.
381 foreach (var extension in this.BackendExtensions)
382 {
383 extension.SymbolsFinalized(section);
384 }
385
386 if (this.Messaging.EncounteredError)
387 {
388 return;
389 }
390
391 // Now that extensions can't change anything else, verify everything is still valid.
392 {
393 var command = new PerformBundleBackendValidationCommand(this.Messaging, this.InternalBurnBackendHelper, section, facades);
394 command.Execute();
395 }
396
397 if (this.Messaging.EncounteredError)
398 {
399 return;
400 }
401
402 // Generate data for all manifests.
403 {
404 var command = new GenerateManifestDataFromIRCommand(this.Messaging, section, this.BackendExtensions, this.InternalBurnBackendHelper, extensionSearchSymbolsById);
405 command.Execute();
406 }
407
408 if (this.Messaging.EncounteredError)
409 {
410 return;
411 }
412
413 // Generate the core-defined BA manifest tables...
414 string baManifestPath;
415 {
416 var command = new CreateBootstrapperApplicationManifestCommand(section, bundleSymbol, boundaries, facades, uxPayloadIndex, payloadSymbols, packagesPayloads, this.IntermediateFolder, this.InternalBurnBackendHelper);
417 command.Execute();
418
419 var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
420 baManifestPath = command.OutputPath;
421 payloadSymbols.Add(baManifestPayload.Id.Id, baManifestPayload);
422 ++uxPayloadIndex;
423
424 trackedFiles.Add(this.BackendHelper.TrackFile(baManifestPath, TrackedFileType.Temporary));
425 }
426
427 // Generate the bundle extension manifest...
428 string bextManifestPath;
429 {
430 var command = new CreateBootstrapperExtensionManifestCommand(section, bundleSymbol, uxPayloadIndex, this.IntermediateFolder, this.InternalBurnBackendHelper);
431 command.Execute();
432
433 var bextManifestPayload = command.BootstrapperExtensionManifestPayloadRow;
434 bextManifestPath = command.OutputPath;
435 payloadSymbols.Add(bextManifestPayload.Id.Id, bextManifestPayload);
436 ++uxPayloadIndex;
437
438 trackedFiles.Add(this.BackendHelper.TrackFile(bextManifestPath, TrackedFileType.Temporary));
439 }
440
441 var containers = section.Symbols.OfType<WixBundleContainerSymbol>().ToDictionary(t => t.Id.Id);
442 {
443 var command = new DetectPayloadCollisionsCommand(this.Messaging, containers, facades.Values, payloadSymbols, packagesPayloads);
444 command.Execute();
445 }
446
447 if (this.Messaging.EncounteredError)
448 {
449 return;
450 }
451
452 // Create all the containers except the UX container first so the manifest (that goes in the UX container)
453 // can contain all size and hash information about the non-UX containers.
454 WixBundleContainerSymbol uxContainer;
455 IEnumerable<WixBundlePayloadSymbol> uxPayloads;
456 {
457 var command = new CreateNonUXContainers(this.BackendHelper, this.Messaging, containers.Values, payloadSymbols, this.IntermediateFolder, layoutDirectory, this.DefaultCompressionLevel);
458 command.Execute();
459
460 fileTransfers.AddRange(command.FileTransfers);
461 trackedFiles.AddRange(command.TrackedFiles);
462
463 uxContainer = command.UXContainer;
464 uxPayloads = command.UXContainerPayloads;
465 }
466
467 if (this.Messaging.EncounteredError)
468 {
469 return;
470 }
471
472 // Resolve the download URLs now that we have all of the containers and payloads calculated.
473 {
474 var command = new ResolveDownloadUrlsCommand(this.Messaging, this.BackendExtensions, containers.Values, payloadSymbols);
475 command.Execute();
476 }
477
478 // Create the bundle manifest.
479 string manifestPath;
480 {
481 var executableName = Path.GetFileName(this.OutputPath);
482
483 var command = new CreateBurnManifestCommand(executableName, section, bundleSymbol, primaryBootstrapperApplicationSymbol, secondaryBootstrapperApplicationSymbol, containers.Values, chainSymbol, facades, boundaries, uxPayloads, payloadSymbols, packagesPayloads, orderedSearches, this.IntermediateFolder);
484 command.Execute();
485
486 manifestPath = command.OutputPath;
487 trackedFiles.Add(this.BackendHelper.TrackFile(manifestPath, TrackedFileType.Temporary));
488 }
489
490 // Create the UX container.
491 {
492 var command = new CreateContainerCommand(manifestPath, uxPayloads, uxContainer.WorkingPath, this.DefaultCompressionLevel);
493 command.Execute();
494
495 uxContainer.Hash = command.Hash;
496 uxContainer.Size = command.Size;
497
498 trackedFiles.Add(this.BackendHelper.TrackFile(uxContainer.WorkingPath, TrackedFileType.Temporary, uxContainer.SourceLineNumbers));
499 }
500
501 {
502 var command = new CreateBundleExeCommand(this.Messaging, this.FileSystem, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleSymbol, uxContainer, containers.Values);
503 command.Execute();
504
505 fileTransfers.Add(command.Transfer);
506 trackedFiles.Add(this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltTargetOutput));
507 }
508
509 #if TODO // does this need to come back, or do they only need to be in TrackedFiles?
510 this.ContentFilePaths = payloadSymbols.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList();
511 #endif
512 this.FileTransfers = fileTransfers;
513 this.TrackedFiles = trackedFiles;
514 this.Wixout = this.CreateWixout(trackedFiles, this.Output, manifestPath, baManifestPath, bextManifestPath);
515 }
516
517 private void ProcessBundleVersion(WixBundleSymbol bundleSymbol)
518 {
519 if (WixVersion.TryParse(bundleSymbol.Version, out var wixVersion))
520 {
521 // Trim the prefix from the version if it is there.
522 if (wixVersion.Prefix.HasValue)
523 {
524 bundleSymbol.Version = bundleSymbol.Version.Substring(1);
525 }
526 }
527 else
528 {
529 this.Messaging.Write(ErrorMessages.IllegalVersionValue(bundleSymbol.SourceLineNumbers, "Bundle", "Version", bundleSymbol.Version));
530 }
531 }
532
533 private WixOutput CreateWixout(List<ITrackedFile> trackedFiles, Intermediate intermediate, string manifestPath, string baDataPath, string bextDataPath)
534 {
535 WixOutput wixout;
536
537 if (String.IsNullOrEmpty(this.OutputPdbPath))
538 {
539 wixout = WixOutput.Create();
540 }
541 else
542 {
543 var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.BuiltPdbOutput);
544 trackedFiles.Add(trackPdb);
545
546 wixout = WixOutput.Create(trackPdb.Path);
547 }
548
549 intermediate.Save(wixout);
550
551 wixout.ImportDataStream(BurnConstants.BurnManifestWixOutputStreamName, manifestPath);
552 wixout.ImportDataStream(BurnConstants.BootstrapperApplicationDataWixOutputStreamName, baDataPath);
553 wixout.ImportDataStream(BurnConstants.BootstrapperExtensionDataWixOutputStreamName, bextDataPath);
554
555 wixout.Reopen();
556
557 return wixout;
558 }
559
560 /// <summary>
561 /// Populates the variable cache with specific package properties.
562 /// </summary>
563 /// <param name="facade">The package facade with properties to cache.</param>
564 /// <param name="variableCache">The property cache.</param>
565 private static void PopulatePackageVariableCache(PackageFacade facade, IDictionary<string, string> variableCache)
566 {
567 var package = facade.PackageSymbol;
568 var id = package.Id.Id;
569
570 variableCache.Add(String.Concat("packageDescription.", id), package.Description ?? String.Empty);
571 variableCache.Add(String.Concat("packageName.", id), package.DisplayName ?? String.Empty);
572 variableCache.Add(String.Concat("packageVersion.", id), package.Version);
573
574 if (facade.SpecificPackageSymbol is WixBundleMsiPackageSymbol msiPackage)
575 {
576 variableCache.Add(String.Concat("packageLanguage.", id), msiPackage.ProductLanguage.ToString());
577 variableCache.Add(String.Concat("packageManufacturer.", id), msiPackage.Manufacturer ?? String.Empty);
578 }
579 else
580 {
581 variableCache.Add(String.Concat("packageLanguage.", id), String.Empty);
582 variableCache.Add(String.Concat("packageManufacturer.", id), String.Empty);
583 }
584 }
585
586 private void ResolveBundleInstallScope(IntermediateSection section, WixBundleSymbol bundleSymbol, IEnumerable<PackageFacade> facades)
587 {
588 var dependencySymbolsById = section.Symbols.OfType<WixDependencyProviderSymbol>().ToDictionary(t => t.Id.Id);
589
590 foreach (var facade in facades)
591 {
592 if (bundleSymbol.PerMachine && facade.PackageSymbol.PerMachine.HasValue && !facade.PackageSymbol.PerMachine.Value)
593 {
594 this.Messaging.Write(VerboseMessages.SwitchingToPerUserPackage(facade.PackageSymbol.SourceLineNumbers, facade.PackageId));
595
596 bundleSymbol.PerMachine = false;
597 break;
598 }
599 }
600
601 foreach (var facade in facades)
602 {
603 // Update package scope from bundle scope if default.
604 if (!facade.PackageSymbol.PerMachine.HasValue)
605 {
606 facade.PackageSymbol.PerMachine = bundleSymbol.PerMachine;
607 }
608
609 // We will only register packages in the same scope as the bundle. Warn if any packages with providers
610 // are in a different scope and not permanent (permanents typically don't need a ref-count).
611 if (!bundleSymbol.PerMachine &&
612 facade.PackageSymbol.PerMachine.Value &&
613 !facade.PackageSymbol.Permanent &&
614 dependencySymbolsById.ContainsKey(facade.PackageId))
615 {
616 this.Messaging.Write(WarningMessages.NoPerMachineDependencies(facade.PackageSymbol.SourceLineNumbers, facade.PackageId));
617 }
618 }
619 }
620
621 private void DetectDuplicateCacheIds(IEnumerable<PackageFacade> facades)
622 {
623 var duplicateCacheIdDetector = new Dictionary<string, WixBundlePackageSymbol>();
624
625 foreach (var facade in facades)
626 {
627 if (duplicateCacheIdDetector.TryGetValue(facade.PackageSymbol.CacheId, out var collisionPackage))
628 {
629 this.Messaging.Write(BurnBackendErrors.DuplicateCacheIds(facade.PackageSymbol.SourceLineNumbers, facade.PackageSymbol.CacheId, facade.PackageId));
630 this.Messaging.Write(BurnBackendErrors.DuplicateCacheIds2(collisionPackage.SourceLineNumbers));
631 }
632 else
633 {
634 duplicateCacheIdDetector.Add(facade.PackageSymbol.CacheId, facade.PackageSymbol);
635 }
636 }
637 }
638
639 private IEnumerable<T> GetRequiredSymbols<T>() where T : IntermediateSymbol
640 {
641 var symbols = this.Output.Sections.Single().Symbols.OfType<T>().ToList();
642
643 if (0 == symbols.Count)
644 {
645 throw new WixException(ErrorMessages.MissingBundleInformation(typeof(T).Name));
646 }
647
648 return symbols;
649 }
650
651 private T GetSingleSymbol<T>(string elementName) where T : IntermediateSymbol
652 {
653 var symbols = this.Output.Sections.Single().Symbols.OfType<T>().ToList();
654
655 if (0 == symbols.Count)
656 {
657 throw new WixException(ErrorMessages.MissingBundleInformation(elementName));
658 }
659 else if (1 < symbols.Count)
660 {
661 // We'll show the first two source line collisions. If there are more than that, the user
662 // may have to build multiple times to find them all. This should be very rare.
663 throw new WixException(BurnBackendErrors.MultipleSingletonSymbolsFound(symbols[0].SourceLineNumbers, elementName, symbols[1].SourceLineNumbers));
664 }
665
666 return symbols[0];
667 }
668
669 private static Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> RecalculatePackagesPayloads(Dictionary<string, WixBundlePayloadSymbol> payloadSymbols, IEnumerable<WixGroupSymbol> wixGroupSymbols)
670 {
671 var packagesPayloads = new Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>>();
672
673 foreach (var groupSymbol in wixGroupSymbols)
674 {
675 if (ComplexReferenceChildType.Payload == groupSymbol.ChildType)
676 {
677 var payloadSymbol = payloadSymbols[groupSymbol.ChildId];
678
679 if (ComplexReferenceParentType.Package == groupSymbol.ParentType)
680 {
681 if (!packagesPayloads.TryGetValue(groupSymbol.ParentId, out var packagePayloadsById))
682 {
683 packagePayloadsById = new Dictionary<string, WixBundlePayloadSymbol>();
684 packagesPayloads.Add(groupSymbol.ParentId, packagePayloadsById);
685 }
686
687 packagePayloadsById.Add(payloadSymbol.Id.Id, payloadSymbol);
688 }
689 }
690 }
691
692 return packagesPayloads;
693 }
694 }
695 }