main
cs 331 lines 14 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.Globalization;
8 using System.IO;
9 using System.Linq;
10 using System.Text;
11 using System.Xml;
12 using WixToolset.Data;
13 using WixToolset.Data.Burn;
14 using WixToolset.Data.Symbols;
15
16 internal class CreateBootstrapperApplicationManifestCommand
17 {
18 public CreateBootstrapperApplicationManifestCommand(IntermediateSection section, WixBundleSymbol bundleSymbol, IEnumerable<WixBundleRollbackBoundarySymbol> boundaries, PackageFacades packageFacades, int lastUXPayloadIndex, Dictionary<string, WixBundlePayloadSymbol> payloadSymbols, Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> packagesPayloads, string intermediateFolder, IInternalBurnBackendHelper internalBurnBackendHelper)
19 {
20 this.Section = section;
21 this.BundleSymbol = bundleSymbol;
22 this.RollbackBoundaries = boundaries;
23 this.PackagesFacades = packageFacades;
24 this.LastUXPayloadIndex = lastUXPayloadIndex;
25 this.Payloads = payloadSymbols;
26 this.PackagesPayloads = packagesPayloads;
27 this.IntermediateFolder = intermediateFolder;
28 this.InternalBurnBackendHelper = internalBurnBackendHelper;
29 }
30
31 private IntermediateSection Section { get; }
32
33 private WixBundleSymbol BundleSymbol { get; }
34
35 private IEnumerable<WixBundleRollbackBoundarySymbol> RollbackBoundaries { get; }
36
37 private PackageFacades PackagesFacades { get; }
38
39 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; }
40
41 private int LastUXPayloadIndex { get; }
42
43 private Dictionary<string, WixBundlePayloadSymbol> Payloads { get; }
44
45 private Dictionary<string, Dictionary<string, WixBundlePayloadSymbol>> PackagesPayloads { get; }
46
47 private string IntermediateFolder { get; }
48
49 public WixBundlePayloadSymbol BootstrapperApplicationManifestPayloadRow { get; private set; }
50
51 public string OutputPath { get; private set; }
52
53 public void Execute()
54 {
55 this.OutputPath = this.CreateBootstrapperApplicationManifest();
56
57 this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(this.OutputPath);
58 }
59
60 private string CreateBootstrapperApplicationManifest()
61 {
62 var path = Path.Combine(this.IntermediateFolder, "wix-badata.xml");
63
64 Directory.CreateDirectory(Path.GetDirectoryName(path));
65
66 using (var writer = new XmlTextWriter(path, Encoding.Unicode))
67 {
68 writer.Formatting = Formatting.Indented;
69 writer.WriteStartDocument();
70 writer.WriteStartElement("BootstrapperApplicationData", BurnConstants.BootstrapperApplicationDataNamespace);
71
72 this.WriteBundleInfo(writer);
73
74 this.WriteRollbackBoundaryInfo(writer);
75
76 this.WritePackageInfo(writer);
77
78 this.WriteFeatureInfo(writer);
79
80 this.WritePayloadInfo(writer);
81
82 this.InternalBurnBackendHelper.WriteBootstrapperApplicationData(writer);
83
84 writer.WriteEndElement();
85 writer.WriteEndDocument();
86 }
87
88 return path;
89 }
90
91 private void WriteBundleInfo(XmlTextWriter writer)
92 {
93 writer.WriteStartElement("WixBundleProperties");
94
95 writer.WriteAttributeString("DisplayName", this.BundleSymbol.Name);
96 writer.WriteAttributeString("LogPathVariable", this.BundleSymbol.LogPathVariable);
97 writer.WriteAttributeString("Compressed", this.BundleSymbol.Compressed == true ? "yes" : "no");
98 writer.WriteAttributeString("Id", this.BundleSymbol.BundleId.ToUpperInvariant());
99 writer.WriteAttributeString("UpgradeCode", this.BundleSymbol.UpgradeCode);
100 writer.WriteAttributeString("PerMachine", this.BundleSymbol.PerMachine ? "yes" : "no");
101
102 writer.WriteEndElement();
103 }
104
105 private void WriteRollbackBoundaryInfo(XmlTextWriter writer)
106 {
107 foreach (var rollbackBoundary in this.RollbackBoundaries)
108 {
109 writer.WriteStartElement("WixRollbackBoundary");
110 writer.WriteAttributeString("Id", rollbackBoundary.Id.Id);
111 writer.WriteAttributeString("Vital", rollbackBoundary.Vital ? "yes" : "no");
112 writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction ? "yes" : "no");
113
114 if (!String.IsNullOrEmpty(rollbackBoundary.LogPathVariable))
115 {
116 writer.WriteAttributeString("LogPathVariable", rollbackBoundary.LogPathVariable);
117 }
118
119 writer.WriteEndElement();
120 }
121 }
122
123 private void WritePackageInfo(XmlTextWriter writer)
124 {
125 foreach (var package in this.PackagesFacades.OrderedValues)
126 {
127 if (!this.PackagesPayloads.TryGetValue(package.PackageId, out var payloads))
128 {
129 continue;
130 }
131
132 var packagePayload = payloads[package.PackageSymbol.PayloadRef];
133
134 var size = package.PackageSymbol.Size.ToString(CultureInfo.InvariantCulture);
135
136 writer.WriteStartElement("WixPackageProperties");
137
138 writer.WriteAttributeString("Package", package.PackageId);
139 writer.WriteAttributeString("Vital", package.PackageSymbol.Vital ? "yes" : "no");
140
141 if (!String.IsNullOrEmpty(package.PackageSymbol.DisplayName))
142 {
143 writer.WriteAttributeString("DisplayName", package.PackageSymbol.DisplayName);
144 }
145
146 if (!String.IsNullOrEmpty(package.PackageSymbol.Description))
147 {
148 writer.WriteAttributeString("Description", package.PackageSymbol.Description);
149 }
150
151 writer.WriteAttributeString("DownloadSize", size);
152 writer.WriteAttributeString("PackageSize", size);
153 writer.WriteAttributeString("InstalledSize", package.PackageSymbol.InstallSize?.ToString(CultureInfo.InvariantCulture) ?? size);
154 writer.WriteAttributeString("PackageType", package.PackageSymbol.Type.ToString());
155 writer.WriteAttributeString("Permanent", package.PackageSymbol.Permanent ? "yes" : "no");
156 writer.WriteAttributeString("LogPathVariable", package.PackageSymbol.LogPathVariable);
157 writer.WriteAttributeString("RollbackLogPathVariable", package.PackageSymbol.RollbackLogPathVariable);
158 writer.WriteAttributeString("Compressed", packagePayload.Packaging == PackagingType.Embedded ? "yes" : "no");
159
160 if (package.SpecificPackageSymbol is WixBundleMsiPackageSymbol msiPackage)
161 {
162 if (!String.IsNullOrEmpty(msiPackage.ProductCode))
163 {
164 writer.WriteAttributeString("ProductCode", msiPackage.ProductCode);
165 }
166
167 if (!String.IsNullOrEmpty(msiPackage.UpgradeCode))
168 {
169 writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode);
170 }
171 }
172 else if (package.SpecificPackageSymbol is WixBundleMspPackageSymbol mspPackage)
173 {
174 if (!String.IsNullOrEmpty(mspPackage.PatchCode))
175 {
176 writer.WriteAttributeString("ProductCode", mspPackage.PatchCode);
177 }
178 }
179
180 if (!String.IsNullOrEmpty(package.PackageSymbol.Version))
181 {
182 writer.WriteAttributeString("Version", package.PackageSymbol.Version);
183 }
184
185 if (!String.IsNullOrEmpty(package.PackageSymbol.InstallCondition))
186 {
187 writer.WriteAttributeString("InstallCondition", package.PackageSymbol.InstallCondition);
188 }
189
190 if (!String.IsNullOrEmpty(package.PackageSymbol.RepairCondition))
191 {
192 writer.WriteAttributeString("RepairCondition", package.PackageSymbol.RepairCondition);
193 }
194
195 switch (package.PackageSymbol.Cache)
196 {
197 case BundleCacheType.Remove:
198 writer.WriteAttributeString("Cache", "remove");
199 break;
200 case BundleCacheType.Keep:
201 writer.WriteAttributeString("Cache", "keep");
202 break;
203 case BundleCacheType.Force:
204 writer.WriteAttributeString("Cache", "force");
205 break;
206 }
207
208 writer.WriteEndElement();
209 }
210 }
211
212 private void WriteFeatureInfo(XmlTextWriter writer)
213 {
214 var featureSymbols = this.Section.Symbols.OfType<WixBundleMsiFeatureSymbol>();
215
216 foreach (var featureSymbol in featureSymbols)
217 {
218 if (!this.PackagesFacades.TryGetFacadesByPackagePayloadId(featureSymbol.PackagePayloadRef, out var facades))
219 {
220 continue;
221 }
222
223 foreach (var facade in facades)
224 {
225 if (!(facade.SpecificPackageSymbol is WixBundleMsiPackageSymbol msiPackage) || !msiPackage.EnableFeatureSelection)
226 {
227 continue;
228 }
229
230 writer.WriteStartElement("WixPackageFeatureInfo");
231
232 writer.WriteAttributeString("Package", facade.PackageId);
233 writer.WriteAttributeString("Feature", featureSymbol.Name);
234 writer.WriteAttributeString("Size", featureSymbol.Size.ToString(CultureInfo.InvariantCulture));
235
236 if (!String.IsNullOrEmpty(featureSymbol.Parent))
237 {
238 writer.WriteAttributeString("Parent", featureSymbol.Parent);
239 }
240
241 if (!String.IsNullOrEmpty(featureSymbol.Title))
242 {
243 writer.WriteAttributeString("Title", featureSymbol.Title);
244 }
245
246 if (!String.IsNullOrEmpty(featureSymbol.Description))
247 {
248 writer.WriteAttributeString("Description", featureSymbol.Description);
249 }
250
251 writer.WriteAttributeString("Display", featureSymbol.Display.ToString(CultureInfo.InvariantCulture));
252 writer.WriteAttributeString("Level", featureSymbol.Level.ToString(CultureInfo.InvariantCulture));
253 writer.WriteAttributeString("Directory", featureSymbol.Directory);
254 writer.WriteAttributeString("Attributes", featureSymbol.Attributes.ToString(CultureInfo.InvariantCulture));
255
256 writer.WriteEndElement();
257 }
258 }
259 }
260
261 private void WritePayloadInfo(XmlTextWriter writer)
262 {
263 foreach (var kvp in this.PackagesPayloads.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))
264 {
265 var packageId = kvp.Key;
266 var payloadsById = kvp.Value;
267
268 foreach (var payloadSymbol in payloadsById.Values.OrderBy(p => p.Id.Id, StringComparer.Ordinal))
269 {
270 this.WritePayloadInfo(writer, payloadSymbol, packageId);
271 }
272 }
273
274 foreach (var payloadSymbol in this.Payloads.Values.Where(p => p.LayoutOnly).OrderBy(p => p.Id.Id, StringComparer.Ordinal))
275 {
276 this.WritePayloadInfo(writer, payloadSymbol, null);
277 }
278 }
279
280 private void WritePayloadInfo(XmlTextWriter writer, WixBundlePayloadSymbol payloadSymbol, string packageId)
281 {
282 writer.WriteStartElement("WixPayloadProperties");
283
284 if (!String.IsNullOrEmpty(packageId))
285 {
286 writer.WriteAttributeString("Package", packageId);
287 }
288
289 writer.WriteAttributeString("Payload", payloadSymbol.Id.Id);
290
291 if (!String.IsNullOrEmpty(payloadSymbol.ContainerRef))
292 {
293 writer.WriteAttributeString("Container", payloadSymbol.ContainerRef);
294 }
295
296 writer.WriteAttributeString("Name", payloadSymbol.Name);
297 writer.WriteAttributeString("Size", payloadSymbol.FileSize.Value.ToString(CultureInfo.InvariantCulture));
298
299 if (!String.IsNullOrEmpty(payloadSymbol.DownloadUrl))
300 {
301 writer.WriteAttributeString("DownloadUrl", payloadSymbol.DownloadUrl);
302 }
303
304 writer.WriteEndElement();
305 }
306
307 private WixBundlePayloadSymbol CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath)
308 {
309 var generatedId = this.InternalBurnBackendHelper.GenerateIdentifier("ux", BurnCommon.BADataFileName);
310
311 var symbol = this.Section.AddSymbol(new WixBundlePayloadSymbol(this.BundleSymbol.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
312 {
313 Name = BurnCommon.BADataFileName,
314 SourceFile = new IntermediateFieldPathValue { Path = baManifestPath },
315 Compressed = true,
316 UnresolvedSourceFile = baManifestPath,
317 ContainerRef = BurnConstants.BurnUXContainerName,
318 EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex),
319 Packaging = PackagingType.Embedded,
320 });
321
322 var fileInfo = new FileInfo(baManifestPath);
323
324 symbol.FileSize = (int)fileInfo.Length;
325
326 symbol.Hash = BundleHashAlgorithm.Hash(fileInfo);
327
328 return symbol;
329 }
330 }
331 }