main
cs 809 lines 39.3 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.Bundles
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.Text;
12 using System.Xml;
13 using WixToolset.Data;
14 using WixToolset.Data.Burn;
15 using WixToolset.Data.Symbols;
16
17 internal class CreateBurnManifestCommand
18 {
19 public CreateBurnManifestCommand(string executableName, IntermediateSection section, WixBundleSymbol bundleSymbol, WixBootstrapperApplicationSymbol primaryBundleApplicationSymbol, WixBootstrapperApplicationSymbol secondaryBundleApplicationSymbol, IEnumerable<WixBundleContainerSymbol> containers, WixChainSymbol chainSymbol, PackageFacades packageFacades, IEnumerable<WixBundleRollbackBoundarySymbol> boundaries, IEnumerable<WixBundlePayloadSymbol> uxPayloads, Dictionary<string, WixBundlePayloadSymbol> allPayloadsById, Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> packagesPayloads, IEnumerable<ISearchFacade> orderedSearches, string intermediateFolder)
20 {
21 this.ExecutableName = executableName;
22 this.Section = section;
23 this.BundleSymbol = bundleSymbol;
24 this.PrimaryBundleApplicationSymbol = primaryBundleApplicationSymbol;
25 this.SecondaryBundleApplicationSymbol = secondaryBundleApplicationSymbol;
26 this.Chain = chainSymbol;
27 this.Containers = containers;
28 this.PackageFacades = packageFacades;
29 this.RollbackBoundaries = boundaries;
30 this.UXContainerPayloads = uxPayloads;
31 this.Payloads = allPayloadsById;
32 this.PackagesPayloads = packagesPayloads;
33 this.OrderedSearches = orderedSearches;
34 this.IntermediateFolder = intermediateFolder;
35 }
36
37 public string OutputPath { get; private set; }
38
39 private string ExecutableName { get; }
40
41 private IntermediateSection Section { get; }
42
43 private WixBundleSymbol BundleSymbol { get; }
44
45 private WixBootstrapperApplicationSymbol PrimaryBundleApplicationSymbol { get; }
46
47 private WixBootstrapperApplicationSymbol SecondaryBundleApplicationSymbol { get; }
48
49 private WixChainSymbol Chain { get; }
50
51 private IEnumerable<WixBundleRollbackBoundarySymbol> RollbackBoundaries { get; }
52
53 private PackageFacades PackageFacades { get; }
54
55 private IEnumerable<ISearchFacade> OrderedSearches { get; }
56
57 private Dictionary<string, WixBundlePayloadSymbol> Payloads { get; }
58
59 private Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> PackagesPayloads { get; }
60
61 private IEnumerable<WixBundleContainerSymbol> Containers { get; }
62
63 private IEnumerable<WixBundlePayloadSymbol> UXContainerPayloads { get; }
64
65 private string IntermediateFolder { get; }
66
67 public void Execute()
68 {
69 this.OutputPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml");
70
71 using (var writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8))
72 {
73 writer.WriteStartDocument();
74
75 writer.WriteStartElement("BurnManifest", BurnCommon.BurnNamespace);
76
77 // Write attributes to support harvesting bundles.
78 writer.WriteAttributeString("EngineVersion", $"{SomeVerInfo.Major}.{SomeVerInfo.Minor}.{SomeVerInfo.Patch}.{SomeVerInfo.Commits}");
79 writer.WriteAttributeString("ProtocolVersion", BurnCommon.BURN_PROTOCOL_VERSION.ToString());
80 writer.WriteAttributeString("Win64", this.BundleSymbol.Platform == Platform.X86 ? "no" : "yes");
81
82 // Write the condition, if there is one
83 if (null != this.BundleSymbol.Condition)
84 {
85 writer.WriteElementString("Condition", this.BundleSymbol.Condition);
86 }
87
88 // Write the log element if default logging wasn't disabled.
89 if (!String.IsNullOrEmpty(this.BundleSymbol.LogPrefix))
90 {
91 writer.WriteStartElement("Log");
92 if (!String.IsNullOrEmpty(this.BundleSymbol.LogPathVariable))
93 {
94 writer.WriteAttributeString("PathVariable", this.BundleSymbol.LogPathVariable);
95 }
96 writer.WriteAttributeString("Prefix", this.BundleSymbol.LogPrefix);
97 writer.WriteAttributeString("Extension", this.BundleSymbol.LogExtension);
98 writer.WriteEndElement();
99 }
100
101
102 // Get update if specified.
103 var updateSymbol = this.Section.Symbols.OfType<WixBundleUpdateSymbol>().FirstOrDefault();
104
105 if (null != updateSymbol)
106 {
107 writer.WriteStartElement("Update");
108 writer.WriteAttributeString("Location", updateSymbol.Location);
109 writer.WriteEndElement(); // </Update>
110 }
111
112 // Write the RelatedBundle elements
113
114 // For the related bundles with duplicated identifiers the second instance is ignored (i.e. the Duplicates
115 // enumeration in the index row list is not used).
116 var relatedBundles = this.Section.Symbols.OfType<WixRelatedBundleSymbol>();
117 var distinctRelatedBundles = new HashSet<string>();
118
119 foreach (var relatedBundle in relatedBundles)
120 {
121 if (distinctRelatedBundles.Add(relatedBundle.BundleId))
122 {
123 writer.WriteStartElement("RelatedBundle");
124 writer.WriteAttributeString("Id", relatedBundle.BundleId);
125 writer.WriteAttributeString("Action", relatedBundle.Action.ToString());
126 writer.WriteEndElement();
127 }
128 }
129
130 // Write the variables
131 var variables = this.Section.Symbols.OfType<WixBundleVariableSymbol>();
132
133 foreach (var variable in variables)
134 {
135 writer.WriteStartElement("Variable");
136 writer.WriteAttributeString("Id", variable.Id.Id);
137 if (variable.Type != WixBundleVariableType.Unknown)
138 {
139 writer.WriteAttributeString("Value", variable.Value);
140
141 switch (variable.Type)
142 {
143 case WixBundleVariableType.Formatted:
144 writer.WriteAttributeString("Type", "formatted");
145 break;
146 case WixBundleVariableType.Numeric:
147 writer.WriteAttributeString("Type", "numeric");
148 break;
149 case WixBundleVariableType.String:
150 writer.WriteAttributeString("Type", "string");
151 break;
152 case WixBundleVariableType.Version:
153 writer.WriteAttributeString("Type", "version");
154 break;
155 }
156 }
157 writer.WriteAttributeString("Hidden", variable.Hidden ? "yes" : "no");
158 writer.WriteAttributeString("Persisted", variable.Persisted ? "yes" : "no");
159 writer.WriteEndElement();
160 }
161
162 // Write the searches
163 foreach (var searchinfo in this.OrderedSearches)
164 {
165 searchinfo.WriteXml(writer);
166 }
167
168 // write the UX element
169 writer.WriteStartElement("UX");
170
171 writer.WriteAttributeString("PrimaryPayloadId", this.PrimaryBundleApplicationSymbol.ExePayloadRef);
172
173 if (!String.IsNullOrEmpty(this.SecondaryBundleApplicationSymbol?.ExePayloadRef))
174 {
175 writer.WriteAttributeString("SecondaryPayloadId", this.SecondaryBundleApplicationSymbol.ExePayloadRef);
176 }
177
178 // write the UX allPayloads...
179 foreach (var payload in this.UXContainerPayloads)
180 {
181 this.WriteBurnManifestUXPayload(writer, payload);
182 }
183
184 writer.WriteEndElement(); // </UX>
185
186 foreach (var container in this.Containers)
187 {
188 if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id)
189 {
190 writer.WriteStartElement("Container");
191 this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container);
192 writer.WriteEndElement();
193 }
194 }
195
196 foreach (var payload in this.Payloads.Values.Where(p => p.ContainerRef != BurnConstants.BurnUXContainerName))
197 {
198 this.WriteBurnManifestPayload(writer, payload);
199 }
200
201 foreach (var rollbackBoundary in this.RollbackBoundaries)
202 {
203 writer.WriteStartElement("RollbackBoundary");
204 writer.WriteAttributeString("Id", rollbackBoundary.Id.Id);
205 writer.WriteAttributeString("Vital", rollbackBoundary.Vital ? "yes" : "no");
206 writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction ? "yes" : "no");
207
208 if (!String.IsNullOrEmpty(rollbackBoundary.LogPathVariable))
209 {
210 writer.WriteAttributeString("LogPathVariable", rollbackBoundary.LogPathVariable);
211 }
212
213 writer.WriteEndElement();
214 }
215
216 // Write the registration information...
217 writer.WriteStartElement("Registration");
218
219 writer.WriteAttributeString("Id", this.BundleSymbol.BundleId);
220 writer.WriteAttributeString("ExecutableName", this.ExecutableName);
221 writer.WriteAttributeString("PerMachine", this.BundleSymbol.PerMachine ? "yes" : "no");
222 writer.WriteAttributeString("Tag", this.BundleSymbol.Tag);
223 writer.WriteAttributeString("Version", this.BundleSymbol.Version);
224 writer.WriteAttributeString("ProviderKey", this.BundleSymbol.ProviderKey);
225
226 writer.WriteStartElement("Arp");
227 writer.WriteAttributeString("DisplayName", this.BundleSymbol.Name);
228 writer.WriteAttributeString("DisplayVersion", this.BundleSymbol.Version);
229
230 if (!String.IsNullOrEmpty(this.BundleSymbol.InProgressName))
231 {
232 writer.WriteAttributeString("InProgressDisplayName", this.BundleSymbol.InProgressName);
233 }
234
235 if (!String.IsNullOrEmpty(this.BundleSymbol.Manufacturer))
236 {
237 writer.WriteAttributeString("Publisher", this.BundleSymbol.Manufacturer);
238 }
239
240 if (!String.IsNullOrEmpty(this.BundleSymbol.HelpUrl))
241 {
242 writer.WriteAttributeString("HelpLink", this.BundleSymbol.HelpUrl);
243 }
244
245 if (!String.IsNullOrEmpty(this.BundleSymbol.HelpTelephone))
246 {
247 writer.WriteAttributeString("HelpTelephone", this.BundleSymbol.HelpTelephone);
248 }
249
250 if (!String.IsNullOrEmpty(this.BundleSymbol.AboutUrl))
251 {
252 writer.WriteAttributeString("AboutUrl", this.BundleSymbol.AboutUrl);
253 }
254
255 if (!String.IsNullOrEmpty(this.BundleSymbol.UpdateUrl))
256 {
257 writer.WriteAttributeString("UpdateUrl", this.BundleSymbol.UpdateUrl);
258 }
259
260 if (!String.IsNullOrEmpty(this.BundleSymbol.ParentName))
261 {
262 writer.WriteAttributeString("ParentDisplayName", this.BundleSymbol.ParentName);
263 }
264
265 switch (this.BundleSymbol.DisableModify)
266 {
267 case WixBundleModifyType.Disabled:
268 writer.WriteAttributeString("DisableModify", "yes");
269 break;
270 case WixBundleModifyType.SingleChangeUninstallButton:
271 writer.WriteAttributeString("DisableModify", "button");
272 break;
273 }
274
275 if (this.BundleSymbol.DisableRemove)
276 {
277 writer.WriteAttributeString("DisableRemove", "yes");
278 }
279
280 writer.WriteEndElement(); // </Arp>
281
282 // Get update registration if specified.
283 var updateRegistrationInfo = this.Section.Symbols.OfType<WixUpdateRegistrationSymbol>().FirstOrDefault();
284
285 if (null != updateRegistrationInfo)
286 {
287 writer.WriteStartElement("Update"); // <Update>
288 writer.WriteAttributeString("Manufacturer", updateRegistrationInfo.Manufacturer);
289
290 if (!String.IsNullOrEmpty(updateRegistrationInfo.Department))
291 {
292 writer.WriteAttributeString("Department", updateRegistrationInfo.Department);
293 }
294
295 if (!String.IsNullOrEmpty(updateRegistrationInfo.ProductFamily))
296 {
297 writer.WriteAttributeString("ProductFamily", updateRegistrationInfo.ProductFamily);
298 }
299
300 writer.WriteAttributeString("Name", updateRegistrationInfo.Name);
301 writer.WriteAttributeString("Classification", updateRegistrationInfo.Classification);
302 writer.WriteEndElement(); // </Update>
303 }
304
305 foreach (var bundleTagSymbol in this.Section.Symbols.OfType<WixBundleTagSymbol>())
306 {
307 writer.WriteStartElement("SoftwareTag");
308 writer.WriteAttributeString("Filename", bundleTagSymbol.Filename);
309 writer.WriteAttributeString("Regid", bundleTagSymbol.Regid);
310 writer.WriteAttributeString("Path", bundleTagSymbol.InstallPath);
311 writer.WriteCData(bundleTagSymbol.Xml);
312 writer.WriteEndElement();
313 }
314
315 writer.WriteEndElement(); // </Register>
316
317 // write the Chain...
318 writer.WriteStartElement("Chain");
319 if (this.Chain.DisableRollback)
320 {
321 writer.WriteAttributeString("DisableRollback", "yes");
322 }
323
324 if (this.Chain.DisableSystemRestore)
325 {
326 writer.WriteAttributeString("DisableSystemRestore", "yes");
327 }
328
329 if (this.Chain.ParallelCache)
330 {
331 writer.WriteAttributeString("ParallelCache", "yes");
332 }
333
334 // Index a few tables by package.
335 var targetCodesByPackagePayload = this.Section.Symbols.OfType<WixBundlePatchTargetCodeSymbol>().ToLookup(r => r.PackagePayloadRef);
336 var msiFeaturesByPackagePayload = this.Section.Symbols.OfType<WixBundleMsiFeatureSymbol>().ToLookup(r => r.PackagePayloadRef);
337 var msiPropertiesByPackage = this.Section.Symbols.OfType<WixBundleMsiPropertySymbol>().ToLookup(r => r.PackageRef);
338 var relatedBundlesByPackagePayload = this.Section.Symbols.OfType<WixBundlePackageRelatedBundleSymbol>().ToLookup(r => r.PackagePayloadRef);
339 var relatedPackagesByPackagePayload = this.Section.Symbols.OfType<WixBundleRelatedPackageSymbol>().ToLookup(r => r.PackagePayloadRef);
340 var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.TargetPackageRef);
341 var exitCodesByPackage = this.Section.Symbols.OfType<WixBundlePackageExitCodeSymbol>().ToLookup(r => r.ChainPackageId);
342 var commandLinesByPackage = this.Section.Symbols.OfType<WixBundlePackageCommandLineSymbol>().ToLookup(r => r.WixBundlePackageRef);
343
344 var dependenciesByPackage = this.Section.Symbols.OfType<WixDependencyProviderSymbol>().ToLookup(p => p.ParentRef);
345
346
347 // Build up the list of target codes from all the MSPs in the chain.
348 var targetCodes = new List<WixBundlePatchTargetCodeSymbol>();
349
350 foreach (var package in this.PackageFacades.OrderedValues)
351 {
352 var packagePayloadId = package.PackageSymbol.PayloadRef;
353
354 writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.PackageSymbol.Type));
355
356 writer.WriteAttributeString("Id", package.PackageId);
357
358 switch (package.PackageSymbol.Cache)
359 {
360 case BundleCacheType.Remove:
361 writer.WriteAttributeString("Cache", "remove");
362 break;
363 case BundleCacheType.Keep:
364 writer.WriteAttributeString("Cache", "keep");
365 break;
366 case BundleCacheType.Force:
367 writer.WriteAttributeString("Cache", "force");
368 break;
369 }
370
371 writer.WriteAttributeString("CacheId", package.PackageSymbol.CacheId);
372 writer.WriteAttributeString("InstallSize", Convert.ToString(package.PackageSymbol.InstallSize));
373 writer.WriteAttributeString("Size", Convert.ToString(package.PackageSymbol.Size));
374 writer.WriteAttributeString("PerMachine", package.PackageSymbol.PerMachine.HasValue && package.PackageSymbol.PerMachine.Value ? "yes" : "no");
375 writer.WriteAttributeString("Permanent", package.PackageSymbol.Permanent ? "yes" : "no");
376 writer.WriteAttributeString("Vital", package.PackageSymbol.Vital ? "yes" : "no");
377
378 if (null != package.PackageSymbol.RollbackBoundaryRef)
379 {
380 writer.WriteAttributeString("RollbackBoundaryForward", package.PackageSymbol.RollbackBoundaryRef);
381 }
382
383 if (!String.IsNullOrEmpty(package.PackageSymbol.RollbackBoundaryBackwardRef))
384 {
385 writer.WriteAttributeString("RollbackBoundaryBackward", package.PackageSymbol.RollbackBoundaryBackwardRef);
386 }
387
388 if (!String.IsNullOrEmpty(package.PackageSymbol.LogPathVariable))
389 {
390 writer.WriteAttributeString("LogPathVariable", package.PackageSymbol.LogPathVariable);
391 }
392
393 if (!String.IsNullOrEmpty(package.PackageSymbol.RollbackLogPathVariable))
394 {
395 writer.WriteAttributeString("RollbackLogPathVariable", package.PackageSymbol.RollbackLogPathVariable);
396 }
397
398 if (!String.IsNullOrEmpty(package.PackageSymbol.InstallCondition))
399 {
400 writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition);
401 }
402
403 if (!String.IsNullOrEmpty(package.PackageSymbol.RepairCondition))
404 {
405 writer.WriteAttributeString("RepairCondition", package.PackageSymbol.RepairCondition);
406 }
407
408 if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE
409 {
410 writer.WriteAttributeString("BundleId", bundlePackage.BundleId);
411 writer.WriteAttributeString("Version", bundlePackage.Version);
412 writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand);
413 writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand);
414 writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand);
415 writer.WriteAttributeString("SupportsBurnProtocol", bundlePackage.SupportsBurnProtocol ? "yes" : "no");
416 writer.WriteAttributeString("Win64", package.PackageSymbol.Win64 ? "yes" : "no");
417
418 if (!package.PackageSymbol.Visible)
419 {
420 writer.WriteAttributeString("HideARP", "yes");
421 }
422 }
423 else if (package.SpecificPackageSymbol is WixBundleExePackageSymbol exePackage) // EXE
424 {
425 writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand);
426 writer.WriteAttributeString("RepairArguments", exePackage.RepairCommand);
427 writer.WriteAttributeString("Repairable", exePackage.Repairable ? "yes" : "no");
428
429 switch (exePackage.DetectionType)
430 {
431 case WixBundleExePackageDetectionType.Condition:
432 writer.WriteAttributeString("DetectionType", "condition");
433 writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition);
434
435 if (exePackage.Uninstallable)
436 {
437 writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand);
438 writer.WriteAttributeString("Uninstallable", "yes");
439 }
440 break;
441 case WixBundleExePackageDetectionType.Arp:
442 writer.WriteAttributeString("DetectionType", "arp");
443 writer.WriteAttributeString("ArpId", exePackage.ArpId);
444 writer.WriteAttributeString("ArpDisplayVersion", exePackage.ArpDisplayVersion);
445
446 if (exePackage.ArpWin64)
447 {
448 writer.WriteAttributeString("ArpWin64", "yes");
449 }
450
451 if (exePackage.ArpUseUninstallString)
452 {
453 writer.WriteAttributeString("ArpUseUninstallString", "yes");
454 }
455
456 if (!String.IsNullOrEmpty(exePackage.UninstallCommand))
457 {
458 writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand);
459 }
460 break;
461 case WixBundleExePackageDetectionType.None:
462 writer.WriteAttributeString("DetectionType", "none");
463 break;
464 }
465
466 if (!String.IsNullOrEmpty(exePackage.ExeProtocol))
467 {
468 writer.WriteAttributeString("Protocol", exePackage.ExeProtocol);
469 }
470
471 if (exePackage.IsBundle)
472 {
473 writer.WriteAttributeString("Bundle", "yes");
474 }
475 }
476 else if (package.SpecificPackageSymbol is WixBundleMsiPackageSymbol msiPackage) // MSI
477 {
478 writer.WriteAttributeString("ProductCode", msiPackage.ProductCode);
479 writer.WriteAttributeString("Language", msiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture));
480 writer.WriteAttributeString("Version", msiPackage.ProductVersion);
481 if (!String.IsNullOrEmpty(msiPackage.UpgradeCode))
482 {
483 writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode);
484 }
485
486 // If feature selection is enabled, represent the Feature table in the manifest.
487 if (msiPackage.EnableFeatureSelection)
488 {
489 var packageMsiFeatures = msiFeaturesByPackagePayload[packagePayloadId];
490
491 foreach (var feature in packageMsiFeatures)
492 {
493 writer.WriteStartElement("MsiFeature");
494 writer.WriteAttributeString("Id", feature.Name);
495 writer.WriteEndElement();
496 }
497 }
498 }
499 else if (package.SpecificPackageSymbol is WixBundleMspPackageSymbol mspPackage) // MSP
500 {
501 writer.WriteAttributeString("PatchCode", mspPackage.PatchCode);
502 writer.WriteAttributeString("PatchXml", mspPackage.PatchXml);
503
504 // If there is still a chance that all of our patches will target a narrow set of
505 // product codes, add the patch list to the overall list.
506 if (null != targetCodes)
507 {
508 foreach (var patchTargetCode in targetCodesByPackagePayload[packagePayloadId])
509 {
510 if (patchTargetCode.Type == WixBundlePatchTargetCodeType.Unspecified)
511 {
512 targetCodes = null;
513 break;
514 }
515
516 targetCodes.Add(patchTargetCode);
517 }
518 }
519 }
520 else if (package.SpecificPackageSymbol is WixBundleMsuPackageSymbol msuPackage) // MSU
521 {
522 writer.WriteAttributeString("DetectCondition", msuPackage.DetectCondition);
523 }
524
525 var packageMsiProperties = msiPropertiesByPackage[package.PackageId];
526
527 foreach (var msiProperty in packageMsiProperties)
528 {
529 writer.WriteStartElement("MsiProperty");
530 writer.WriteAttributeString("Id", msiProperty.Name);
531 writer.WriteAttributeString("Value", msiProperty.Value);
532 if (!String.IsNullOrEmpty(msiProperty.Condition))
533 {
534 writer.WriteAttributeString("Condition", msiProperty.Condition);
535 }
536 writer.WriteEndElement();
537 }
538
539 var packageSlipstreamMsps = slipstreamMspsByPackage[package.PackageId];
540
541 foreach (var slipstreamMsp in packageSlipstreamMsps)
542 {
543 writer.WriteStartElement("SlipstreamMsp");
544 writer.WriteAttributeString("Id", slipstreamMsp.MspPackageRef);
545 writer.WriteEndElement();
546 }
547
548 var packageExitCodes = exitCodesByPackage[package.PackageId];
549
550 foreach (var exitCode in packageExitCodes)
551 {
552 writer.WriteStartElement("ExitCode");
553
554 if (exitCode.Code.HasValue)
555 {
556 writer.WriteAttributeString("Code", exitCode.Code.Value.ToString(CultureInfo.InvariantCulture));
557 }
558 else
559 {
560 writer.WriteAttributeString("Code", "*");
561 }
562
563 writer.WriteAttributeString("Type", ((int)exitCode.Behavior).ToString(CultureInfo.InvariantCulture));
564 writer.WriteEndElement();
565 }
566
567 var packageCommandLines = commandLinesByPackage[package.PackageId];
568
569 foreach (var commandLine in packageCommandLines)
570 {
571 writer.WriteStartElement("CommandLine");
572 writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument);
573 writer.WriteAttributeString("UninstallArgument", commandLine.UninstallArgument);
574 writer.WriteAttributeString("RepairArgument", commandLine.RepairArgument);
575 writer.WriteAttributeString("Condition", commandLine.Condition);
576 writer.WriteEndElement();
577 }
578
579 // Output the dependency information.
580 var dependencies = dependenciesByPackage[package.PackageId];
581
582 foreach (var dependency in dependencies)
583 {
584 writer.WriteStartElement("Provides");
585 writer.WriteAttributeString("Key", dependency.ProviderKey);
586
587 if (!String.IsNullOrEmpty(dependency.Version))
588 {
589 writer.WriteAttributeString("Version", dependency.Version);
590 }
591
592 if (!String.IsNullOrEmpty(dependency.DisplayName))
593 {
594 writer.WriteAttributeString("DisplayName", dependency.DisplayName);
595 }
596
597 if (dependency.Imported)
598 {
599 // The package dependency was harvested from the package.
600 writer.WriteAttributeString("Imported", "yes");
601 }
602
603 writer.WriteEndElement();
604 }
605
606 var packageRelatedBundles = relatedBundlesByPackagePayload[packagePayloadId];
607
608 foreach (var relatedBundle in packageRelatedBundles)
609 {
610 writer.WriteStartElement("RelatedBundle");
611 writer.WriteAttributeString("Id", relatedBundle.BundleId);
612 writer.WriteAttributeString("Action", relatedBundle.Action.ToString());
613 writer.WriteEndElement();
614 }
615
616 var packageRelatedPackages = relatedPackagesByPackagePayload[packagePayloadId];
617
618 foreach (var related in packageRelatedPackages)
619 {
620 writer.WriteStartElement("RelatedPackage");
621 writer.WriteAttributeString("Id", related.RelatedId);
622 if (!String.IsNullOrEmpty(related.MinVersion))
623 {
624 writer.WriteAttributeString("MinVersion", related.MinVersion);
625 writer.WriteAttributeString("MinInclusive", related.MinInclusive ? "yes" : "no");
626 }
627 if (!String.IsNullOrEmpty(related.MaxVersion))
628 {
629 writer.WriteAttributeString("MaxVersion", related.MaxVersion);
630 writer.WriteAttributeString("MaxInclusive", related.MaxInclusive ? "yes" : "no");
631 }
632 writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no");
633
634 var relatedLanguages = related.Languages?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
635
636 if (null != relatedLanguages && 0 < relatedLanguages.Length)
637 {
638 writer.WriteAttributeString("LangInclusive", related.LangInclusive ? "yes" : "no");
639 foreach (var language in relatedLanguages)
640 {
641 writer.WriteStartElement("Language");
642 writer.WriteAttributeString("Id", language);
643 writer.WriteEndElement();
644 }
645 }
646 writer.WriteEndElement();
647 }
648
649 // Write any contained Payloads with the PackagePayload being first
650 writer.WriteStartElement("PayloadRef");
651 writer.WriteAttributeString("Id", packagePayloadId);
652 writer.WriteEndElement();
653
654 var packagePayloads = this.PackagesPayloads[package.PackageId];
655
656 foreach (var payload in packagePayloads.Values)
657 {
658 if (payload.Id.Id != packagePayloadId)
659 {
660 writer.WriteStartElement("PayloadRef");
661 writer.WriteAttributeString("Id", payload.Id.Id);
662 writer.WriteEndElement();
663 }
664 }
665
666 writer.WriteEndElement(); // </XxxPackage>
667 }
668 writer.WriteEndElement(); // </Chain>
669
670 if (null != targetCodes)
671 {
672 foreach (var targetCode in targetCodes)
673 {
674 Debug.Assert(targetCode.Type == WixBundlePatchTargetCodeType.ProductCode || targetCode.Type == WixBundlePatchTargetCodeType.UpgradeCode);
675
676 writer.WriteStartElement("PatchTargetCode");
677 writer.WriteAttributeString("TargetCode", targetCode.TargetCode);
678 writer.WriteAttributeString("Product", targetCode.Type == WixBundlePatchTargetCodeType.ProductCode ? "yes" : "no");
679 writer.WriteEndElement();
680 }
681 }
682
683 // Write the ApprovedExeForElevation elements.
684 var approvedExesForElevation = this.Section.Symbols.OfType<WixApprovedExeForElevationSymbol>();
685
686 foreach (var approvedExeForElevation in approvedExesForElevation)
687 {
688 writer.WriteStartElement("ApprovedExeForElevation");
689 writer.WriteAttributeString("Id", approvedExeForElevation.Id.Id);
690 writer.WriteAttributeString("Key", approvedExeForElevation.Key);
691
692 if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName))
693 {
694 writer.WriteAttributeString("ValueName", approvedExeForElevation.ValueName);
695 }
696
697 if (approvedExeForElevation.Win64)
698 {
699 writer.WriteAttributeString("Win64", "yes");
700 }
701
702 writer.WriteEndElement();
703 }
704
705 // Write the BootstrapperExtension elements.
706 var bootstrapperExtensions = this.Section.Symbols.OfType<WixBootstrapperExtensionSymbol>();
707 var uxPayloadsById = this.UXContainerPayloads.ToDictionary(p => p.Id.Id);
708
709 foreach (var bootstrapperExtension in bootstrapperExtensions)
710 {
711 var entryPayload = uxPayloadsById[bootstrapperExtension.PayloadRef];
712
713 writer.WriteStartElement("BootstrapperExtension");
714 writer.WriteAttributeString("Id", bootstrapperExtension.Id.Id);
715 writer.WriteAttributeString("EntryPayloadSourcePath", entryPayload.EmbeddedId);
716
717 writer.WriteEndElement();
718 }
719
720 writer.WriteEndDocument(); // </BurnManifest>
721 }
722 }
723
724 private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerSymbol container)
725 {
726 writer.WriteAttributeString("Id", container.Id.Id);
727 writer.WriteAttributeString("FileSize", container.Size.Value.ToString(CultureInfo.InvariantCulture));
728 writer.WriteAttributeString("Hash", container.Hash);
729
730 if (ContainerType.Detached == container.Type)
731 {
732 if (!String.IsNullOrEmpty(container.DownloadUrl))
733 {
734 writer.WriteAttributeString("DownloadUrl", container.DownloadUrl);
735 }
736
737 writer.WriteAttributeString("FilePath", container.Name);
738 }
739 else if (ContainerType.Attached == container.Type)
740 {
741 writer.WriteAttributeString("FilePath", executableName); // attached containers use the name of the bundle since they are attached to the executable.
742 writer.WriteAttributeString("AttachedIndex", container.AttachedContainerIndex.Value.ToString(CultureInfo.InvariantCulture));
743 writer.WriteAttributeString("Attached", "yes");
744 writer.WriteAttributeString("Primary", "yes");
745 }
746 }
747
748 private void WriteBurnManifestPayload(XmlTextWriter writer, WixBundlePayloadSymbol payload)
749 {
750 writer.WriteStartElement("Payload");
751
752 writer.WriteAttributeString("Id", payload.Id.Id);
753 writer.WriteAttributeString("FilePath", payload.Name);
754 writer.WriteAttributeString("FileSize", payload.FileSize.Value.ToString(CultureInfo.InvariantCulture));
755
756 if (!String.IsNullOrEmpty(payload.CertificatePublicKey) && !String.IsNullOrEmpty(payload.CertificateThumbprint))
757 {
758 writer.WriteAttributeString("CertificateRootPublicKeyIdentifier", payload.CertificatePublicKey);
759 writer.WriteAttributeString("CertificateRootThumbprint", payload.CertificateThumbprint);
760 }
761 else
762 {
763 writer.WriteAttributeString("Hash", payload.Hash);
764 }
765
766 if (payload.LayoutOnly)
767 {
768 writer.WriteAttributeString("LayoutOnly", "yes");
769 }
770
771 if (!String.IsNullOrEmpty(payload.DownloadUrl))
772 {
773 writer.WriteAttributeString("DownloadUrl", payload.DownloadUrl);
774 }
775
776 switch (payload.Packaging)
777 {
778 case PackagingType.Embedded: // this means it's in a container.
779 Debug.Assert(BurnConstants.BurnUXContainerName != payload.ContainerRef);
780
781 writer.WriteAttributeString("Packaging", "embedded");
782 writer.WriteAttributeString("SourcePath", payload.EmbeddedId);
783 writer.WriteAttributeString("Container", payload.ContainerRef);
784 break;
785
786 case PackagingType.External:
787 writer.WriteAttributeString("Packaging", "external");
788 writer.WriteAttributeString("SourcePath", payload.Name);
789 break;
790 }
791
792 writer.WriteEndElement();
793 }
794
795 private void WriteBurnManifestUXPayload(XmlTextWriter writer, WixBundlePayloadSymbol payload)
796 {
797 Debug.Assert(PackagingType.Embedded == payload.Packaging);
798 Debug.Assert(BurnConstants.BurnUXContainerName == payload.ContainerRef);
799
800 writer.WriteStartElement("Payload");
801
802 writer.WriteAttributeString("Id", payload.Id.Id);
803 writer.WriteAttributeString("FilePath", payload.Name);
804 writer.WriteAttributeString("SourcePath", payload.EmbeddedId);
805
806 writer.WriteEndElement();
807 }
808 }
809 }