aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-05-05 18:08:06 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-05-06 12:06:56 -0500
commitd5744da0117199f23bf72f5c2ba7cd1c6f52e173 (patch)
treeabb1345d95de60ca152ab0bee68f863c8d475ad6 /src
parent29f7e00586412163a20e298fbf84505f8a917425 (diff)
downloadwix-d5744da0117199f23bf72f5c2ba7cd1c6f52e173.tar.gz
wix-d5744da0117199f23bf72f5c2ba7cd1c6f52e173.tar.bz2
wix-d5744da0117199f23bf72f5c2ba7cd1c6f52e173.zip
Harvest BundlePackage payloads.
Fixes 6757
Diffstat (limited to 'src')
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs16
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs14
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs190
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs5
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs16
-rw-r--r--src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs304
-rw-r--r--src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs11
-rw-r--r--src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs108
-rw-r--r--src/wix/WixToolset.Core/Compile/CompilerPayload.cs10
-rw-r--r--src/wix/WixToolset.Core/Compiler_Bundle.cs70
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs18
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs145
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs24
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat)0
18 files changed, 752 insertions, 187 deletions
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
index a171682d..46bfe034 100644
--- a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
@@ -10,6 +10,7 @@ namespace WixToolset.Data
10 SymbolDefinitionType.WixBundleBundlePackagePayload, 10 SymbolDefinitionType.WixBundleBundlePackagePayload,
11 new IntermediateFieldDefinition[] 11 new IntermediateFieldDefinition[]
12 { 12 {
13 new IntermediateFieldDefinition(nameof(WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration), IntermediateFieldType.Number),
13 }, 14 },
14 typeof(WixBundleBundlePackagePayloadSymbol)); 15 typeof(WixBundleBundlePackagePayloadSymbol));
15 } 16 }
@@ -19,6 +20,15 @@ namespace WixToolset.Data.Symbols
19{ 20{
20 public enum WixBundleBundlePackagePayloadSymbolFields 21 public enum WixBundleBundlePackagePayloadSymbolFields
21 { 22 {
23 PayloadGeneration,
24 }
25
26 public enum BundlePackagePayloadGenerationType
27 {
28 None,
29 ExternalWithoutDownloadUrl,
30 External,
31 All,
22 } 32 }
23 33
24 public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol 34 public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol
@@ -32,5 +42,11 @@ namespace WixToolset.Data.Symbols
32 } 42 }
33 43
34 public IntermediateField this[WixBundleBundlePackagePayloadSymbolFields index] => this.Fields[(int)index]; 44 public IntermediateField this[WixBundleBundlePackagePayloadSymbolFields index] => this.Fields[(int)index];
45
46 public BundlePackagePayloadGenerationType PayloadGeneration
47 {
48 get => (BundlePackagePayloadGenerationType)this.Fields[(int)WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration].AsNumber();
49 set => this.Set((int)WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration, (int)value);
50 }
35 } 51 }
36} 52}
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index 1c88fdeb..348a4b41 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -180,7 +180,7 @@ namespace WixToolset.Core.Burn
180 { 180 {
181 case WixBundlePackageType.Bundle: 181 case WixBundlePackageType.Bundle:
182 { 182 {
183 var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder); 183 var command = new ProcessBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder);
184 command.Execute(); 184 command.Execute();
185 185
186 trackedFiles.AddRange(command.TrackedFiles); 186 trackedFiles.AddRange(command.TrackedFiles);
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
index f8c70c1a..8b327b6a 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
@@ -2,6 +2,7 @@
2 2
3namespace WixToolset.Core.Burn.Bundles 3namespace WixToolset.Core.Burn.Bundles
4{ 4{
5 using System;
5 using System.Collections.Generic; 6 using System.Collections.Generic;
6 using System.Linq; 7 using System.Linq;
7 using WixToolset.Data; 8 using WixToolset.Data;
@@ -147,7 +148,7 @@ namespace WixToolset.Core.Burn.Bundles
147 case WixBundlePackageType.Bundle: 148 case WixBundlePackageType.Bundle:
148 if (bundlePackages.TryGetValue(id, out var bundlePackage)) 149 if (bundlePackages.TryGetValue(id, out var bundlePackage))
149 { 150 {
150 facades.Add(new PackageFacade(package, bundlePackage)); 151 facades.Add(new PackageFacade(package, bundlePackage, packagePayload));
151 } 152 }
152 else 153 else
153 { 154 {
@@ -158,7 +159,7 @@ namespace WixToolset.Core.Burn.Bundles
158 case WixBundlePackageType.Exe: 159 case WixBundlePackageType.Exe:
159 if (exePackages.TryGetValue(id, out var exePackage)) 160 if (exePackages.TryGetValue(id, out var exePackage))
160 { 161 {
161 facades.Add(new PackageFacade(package, exePackage)); 162 facades.Add(new PackageFacade(package, exePackage, packagePayload));
162 } 163 }
163 else 164 else
164 { 165 {
@@ -169,7 +170,7 @@ namespace WixToolset.Core.Burn.Bundles
169 case WixBundlePackageType.Msi: 170 case WixBundlePackageType.Msi:
170 if (msiPackages.TryGetValue(id, out var msiPackage)) 171 if (msiPackages.TryGetValue(id, out var msiPackage))
171 { 172 {
172 facades.Add(new PackageFacade(package, msiPackage)); 173 facades.Add(new PackageFacade(package, msiPackage, packagePayload));
173 } 174 }
174 else 175 else
175 { 176 {
@@ -180,7 +181,7 @@ namespace WixToolset.Core.Burn.Bundles
180 case WixBundlePackageType.Msp: 181 case WixBundlePackageType.Msp:
181 if (mspPackages.TryGetValue(id, out var mspPackage)) 182 if (mspPackages.TryGetValue(id, out var mspPackage))
182 { 183 {
183 facades.Add(new PackageFacade(package, mspPackage)); 184 facades.Add(new PackageFacade(package, mspPackage, packagePayload));
184 } 185 }
185 else 186 else
186 { 187 {
@@ -191,13 +192,16 @@ namespace WixToolset.Core.Burn.Bundles
191 case WixBundlePackageType.Msu: 192 case WixBundlePackageType.Msu:
192 if (msuPackages.TryGetValue(id, out var msuPackage)) 193 if (msuPackages.TryGetValue(id, out var msuPackage))
193 { 194 {
194 facades.Add(new PackageFacade(package, msuPackage)); 195 facades.Add(new PackageFacade(package, msuPackage, packagePayload));
195 } 196 }
196 else 197 else
197 { 198 {
198 this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsuPackage", id)); 199 this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsuPackage", id));
199 } 200 }
200 break; 201 break;
202
203 default:
204 throw new NotImplementedException();
201 } 205 }
202 } 206 }
203 207
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
index 84ce6051..89e0da98 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
@@ -5,36 +5,48 @@ namespace WixToolset.Core.Burn.Bundles
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Text; 8 using System.Linq;
9 using System.Xml; 9 using System.Xml;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Symbols; 11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Data; 13 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services; 14 using WixToolset.Extensibility.Services;
14 15
15 internal class HarvestBundlePackageCommand 16 internal class HarvestBundlePackageCommand
16 { 17 {
17 public HarvestBundlePackageCommand(IServiceProvider serviceProvider, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol) 18 public HarvestBundlePackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendBinderExtension> backendExtensions, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol, WixBundleBundlePackagePayloadSymbol packagePayloadSymbol, Dictionary<string, WixBundlePayloadSymbol> packagePayloadsById)
18 { 19 {
19 this.Messaging = serviceProvider.GetService<IMessaging>(); 20 this.Messaging = serviceProvider.GetService<IMessaging>();
20 this.BackendHelper = serviceProvider.GetService<IBackendHelper>(); 21 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
22 this.BackendExtensions = backendExtensions;
21 this.IntermediateFolder = intermediateFolder; 23 this.IntermediateFolder = intermediateFolder;
22 24
23 this.PackagePayload = payloadSymbol; 25 this.PackagePayload = payloadSymbol;
26 this.BundlePackagePayload = packagePayloadSymbol;
27 this.PackagePayloadsById = packagePayloadsById;
24 } 28 }
25 29
26 private IMessaging Messaging { get; } 30 private IMessaging Messaging { get; }
27 31
28 private IBackendHelper BackendHelper { get; } 32 private IBackendHelper BackendHelper { get; }
29 33
34 private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
35
30 private string IntermediateFolder { get; } 36 private string IntermediateFolder { get; }
31 37
32 private WixBundlePayloadSymbol PackagePayload { get; } 38 private WixBundlePayloadSymbol PackagePayload { get; }
33 39
40 private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; }
41
42 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloadsById { get; }
43
34 public WixBundleHarvestedBundlePackageSymbol HarvestedBundlePackage { get; private set; } 44 public WixBundleHarvestedBundlePackageSymbol HarvestedBundlePackage { get; private set; }
35 45
36 public WixBundleHarvestedDependencyProviderSymbol HarvestedDependencyProvider { get; private set; } 46 public WixBundleHarvestedDependencyProviderSymbol HarvestedDependencyProvider { get; private set; }
37 47
48 public List<WixBundlePayloadSymbol> Payloads { get; } = new List<WixBundlePayloadSymbol>();
49
38 public List<WixBundlePackageRelatedBundleSymbol> RelatedBundles { get; } = new List<WixBundlePackageRelatedBundleSymbol>(); 50 public List<WixBundlePackageRelatedBundleSymbol> RelatedBundles { get; } = new List<WixBundlePackageRelatedBundleSymbol>();
39 51
40 public List<ITrackedFile> TrackedFiles { get; } = new List<ITrackedFile>(); 52 public List<ITrackedFile> TrackedFiles { get; } = new List<ITrackedFile>();
@@ -114,9 +126,9 @@ namespace WixToolset.Core.Burn.Bundles
114 126
115 installSize = this.ProcessPackages(document, namespaceManager); 127 installSize = this.ProcessPackages(document, namespaceManager);
116 128
117 this.ProcessRelatedBundles(document, namespaceManager, sourcePath); 129 this.ProcessPayloads(document, namespaceManager, this.BundlePackagePayload.PayloadGeneration);
118 130
119 // TODO: Add payloads? 131 this.ProcessRelatedBundles(document, namespaceManager, sourcePath);
120 } 132 }
121 catch (Exception e) 133 catch (Exception e)
122 { 134 {
@@ -221,6 +233,158 @@ namespace WixToolset.Core.Burn.Bundles
221 return packageInstallSize; 233 return packageInstallSize;
222 } 234 }
223 235
236 private void ProcessPayloads(XmlDocument document, XmlNamespaceManager namespaceManager, BundlePackagePayloadGenerationType payloadGenerationType)
237 {
238 if (payloadGenerationType == BundlePackagePayloadGenerationType.None)
239 {
240 return;
241 }
242
243 var payloadNames = new HashSet<string>(this.PackagePayloadsById.Values.Select(p => p.Name), StringComparer.OrdinalIgnoreCase);
244
245 var containersById = new Dictionary<string, ManifestContainer>();
246
247 foreach (XmlElement containerElement in document.SelectNodes("/burn:BurnManifest/burn:Container", namespaceManager))
248 {
249 var container = new ManifestContainer();
250 container.Attached = containerElement.GetAttribute("Attached") == "yes";
251 container.DownloadUrl = containerElement.GetAttribute("DownloadUrl");
252 container.FilePath = containerElement.GetAttribute("FilePath");
253 container.Id = containerElement.GetAttribute("Id");
254 containersById.Add(container.Id, container);
255
256 if (container.Attached)
257 {
258 continue;
259 }
260
261 switch (payloadGenerationType)
262 {
263 case BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl:
264 if (!String.IsNullOrEmpty(container.DownloadUrl))
265 {
266 continue;
267 }
268 break;
269 }
270
271 // If we didn't find the Payload as an existing child of the package, we need to
272 // add it. We expect the file to exist on-disk in the same relative location as
273 // the bundle expects to find it...
274 container.IncludedAsPayload = true;
275 var containerName = container.FilePath;
276 var containerFullName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), containerName);
277
278 if (!payloadNames.Contains(containerFullName))
279 {
280 var generatedId = this.BackendHelper.GenerateIdentifier("hcp", this.PackagePayload.Id.Id, containerName);
281 var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, containerName, "Container", this.PackagePayload.SourceLineNumbers);
282
283 this.Payloads.Add(new WixBundlePayloadSymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
284 {
285 Name = containerFullName,
286 SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
287 Compressed = this.PackagePayload.Compressed,
288 UnresolvedSourceFile = containerFullName,
289 ContainerRef = this.PackagePayload.ContainerRef,
290 DownloadUrl = this.PackagePayload.DownloadUrl,
291 Packaging = this.PackagePayload.Packaging,
292 ParentPackagePayloadRef = this.PackagePayload.Id.Id,
293 });
294 }
295 }
296
297 foreach (XmlElement payloadElement in document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager))
298 {
299 var payload = new ManifestPayload();
300 payload.Container = payloadElement.GetAttribute("Container");
301 payload.DownloadUrl = payloadElement.GetAttribute("DownloadUrl");
302 payload.FilePath = payloadElement.GetAttribute("FilePath");
303 payload.Id = payloadElement.GetAttribute("Id");
304
305 if (payload.Container == null || !containersById.TryGetValue(payload.Container, out var container))
306 {
307 container = null;
308 }
309
310 if (container != null && container.IncludedAsPayload)
311 {
312 // Don't include payload if it's in a container that's already included.
313 continue;
314 }
315
316 switch (payloadGenerationType)
317 {
318 case BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl:
319 if (container != null || !String.IsNullOrEmpty(payload.DownloadUrl))
320 {
321 continue;
322 }
323 break;
324 case BundlePackagePayloadGenerationType.External:
325 if (container != null)
326 {
327 continue;
328 }
329 break;
330 }
331
332 // If we didn't find the Payload as an existing child of the package, we need to
333 // add it. We expect the file to exist on-disk in the same relative location as
334 // the bundle expects to find it...
335 var payloadName = payload.FilePath;
336 var payloadFullName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), payloadName);
337
338 if (!payloadNames.Contains(payloadFullName))
339 {
340 var generatedId = this.BackendHelper.GenerateIdentifier("hpp", this.PackagePayload.Id.Id, payloadName);
341 var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, payloadName, "Payload", this.PackagePayload.SourceLineNumbers);
342
343 this.Payloads.Add(new WixBundlePayloadSymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId))
344 {
345 Name = payloadFullName,
346 SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
347 Compressed = this.PackagePayload.Compressed,
348 UnresolvedSourceFile = payloadFullName,
349 ContainerRef = this.PackagePayload.ContainerRef,
350 DownloadUrl = this.PackagePayload.DownloadUrl,
351 Packaging = this.PackagePayload.Packaging,
352 ParentPackagePayloadRef = this.PackagePayload.Id.Id,
353 });
354 }
355 }
356 }
357
358 private string ResolveRelatedFile(string resolvedSource, string unresolvedSource, string relatedSource, string type, SourceLineNumber sourceLineNumbers)
359 {
360 var checkedPaths = new List<string>();
361
362 foreach (var extension in this.BackendExtensions)
363 {
364 var resolved = extension.ResolveRelatedFile(unresolvedSource, relatedSource, type, sourceLineNumbers);
365
366 if (resolved?.CheckedPaths != null)
367 {
368 checkedPaths.AddRange(resolved.CheckedPaths);
369 }
370
371 if (!String.IsNullOrEmpty(resolved?.Path))
372 {
373 return resolved?.Path;
374 }
375 }
376
377 var resolvedPath = Path.Combine(Path.GetDirectoryName(resolvedSource), relatedSource);
378
379 if (!File.Exists(resolvedPath))
380 {
381 checkedPaths.Add(resolvedPath);
382 this.Messaging.Write(ErrorMessages.FileNotFound(sourceLineNumbers, resolvedPath, type, checkedPaths));
383 }
384
385 return resolvedPath;
386 }
387
224 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath) 388 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath)
225 { 389 {
226 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers; 390 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
@@ -244,5 +408,23 @@ namespace WixToolset.Core.Burn.Bundles
244 }); 408 });
245 } 409 }
246 } 410 }
411
412 private class ManifestContainer
413 {
414 public bool Attached { get; set; }
415 public string DownloadUrl { get; set; }
416 public string FilePath { get; set; }
417 public string Id { get; set; }
418
419 public bool IncludedAsPayload { get; set; }
420 }
421
422 private class ManifestPayload
423 {
424 public string Container { get; set; }
425 public string DownloadUrl { get; set; }
426 public string FilePath { get; set; }
427 public string Id { get; set; }
428 }
247 } 429 }
248} 430}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs b/src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs
index 471262de..23da4e5e 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs
@@ -8,12 +8,13 @@ namespace WixToolset.Core.Burn.Bundles
8 8
9 internal class PackageFacade 9 internal class PackageFacade
10 { 10 {
11 public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol) 11 public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol, IntermediateSymbol specificPackagePayloadSymbol)
12 { 12 {
13 Debug.Assert(packageSymbol.Id.Id == specificPackageSymbol.Id.Id); 13 Debug.Assert(packageSymbol.Id.Id == specificPackageSymbol.Id.Id);
14 14
15 this.PackageSymbol = packageSymbol; 15 this.PackageSymbol = packageSymbol;
16 this.SpecificPackageSymbol = specificPackageSymbol; 16 this.SpecificPackageSymbol = specificPackageSymbol;
17 this.SpecificPackagePayloadSymbol = specificPackagePayloadSymbol;
17 } 18 }
18 19
19 public string PackageId => this.PackageSymbol.Id.Id; 20 public string PackageId => this.PackageSymbol.Id.Id;
@@ -21,5 +22,7 @@ namespace WixToolset.Core.Burn.Bundles
21 public WixBundlePackageSymbol PackageSymbol { get; } 22 public WixBundlePackageSymbol PackageSymbol { get; }
22 23
23 public IntermediateSymbol SpecificPackageSymbol { get; } 24 public IntermediateSymbol SpecificPackageSymbol { get; }
25
26 public IntermediateSymbol SpecificPackagePayloadSymbol { get; }
24 } 27 }
25} 28}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
index af37676c..36602886 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Burn.Bundles
7 using System.Linq; 7 using System.Linq;
8 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Data.Symbols; 9 using WixToolset.Data.Symbols;
10 using WixToolset.Extensibility;
10 using WixToolset.Extensibility.Data; 11 using WixToolset.Extensibility.Data;
11 using WixToolset.Extensibility.Services; 12 using WixToolset.Extensibility.Services;
12 13
@@ -15,16 +16,18 @@ namespace WixToolset.Core.Burn.Bundles
15 /// </summary> 16 /// </summary>
16 internal class ProcessBundlePackageCommand 17 internal class ProcessBundlePackageCommand
17 { 18 {
18 public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads, string intermediateFolder) 19 public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendBinderExtension> backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadSymbol> packagePayloads, string intermediateFolder)
19 { 20 {
20 this.ServiceProvider = serviceProvider; 21 this.ServiceProvider = serviceProvider;
21 this.Messaging = serviceProvider.GetService<IMessaging>(); 22 this.Messaging = serviceProvider.GetService<IMessaging>();
23 this.BackendExtensions = backendExtensions;
22 this.PackagePayloads = packagePayloads; 24 this.PackagePayloads = packagePayloads;
23 this.Section = section; 25 this.Section = section;
24 this.IntermediateFolder = intermediateFolder; 26 this.IntermediateFolder = intermediateFolder;
25 27
26 this.ChainPackage = facade.PackageSymbol; 28 this.ChainPackage = facade.PackageSymbol;
27 this.BundlePackage = (WixBundleBundlePackageSymbol)facade.SpecificPackageSymbol; 29 this.BundlePackage = (WixBundleBundlePackageSymbol)facade.SpecificPackageSymbol;
30 this.BundlePackagePayload = (WixBundleBundlePackagePayloadSymbol)facade.SpecificPackagePayloadSymbol;
28 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef]; 31 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef];
29 } 32 }
30 33
@@ -32,12 +35,16 @@ namespace WixToolset.Core.Burn.Bundles
32 35
33 private IMessaging Messaging { get; } 36 private IMessaging Messaging { get; }
34 37
38 private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
39
35 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; } 40 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; }
36 41
37 private WixBundlePackageSymbol ChainPackage { get; } 42 private WixBundlePackageSymbol ChainPackage { get; }
38 43
39 private WixBundleBundlePackageSymbol BundlePackage { get; } 44 private WixBundleBundlePackageSymbol BundlePackage { get; }
40 45
46 private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; }
47
41 private string PackageId => this.ChainPackage.Id.Id; 48 private string PackageId => this.ChainPackage.Id.Id;
42 49
43 private WixBundlePayloadSymbol PackagePayload { get; } 50 private WixBundlePayloadSymbol PackagePayload { get; }
@@ -100,7 +107,7 @@ namespace WixToolset.Core.Burn.Bundles
100 107
101 private WixBundleHarvestedBundlePackageSymbol HarvestPackage() 108 private WixBundleHarvestedBundlePackageSymbol HarvestPackage()
102 { 109 {
103 var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.IntermediateFolder, this.PackagePayload); 110 var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, this.IntermediateFolder, this.PackagePayload, this.BundlePackagePayload, this.PackagePayloads);
104 command.Execute(); 111 command.Execute();
105 112
106 this.TrackedFiles.AddRange(command.TrackedFiles); 113 this.TrackedFiles.AddRange(command.TrackedFiles);
@@ -112,6 +119,11 @@ namespace WixToolset.Core.Burn.Bundles
112 this.Section.AddSymbol(command.HarvestedBundlePackage); 119 this.Section.AddSymbol(command.HarvestedBundlePackage);
113 this.Section.AddSymbol(command.HarvestedDependencyProvider); 120 this.Section.AddSymbol(command.HarvestedDependencyProvider);
114 121
122 foreach (var payload in command.Payloads)
123 {
124 this.Section.AddSymbol(payload);
125 }
126
115 foreach (var relatedBundle in command.RelatedBundles) 127 foreach (var relatedBundle in command.RelatedBundles)
116 { 128 {
117 this.Section.AddSymbol(relatedBundle); 129 this.Section.AddSymbol(relatedBundle);
diff --git a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
index 6eed62eb..e812bc8a 100644
--- a/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
+++ b/src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
@@ -14,6 +14,7 @@ namespace WixToolset.Core.Burn.CommandLine
14 using WixToolset.Core.Native; 14 using WixToolset.Core.Native;
15 using WixToolset.Data; 15 using WixToolset.Data;
16 using WixToolset.Data.Symbols; 16 using WixToolset.Data.Symbols;
17 using WixToolset.Extensibility;
17 using WixToolset.Extensibility.Services; 18 using WixToolset.Extensibility.Services;
18 19
19 internal class RemotePayloadSubcommand : BurnSubcommandBase 20 internal class RemotePayloadSubcommand : BurnSubcommandBase
@@ -30,6 +31,9 @@ namespace WixToolset.Core.Burn.CommandLine
30 this.ServiceProvider = serviceProvider; 31 this.ServiceProvider = serviceProvider;
31 this.Messaging = serviceProvider.GetService<IMessaging>(); 32 this.Messaging = serviceProvider.GetService<IMessaging>();
32 this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>(); 33 this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>();
34 var extensionManager = serviceProvider.GetService<IExtensionManager>();
35
36 this.BackendExtensions = extensionManager.GetServices<IBurnBackendBinderExtension>();
33 } 37 }
34 38
35 private IServiceProvider ServiceProvider { get; } 39 private IServiceProvider ServiceProvider { get; }
@@ -38,6 +42,8 @@ namespace WixToolset.Core.Burn.CommandLine
38 42
39 private IPayloadHarvester PayloadHarvester { get; } 43 private IPayloadHarvester PayloadHarvester { get; }
40 44
45 private IReadOnlyCollection<IBurnBackendBinderExtension> BackendExtensions { get; }
46
41 private List<string> BasePaths { get; } = new List<string>(); 47 private List<string> BasePaths { get; } = new List<string>();
42 48
43 private string DownloadUrl { get; set; } 49 private string DownloadUrl { get; set; }
@@ -50,6 +56,8 @@ namespace WixToolset.Core.Burn.CommandLine
50 56
51 private WixBundlePackageType? PackageType { get; set; } 57 private WixBundlePackageType? PackageType { get; set; }
52 58
59 private BundlePackagePayloadGenerationType BundlePayloadGeneration { get; set; } = BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl;
60
53 private bool Recurse { get; set; } 61 private bool Recurse { get; set; }
54 62
55 private bool UseCertificate { get; set; } 63 private bool UseCertificate { get; set; }
@@ -107,6 +115,15 @@ namespace WixToolset.Core.Burn.CommandLine
107 this.BasePaths.Add(parser.GetNextArgumentAsDirectoryOrError(argument)); 115 this.BasePaths.Add(parser.GetNextArgumentAsDirectoryOrError(argument));
108 return true; 116 return true;
109 117
118 case "bundlepayloadgeneration":
119 var bundlePayloadGenerationValue = parser.GetNextArgumentOrError(argument);
120 if (Enum.TryParse<BundlePackagePayloadGenerationType>(bundlePayloadGenerationValue, ignoreCase: true, out var bundlePayloadGeneration))
121 {
122 this.BundlePayloadGeneration = bundlePayloadGeneration;
123 return true;
124 }
125 break;
126
110 case "du": 127 case "du":
111 case "downloadurl": 128 case "downloadurl":
112 this.DownloadUrl = parser.GetNextArgumentOrError(argument); 129 this.DownloadUrl = parser.GetNextArgumentOrError(argument);
@@ -175,152 +192,74 @@ namespace WixToolset.Core.Burn.CommandLine
175 private IEnumerable<XElement> HarvestRemotePayloads(IEnumerable<string> paths) 192 private IEnumerable<XElement> HarvestRemotePayloads(IEnumerable<string> paths)
176 { 193 {
177 var first = true; 194 var first = true;
178 var hashes = new Dictionary<string, CertificateHashes>(); 195 var hashes = this.GetHashes(paths);
179
180 if (this.UseCertificate)
181 {
182 hashes = CertificateHashes.Read(paths).Where(c => !String.IsNullOrEmpty(c.PublicKey) && !String.IsNullOrEmpty(c.Thumbprint) && c.Exception is null).ToDictionary(c => c.Path);
183 }
184 196
185 foreach (var path in paths) 197 foreach (var path in paths)
186 { 198 {
187 var element = this.CreateRemotePayloadElement(path, first); 199 var harvestedFile = this.HarvestFile(path, first, hashes);
188 first = false; 200 first = false;
189 201
190 if (element == null) 202 if (harvestedFile == null)
191 { 203 {
192 continue; 204 continue;
193 } 205 }
194 206
195 var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id")) 207 if (harvestedFile.PackagePayloads.Any())
196 { 208 {
197 SourceFile = new IntermediateFieldPathValue { Path = path }, 209 var packageHashes = this.GetHashes(harvestedFile.PackagePayloads.Select(x => x.SourceFile.Path));
198 };
199
200 this.PayloadHarvester.HarvestStandardInformation(payloadSymbol);
201
202 element.Add(new XAttribute("Name", Path.GetFileName(path)));
203
204 if (!String.IsNullOrEmpty(payloadSymbol.DisplayName))
205 {
206 element.Add(new XAttribute("ProductName", payloadSymbol.DisplayName));
207 }
208 210
209 if (!String.IsNullOrEmpty(payloadSymbol.Description)) 211 foreach (var payloadSymbol in harvestedFile.PackagePayloads)
210 {
211 element.Add(new XAttribute("Description", payloadSymbol.Description));
212 }
213
214 if (!String.IsNullOrEmpty(this.DownloadUrl))
215 {
216 var filename = this.GetRelativeFileName(payloadSymbol.SourceFile.Path);
217 var formattedUrl = String.Format(this.DownloadUrl, filename);
218
219 if (Uri.TryCreate(formattedUrl, UriKind.Absolute, out var url))
220 { 212 {
221 element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri)); 213 var harvestedPackageFile = this.HarvestFile(payloadSymbol.SourceFile.Path, false, packageHashes);
214 yield return harvestedPackageFile.Element;
222 } 215 }
223 } 216 }
224 217
225 if (hashes.TryGetValue(path, out var certificateHashes)) 218 yield return harvestedFile.Element;
226 { 219 }
227 element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey)); 220 }
228 element.Add(new XAttribute("CertificateThumbprint", certificateHashes.Thumbprint));
229 }
230
231 if (!String.IsNullOrEmpty(payloadSymbol.Hash))
232 {
233 element.Add(new XAttribute("Hash", payloadSymbol.Hash));
234 }
235
236 if (payloadSymbol.FileSize.HasValue)
237 {
238 element.Add(new XAttribute("Size", payloadSymbol.FileSize.Value));
239 }
240
241 if (!String.IsNullOrEmpty(payloadSymbol.Version))
242 {
243 element.Add(new XAttribute("Version", payloadSymbol.Version));
244 }
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 221
298 element.Add(bundleElement); 222 private Dictionary<string, CertificateHashes> GetHashes(IEnumerable<string> paths)
299 } 223 {
300 } 224 var hashes = new Dictionary<string, CertificateHashes>();
301 225
302 yield return element; 226 if (this.UseCertificate)
227 {
228 hashes = CertificateHashes.Read(paths)
229 .Where(c => !String.IsNullOrEmpty(c.PublicKey) && !String.IsNullOrEmpty(c.Thumbprint) && c.Exception is null)
230 .ToDictionary(c => c.Path);
303 } 231 }
232
233 return hashes;
304 } 234 }
305 235
306 private XElement CreateRemotePayloadElement(string path, bool firstHarvest) 236 private HarvestedFile HarvestFile(string path, bool isPackage, Dictionary<string, CertificateHashes> hashes)
307 { 237 {
308 if (firstHarvest) 238 XElement element;
239 WixBundlePackageType? packageType = null;
240
241 if (isPackage)
309 { 242 {
310 var extension = this.PackageType.HasValue ? this.PackageType.ToString() : Path.GetExtension(path); 243 var extension = this.PackageType.HasValue ? this.PackageType.ToString() : Path.GetExtension(path);
311 244
312 switch (extension.ToUpperInvariant()) 245 switch (extension.ToUpperInvariant())
313 { 246 {
314 case "BUNDLE": 247 case "BUNDLE":
315 return new XElement(BundlePackagePayloadName); 248 packageType = WixBundlePackageType.Bundle;
249 element = new XElement(BundlePackagePayloadName);
250 break;
316 251
317 case "EXE": 252 case "EXE":
318 case ".EXE": 253 case ".EXE":
319 return new XElement(ExePackagePayloadName); 254 packageType = WixBundlePackageType.Exe;
255 element = new XElement(ExePackagePayloadName);
256 break;
320 257
321 case "MSU": 258 case "MSU":
322 case ".MSU": 259 case ".MSU":
323 return new XElement(MsuPackagePayloadName); 260 packageType = WixBundlePackageType.Msu;
261 element = new XElement(MsuPackagePayloadName);
262 break;
324 263
325 default: 264 default:
326 this.Messaging.Write(BurnBackendErrors.UnsupportedRemotePackagePayload(extension, path)); 265 this.Messaging.Write(BurnBackendErrors.UnsupportedRemotePackagePayload(extension, path));
@@ -329,8 +268,75 @@ namespace WixToolset.Core.Burn.CommandLine
329 } 268 }
330 else 269 else
331 { 270 {
332 return new XElement(PayloadName); 271 element = new XElement(PayloadName);
333 } 272 }
273
274 var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id"))
275 {
276 SourceFile = new IntermediateFieldPathValue { Path = path },
277 Name = Path.GetFileName(path),
278 };
279
280 this.PayloadHarvester.HarvestStandardInformation(payloadSymbol);
281
282 element.Add(new XAttribute("Name", payloadSymbol.Name));
283
284 if (!String.IsNullOrEmpty(payloadSymbol.DisplayName))
285 {
286 element.Add(new XAttribute("ProductName", payloadSymbol.DisplayName));
287 }
288
289 if (!String.IsNullOrEmpty(payloadSymbol.Description))
290 {
291 element.Add(new XAttribute("Description", payloadSymbol.Description));
292 }
293
294 if (!String.IsNullOrEmpty(this.DownloadUrl))
295 {
296 var filename = this.GetRelativeFileName(payloadSymbol.SourceFile.Path);
297 var formattedUrl = String.Format(this.DownloadUrl, filename);
298
299 if (Uri.TryCreate(formattedUrl, UriKind.Absolute, out var url))
300 {
301 element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri));
302 }
303 }
304
305 if (hashes.TryGetValue(path, out var certificateHashes))
306 {
307 element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey));
308 element.Add(new XAttribute("CertificateThumbprint", certificateHashes.Thumbprint));
309 }
310
311 if (!String.IsNullOrEmpty(payloadSymbol.Hash))
312 {
313 element.Add(new XAttribute("Hash", payloadSymbol.Hash));
314 }
315
316 if (payloadSymbol.FileSize.HasValue)
317 {
318 element.Add(new XAttribute("Size", payloadSymbol.FileSize.Value));
319 }
320
321 if (!String.IsNullOrEmpty(payloadSymbol.Version))
322 {
323 element.Add(new XAttribute("Version", payloadSymbol.Version));
324 }
325
326 var harvestedFile = new HarvestedFile
327 {
328 Element = element,
329 PayloadSymbol = payloadSymbol,
330 };
331
332 switch (packageType)
333 {
334 case WixBundlePackageType.Bundle:
335 this.HarvestBundle(harvestedFile);
336 break;
337 }
338
339 return harvestedFile;
334 } 340 }
335 341
336 private string GetRelativeFileName(string path) 342 private string GetRelativeFileName(string path)
@@ -345,5 +351,75 @@ namespace WixToolset.Core.Burn.CommandLine
345 351
346 return Path.GetFileName(path); 352 return Path.GetFileName(path);
347 } 353 }
354
355 private void HarvestBundle(HarvestedFile harvestedFile)
356 {
357 var packagePayloadSymbol = new WixBundleBundlePackagePayloadSymbol(null, new Identifier(AccessModifier.Section, harvestedFile.PayloadSymbol.Id.Id))
358 {
359 PayloadGeneration = this.BundlePayloadGeneration,
360 };
361
362 var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, this.IntermediateFolder, harvestedFile.PayloadSymbol, packagePayloadSymbol, new Dictionary<string, WixBundlePayloadSymbol>());
363 command.Execute();
364
365 if (!this.Messaging.EncounteredError)
366 {
367 var bundleElement = new XElement(RemoteBundleName);
368
369 bundleElement.Add(new XAttribute("BundleId", command.HarvestedBundlePackage.BundleId));
370
371 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.DisplayName))
372 {
373 bundleElement.Add(new XAttribute("DisplayName", command.HarvestedBundlePackage.DisplayName));
374 }
375
376 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.EngineVersion))
377 {
378 bundleElement.Add(new XAttribute("EngineVersion", command.HarvestedBundlePackage.EngineVersion));
379 }
380
381 bundleElement.Add(new XAttribute("InstallSize", command.HarvestedBundlePackage.InstallSize));
382 bundleElement.Add(new XAttribute("ManifestNamespace", command.HarvestedBundlePackage.ManifestNamespace));
383 bundleElement.Add(new XAttribute("PerMachine", command.HarvestedBundlePackage.PerMachine ? "yes" : "no"));
384 bundleElement.Add(new XAttribute("ProviderKey", command.HarvestedDependencyProvider.ProviderKey));
385 bundleElement.Add(new XAttribute("ProtocolVersion", command.HarvestedBundlePackage.ProtocolVersion));
386
387 if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.Version))
388 {
389 bundleElement.Add(new XAttribute("Version", command.HarvestedBundlePackage.Version));
390 }
391
392 bundleElement.Add(new XAttribute("Win64", command.HarvestedBundlePackage.Win64 ? "yes" : "no"));
393
394 var setUpgradeCode = false;
395 foreach (var relatedBundle in command.RelatedBundles)
396 {
397 if (!setUpgradeCode && relatedBundle.Action == RelatedBundleActionType.Upgrade)
398 {
399 setUpgradeCode = true;
400 bundleElement.Add(new XAttribute("UpgradeCode", relatedBundle.BundleId));
401 continue;
402 }
403
404 var relatedBundleElement = new XElement(RemoteRelatedBundleName);
405
406 relatedBundleElement.Add(new XAttribute("Id", relatedBundle.BundleId));
407 relatedBundleElement.Add(new XAttribute("Action", relatedBundle.Action.ToString()));
408
409 bundleElement.Add(relatedBundleElement);
410 }
411
412 harvestedFile.PackagePayloads.AddRange(command.Payloads);
413
414 harvestedFile.Element.Add(bundleElement);
415 }
416 }
417
418 private class HarvestedFile
419 {
420 public XElement Element { get; set; }
421 public WixBundlePayloadSymbol PayloadSymbol { get; set; }
422 public List<WixBundlePayloadSymbol> PackagePayloads { get; } = new List<WixBundlePayloadSymbol>();
423 }
348 } 424 }
349} 425}
diff --git a/src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs b/src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs
index f4966f74..b269fb25 100644
--- a/src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs
+++ b/src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs
@@ -13,6 +13,17 @@ namespace WixToolset.Core.TestPackage
13 public static class XmlNodeExtensions 13 public static class XmlNodeExtensions
14 { 14 {
15 /// <summary> 15 /// <summary>
16 /// Adds root element around XML then returns it using single quotes and stripping all namespaces.
17 /// </summary>
18 /// <param name="xmlFragment"></param>
19 /// <param name="ignoredAttributesByElementName">Attributes for which the value should be set to '*'.</param>
20 /// <returns></returns>
21 public static string GetFragmentTestXml(this string xmlFragment, Dictionary<string, List<string>> ignoredAttributesByElementName = null)
22 {
23 return $"<root>{xmlFragment}</root>".GetTestXml(ignoredAttributesByElementName);
24 }
25
26 /// <summary>
16 /// Returns the node's outer XML using single quotes and stripping all namespaces. 27 /// Returns the node's outer XML using single quotes and stripping all namespaces.
17 /// </summary> 28 /// </summary>
18 /// <param name="node"></param> 29 /// <param name="node"></param>
diff --git a/src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs b/src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs
new file mode 100644
index 00000000..6b95de5f
--- /dev/null
+++ b/src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs
@@ -0,0 +1,108 @@
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
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Text;
8 using System.Xml.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11
12 internal class CompilerPackagePayload
13 {
14 public CompilerPackagePayload(CompilerPayload compilerPayload, WixBundlePackageType packageType)
15 {
16 this.CompilerPayload = compilerPayload;
17 this.PackageType = packageType;
18 }
19
20 private CompilerCore Core => this.CompilerPayload.Core;
21
22 private XElement Element => this.CompilerPayload.Element;
23
24 private SourceLineNumber SourceLineNumbers => this.CompilerPayload.SourceLineNumbers;
25
26 public CompilerPayload CompilerPayload { get; }
27
28 private WixBundlePackageType PackageType { get; }
29
30 public BundlePackagePayloadGenerationType? PayloadGenerationType { get; set; }
31
32 public IntermediateSymbol CreatePackagePayloadSymbol(ComplexReferenceParentType parentType, string parentId)
33 {
34 var payload = this.CompilerPayload.CreatePayloadSymbol(parentType, parentId);
35 if (payload == null)
36 {
37 return null;
38 }
39
40 IntermediateSymbol packagePayload;
41
42 switch (this.PackageType)
43 {
44 case WixBundlePackageType.Bundle:
45 packagePayload = this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(payload.SourceLineNumbers, payload.Id)
46 {
47 PayloadGeneration = this.PayloadGenerationType ?? BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl,
48 });
49 break;
50
51 case WixBundlePackageType.Exe:
52 packagePayload = this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(payload.SourceLineNumbers, payload.Id));
53 break;
54
55 case WixBundlePackageType.Msi:
56 packagePayload = this.Core.AddSymbol(new WixBundleMsiPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id));
57 break;
58
59 case WixBundlePackageType.Msp:
60 packagePayload = this.Core.AddSymbol(new WixBundleMspPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id));
61 break;
62
63 case WixBundlePackageType.Msu:
64 packagePayload = this.Core.AddSymbol(new WixBundleMsuPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id));
65 break;
66
67 default:
68 throw new NotImplementedException();
69 }
70
71 this.Core.CreateGroupAndOrderingRows(payload.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackagePayload, payload.Id?.Id, ComplexReferenceChildType.Unknown, null);
72
73 return packagePayload;
74 }
75
76 public bool ParsePayloadGeneration(XAttribute attrib)
77 {
78 if (this.PackageType != WixBundlePackageType.Bundle)
79 {
80 return false;
81 }
82
83 var value = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
84 switch (value)
85 {
86 case "none":
87 this.PayloadGenerationType = BundlePackagePayloadGenerationType.None;
88 break;
89 case "externalWithoutDownloadUrl":
90 this.PayloadGenerationType = BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl;
91 break;
92 case "external":
93 this.PayloadGenerationType = BundlePackagePayloadGenerationType.External;
94 break;
95 case "all":
96 this.PayloadGenerationType = BundlePackagePayloadGenerationType.All;
97 break;
98 case "":
99 break;
100 default:
101 this.Core.Write(ErrorMessages.IllegalAttributeValue(this.SourceLineNumbers, this.Element.Name.LocalName, attrib.Name.LocalName, value, "none", "externalWithoutDownloadUrl", "external", "all"));
102 break;
103 }
104
105 return true;
106 }
107 }
108}
diff --git a/src/wix/WixToolset.Core/Compile/CompilerPayload.cs b/src/wix/WixToolset.Core/Compile/CompilerPayload.cs
index cee9b377..2cdc70cf 100644
--- a/src/wix/WixToolset.Core/Compile/CompilerPayload.cs
+++ b/src/wix/WixToolset.Core/Compile/CompilerPayload.cs
@@ -11,8 +11,6 @@ namespace WixToolset.Core
11 11
12 internal class CompilerPayload 12 internal class CompilerPayload
13 { 13 {
14 public string Version { get; set; }
15
16 public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element) 14 public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element)
17 { 15 {
18 this.Core = core; 16 this.Core = core;
@@ -20,11 +18,11 @@ namespace WixToolset.Core
20 this.SourceLineNumbers = sourceLineNumbers; 18 this.SourceLineNumbers = sourceLineNumbers;
21 } 19 }
22 20
23 private CompilerCore Core { get; } 21 public CompilerCore Core { get; }
24 22
25 private XElement Element { get; } 23 public XElement Element { get; }
26 24
27 private SourceLineNumber SourceLineNumbers { get; } 25 public SourceLineNumber SourceLineNumbers { get; }
28 26
29 public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default; 27 public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default;
30 28
@@ -52,6 +50,8 @@ namespace WixToolset.Core
52 50
53 public string SourceFile { get; set; } 51 public string SourceFile { get; set; }
54 52
53 public string Version { get; set; }
54
55 private void CalculateAndVerifyFields() 55 private void CalculateAndVerifyFields()
56 { 56 {
57 var isRemote = this.IsRemoteAllowed && (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint) || !String.IsNullOrEmpty(this.Hash)); 57 var isRemote = this.IsRemoteAllowed && (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint) || !String.IsNullOrEmpty(this.Hash));
diff --git a/src/wix/WixToolset.Core/Compiler_Bundle.cs b/src/wix/WixToolset.Core/Compiler_Bundle.cs
index 88188f6b..3039a205 100644
--- a/src/wix/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/wix/WixToolset.Core/Compiler_Bundle.cs
@@ -1555,12 +1555,8 @@ namespace WixToolset.Core
1555 1555
1556 if (packageType.HasValue) 1556 if (packageType.HasValue)
1557 { 1557 {
1558 var compilerPayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null); 1558 var compilerPackagePayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null);
1559 var payloadSymbol = compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id); 1559 compilerPackagePayload.CreatePackagePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id);
1560 if (payloadSymbol != null)
1561 {
1562 this.CreatePackagePayloadSymbol(payloadSymbol.SourceLineNumbers, packageType.Value, payloadSymbol.Id, ComplexReferenceParentType.PayloadGroup, id);
1563 }
1564 } 1560 }
1565 } 1561 }
1566 else 1562 else
@@ -2030,7 +2026,7 @@ namespace WixToolset.Core
2030 string msuKB = null; 2026 string msuKB = null;
2031 var enableFeatureSelection = YesNoType.NotSet; 2027 var enableFeatureSelection = YesNoType.NotSet;
2032 var forcePerMachine = YesNoType.NotSet; 2028 var forcePerMachine = YesNoType.NotSet;
2033 CompilerPayload childPackageCompilerPayload = null; 2029 CompilerPackagePayload childCompilerPackagePayload = null;
2034 var bundle = YesNoType.NotSet; 2030 var bundle = YesNoType.NotSet;
2035 var slipstream = YesNoType.NotSet; 2031 var slipstream = YesNoType.NotSet;
2036 var hasPayloadInfo = false; 2032 var hasPayloadInfo = false;
@@ -2192,7 +2188,7 @@ namespace WixToolset.Core
2192 { 2188 {
2193 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); 2189 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2194 2190
2195 if (childPackageCompilerPayload != null) 2191 if (childCompilerPackagePayload != null)
2196 { 2192 {
2197 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); 2193 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
2198 } 2194 }
@@ -2201,12 +2197,12 @@ namespace WixToolset.Core
2201 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed")); 2197 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed"));
2202 } 2198 }
2203 2199
2204 childPackageCompilerPayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id); 2200 childCompilerPackagePayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id);
2205 } 2201 }
2206 2202
2207 if (compilerPayload.Id == null && childPackageCompilerPayload != null) 2203 if (compilerPayload.Id == null && childCompilerPackagePayload != null)
2208 { 2204 {
2209 compilerPayload.Id = childPackageCompilerPayload.Id; 2205 compilerPayload.Id = childCompilerPackagePayload.CompilerPayload.Id;
2210 } 2206 }
2211 2207
2212 compilerPayload.FinishCompilingPackage(); 2208 compilerPayload.FinishCompilingPackage();
@@ -2427,13 +2423,8 @@ namespace WixToolset.Core
2427 2423
2428 if (!this.Core.EncounteredError) 2424 if (!this.Core.EncounteredError)
2429 { 2425 {
2430 var packageCompilerPayload = childPackageCompilerPayload ?? (hasPayloadInfo ? compilerPayload : null); 2426 var compilerPackagePayload = childCompilerPackagePayload ?? (hasPayloadInfo ? new CompilerPackagePayload(compilerPayload, packageType) : null);
2431 if (packageCompilerPayload != null) 2427 compilerPackagePayload?.CreatePackagePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2432 {
2433 var payload = packageCompilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2434
2435 this.CreatePackagePayloadSymbol(sourceLineNumbers, packageType, payload.Id, ComplexReferenceParentType.Package, id);
2436 }
2437 2428
2438 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id)); 2429 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
2439 2430
@@ -2536,35 +2527,7 @@ namespace WixToolset.Core
2536 return id.Id; 2527 return id.Id;
2537 } 2528 }
2538 2529
2539 private void CreatePackagePayloadSymbol(SourceLineNumber sourceLineNumbers, WixBundlePackageType packageType, Identifier payloadId, ComplexReferenceParentType parentType, Identifier parentId) 2530 private CompilerPackagePayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId)
2540 {
2541 switch (packageType)
2542 {
2543 case WixBundlePackageType.Bundle:
2544 this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(sourceLineNumbers, payloadId));
2545 break;
2546
2547 case WixBundlePackageType.Exe:
2548 this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId));
2549 break;
2550
2551 case WixBundlePackageType.Msi:
2552 this.Core.AddSymbol(new WixBundleMsiPackagePayloadSymbol(sourceLineNumbers, payloadId));
2553 break;
2554
2555 case WixBundlePackageType.Msp:
2556 this.Core.AddSymbol(new WixBundleMspPackagePayloadSymbol(sourceLineNumbers, payloadId));
2557 break;
2558
2559 case WixBundlePackageType.Msu:
2560 this.Core.AddSymbol(new WixBundleMsuPackagePayloadSymbol(sourceLineNumbers, payloadId));
2561 break;
2562 }
2563
2564 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PackagePayload, payloadId?.Id, ComplexReferenceChildType.Unknown, null);
2565 }
2566
2567 private CompilerPayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId)
2568 { 2531 {
2569 sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node); 2532 sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node);
2570 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node) 2533 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
@@ -2572,6 +2535,7 @@ namespace WixToolset.Core
2572 Id = defaultId, 2535 Id = defaultId,
2573 IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu, 2536 IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu,
2574 }; 2537 };
2538 var compilerPackagePayload = new CompilerPackagePayload(compilerPayload, packageType);
2575 2539
2576 // This list lets us evaluate extension attributes *after* all core attributes 2540 // This list lets us evaluate extension attributes *after* all core attributes
2577 // have been parsed and dealt with, regardless of authoring order. 2541 // have been parsed and dealt with, regardless of authoring order.
@@ -2627,6 +2591,9 @@ namespace WixToolset.Core
2627 compilerPayload.ParseHash(attrib); 2591 compilerPayload.ParseHash(attrib);
2628 } 2592 }
2629 break; 2593 break;
2594 case "PayloadGeneration":
2595 allowed = compilerPackagePayload.ParsePayloadGeneration(attrib);
2596 break;
2630 case "ProductName": 2597 case "ProductName":
2631 allowed = compilerPayload.IsRemoteAllowed; 2598 allowed = compilerPayload.IsRemoteAllowed;
2632 if (allowed) 2599 if (allowed)
@@ -2691,11 +2658,18 @@ namespace WixToolset.Core
2691 2658
2692 if (allowed) 2659 if (allowed)
2693 { 2660 {
2661 if (compilerPackagePayload.PayloadGenerationType.HasValue)
2662 {
2663 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2664 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle", "PayloadGeneration"));
2665 }
2666
2694 if (remoteBundleSeen) 2667 if (remoteBundleSeen)
2695 { 2668 {
2696 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); 2669 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2697 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle")); 2670 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2698 } 2671 }
2672
2699 this.ParseRemoteBundleElement(child, compilerPayload.Id.Id); 2673 this.ParseRemoteBundleElement(child, compilerPayload.Id.Id);
2700 remoteBundleSeen = true; 2674 remoteBundleSeen = true;
2701 } 2675 }
@@ -2723,7 +2697,7 @@ namespace WixToolset.Core
2723 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "RemoteBundle")); 2697 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2724 } 2698 }
2725 2699
2726 return compilerPayload; 2700 return compilerPackagePayload;
2727 } 2701 }
2728 2702
2729 private void ParseRemoteBundleElement(XElement node, string packagePayloadId) 2703 private void ParseRemoteBundleElement(XElement node, string packagePayloadId)
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
index 63659936..eb5cf211 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
@@ -67,6 +67,7 @@ namespace WixToolsetTest.CoreIntegration
67 { 67 {
68 "build", 68 "build",
69 Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"), 69 Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"),
70 "-bindpath", Path.Combine(folder, ".Data"),
70 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), 71 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
71 "-bindpath", binFolder, 72 "-bindpath", binFolder,
72 "-intermediateFolder", parentIntermediateFolder, 73 "-intermediateFolder", parentIntermediateFolder,
@@ -105,6 +106,7 @@ namespace WixToolsetTest.CoreIntegration
105 "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" + 106 "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
106 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" + 107 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
107 "<PayloadRef Id='chain.exe' />" + 108 "<PayloadRef Id='chain.exe' />" +
109 "<PayloadRef Id='payP6wZpeHEAZbDUQPEKeCpQ_9bN.4' />" +
108 "</BundlePackage>", 110 "</BundlePackage>",
109 }, bundlePackages); 111 }, bundlePackages);
110 112
@@ -129,7 +131,7 @@ namespace WixToolsetTest.CoreIntegration
129 .ToArray(); 131 .ToArray();
130 WixAssert.CompareLineByLine(new string[] 132 WixAssert.CompareLineByLine(new string[]
131 { 133 {
132 "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundle' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='yes' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='yes' Version='1.0.0.0' Cache='keep' />", 134 "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundle' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='yes' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='no' Version='1.0.0.0' Cache='keep' />",
133 }, packageElements); 135 }, packageElements);
134 136
135 // grandparent.exe 137 // grandparent.exe
@@ -178,6 +180,20 @@ namespace WixToolsetTest.CoreIntegration
178 "</BundlePackage>", 180 "</BundlePackage>",
179 }, bundlePackages); 181 }, bundlePackages);
180 182
183 ignoreAttributesByElementName = new Dictionary<string, List<string>>
184 {
185 { "Payload", new List<string> { "FileSize", "Hash" } },
186 };
187 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload")
188 .Cast<XmlElement>()
189 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
190 .ToArray();
191 WixAssert.CompareLineByLine(new string[]
192 {
193 "<Payload Id='payP6wZpeHEAZbDUQPEKeCpQ_9bN.4' FilePath='signed_cab1.cab' FileSize='*' Hash='*' Packaging='external' SourcePath='signed_cab1.cab' />",
194 "<Payload Id='chain.exe' FilePath='chain.exe' FileSize='*' Hash='*' Packaging='external' SourcePath='chain.exe' />",
195 }, payloads);
196
181 registrations = grandparentExtractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration") 197 registrations = grandparentExtractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
182 .Cast<XmlElement>() 198 .Cast<XmlElement>()
183 .Select(e => e.GetTestXml()) 199 .Select(e => e.GetTestXml())
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
index 41791bd0..7126b0d5 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
@@ -2,6 +2,7 @@
2 2
3namespace WixToolsetTest.CoreIntegration 3namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System.Collections.Generic;
5 using System.IO; 6 using System.IO;
6 using System.Linq; 7 using System.Linq;
7 using WixBuildTools.TestSupport; 8 using WixBuildTools.TestSupport;
@@ -11,6 +12,142 @@ namespace WixToolsetTest.CoreIntegration
11 public class RemotePayloadFixture 12 public class RemotePayloadFixture
12 { 13 {
13 [Fact] 14 [Fact]
15 public void CanGetRemoteBundlePayload()
16 {
17 var folder = TestData.Get(@"TestData");
18
19 using (var fs = new DisposableFileSystem())
20 {
21 var outputFolder = fs.GetFolder();
22 var intermediateFolder = Path.Combine(outputFolder, "obj");
23 var exePath = Path.Combine(outputFolder, @"bin\test.exe");
24
25 var result = WixRunner.Execute(new[]
26 {
27 "build",
28 Path.Combine(folder, "RemotePayload", "DiversePayloadsBundle.wxs"),
29 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
30 "-bindpath", Path.Combine(folder, ".Data"),
31 "-intermediateFolder", intermediateFolder,
32 "-o", exePath,
33 });
34
35 result.AssertSuccess();
36
37 File.Copy(Path.Combine(folder, ".Data", "signed_bundle_engine.exe"), Path.Combine(outputFolder, "bin", "signed_bundle_engine.exe"));
38
39 // None
40 var noneOutFile = Path.Combine(outputFolder, "none_out.xml");
41
42 result = WixRunner.Execute(new[]
43 {
44 "burn", "remotepayload",
45 exePath,
46 "-o", noneOutFile,
47 "-packagetype", "bundle",
48 "-bundlepayloadgeneration", "none",
49 });
50
51 result.AssertSuccess();
52
53 var xml = File.ReadAllText(noneOutFile);
54 var ignoreAttributesByElementName = new Dictionary<string, List<string>>
55 {
56 { "BundlePackagePayload", new List<string> { "Size", "Hash" } },
57 { "RemoteBundle", new List<string> { "BundleId", "EngineVersion", "ProviderKey" } },
58 { "Payload", new List<string> { "Size", "Hash" } },
59 };
60 WixAssert.StringEqual(
61 "<root>" +
62 "<BundlePackagePayload Name='test.exe' ProductName='DiversePayloadsBundle' Description='DiversePayloadsBundle' Hash='*' Size='*' Version='1.0.0.0'>" +
63 "<RemoteBundle BundleId='*' DisplayName='DiversePayloadsBundle' EngineVersion='*' InstallSize='3790116' ManifestNamespace='http://wixtoolset.org/schemas/v4/2008/Burn' PerMachine='yes' ProviderKey='*' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{FEF1D2B8-4737-4A2A-9F91-77F7294FB55B}' />" +
64 "</BundlePackagePayload>" +
65 "</root>", xml.GetFragmentTestXml(ignoreAttributesByElementName));
66
67 // ExternalWithoutDownloadUrl
68 var externalWithoutDownloadUrlOutFile = Path.Combine(outputFolder, "externalWithoutDownloadUrl_out.xml");
69
70 result = WixRunner.Execute(new[]
71 {
72 "burn", "remotepayload",
73 exePath,
74 "-o", externalWithoutDownloadUrlOutFile,
75 "-packagetype", "bundle",
76 "-bundlepayloadgeneration", "externalWithoutDownloadUrl",
77 });
78
79 result.AssertSuccess();
80
81 xml = File.ReadAllText(externalWithoutDownloadUrlOutFile);
82 WixAssert.StringEqual(
83 "<root>" +
84 "<Payload Name='External.cab' Hash='*' Size='*' />" +
85 "<Payload Name='test.msi' Hash='*' Size='*' />" +
86 "<Payload Name='test.txt' Hash='*' Size='*' />" +
87 "<Payload Name='Shared.dll' Hash='*' Size='*' />" +
88 "<BundlePackagePayload Name='test.exe' ProductName='DiversePayloadsBundle' Description='DiversePayloadsBundle' Hash='*' Size='*' Version='1.0.0.0'>" +
89 "<RemoteBundle BundleId='*' DisplayName='DiversePayloadsBundle' EngineVersion='*' InstallSize='3790116' ManifestNamespace='http://wixtoolset.org/schemas/v4/2008/Burn' PerMachine='yes' ProviderKey='*' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{FEF1D2B8-4737-4A2A-9F91-77F7294FB55B}' />" +
90 "</BundlePackagePayload>" +
91 "</root>", xml.GetFragmentTestXml(ignoreAttributesByElementName));
92
93 // External
94 var externalOutFile = Path.Combine(outputFolder, "external_out.xml");
95
96 result = WixRunner.Execute(new[]
97 {
98 "burn", "remotepayload",
99 exePath,
100 "-o", externalOutFile,
101 "-packagetype", "bundle",
102 "-bundlepayloadgeneration", "external",
103 });
104
105 result.AssertSuccess();
106
107 xml = File.ReadAllText(externalOutFile);
108 WixAssert.StringEqual(
109 "<root>" +
110 "<Payload Name='External.cab' Hash='*' Size='*' />" +
111 "<Payload Name='Windows8.1-KB2937592-x86.msu' Hash='*' Size='*' />" +
112 "<Payload Name='test.msi' Hash='*' Size='*' />" +
113 "<Payload Name='test.txt' Hash='*' Size='*' />" +
114 "<Payload Name='Shared.dll' Hash='*' Size='*' />" +
115 "<BundlePackagePayload Name='test.exe' ProductName='DiversePayloadsBundle' Description='DiversePayloadsBundle' Hash='*' Size='*' Version='1.0.0.0'>" +
116 "<RemoteBundle BundleId='*' DisplayName='DiversePayloadsBundle' EngineVersion='*' InstallSize='3790116' ManifestNamespace='http://wixtoolset.org/schemas/v4/2008/Burn' PerMachine='yes' ProviderKey='*' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{FEF1D2B8-4737-4A2A-9F91-77F7294FB55B}' />" +
117 "</BundlePackagePayload>" +
118 "</root>", xml.GetFragmentTestXml(ignoreAttributesByElementName));
119
120 // All
121 var allOutFile = Path.Combine(outputFolder, "all_out.xml");
122
123 result = WixRunner.Execute(new[]
124 {
125 "burn", "remotepayload",
126 exePath,
127 "-o", allOutFile,
128 "-packagetype", "bundle",
129 "-bundlepayloadgeneration", "all",
130 });
131
132 result.AssertSuccess();
133
134 xml = File.ReadAllText(allOutFile);
135 WixAssert.StringEqual(
136 "<root>" +
137 "<Payload Name='External.cab' Hash='*' Size='*' />" +
138 "<Payload Name='signed_bundle_engine.exe' ProductName='~TestBundle' Description='~TestBundle' Hash='*' Size='*' Version='1.0.0.0' />" +
139 "<Payload Name='Windows8.1-KB2937592-x86.msu' Hash='*' Size='*' />" +
140 "<Payload Name='test.msi' Hash='*' Size='*' />" +
141 "<Payload Name='test.txt' Hash='*' Size='*' />" +
142 "<Payload Name='Shared.dll' Hash='*' Size='*' />" +
143 "<BundlePackagePayload Name='test.exe' ProductName='DiversePayloadsBundle' Description='DiversePayloadsBundle' Hash='*' Size='*' Version='1.0.0.0'>" +
144 "<RemoteBundle BundleId='*' DisplayName='DiversePayloadsBundle' EngineVersion='*' InstallSize='3790116' ManifestNamespace='http://wixtoolset.org/schemas/v4/2008/Burn' PerMachine='yes' ProviderKey='*' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{FEF1D2B8-4737-4A2A-9F91-77F7294FB55B}' />" +
145 "</BundlePackagePayload>" +
146 "</root>", xml.GetFragmentTestXml(ignoreAttributesByElementName));
147 }
148 }
149
150 [Fact]
14 public void CanGetRemoteV3BundlePayload() 151 public void CanGetRemoteV3BundlePayload()
15 { 152 {
16 var folder = TestData.Get(@"TestData"); 153 var folder = TestData.Get(@"TestData");
@@ -181,7 +318,7 @@ namespace WixToolsetTest.CoreIntegration
181 "-recurse", 318 "-recurse",
182 "-du", "https://www.example.com/files/{0}", 319 "-du", "https://www.example.com/files/{0}",
183 Path.Combine(folder, ".Data", "burn.exe"), 320 Path.Combine(folder, ".Data", "burn.exe"),
184 Path.Combine(folder, "RemotePayload", "*"), 321 Path.Combine(folder, "RemotePayload", "recurse", "*"),
185 "-basepath", folder, 322 "-basepath", folder,
186 "-bp", Path.Combine(folder, ".Data"), 323 "-bp", Path.Combine(folder, ".Data"),
187 "-o", outFile 324 "-o", outFile
@@ -195,9 +332,9 @@ namespace WixToolsetTest.CoreIntegration
195 WixAssert.CompareLineByLine(new[] 332 WixAssert.CompareLineByLine(new[]
196 { 333 {
197 @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />", 334 @"<ExePackagePayload Name='burn.exe' ProductName='Windows Installer XML Toolset' Description='WiX Toolset Bootstrapper' DownloadUrl='https://www.example.com/files/burn.exe' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Size='463360' Version='3.14.1703.0' />",
198 @"<Payload Name='a.dat' DownloadUrl='https://www.example.com/files/RemotePayload/a.dat' Hash='D13926E5CBE5ED8B46133F9199FAF2FF25B25981C67A31AE2BC3F6C20390FACBFADCD89BD22D3445D95B989C8EACFB1E68DB634BECB5C9624865BA453BCE362A' Size='16' />", 335 @"<Payload Name='a.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/a.dat' Hash='D13926E5CBE5ED8B46133F9199FAF2FF25B25981C67A31AE2BC3F6C20390FACBFADCD89BD22D3445D95B989C8EACFB1E68DB634BECB5C9624865BA453BCE362A' Size='16' />",
199 @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/subfolder/b.dat' Hash='5F94707BC29ADFE3B9615E6753388707FD0B8F5FD9EEEC2B17E21E72F1635FF7D7A101E7D14F614E111F263CB9AC4D0940BE1247881A7844F226D6C400293D8E' Size='37' />", 336 @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/b.dat' Hash='5F94707BC29ADFE3B9615E6753388707FD0B8F5FD9EEEC2B17E21E72F1635FF7D7A101E7D14F614E111F263CB9AC4D0940BE1247881A7844F226D6C400293D8E' Size='37' />",
200 @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />", 337 @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />",
201 }, elements); 338 }, elements);
202 } 339 }
203 } 340 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
index a1b182d3..3039b674 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
@@ -1,10 +1,12 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Bundle Name="BundlePackageBundle" Version="1.0.1.0" Manufacturer="Example Corporation" UpgradeCode="{4BE34BEE-CA23-488E-96A0-B15878E3654B}"> 2 <Bundle Name="BundlePackageBundle" Version="1.0.1.0" Manufacturer="Example Corporation" UpgradeCode="{4BE34BEE-CA23-488E-96A0-B15878E3654B}" Compressed="no">
3 <BootstrapperApplication> 3 <BootstrapperApplication>
4 <BootstrapperApplicationDll SourceFile="fakeba.dll" /> 4 <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 </BootstrapperApplication> 5 </BootstrapperApplication>
6 <Chain> 6 <Chain>
7 <BundlePackage SourceFile="chain.exe" Visible="no" Permanent="yes" /> 7 <BundlePackage SourceFile="chain.exe" Visible="no" Permanent="yes">
8 <Payload SourceFile="signed_cab1.cab" />
9 </BundlePackage>
8 </Chain> 10 </Chain>
9 </Bundle> 11 </Bundle>
10</Wix> 12</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs
new file mode 100644
index 00000000..f65b5db6
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs
@@ -0,0 +1,24 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Bundle Name="DiversePayloadsBundle" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="{FEF1D2B8-4737-4A2A-9F91-77F7294FB55B}">
3 <BootstrapperApplication>
4 <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 </BootstrapperApplication>
6 <Chain>
7 <PackageGroupRef Id="ExternalContainerPackages" />
8 <ExePackage SourceFile="signed_bundle_engine.exe" DetectCondition="none" Permanent="yes" />
9 <MsuPackage SourceFile="Windows8.1-KB2937592-x86.msu" DetectCondition="none" Permanent="yes" Compressed="no" DownloadUrl="http://example.com/test.msu" />
10 <MsiPackage SourceFile="test.msi" Compressed="no" />
11 </Chain>
12 <Container Name="External.cab">
13 <PackageGroupRef Id="ExternalContainerPackages" />
14 </Container>
15 </Bundle>
16
17 <Fragment>
18 <PackageGroup Id="ExternalContainerPackages">
19 <ExePackage SourceFile="burn.exe" DetectCondition="none" Permanent="yes">
20 <Payload SourceFile="signed_cab1.cab" />
21 </ExePackage>
22 </PackageGroup>
23 </Fragment>
24</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat
index a96b19b3..a96b19b3 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat
index 35c4c043..35c4c043 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat
index 937aa91f..937aa91f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat