@joebigelow / wix-1 / commits / d5744da0

Harvest BundlePackage payloads.

Fixes 6757

Sean Hall committed May 5, 2022 at 18:08 UTC d5744da0117199f23bf72f5c2ba7cd1c6f52e173
18 files changed +752 -187
src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackagePayloadSymbol.cs
+16
@@ -10,6 +10,7 @@ namespace WixToolset.Data
10 SymbolDefinitionType.WixBundleBundlePackagePayload,
11 new IntermediateFieldDefinition[]
12 {
13 + new IntermediateFieldDefinition(nameof(WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration), IntermediateFieldType.Number),
14 },
15 typeof(WixBundleBundlePackagePayloadSymbol));
16 }
@@ -19,6 +20,15 @@ namespace WixToolset.Data.Symbols
20 {
21 public enum WixBundleBundlePackagePayloadSymbolFields
22 {
23 + PayloadGeneration,
24 + }
25 +
26 + public enum BundlePackagePayloadGenerationType
27 + {
28 + None,
29 + ExternalWithoutDownloadUrl,
30 + External,
31 + All,
32 }
33
34 public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol
@@ -32,5 +42,11 @@ namespace WixToolset.Data.Symbols
42 }
43
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 + }
51 }
52 }
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+1 -1
@@ -180,7 +180,7 @@ namespace WixToolset.Core.Burn
180 {
181 case WixBundlePackageType.Bundle:
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();
185
186 trackedFiles.AddRange(command.TrackedFiles);
src/wix/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
+9 -5
@@ -2,6 +2,7 @@
2
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 + using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
@@ -147,7 +148,7 @@ namespace WixToolset.Core.Burn.Bundles
148 case WixBundlePackageType.Bundle:
149 if (bundlePackages.TryGetValue(id, out var bundlePackage))
150 {
150 - facades.Add(new PackageFacade(package, bundlePackage));
151 + facades.Add(new PackageFacade(package, bundlePackage, packagePayload));
152 }
153 else
154 {
@@ -158,7 +159,7 @@ namespace WixToolset.Core.Burn.Bundles
159 case WixBundlePackageType.Exe:
160 if (exePackages.TryGetValue(id, out var exePackage))
161 {
161 - facades.Add(new PackageFacade(package, exePackage));
162 + facades.Add(new PackageFacade(package, exePackage, packagePayload));
163 }
164 else
165 {
@@ -169,7 +170,7 @@ namespace WixToolset.Core.Burn.Bundles
170 case WixBundlePackageType.Msi:
171 if (msiPackages.TryGetValue(id, out var msiPackage))
172 {
172 - facades.Add(new PackageFacade(package, msiPackage));
173 + facades.Add(new PackageFacade(package, msiPackage, packagePayload));
174 }
175 else
176 {
@@ -180,7 +181,7 @@ namespace WixToolset.Core.Burn.Bundles
181 case WixBundlePackageType.Msp:
182 if (mspPackages.TryGetValue(id, out var mspPackage))
183 {
183 - facades.Add(new PackageFacade(package, mspPackage));
184 + facades.Add(new PackageFacade(package, mspPackage, packagePayload));
185 }
186 else
187 {
@@ -191,13 +192,16 @@ namespace WixToolset.Core.Burn.Bundles
192 case WixBundlePackageType.Msu:
193 if (msuPackages.TryGetValue(id, out var msuPackage))
194 {
194 - facades.Add(new PackageFacade(package, msuPackage));
195 + facades.Add(new PackageFacade(package, msuPackage, packagePayload));
196 }
197 else
198 {
199 this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsuPackage", id));
200 }
201 break;
202 +
203 + default:
204 + throw new NotImplementedException();
205 }
206 }
207
src/wix/WixToolset.Core.Burn/Bundles/HarvestBundlePackageCommand.cs
+186 -4
@@ -5,36 +5,48 @@ namespace WixToolset.Core.Burn.Bundles
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 - using System.Text;
8 + using System.Linq;
9 using System.Xml;
10 using WixToolset.Data;
11 using WixToolset.Data.Symbols;
12 + using WixToolset.Extensibility;
13 using WixToolset.Extensibility.Data;
14 using WixToolset.Extensibility.Services;
15
16 internal class HarvestBundlePackageCommand
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)
19 {
20 this.Messaging = serviceProvider.GetService<IMessaging>();
21 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
22 + this.BackendExtensions = backendExtensions;
23 this.IntermediateFolder = intermediateFolder;
24
25 this.PackagePayload = payloadSymbol;
26 + this.BundlePackagePayload = packagePayloadSymbol;
27 + this.PackagePayloadsById = packagePayloadsById;
28 }
29
30 private IMessaging Messaging { get; }
31
32 private IBackendHelper BackendHelper { get; }
33
34 + private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
35 +
36 private string IntermediateFolder { get; }
37
38 private WixBundlePayloadSymbol PackagePayload { get; }
39
40 + private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; }
41 +
42 + private Dictionary<string, WixBundlePayloadSymbol> PackagePayloadsById { get; }
43 +
44 public WixBundleHarvestedBundlePackageSymbol HarvestedBundlePackage { get; private set; }
45
46 public WixBundleHarvestedDependencyProviderSymbol HarvestedDependencyProvider { get; private set; }
47
48 + public List<WixBundlePayloadSymbol> Payloads { get; } = new List<WixBundlePayloadSymbol>();
49 +
50 public List<WixBundlePackageRelatedBundleSymbol> RelatedBundles { get; } = new List<WixBundlePackageRelatedBundleSymbol>();
51
52 public List<ITrackedFile> TrackedFiles { get; } = new List<ITrackedFile>();
@@ -114,9 +126,9 @@ namespace WixToolset.Core.Burn.Bundles
126
127 installSize = this.ProcessPackages(document, namespaceManager);
128
117 - this.ProcessRelatedBundles(document, namespaceManager, sourcePath);
129 + this.ProcessPayloads(document, namespaceManager, this.BundlePackagePayload.PayloadGeneration);
130
119 - // TODO: Add payloads?
131 + this.ProcessRelatedBundles(document, namespaceManager, sourcePath);
132 }
133 catch (Exception e)
134 {
@@ -221,6 +233,158 @@ namespace WixToolset.Core.Burn.Bundles
233 return packageInstallSize;
234 }
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 +
388 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath)
389 {
390 var sourceLineNumbers = this.PackagePayload.SourceLineNumbers;
@@ -244,5 +408,23 @@ namespace WixToolset.Core.Burn.Bundles
408 });
409 }
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 + }
429 }
430 }
src/wix/WixToolset.Core.Burn/Bundles/PackageFacade.cs
+4 -1
@@ -8,12 +8,13 @@ namespace WixToolset.Core.Burn.Bundles
8
9 internal class PackageFacade
10 {
11 - public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol)
11 + public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol, IntermediateSymbol specificPackagePayloadSymbol)
12 {
13 Debug.Assert(packageSymbol.Id.Id == specificPackageSymbol.Id.Id);
14
15 this.PackageSymbol = packageSymbol;
16 this.SpecificPackageSymbol = specificPackageSymbol;
17 + this.SpecificPackagePayloadSymbol = specificPackagePayloadSymbol;
18 }
19
20 public string PackageId => this.PackageSymbol.Id.Id;
@@ -21,5 +22,7 @@ namespace WixToolset.Core.Burn.Bundles
22 public WixBundlePackageSymbol PackageSymbol { get; }
23
24 public IntermediateSymbol SpecificPackageSymbol { get; }
25 +
26 + public IntermediateSymbol SpecificPackagePayloadSymbol { get; }
27 }
28 }
src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+14 -2
@@ -7,6 +7,7 @@ namespace WixToolset.Core.Burn.Bundles
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 + using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services;
13
@@ -15,16 +16,18 @@ namespace WixToolset.Core.Burn.Bundles
16 /// </summary>
17 internal class ProcessBundlePackageCommand
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)
20 {
21 this.ServiceProvider = serviceProvider;
22 this.Messaging = serviceProvider.GetService<IMessaging>();
23 + this.BackendExtensions = backendExtensions;
24 this.PackagePayloads = packagePayloads;
25 this.Section = section;
26 this.IntermediateFolder = intermediateFolder;
27
28 this.ChainPackage = facade.PackageSymbol;
29 this.BundlePackage = (WixBundleBundlePackageSymbol)facade.SpecificPackageSymbol;
30 + this.BundlePackagePayload = (WixBundleBundlePackagePayloadSymbol)facade.SpecificPackagePayloadSymbol;
31 this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef];
32 }
33
@@ -32,12 +35,16 @@ namespace WixToolset.Core.Burn.Bundles
35
36 private IMessaging Messaging { get; }
37
38 + private IEnumerable<IBurnBackendBinderExtension> BackendExtensions { get; }
39 +
40 private Dictionary<string, WixBundlePayloadSymbol> PackagePayloads { get; }
41
42 private WixBundlePackageSymbol ChainPackage { get; }
43
44 private WixBundleBundlePackageSymbol BundlePackage { get; }
45
46 + private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; }
47 +
48 private string PackageId => this.ChainPackage.Id.Id;
49
50 private WixBundlePayloadSymbol PackagePayload { get; }
@@ -100,7 +107,7 @@ namespace WixToolset.Core.Burn.Bundles
107
108 private WixBundleHarvestedBundlePackageSymbol HarvestPackage()
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);
111 command.Execute();
112
113 this.TrackedFiles.AddRange(command.TrackedFiles);
@@ -112,6 +119,11 @@ namespace WixToolset.Core.Burn.Bundles
119 this.Section.AddSymbol(command.HarvestedBundlePackage);
120 this.Section.AddSymbol(command.HarvestedDependencyProvider);
121
122 + foreach (var payload in command.Payloads)
123 + {
124 + this.Section.AddSymbol(payload);
125 + }
126 +
127 foreach (var relatedBundle in command.RelatedBundles)
128 {
129 this.Section.AddSymbol(relatedBundle);
src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
+190 -114
@@ -14,6 +14,7 @@ namespace WixToolset.Core.Burn.CommandLine
14 using WixToolset.Core.Native;
15 using WixToolset.Data;
16 using WixToolset.Data.Symbols;
17 + using WixToolset.Extensibility;
18 using WixToolset.Extensibility.Services;
19
20 internal class RemotePayloadSubcommand : BurnSubcommandBase
@@ -30,6 +31,9 @@ namespace WixToolset.Core.Burn.CommandLine
31 this.ServiceProvider = serviceProvider;
32 this.Messaging = serviceProvider.GetService<IMessaging>();
33 this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>();
34 + var extensionManager = serviceProvider.GetService<IExtensionManager>();
35 +
36 + this.BackendExtensions = extensionManager.GetServices<IBurnBackendBinderExtension>();
37 }
38
39 private IServiceProvider ServiceProvider { get; }
@@ -38,6 +42,8 @@ namespace WixToolset.Core.Burn.CommandLine
42
43 private IPayloadHarvester PayloadHarvester { get; }
44
45 + private IReadOnlyCollection<IBurnBackendBinderExtension> BackendExtensions { get; }
46 +
47 private List<string> BasePaths { get; } = new List<string>();
48
49 private string DownloadUrl { get; set; }
@@ -50,6 +56,8 @@ namespace WixToolset.Core.Burn.CommandLine
56
57 private WixBundlePackageType? PackageType { get; set; }
58
59 + private BundlePackagePayloadGenerationType BundlePayloadGeneration { get; set; } = BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl;
60 +
61 private bool Recurse { get; set; }
62
63 private bool UseCertificate { get; set; }
@@ -107,6 +115,15 @@ namespace WixToolset.Core.Burn.CommandLine
115 this.BasePaths.Add(parser.GetNextArgumentAsDirectoryOrError(argument));
116 return true;
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 +
127 case "du":
128 case "downloadurl":
129 this.DownloadUrl = parser.GetNextArgumentOrError(argument);
@@ -175,152 +192,74 @@ namespace WixToolset.Core.Burn.CommandLine
192 private IEnumerable<XElement> HarvestRemotePayloads(IEnumerable<string> paths)
193 {
194 var first = true;
178 - var hashes = new Dictionary<string, CertificateHashes>();
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 - }
195 + var hashes = this.GetHashes(paths);
196
197 foreach (var path in paths)
198 {
187 - var element = this.CreateRemotePayloadElement(path, first);
199 + var harvestedFile = this.HarvestFile(path, first, hashes);
200 first = false;
201
190 - if (element == null)
202 + if (harvestedFile == null)
203 {
204 continue;
205 }
206
195 - var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id"))
207 + if (harvestedFile.PackagePayloads.Any())
208 {
197 - SourceFile = new IntermediateFieldPathValue { Path = 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 - }
209 + var packageHashes = this.GetHashes(harvestedFile.PackagePayloads.Select(x => x.SourceFile.Path));
210
209 - if (!String.IsNullOrEmpty(payloadSymbol.Description))
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))
211 + foreach (var payloadSymbol in harvestedFile.PackagePayloads)
212 {
221 - element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri));
213 + var harvestedPackageFile = this.HarvestFile(payloadSymbol.SourceFile.Path, false, packageHashes);
214 + yield return harvestedPackageFile.Element;
215 }
216 }
217
225 - if (hashes.TryGetValue(path, out var certificateHashes))
226 - {
227 - element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey));
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 - }
218 + yield return harvestedFile.Element;
219 + }
220 + }
221
298 - element.Add(bundleElement);
299 - }
300 - }
222 + private Dictionary<string, CertificateHashes> GetHashes(IEnumerable<string> paths)
223 + {
224 + var hashes = new Dictionary<string, CertificateHashes>();
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);
231 }
232 +
233 + return hashes;
234 }
235
306 - private XElement CreateRemotePayloadElement(string path, bool firstHarvest)
236 + private HarvestedFile HarvestFile(string path, bool isPackage, Dictionary<string, CertificateHashes> hashes)
237 {
308 - if (firstHarvest)
238 + XElement element;
239 + WixBundlePackageType? packageType = null;
240 +
241 + if (isPackage)
242 {
243 var extension = this.PackageType.HasValue ? this.PackageType.ToString() : Path.GetExtension(path);
244
245 switch (extension.ToUpperInvariant())
246 {
247 case "BUNDLE":
315 - return new XElement(BundlePackagePayloadName);
248 + packageType = WixBundlePackageType.Bundle;
249 + element = new XElement(BundlePackagePayloadName);
250 + break;
251
252 case "EXE":
253 case ".EXE":
319 - return new XElement(ExePackagePayloadName);
254 + packageType = WixBundlePackageType.Exe;
255 + element = new XElement(ExePackagePayloadName);
256 + break;
257
258 case "MSU":
259 case ".MSU":
323 - return new XElement(MsuPackagePayloadName);
260 + packageType = WixBundlePackageType.Msu;
261 + element = new XElement(MsuPackagePayloadName);
262 + break;
263
264 default:
265 this.Messaging.Write(BurnBackendErrors.UnsupportedRemotePackagePayload(extension, path));
@@ -329,8 +268,75 @@ namespace WixToolset.Core.Burn.CommandLine
268 }
269 else
270 {
332 - return new XElement(PayloadName);
271 + element = new XElement(PayloadName);
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;
340 }
341
342 private string GetRelativeFileName(string path)
@@ -345,5 +351,75 @@ namespace WixToolset.Core.Burn.CommandLine
351
352 return Path.GetFileName(path);
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 + }
424 }
425 }
src/wix/WixToolset.Core.TestPackage/XmlNodeExtensions.cs
+11
@@ -12,6 +12,17 @@ namespace WixToolset.Core.TestPackage
12 /// </summary>
13 public static class XmlNodeExtensions
14 {
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>
27 /// Returns the node's outer XML using single quotes and stripping all namespaces.
28 /// </summary>
src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs new
+108
@@ -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 +
3 +namespace 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 +}
src/wix/WixToolset.Core/Compile/CompilerPayload.cs
+5 -5
@@ -11,8 +11,6 @@ namespace WixToolset.Core
11
12 internal class CompilerPayload
13 {
14 - public string Version { get; set; }
15 -
14 public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element)
15 {
16 this.Core = core;
@@ -20,11 +18,11 @@ namespace WixToolset.Core
18 this.SourceLineNumbers = sourceLineNumbers;
19 }
20
23 - private CompilerCore Core { get; }
21 + public CompilerCore Core { get; }
22
25 - private XElement Element { get; }
23 + public XElement Element { get; }
24
27 - private SourceLineNumber SourceLineNumbers { get; }
25 + public SourceLineNumber SourceLineNumbers { get; }
26
27 public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default;
28
@@ -52,6 +50,8 @@ namespace WixToolset.Core
50
51 public string SourceFile { get; set; }
52
53 + public string Version { get; set; }
54 +
55 private void CalculateAndVerifyFields()
56 {
57 var isRemote = this.IsRemoteAllowed && (!String.IsNullOrEmpty(this.CertificatePublicKey) || !String.IsNullOrEmpty(this.CertificateThumbprint) || !String.IsNullOrEmpty(this.Hash));
src/wix/WixToolset.Core/Compiler_Bundle.cs
+22 -48
@@ -1555,12 +1555,8 @@ namespace WixToolset.Core
1555
1556 if (packageType.HasValue)
1557 {
1558 - var compilerPayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null);
1559 - var payloadSymbol = compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id);
1560 - if (payloadSymbol != null)
1561 - {
1562 - this.CreatePackagePayloadSymbol(payloadSymbol.SourceLineNumbers, packageType.Value, payloadSymbol.Id, ComplexReferenceParentType.PayloadGroup, id);
1563 - }
1558 + var compilerPackagePayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null);
1559 + compilerPackagePayload.CreatePackagePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id);
1560 }
1561 }
1562 else
@@ -2030,7 +2026,7 @@ namespace WixToolset.Core
2026 string msuKB = null;
2027 var enableFeatureSelection = YesNoType.NotSet;
2028 var forcePerMachine = YesNoType.NotSet;
2033 - CompilerPayload childPackageCompilerPayload = null;
2029 + CompilerPackagePayload childCompilerPackagePayload = null;
2030 var bundle = YesNoType.NotSet;
2031 var slipstream = YesNoType.NotSet;
2032 var hasPayloadInfo = false;
@@ -2192,7 +2188,7 @@ namespace WixToolset.Core
2188 {
2189 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2190
2195 - if (childPackageCompilerPayload != null)
2191 + if (childCompilerPackagePayload != null)
2192 {
2193 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
2194 }
@@ -2201,12 +2197,12 @@ namespace WixToolset.Core
2197 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed"));
2198 }
2199
2204 - childPackageCompilerPayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id);
2200 + childCompilerPackagePayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id);
2201 }
2202
2207 - if (compilerPayload.Id == null && childPackageCompilerPayload != null)
2203 + if (compilerPayload.Id == null && childCompilerPackagePayload != null)
2204 {
2209 - compilerPayload.Id = childPackageCompilerPayload.Id;
2205 + compilerPayload.Id = childCompilerPackagePayload.CompilerPayload.Id;
2206 }
2207
2208 compilerPayload.FinishCompilingPackage();
@@ -2427,13 +2423,8 @@ namespace WixToolset.Core
2423
2424 if (!this.Core.EncounteredError)
2425 {
2430 - var packageCompilerPayload = childPackageCompilerPayload ?? (hasPayloadInfo ? compilerPayload : null);
2431 - if (packageCompilerPayload != null)
2432 - {
2433 - var payload = packageCompilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2434 -
2435 - this.CreatePackagePayloadSymbol(sourceLineNumbers, packageType, payload.Id, ComplexReferenceParentType.Package, id);
2436 - }
2426 + var compilerPackagePayload = childCompilerPackagePayload ?? (hasPayloadInfo ? new CompilerPackagePayload(compilerPayload, packageType) : null);
2427 + compilerPackagePayload?.CreatePackagePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2428
2429 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
2430
@@ -2536,35 +2527,7 @@ namespace WixToolset.Core
2527 return id.Id;
2528 }
2529
2539 - private void CreatePackagePayloadSymbol(SourceLineNumber sourceLineNumbers, WixBundlePackageType packageType, Identifier payloadId, ComplexReferenceParentType parentType, Identifier parentId)
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)
2530 + private CompilerPackagePayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId)
2531 {
2532 sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node);
2533 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
@@ -2572,6 +2535,7 @@ namespace WixToolset.Core
2535 Id = defaultId,
2536 IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu,
2537 };
2538 + var compilerPackagePayload = new CompilerPackagePayload(compilerPayload, packageType);
2539
2540 // This list lets us evaluate extension attributes *after* all core attributes
2541 // have been parsed and dealt with, regardless of authoring order.
@@ -2627,6 +2591,9 @@ namespace WixToolset.Core
2591 compilerPayload.ParseHash(attrib);
2592 }
2593 break;
2594 + case "PayloadGeneration":
2595 + allowed = compilerPackagePayload.ParsePayloadGeneration(attrib);
2596 + break;
2597 case "ProductName":
2598 allowed = compilerPayload.IsRemoteAllowed;
2599 if (allowed)
@@ -2691,11 +2658,18 @@ namespace WixToolset.Core
2658
2659 if (allowed)
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 +
2667 if (remoteBundleSeen)
2668 {
2669 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2670 this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2671 }
2672 +
2673 this.ParseRemoteBundleElement(child, compilerPayload.Id.Id);
2674 remoteBundleSeen = true;
2675 }
@@ -2723,7 +2697,7 @@ namespace WixToolset.Core
2697 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "RemoteBundle"));
2698 }
2699
2726 - return compilerPayload;
2700 + return compilerPackagePayload;
2701 }
2702
2703 private void ParseRemoteBundleElement(XElement node, string packagePayloadId)
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+17 -1
@@ -67,6 +67,7 @@ namespace WixToolsetTest.CoreIntegration
67 {
68 "build",
69 Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"),
70 + "-bindpath", Path.Combine(folder, ".Data"),
71 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
72 "-bindpath", binFolder,
73 "-intermediateFolder", parentIntermediateFolder,
@@ -105,6 +106,7 @@ namespace WixToolsetTest.CoreIntegration
106 "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
107 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
108 "<PayloadRef Id='chain.exe' />" +
109 + "<PayloadRef Id='payP6wZpeHEAZbDUQPEKeCpQ_9bN.4' />" +
110 "</BundlePackage>",
111 }, bundlePackages);
112
@@ -129,7 +131,7 @@ namespace WixToolsetTest.CoreIntegration
131 .ToArray();
132 WixAssert.CompareLineByLine(new string[]
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' />",
135 }, packageElements);
136
137 // grandparent.exe
@@ -178,6 +180,20 @@ namespace WixToolsetTest.CoreIntegration
180 "</BundlePackage>",
181 }, bundlePackages);
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 +
197 registrations = grandparentExtractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
198 .Cast<XmlElement>()
199 .Select(e => e.GetTestXml())
src/wix/test/WixToolsetTest.CoreIntegration/RemotePayloadFixture.cs
+141 -4
@@ -2,6 +2,7 @@
2
3 namespace WixToolsetTest.CoreIntegration
4 {
5 + using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using WixBuildTools.TestSupport;
@@ -10,6 +11,142 @@ namespace WixToolsetTest.CoreIntegration
11
12 public class RemotePayloadFixture
13 {
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]
151 public void CanGetRemoteV3BundlePayload()
152 {
@@ -181,7 +318,7 @@ namespace WixToolsetTest.CoreIntegration
318 "-recurse",
319 "-du", "https://www.example.com/files/{0}",
320 Path.Combine(folder, ".Data", "burn.exe"),
184 - Path.Combine(folder, "RemotePayload", "*"),
321 + Path.Combine(folder, "RemotePayload", "recurse", "*"),
322 "-basepath", folder,
323 "-bp", Path.Combine(folder, ".Data"),
324 "-o", outFile
@@ -195,9 +332,9 @@ namespace WixToolsetTest.CoreIntegration
332 WixAssert.CompareLineByLine(new[]
333 {
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' />",
199 - @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/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' />",
335 + @"<Payload Name='a.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/a.dat' Hash='D13926E5CBE5ED8B46133F9199FAF2FF25B25981C67A31AE2BC3F6C20390FACBFADCD89BD22D3445D95B989C8EACFB1E68DB634BECB5C9624865BA453BCE362A' Size='16' />",
336 + @"<Payload Name='b.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/b.dat' Hash='5F94707BC29ADFE3B9615E6753388707FD0B8F5FD9EEEC2B17E21E72F1635FF7D7A101E7D14F614E111F263CB9AC4D0940BE1247881A7844F226D6C400293D8E' Size='37' />",
337 + @"<Payload Name='c.dat' DownloadUrl='https://www.example.com/files/RemotePayload/recurse/subfolder/c.dat' Hash='97D6209A5571E05E4F72F9C6BF0987651FA03E63F971F9B53C2B3D798A666D9864F232D4E2D6442E47D9D72B282309B6EEFF4EE017B43B706FA92A0F5EF74734' Size='42' />",
338 }, elements);
339 }
340 }
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/BundlePackage.wxs
+4 -2
@@ -1,10 +1,12 @@
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>
4 <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 </BootstrapperApplication>
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>
10 </Chain>
11 </Bundle>
12 </Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs new
+24
@@ -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>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat renamed
src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat renamed
src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat renamed