aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-04-13 12:17:36 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-13 13:13:48 -0500
commit535e4165e3121a14e5799f575a1010671212c539 (patch)
tree7b7f5b7da112d7c519a20fd109d2537cdc1152f1
parentfaa9777b7720a935b28c33c3fa1088cd7bcc2f86 (diff)
downloadwix-535e4165e3121a14e5799f575a1010671212c539.tar.gz
wix-535e4165e3121a14e5799f575a1010671212c539.tar.bz2
wix-535e4165e3121a14e5799f575a1010671212c539.zip
Add bundle support to "burn remotepayload" command.
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs248
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs215
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs79
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs35
7 files changed, 379 insertions, 206 deletions
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index ffc219dc..1c88fdeb 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -182,6 +182,8 @@ namespace WixToolset.Core.Burn
182 { 182 {
183 var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder); 183 var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder);
184 command.Execute(); 184 command.Execute();
185
186 trackedFiles.AddRange(command.TrackedFiles);
185 } 187 }
186 break; 188 break;
187 189
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
new file mode 100644
index 00000000..84ce6051
--- /dev/null
+++ b/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
@@ -0,0 +1,248 @@
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.Bundles
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Text;
9 using System.Xml;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services;
14
15 internal class HarvestBundlePackageCommand
16 {
17 public HarvestBundlePackageCommand(IServiceProvider serviceProvider, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol)
18 {
19 this.Messaging = serviceProvider.GetService<IMessaging>();
20 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
21 this.IntermediateFolder = intermediateFolder;
22
23 this.PackagePayload = payloadSymbol;
24 }
25
26 private IMessaging Messaging { get; }
27
28 private IBackendHelper BackendHelper { get; }
29
30 private string IntermediateFolder { get; }
31
32 private WixBundlePayloadSymbol PackagePayload { get; }
33
34 public WixBundleHarvestedBundlePackageSymbol HarvestedBundlePackage { get; private set; }
35
36 public WixBundleHarvestedDependencyProviderSymbol HarvestedDependencyProvider { get; private set; }
37
38 public List<WixBundlePackageRelatedBundleSymbol> RelatedBundles { get; } = new List<WixBundlePackageRelatedBundleSymbol>();
39
40 public List<ITrackedFile> TrackedFiles { get; } = new List<ITrackedFile>();
41
42 public void Execute()
43 {
44 bool win64;
45 string bundleId;
46 string engineVersion;
47 int protocolVersion;
48 string manifestNamespace;
49 bool perMachine;
50 string version;
51 string displayName;
52 long installSize;
53
54 var sourcePath = this.PackagePayload.SourceFile.Path;
55 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
56
57 using (var burnReader = BurnReader.Open(this.Messaging, sourcePath))
58 {
59 if (burnReader.Invalid)
60 {
61 return;
62 }
63
64 var baFolderPath = Path.Combine(this.IntermediateFolder, burnReader.BundleId.ToString());
65
66 if (!burnReader.ExtractUXContainer(baFolderPath, baFolderPath))
67 {
68 return;
69 }
70
71 foreach (var filePath in Directory.EnumerateFiles(baFolderPath, "*.*", SearchOption.AllDirectories))
72 {
73 this.TrackedFiles.Add(this.BackendHelper.TrackFile(filePath, TrackedFileType.Temporary, sourceLineNumbers));
74 }
75
76 bundleId = burnReader.BundleId.ToString("B").ToUpperInvariant();
77
78 try
79 {
80 var document = new XmlDocument();
81 document.Load(Path.Combine(baFolderPath, "manifest.xml"));
82 var namespaceManager = new XmlNamespaceManager(document.NameTable);
83
84 if (document.DocumentElement.LocalName != "BurnManifest")
85 {
86 this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(sourceLineNumbers, sourcePath, $"Expected root element to be 'BurnManifest' but was '{document.DocumentElement.LocalName}'."));
87 return;
88 }
89
90 engineVersion = document.DocumentElement.GetAttribute("EngineVersion");
91 protocolVersion = this.ProcessProtocolVersion(burnReader, document);
92 win64 = this.ProcessWin64(burnReader, document, sourceLineNumbers, sourcePath);
93
94 manifestNamespace = document.DocumentElement.NamespaceURI;
95
96 namespaceManager.AddNamespace("burn", document.DocumentElement.NamespaceURI);
97 var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement;
98 var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement;
99
100 perMachine = registrationElement.GetAttribute("PerMachine") == "yes";
101
102 version = registrationElement.GetAttribute("Version");
103
104 var providerKey = registrationElement.GetAttribute("ProviderKey");
105 var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", this.PackagePayload.Id.Id, providerKey));
106 this.HarvestedDependencyProvider = new WixBundleHarvestedDependencyProviderSymbol(sourceLineNumbers, depId)
107 {
108 PackagePayloadRef = this.PackagePayload.Id.Id,
109 ProviderKey = providerKey,
110 Version = version,
111 };
112
113 displayName = arpElement.GetAttribute("DisplayName");
114
115 installSize = this.ProcessPackages(document, namespaceManager);
116
117 this.ProcessRelatedBundles(document, namespaceManager, sourcePath);
118
119 // TODO: Add payloads?
120 }
121 catch (Exception e)
122 {
123 this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(sourceLineNumbers, sourcePath, e.ToString()));
124 return;
125 }
126 }
127
128 this.HarvestedBundlePackage = new WixBundleHarvestedBundlePackageSymbol(this.PackagePayload.SourceLineNumbers, this.PackagePayload.Id)
129 {
130 Win64 = win64,
131 BundleId = bundleId,
132 EngineVersion = engineVersion,
133 ManifestNamespace = manifestNamespace,
134 ProtocolVersion = protocolVersion,
135 PerMachine = perMachine,
136 Version = version,
137 DisplayName = displayName,
138 InstallSize = installSize,
139 };
140 }
141
142 private int ProcessProtocolVersion(BurnReader burnReader, XmlDocument document)
143 {
144 var protocolVersionValue = document.DocumentElement.GetAttribute("ProtocolVersion");
145
146 if (Int32.TryParse(protocolVersionValue, out var protocolVersion))
147 {
148 return protocolVersion;
149 }
150
151 // Assume that the .wixburn section version will change when the Burn protocol changes.
152 // This should be a safe assumption since only old bundles should be missing the ProtocolVersion from the manifest.
153 return burnReader.Version == 2 ? 1 : 0;
154 }
155
156 private bool ProcessWin64(BurnReader burnReader, XmlDocument document, SourceLineNumber sourceLineNumbers, string sourcePath)
157 {
158 var win64Value = document.DocumentElement.GetAttribute("Win64");
159
160 switch (win64Value)
161 {
162 case "yes":
163 return true;
164 case "no":
165 return false;
166 }
167
168 switch (burnReader.MachineType)
169 {
170 case BurnCommon.IMAGE_FILE_MACHINE_ARM:
171 case BurnCommon.IMAGE_FILE_MACHINE_ARMNT:
172 case BurnCommon.IMAGE_FILE_MACHINE_I386:
173 case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH32:
174 return false;
175 case BurnCommon.IMAGE_FILE_MACHINE_AMD64:
176 case BurnCommon.IMAGE_FILE_MACHINE_ARM64:
177 case BurnCommon.IMAGE_FILE_MACHINE_IA64:
178 case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH64:
179 return true;
180 case BurnCommon.IMAGE_FILE_MACHINE_AM33:
181 case BurnCommon.IMAGE_FILE_MACHINE_EBC:
182 case BurnCommon.IMAGE_FILE_MACHINE_M32R:
183 case BurnCommon.IMAGE_FILE_MACHINE_MIPS16:
184 case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU:
185 case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU16:
186 case BurnCommon.IMAGE_FILE_MACHINE_POWERPC:
187 case BurnCommon.IMAGE_FILE_MACHINE_POWERPCFP:
188 case BurnCommon.IMAGE_FILE_MACHINE_R4000:
189 case BurnCommon.IMAGE_FILE_MACHINE_RISCV32:
190 case BurnCommon.IMAGE_FILE_MACHINE_RISCV64:
191 case BurnCommon.IMAGE_FILE_MACHINE_RISCV128:
192 case BurnCommon.IMAGE_FILE_MACHINE_SH3:
193 case BurnCommon.IMAGE_FILE_MACHINE_SH3DSP:
194 case BurnCommon.IMAGE_FILE_MACHINE_SH4:
195 case BurnCommon.IMAGE_FILE_MACHINE_SH5:
196 case BurnCommon.IMAGE_FILE_MACHINE_THUMB:
197 case BurnCommon.IMAGE_FILE_MACHINE_WCEMIPSV2:
198 default:
199 this.Messaging.Write(BurnBackendWarnings.UnknownCoffMachineType(sourceLineNumbers, sourcePath, burnReader.MachineType));
200 return false;
201 }
202 }
203
204 private long ProcessPackages(XmlDocument document, XmlNamespaceManager namespaceManager)
205 {
206 long packageInstallSize = 0;
207
208 foreach (XmlElement packageElement in document.SelectNodes("/burn:BurnManifest/burn:Chain/*", namespaceManager))
209 {
210 if (!packageElement.Name.EndsWith("Package"))
211 {
212 continue;
213 }
214
215 if (Int64.TryParse(packageElement.GetAttribute("InstallSize"), out var installSize))
216 {
217 packageInstallSize += installSize;
218 }
219 }
220
221 return packageInstallSize;
222 }
223
224 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath)
225 {
226 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
227
228 foreach (XmlElement relatedBundleElement in document.SelectNodes("/burn:BurnManifest/burn:RelatedBundle", namespaceManager))
229 {
230 var id = relatedBundleElement.GetAttribute("Id");
231 var actionValue = relatedBundleElement.GetAttribute("Action");
232
233 if (!Enum.TryParse(actionValue, out RelatedBundleActionType action))
234 {
235 this.Messaging.Write(BurnBackendWarnings.UnknownBundleRelationAction(sourceLineNumbers, sourcePath, actionValue));
236 continue;
237 }
238
239 this.RelatedBundles.Add(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
240 {
241 PackagePayloadRef = this.PackagePayload.Id.Id,
242 BundleId = id,
243 Action = action,
244 });
245 }
246 }
247 }
248}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
index c8e6bcf2..af37676c 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
@@ -4,10 +4,7 @@ namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Linq; 7 using System.Linq;
10 using System.Xml;
11 using WixToolset.Data; 8 using WixToolset.Data;
12 using WixToolset.Data.Symbols; 9 using WixToolset.Data.Symbols;
13 using WixToolset.Extensibility.Data; 10 using WixToolset.Extensibility.Data;
@@ -20,8 +17,8 @@ namespace WixToolset.Core.Burn.Bundles
20 { 17 {
21 public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads, string intermediateFolder) 18 public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads, string intermediateFolder)
22 { 19 {
20 this.ServiceProvider = serviceProvider;
23 this.Messaging = serviceProvider.GetService<IMessaging>(); 21 this.Messaging = serviceProvider.GetService<IMessaging>();
24 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
25 this.PackagePayloads = packagePayloads; 22 this.PackagePayloads = packagePayloads;
26 this.Section = section; 23 this.Section = section;
27 this.IntermediateFolder = intermediateFolder; 24 this.IntermediateFolder = intermediateFolder;
@@ -31,9 +28,9 @@ namespace WixToolset.Core.Burn.Bundles
31 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef]; 28 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef];
32 } 29 }
33 30
34 private IMessaging Messaging { get; } 31 private IServiceProvider ServiceProvider { get; }
35 32
36 private IBackendHelper BackendHelper { get; } 33 private IMessaging Messaging { get; }
37 34
38 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; } 35 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; }
39 36
@@ -101,210 +98,26 @@ namespace WixToolset.Core.Burn.Bundles
101 this.ChainPackage.InstallSize = harvestedBundlePackage.InstallSize; 98 this.ChainPackage.InstallSize = harvestedBundlePackage.InstallSize;
102 } 99 }
103 100
104 public WixBundleHarvestedBundlePackageSymbol HarvestPackage() 101 private WixBundleHarvestedBundlePackageSymbol HarvestPackage()
105 { 102 {
106 bool win64; 103 var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.IntermediateFolder, this.PackagePayload);
107 string bundleId; 104 command.Execute();
108 string engineVersion;
109 int protocolVersion;
110 string manifestNamespace;
111 bool perMachine;
112 string version;
113 string displayName;
114 long installSize;
115
116 var sourcePath = this.PackagePayload.SourceFile.Path;
117 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
118 105
119 using (var burnReader = BurnReader.Open(this.Messaging, sourcePath)) 106 this.TrackedFiles.AddRange(command.TrackedFiles);
107 if (this.Messaging.EncounteredError)
120 { 108 {
121 if (burnReader.Invalid) 109 return null;
122 {
123 return null;
124 }
125
126 var baFolderPath = Path.Combine(this.IntermediateFolder, burnReader.BundleId.ToString());
127
128 if (!burnReader.ExtractUXContainer(baFolderPath, baFolderPath))
129 {
130 return null;
131 }
132
133 foreach (var filePath in Directory.EnumerateFiles(baFolderPath, "*.*", SearchOption.AllDirectories))
134 {
135 this.TrackedFiles.Add(this.BackendHelper.TrackFile(filePath, TrackedFileType.Temporary, sourceLineNumbers));
136 }
137
138 bundleId = burnReader.BundleId.ToString("B").ToUpperInvariant();
139
140 try
141 {
142 var document = new XmlDocument();
143 document.Load(Path.Combine(baFolderPath, "manifest.xml"));
144 var namespaceManager = new XmlNamespaceManager(document.NameTable);
145
146 if (document.DocumentElement.LocalName != "BurnManifest")
147 {
148 this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(sourceLineNumbers, sourcePath, $"Expected root element to be 'BurnManifest' but was '{document.DocumentElement.LocalName}'."));
149 return null;
150 }
151
152 engineVersion = document.DocumentElement.GetAttribute("EngineVersion");
153 protocolVersion = this.ProcessProtocolVersion(burnReader, document);
154 win64 = this.ProcessWin64(burnReader, document, sourceLineNumbers, sourcePath);
155
156 manifestNamespace = document.DocumentElement.NamespaceURI;
157
158 namespaceManager.AddNamespace("burn", document.DocumentElement.NamespaceURI);
159 var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement;
160 var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement;
161
162 perMachine = registrationElement.GetAttribute("PerMachine") == "yes";
163
164 version = registrationElement.GetAttribute("Version");
165
166 var providerKey = registrationElement.GetAttribute("ProviderKey");
167 var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", this.PackagePayload.Id.Id, providerKey));
168 this.Section.AddSymbol(new WixBundleHarvestedDependencyProviderSymbol(sourceLineNumbers, depId)
169 {
170 PackagePayloadRef = this.PackagePayload.Id.Id,
171 ProviderKey = providerKey,
172 Version = version,
173 });
174
175 displayName = arpElement.GetAttribute("DisplayName");
176
177 installSize = this.ProcessPackages(document, namespaceManager);
178
179 this.ProcessRelatedBundles(document, namespaceManager, sourcePath);
180
181 // TODO: Add payloads?
182 }
183 catch (Exception e)
184 {
185 this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(sourceLineNumbers, sourcePath, e.ToString()));
186 return null;
187 }
188 } 110 }
189 111
190 return this.Section.AddSymbol(new WixBundleHarvestedBundlePackageSymbol(this.PackagePayload.SourceLineNumbers, this.PackagePayload.Id) 112 this.Section.AddSymbol(command.HarvestedBundlePackage);
191 { 113 this.Section.AddSymbol(command.HarvestedDependencyProvider);
192 Win64 = win64,
193 BundleId = bundleId,
194 EngineVersion = engineVersion,
195 ManifestNamespace = manifestNamespace,
196 ProtocolVersion = protocolVersion,
197 PerMachine = perMachine,
198 Version = version,
199 DisplayName = displayName,
200 InstallSize = installSize,
201 });
202 }
203
204 private int ProcessProtocolVersion(BurnReader burnReader, XmlDocument document)
205 {
206 var protocolVersionValue = document.DocumentElement.GetAttribute("ProtocolVersion");
207 114
208 if (Int32.TryParse(protocolVersionValue, out var protocolVersion)) 115 foreach (var relatedBundle in command.RelatedBundles)
209 { 116 {
210 return protocolVersion; 117 this.Section.AddSymbol(relatedBundle);
211 } 118 }
212 119
213 // Assume that the .wixburn section version will change when the Burn protocol changes. 120 return command.HarvestedBundlePackage;
214 // This should be a safe assumption since only old bundles should be missing the ProtocolVersion from the manifest.
215 return burnReader.Version == 2 ? 1 : 0;
216 }
217
218 private bool ProcessWin64(BurnReader burnReader, XmlDocument document, SourceLineNumber sourceLineNumbers, string sourcePath)
219 {
220 var win64Value = document.DocumentElement.GetAttribute("Win64");
221
222 switch (win64Value)
223 {
224 case "yes":
225 return true;
226 case "no":
227 return false;
228 }
229
230 switch (burnReader.MachineType)
231 {
232 case BurnCommon.IMAGE_FILE_MACHINE_ARM:
233 case BurnCommon.IMAGE_FILE_MACHINE_ARMNT:
234 case BurnCommon.IMAGE_FILE_MACHINE_I386:
235 case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH32:
236 return false;
237 case BurnCommon.IMAGE_FILE_MACHINE_AMD64:
238 case BurnCommon.IMAGE_FILE_MACHINE_ARM64:
239 case BurnCommon.IMAGE_FILE_MACHINE_IA64:
240 case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH64:
241 return true;
242 case BurnCommon.IMAGE_FILE_MACHINE_AM33:
243 case BurnCommon.IMAGE_FILE_MACHINE_EBC:
244 case BurnCommon.IMAGE_FILE_MACHINE_M32R:
245 case BurnCommon.IMAGE_FILE_MACHINE_MIPS16:
246 case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU:
247 case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU16:
248 case BurnCommon.IMAGE_FILE_MACHINE_POWERPC:
249 case BurnCommon.IMAGE_FILE_MACHINE_POWERPCFP:
250 case BurnCommon.IMAGE_FILE_MACHINE_R4000:
251 case BurnCommon.IMAGE_FILE_MACHINE_RISCV32:
252 case BurnCommon.IMAGE_FILE_MACHINE_RISCV64:
253 case BurnCommon.IMAGE_FILE_MACHINE_RISCV128:
254 case BurnCommon.IMAGE_FILE_MACHINE_SH3:
255 case BurnCommon.IMAGE_FILE_MACHINE_SH3DSP:
256 case BurnCommon.IMAGE_FILE_MACHINE_SH4:
257 case BurnCommon.IMAGE_FILE_MACHINE_SH5:
258 case BurnCommon.IMAGE_FILE_MACHINE_THUMB:
259 case BurnCommon.IMAGE_FILE_MACHINE_WCEMIPSV2:
260 default:
261 this.Messaging.Write(BurnBackendWarnings.UnknownCoffMachineType(sourceLineNumbers, sourcePath, burnReader.MachineType));
262 return false;
263 }
264 }
265
266 private long ProcessPackages(XmlDocument document, XmlNamespaceManager namespaceManager)
267 {
268 long packageInstallSize = 0;
269
270 foreach (XmlElement packageElement in document.SelectNodes("/burn:BurnManifest/burn:Chain/*", namespaceManager))
271 {
272 if (!packageElement.Name.EndsWith("Package"))
273 {
274 continue;
275 }
276
277 if (Int64.TryParse(packageElement.GetAttribute("InstallSize"), out var installSize))
278 {
279 packageInstallSize += installSize;
280 }
281 }
282
283 return packageInstallSize;
284 }
285
286 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath)
287 {
288 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
289
290 foreach (XmlElement relatedBundleElement in document.SelectNodes("/burn:BurnManifest/burn:RelatedBundle", namespaceManager))
291 {
292 var id = relatedBundleElement.GetAttribute("Id");
293 var actionValue = relatedBundleElement.GetAttribute("Action");
294
295 if (!Enum.TryParse(actionValue, out RelatedBundleActionType action))
296 {
297 this.Messaging.Write(BurnBackendWarnings.UnknownBundleRelationAction(sourceLineNumbers, sourcePath, actionValue));
298 continue;
299 }
300
301 this.Section.AddSymbol(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
302 {
303 PackagePayloadRef = this.PackagePayload.Id.Id,
304 BundleId = id,
305 Action = action,
306 });
307 }
308 } 121 }
309 } 122 }
310} 123}
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
index 7b362485..6eed62eb 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
@@ -9,6 +9,7 @@ namespace WixToolset.Core.Burn.CommandLine
9 using System.Threading; 9 using System.Threading;
10 using System.Threading.Tasks; 10 using System.Threading.Tasks;
11 using System.Xml.Linq; 11 using System.Xml.Linq;
12 using WixToolset.Core.Burn.Bundles;
12 using WixToolset.Core.Burn.Interfaces; 13 using WixToolset.Core.Burn.Interfaces;
13 using WixToolset.Core.Native; 14 using WixToolset.Core.Native;
14 using WixToolset.Data; 15 using WixToolset.Data;
@@ -17,16 +18,22 @@ namespace WixToolset.Core.Burn.CommandLine
17 18
18 internal class RemotePayloadSubcommand : BurnSubcommandBase 19 internal class RemotePayloadSubcommand : BurnSubcommandBase
19 { 20 {
21 private static readonly XName BundlePackagePayloadName = "BundlePackagePayload";
20 private static readonly XName ExePackagePayloadName = "ExePackagePayload"; 22 private static readonly XName ExePackagePayloadName = "ExePackagePayload";
21 private static readonly XName MsuPackagePayloadName = "MsuPackagePayload"; 23 private static readonly XName MsuPackagePayloadName = "MsuPackagePayload";
22 private static readonly XName PayloadName = "Payload"; 24 private static readonly XName PayloadName = "Payload";
25 private static readonly XName RemoteBundleName = "RemoteBundle";
26 private static readonly XName RemoteRelatedBundleName = "RemoteRelatedBundle";
23 27
24 public RemotePayloadSubcommand(IServiceProvider serviceProvider) 28 public RemotePayloadSubcommand(IServiceProvider serviceProvider)
25 { 29 {
30 this.ServiceProvider = serviceProvider;
26 this.Messaging = serviceProvider.GetService<IMessaging>(); 31 this.Messaging = serviceProvider.GetService<IMessaging>();
27 this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>(); 32 this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>();
28 } 33 }
29 34
35 private IServiceProvider ServiceProvider { get; }
36
30 private IMessaging Messaging { get; } 37 private IMessaging Messaging { get; }
31 38
32 private IPayloadHarvester PayloadHarvester { get; } 39 private IPayloadHarvester PayloadHarvester { get; }
@@ -37,6 +44,8 @@ namespace WixToolset.Core.Burn.CommandLine
37 44
38 private List<string> InputPaths { get; } = new List<string>(); 45 private List<string> InputPaths { get; } = new List<string>();
39 46
47 private string IntermediateFolder { get; set; }
48
40 private string OutputPath { get; set; } 49 private string OutputPath { get; set; }
41 50
42 private WixBundlePackageType? PackageType { get; set; } 51 private WixBundlePackageType? PackageType { get; set; }
@@ -58,6 +67,11 @@ namespace WixToolset.Core.Burn.CommandLine
58 this.BasePaths.Sort(); 67 this.BasePaths.Sort();
59 this.BasePaths.Reverse(); 68 this.BasePaths.Reverse();
60 69
70 if (String.IsNullOrEmpty(this.IntermediateFolder))
71 {
72 this.IntermediateFolder = Path.GetTempPath();
73 }
74
61 var elements = this.HarvestRemotePayloads(inputPaths); 75 var elements = this.HarvestRemotePayloads(inputPaths);
62 76
63 if (!this.Messaging.EncounteredError) 77 if (!this.Messaging.EncounteredError)
@@ -98,6 +112,10 @@ namespace WixToolset.Core.Burn.CommandLine
98 this.DownloadUrl = parser.GetNextArgumentOrError(argument); 112 this.DownloadUrl = parser.GetNextArgumentOrError(argument);
99 return true; 113 return true;
100 114
115 case "intermediatefolder":
116 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
117 return true;
118
101 case "packagetype": 119 case "packagetype":
102 var packageTypeValue = parser.GetNextArgumentOrError(argument); 120 var packageTypeValue = parser.GetNextArgumentOrError(argument);
103 if (Enum.TryParse<WixBundlePackageType>(packageTypeValue, ignoreCase: true, out var packageType)) 121 if (Enum.TryParse<WixBundlePackageType>(packageTypeValue, ignoreCase: true, out var packageType))
@@ -174,7 +192,7 @@ namespace WixToolset.Core.Burn.CommandLine
174 continue; 192 continue;
175 } 193 }
176 194
177 var payloadSymbol = new WixBundlePayloadSymbol 195 var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id"))
178 { 196 {
179 SourceFile = new IntermediateFieldPathValue { Path = path }, 197 SourceFile = new IntermediateFieldPathValue { Path = path },
180 }; 198 };
@@ -225,6 +243,62 @@ namespace WixToolset.Core.Burn.CommandLine
225 element.Add(new XAttribute("Version", payloadSymbol.Version)); 243 element.Add(new XAttribute("Version", payloadSymbol.Version));
226 } 244 }
227 245
246 if (element.Name == BundlePackagePayloadName)
247 {
248 var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.IntermediateFolder, payloadSymbol);
249 command.Execute();
250
251 if (!this.Messaging.EncounteredError)
252 {
253 var bundleElement = new XElement(RemoteBundleName);
254
255 bundleElement.Add(new XAttribute("BundleId", command.HarvestedBundlePackage.BundleId));
256
257 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.DisplayName))
258 {
259 bundleElement.Add(new XAttribute("DisplayName", command.HarvestedBundlePackage.DisplayName));
260 }
261
262 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.EngineVersion))
263 {
264 bundleElement.Add(new XAttribute("EngineVersion", command.HarvestedBundlePackage.EngineVersion));
265 }
266
267 bundleElement.Add(new XAttribute("InstallSize", command.HarvestedBundlePackage.InstallSize));
268 bundleElement.Add(new XAttribute("ManifestNamespace", command.HarvestedBundlePackage.ManifestNamespace));
269 bundleElement.Add(new XAttribute("PerMachine", command.HarvestedBundlePackage.PerMachine ? "yes" : "no"));
270 bundleElement.Add(new XAttribute("ProviderKey", command.HarvestedDependencyProvider.ProviderKey));
271 bundleElement.Add(new XAttribute("ProtocolVersion", command.HarvestedBundlePackage.ProtocolVersion));
272
273 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.Version))
274 {
275 bundleElement.Add(new XAttribute("Version", command.HarvestedBundlePackage.Version));
276 }
277
278 bundleElement.Add(new XAttribute("Win64", command.HarvestedBundlePackage.Win64 ? "yes" : "no"));
279
280 var setUpgradeCode = false;
281 foreach (var relatedBundle in command.RelatedBundles)
282 {
283 if (!setUpgradeCode && relatedBundle.Action == RelatedBundleActionType.Upgrade)
284 {
285 setUpgradeCode = true;
286 bundleElement.Add(new XAttribute("UpgradeCode", relatedBundle.BundleId));
287 continue;
288 }
289
290 var relatedBundleElement = new XElement(RemoteRelatedBundleName);
291
292 relatedBundleElement.Add(new XAttribute("Id", relatedBundle.BundleId));
293 relatedBundleElement.Add(new XAttribute("Action", relatedBundle.Action.ToString()));
294
295 bundleElement.Add(relatedBundleElement);
296 }
297
298 element.Add(bundleElement);
299 }
300 }
301
228 yield return element; 302 yield return element;
229 } 303 }
230 } 304 }
@@ -237,6 +311,9 @@ namespace WixToolset.Core.Burn.CommandLine
237 311
238 switch (extension.ToUpperInvariant()) 312 switch (extension.ToUpperInvariant())
239 { 313 {
314 case "BUNDLE":
315 return new XElement(BundlePackagePayloadName);
316
240 case "EXE": 317 case "EXE":
241 case ".EXE": 318 case ".EXE":
242 return new XElement(ExePackagePayloadName); 319 return new XElement(ExePackagePayloadName);
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
index 0dd29e38..6387a12a 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
@@ -574,7 +574,7 @@ namespace WixToolsetTest.CoreIntegration
574 } 574 }
575 575
576 [Fact] 576 [Fact]
577 public void CantBuildWithSubfolderContainer() 577 public void CanBuildWithSubfolderContainer()
578 { 578 {
579 var folder = TestData.Get(@"TestData"); 579 var folder = TestData.Get(@"TestData");
580 580
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
index 3dead50d..29ea1e7f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
@@ -90,7 +90,7 @@ namespace WixToolsetTest.CoreIntegration
90 } 90 }
91 91
92 [Fact] 92 [Fact]
93 public void CanSpecifyPackagePayloadInPayloadGroup() 93 public void CanSpecifyExePackagePayloadInPayloadGroup()
94 { 94 {
95 var folder = TestData.Get(@"TestData"); 95 var folder = TestData.Get(@"TestData");
96 96
@@ -135,7 +135,7 @@ namespace WixToolsetTest.CoreIntegration
135 } 135 }
136 136
137 [Fact] 137 [Fact]
138 public void CanSpecifyPackagePayloadWithCertificate() 138 public void CanSpecifyExePackagePayloadWithCertificate()
139 { 139 {
140 var folder = TestData.Get(@"TestData", "PackagePayload"); 140 var folder = TestData.Get(@"TestData", "PackagePayload");
141 141
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
index 434b3621..41791bd0 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
@@ -11,7 +11,40 @@ namespace WixToolsetTest.CoreIntegration
11 public class RemotePayloadFixture 11 public class RemotePayloadFixture
12 { 12 {
13 [Fact] 13 [Fact]
14 public void CanGetRemotePayload() 14 public void CanGetRemoteV3BundlePayload()
15 {
16 var folder = TestData.Get(@"TestData");
17
18 using (var fs = new DisposableFileSystem())
19 {
20 var outputFolder = fs.GetFolder();
21 var outFile = Path.Combine(outputFolder, "out.xml");
22
23 var result = WixRunner.Execute(new[]
24 {
25 "burn", "remotepayload",
26 Path.Combine(folder, ".Data", "v3bundle.exe"),
27 "-downloadurl", "https://www.example.com/files/{0}",
28 "-o", outFile,
29 "-packagetype", "bundle",
30 });
31
32 result.AssertSuccess();
33
34 var elements = File.ReadAllLines(outFile);
35 elements = elements.Select(s => s.Replace("\"", "'")).ToArray();
36
37 WixAssert.CompareLineByLine(new[]
38 {
39 "<BundlePackagePayload Name='v3bundle.exe' ProductName='CustomV3Theme' Description='CustomV3Theme' DownloadUrl='https://www.example.com/files/v3bundle.exe' Hash='80739E7B8C31D75B4CDC48D60D74F5E481CB904212A3AE3FB0920365A163FBF32B0C5C175AB516D4124F107923E96200605DE1D560D362FEB47350FA727823B4' Size='648397' Version='1.0.0.0'>",
40 " <RemoteBundle BundleId='{215A70DB-AB35-48C7-BE51-D66EAAC87177}' DisplayName='CustomV3Theme' InstallSize='1135' ManifestNamespace='http://schemas.microsoft.com/wix/2008/Burn' PerMachine='yes' ProviderKey='{215a70db-ab35-48c7-be51-d66eaac87177}' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{2BF4C01F-C132-4E70-97AB-2BC68C7CCD10}' />",
41 "</BundlePackagePayload>",
42 }, elements);
43 }
44 }
45
46 [Fact]
47 public void CanGetRemoteExePayload()
15 { 48 {
16 var folder = TestData.Get(@"TestData"); 49 var folder = TestData.Get(@"TestData");
17 50