From d5744da0117199f23bf72f5c2ba7cd1c6f52e173 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Thu, 5 May 2022 18:08:06 -0500 Subject: Harvest BundlePackage payloads. Fixes 6757 --- .../Symbols/WixBundleBundlePackagePayloadSymbol.cs | 16 ++ .../WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 2 +- .../Bundles/GetPackageFacadesCommand.cs | 14 +- .../Bundles/HarvestBundlePackageCommand.cs | 190 ++++++++++++- .../WixToolset.Core.Burn/Bundles/PackageFacade.cs | 5 +- .../Bundles/ProcessBundlePackageCommand.cs | 16 +- .../CommandLine/RemotePayloadSubcommand.cs | 304 +++++++++++++-------- .../XmlNodeExtensions.cs | 11 + .../Compile/CompilerPackagePayload.cs | 108 ++++++++ src/wix/WixToolset.Core/Compile/CompilerPayload.cs | 10 +- src/wix/WixToolset.Core/Compiler_Bundle.cs | 70 ++--- .../BundlePackageFixture.cs | 18 +- .../RemotePayloadFixture.cs | 145 +++++++++- .../TestData/BundlePackage/BundlePackage.wxs | 6 +- .../RemotePayload/DiversePayloadsBundle.wxs | 24 ++ .../TestData/RemotePayload/a.dat | 1 - .../TestData/RemotePayload/recurse/a.dat | 1 + .../TestData/RemotePayload/recurse/subfolder/b.dat | 1 + .../TestData/RemotePayload/recurse/subfolder/c.dat | 1 + .../TestData/RemotePayload/subfolder/b.dat | 1 - .../TestData/RemotePayload/subfolder/c.dat | 1 - 21 files changed, 755 insertions(+), 190 deletions(-) create mode 100644 src/wix/WixToolset.Core/Compile/CompilerPackagePayload.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/DiversePayloadsBundle.wxs delete mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat delete mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat delete mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat (limited to 'src') 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 SymbolDefinitionType.WixBundleBundlePackagePayload, new IntermediateFieldDefinition[] { + new IntermediateFieldDefinition(nameof(WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration), IntermediateFieldType.Number), }, typeof(WixBundleBundlePackagePayloadSymbol)); } @@ -19,6 +20,15 @@ namespace WixToolset.Data.Symbols { public enum WixBundleBundlePackagePayloadSymbolFields { + PayloadGeneration, + } + + public enum BundlePackagePayloadGenerationType + { + None, + ExternalWithoutDownloadUrl, + External, + All, } public class WixBundleBundlePackagePayloadSymbol : IntermediateSymbol @@ -32,5 +42,11 @@ namespace WixToolset.Data.Symbols } public IntermediateField this[WixBundleBundlePackagePayloadSymbolFields index] => this.Fields[(int)index]; + + public BundlePackagePayloadGenerationType PayloadGeneration + { + get => (BundlePackagePayloadGenerationType)this.Fields[(int)WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration].AsNumber(); + set => this.Set((int)WixBundleBundlePackagePayloadSymbolFields.PayloadGeneration, (int)value); + } } } 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 { case WixBundlePackageType.Bundle: { - var command = new ProcessBundlePackageCommand(this.ServiceProvider, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder); + var command = new ProcessBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, packagesPayloads[facade.PackageId], this.IntermediateFolder); command.Execute(); 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 @@ namespace WixToolset.Core.Burn.Bundles { + using System; using System.Collections.Generic; using System.Linq; using WixToolset.Data; @@ -147,7 +148,7 @@ namespace WixToolset.Core.Burn.Bundles case WixBundlePackageType.Bundle: if (bundlePackages.TryGetValue(id, out var bundlePackage)) { - facades.Add(new PackageFacade(package, bundlePackage)); + facades.Add(new PackageFacade(package, bundlePackage, packagePayload)); } else { @@ -158,7 +159,7 @@ namespace WixToolset.Core.Burn.Bundles case WixBundlePackageType.Exe: if (exePackages.TryGetValue(id, out var exePackage)) { - facades.Add(new PackageFacade(package, exePackage)); + facades.Add(new PackageFacade(package, exePackage, packagePayload)); } else { @@ -169,7 +170,7 @@ namespace WixToolset.Core.Burn.Bundles case WixBundlePackageType.Msi: if (msiPackages.TryGetValue(id, out var msiPackage)) { - facades.Add(new PackageFacade(package, msiPackage)); + facades.Add(new PackageFacade(package, msiPackage, packagePayload)); } else { @@ -180,7 +181,7 @@ namespace WixToolset.Core.Burn.Bundles case WixBundlePackageType.Msp: if (mspPackages.TryGetValue(id, out var mspPackage)) { - facades.Add(new PackageFacade(package, mspPackage)); + facades.Add(new PackageFacade(package, mspPackage, packagePayload)); } else { @@ -191,13 +192,16 @@ namespace WixToolset.Core.Burn.Bundles case WixBundlePackageType.Msu: if (msuPackages.TryGetValue(id, out var msuPackage)) { - facades.Add(new PackageFacade(package, msuPackage)); + facades.Add(new PackageFacade(package, msuPackage, packagePayload)); } else { this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsuPackage", id)); } break; + + default: + throw new NotImplementedException(); } } 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 using System; using System.Collections.Generic; using System.IO; - using System.Text; + using System.Linq; using System.Xml; using WixToolset.Data; using WixToolset.Data.Symbols; + using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; internal class HarvestBundlePackageCommand { - public HarvestBundlePackageCommand(IServiceProvider serviceProvider, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol) + public HarvestBundlePackageCommand(IServiceProvider serviceProvider, IEnumerable backendExtensions, string intermediateFolder, WixBundlePayloadSymbol payloadSymbol, WixBundleBundlePackagePayloadSymbol packagePayloadSymbol, Dictionary packagePayloadsById) { this.Messaging = serviceProvider.GetService(); this.BackendHelper = serviceProvider.GetService(); + this.BackendExtensions = backendExtensions; this.IntermediateFolder = intermediateFolder; this.PackagePayload = payloadSymbol; + this.BundlePackagePayload = packagePayloadSymbol; + this.PackagePayloadsById = packagePayloadsById; } private IMessaging Messaging { get; } private IBackendHelper BackendHelper { get; } + private IEnumerable BackendExtensions { get; } + private string IntermediateFolder { get; } private WixBundlePayloadSymbol PackagePayload { get; } + private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; } + + private Dictionary PackagePayloadsById { get; } + public WixBundleHarvestedBundlePackageSymbol HarvestedBundlePackage { get; private set; } public WixBundleHarvestedDependencyProviderSymbol HarvestedDependencyProvider { get; private set; } + public List Payloads { get; } = new List(); + public List RelatedBundles { get; } = new List(); public List TrackedFiles { get; } = new List(); @@ -114,9 +126,9 @@ namespace WixToolset.Core.Burn.Bundles installSize = this.ProcessPackages(document, namespaceManager); - this.ProcessRelatedBundles(document, namespaceManager, sourcePath); + this.ProcessPayloads(document, namespaceManager, this.BundlePackagePayload.PayloadGeneration); - // TODO: Add payloads? + this.ProcessRelatedBundles(document, namespaceManager, sourcePath); } catch (Exception e) { @@ -221,6 +233,158 @@ namespace WixToolset.Core.Burn.Bundles return packageInstallSize; } + private void ProcessPayloads(XmlDocument document, XmlNamespaceManager namespaceManager, BundlePackagePayloadGenerationType payloadGenerationType) + { + if (payloadGenerationType == BundlePackagePayloadGenerationType.None) + { + return; + } + + var payloadNames = new HashSet(this.PackagePayloadsById.Values.Select(p => p.Name), StringComparer.OrdinalIgnoreCase); + + var containersById = new Dictionary(); + + foreach (XmlElement containerElement in document.SelectNodes("/burn:BurnManifest/burn:Container", namespaceManager)) + { + var container = new ManifestContainer(); + container.Attached = containerElement.GetAttribute("Attached") == "yes"; + container.DownloadUrl = containerElement.GetAttribute("DownloadUrl"); + container.FilePath = containerElement.GetAttribute("FilePath"); + container.Id = containerElement.GetAttribute("Id"); + containersById.Add(container.Id, container); + + if (container.Attached) + { + continue; + } + + switch (payloadGenerationType) + { + case BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl: + if (!String.IsNullOrEmpty(container.DownloadUrl)) + { + continue; + } + break; + } + + // If we didn't find the Payload as an existing child of the package, we need to + // add it. We expect the file to exist on-disk in the same relative location as + // the bundle expects to find it... + container.IncludedAsPayload = true; + var containerName = container.FilePath; + var containerFullName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), containerName); + + if (!payloadNames.Contains(containerFullName)) + { + var generatedId = this.BackendHelper.GenerateIdentifier("hcp", this.PackagePayload.Id.Id, containerName); + var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, containerName, "Container", this.PackagePayload.SourceLineNumbers); + + this.Payloads.Add(new WixBundlePayloadSymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId)) + { + Name = containerFullName, + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile }, + Compressed = this.PackagePayload.Compressed, + UnresolvedSourceFile = containerFullName, + ContainerRef = this.PackagePayload.ContainerRef, + DownloadUrl = this.PackagePayload.DownloadUrl, + Packaging = this.PackagePayload.Packaging, + ParentPackagePayloadRef = this.PackagePayload.Id.Id, + }); + } + } + + foreach (XmlElement payloadElement in document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager)) + { + var payload = new ManifestPayload(); + payload.Container = payloadElement.GetAttribute("Container"); + payload.DownloadUrl = payloadElement.GetAttribute("DownloadUrl"); + payload.FilePath = payloadElement.GetAttribute("FilePath"); + payload.Id = payloadElement.GetAttribute("Id"); + + if (payload.Container == null || !containersById.TryGetValue(payload.Container, out var container)) + { + container = null; + } + + if (container != null && container.IncludedAsPayload) + { + // Don't include payload if it's in a container that's already included. + continue; + } + + switch (payloadGenerationType) + { + case BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl: + if (container != null || !String.IsNullOrEmpty(payload.DownloadUrl)) + { + continue; + } + break; + case BundlePackagePayloadGenerationType.External: + if (container != null) + { + continue; + } + break; + } + + // If we didn't find the Payload as an existing child of the package, we need to + // add it. We expect the file to exist on-disk in the same relative location as + // the bundle expects to find it... + var payloadName = payload.FilePath; + var payloadFullName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), payloadName); + + if (!payloadNames.Contains(payloadFullName)) + { + var generatedId = this.BackendHelper.GenerateIdentifier("hpp", this.PackagePayload.Id.Id, payloadName); + var payloadSourceFile = this.ResolveRelatedFile(this.PackagePayload.SourceFile.Path, this.PackagePayload.UnresolvedSourceFile, payloadName, "Payload", this.PackagePayload.SourceLineNumbers); + + this.Payloads.Add(new WixBundlePayloadSymbol(this.PackagePayload.SourceLineNumbers, new Identifier(AccessModifier.Section, generatedId)) + { + Name = payloadFullName, + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile }, + Compressed = this.PackagePayload.Compressed, + UnresolvedSourceFile = payloadFullName, + ContainerRef = this.PackagePayload.ContainerRef, + DownloadUrl = this.PackagePayload.DownloadUrl, + Packaging = this.PackagePayload.Packaging, + ParentPackagePayloadRef = this.PackagePayload.Id.Id, + }); + } + } + } + + private string ResolveRelatedFile(string resolvedSource, string unresolvedSource, string relatedSource, string type, SourceLineNumber sourceLineNumbers) + { + var checkedPaths = new List(); + + foreach (var extension in this.BackendExtensions) + { + var resolved = extension.ResolveRelatedFile(unresolvedSource, relatedSource, type, sourceLineNumbers); + + if (resolved?.CheckedPaths != null) + { + checkedPaths.AddRange(resolved.CheckedPaths); + } + + if (!String.IsNullOrEmpty(resolved?.Path)) + { + return resolved?.Path; + } + } + + var resolvedPath = Path.Combine(Path.GetDirectoryName(resolvedSource), relatedSource); + + if (!File.Exists(resolvedPath)) + { + checkedPaths.Add(resolvedPath); + this.Messaging.Write(ErrorMessages.FileNotFound(sourceLineNumbers, resolvedPath, type, checkedPaths)); + } + + return resolvedPath; + } + private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, string sourcePath) { var sourceLineNumbers = this.PackagePayload.SourceLineNumbers; @@ -244,5 +408,23 @@ namespace WixToolset.Core.Burn.Bundles }); } } + + private class ManifestContainer + { + public bool Attached { get; set; } + public string DownloadUrl { get; set; } + public string FilePath { get; set; } + public string Id { get; set; } + + public bool IncludedAsPayload { get; set; } + } + + private class ManifestPayload + { + public string Container { get; set; } + public string DownloadUrl { get; set; } + public string FilePath { get; set; } + public string Id { get; set; } + } } } 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 internal class PackageFacade { - public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol) + public PackageFacade(WixBundlePackageSymbol packageSymbol, IntermediateSymbol specificPackageSymbol, IntermediateSymbol specificPackagePayloadSymbol) { Debug.Assert(packageSymbol.Id.Id == specificPackageSymbol.Id.Id); this.PackageSymbol = packageSymbol; this.SpecificPackageSymbol = specificPackageSymbol; + this.SpecificPackagePayloadSymbol = specificPackagePayloadSymbol; } public string PackageId => this.PackageSymbol.Id.Id; @@ -21,5 +22,7 @@ namespace WixToolset.Core.Burn.Bundles public WixBundlePackageSymbol PackageSymbol { get; } public IntermediateSymbol SpecificPackageSymbol { get; } + + public IntermediateSymbol SpecificPackagePayloadSymbol { get; } } } 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 using System.Linq; using WixToolset.Data; using WixToolset.Data.Symbols; + using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; @@ -15,16 +16,18 @@ namespace WixToolset.Core.Burn.Bundles /// internal class ProcessBundlePackageCommand { - public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IntermediateSection section, PackageFacade facade, Dictionary packagePayloads, string intermediateFolder) + public ProcessBundlePackageCommand(IServiceProvider serviceProvider, IEnumerable backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary packagePayloads, string intermediateFolder) { this.ServiceProvider = serviceProvider; this.Messaging = serviceProvider.GetService(); + this.BackendExtensions = backendExtensions; this.PackagePayloads = packagePayloads; this.Section = section; this.IntermediateFolder = intermediateFolder; this.ChainPackage = facade.PackageSymbol; this.BundlePackage = (WixBundleBundlePackageSymbol)facade.SpecificPackageSymbol; + this.BundlePackagePayload = (WixBundleBundlePackagePayloadSymbol)facade.SpecificPackagePayloadSymbol; this.PackagePayload = packagePayloads[this.ChainPackage.PayloadRef]; } @@ -32,12 +35,16 @@ namespace WixToolset.Core.Burn.Bundles private IMessaging Messaging { get; } + private IEnumerable BackendExtensions { get; } + private Dictionary PackagePayloads { get; } private WixBundlePackageSymbol ChainPackage { get; } private WixBundleBundlePackageSymbol BundlePackage { get; } + private WixBundleBundlePackagePayloadSymbol BundlePackagePayload { get; } + private string PackageId => this.ChainPackage.Id.Id; private WixBundlePayloadSymbol PackagePayload { get; } @@ -100,7 +107,7 @@ namespace WixToolset.Core.Burn.Bundles private WixBundleHarvestedBundlePackageSymbol HarvestPackage() { - var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.IntermediateFolder, this.PackagePayload); + var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, this.IntermediateFolder, this.PackagePayload, this.BundlePackagePayload, this.PackagePayloads); command.Execute(); this.TrackedFiles.AddRange(command.TrackedFiles); @@ -112,6 +119,11 @@ namespace WixToolset.Core.Burn.Bundles this.Section.AddSymbol(command.HarvestedBundlePackage); this.Section.AddSymbol(command.HarvestedDependencyProvider); + foreach (var payload in command.Payloads) + { + this.Section.AddSymbol(payload); + } + foreach (var relatedBundle in command.RelatedBundles) { 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 using WixToolset.Core.Native; using WixToolset.Data; using WixToolset.Data.Symbols; + using WixToolset.Extensibility; using WixToolset.Extensibility.Services; internal class RemotePayloadSubcommand : BurnSubcommandBase @@ -30,6 +31,9 @@ namespace WixToolset.Core.Burn.CommandLine this.ServiceProvider = serviceProvider; this.Messaging = serviceProvider.GetService(); this.PayloadHarvester = serviceProvider.GetService(); + var extensionManager = serviceProvider.GetService(); + + this.BackendExtensions = extensionManager.GetServices(); } private IServiceProvider ServiceProvider { get; } @@ -38,6 +42,8 @@ namespace WixToolset.Core.Burn.CommandLine private IPayloadHarvester PayloadHarvester { get; } + private IReadOnlyCollection BackendExtensions { get; } + private List BasePaths { get; } = new List(); private string DownloadUrl { get; set; } @@ -50,6 +56,8 @@ namespace WixToolset.Core.Burn.CommandLine private WixBundlePackageType? PackageType { get; set; } + private BundlePackagePayloadGenerationType BundlePayloadGeneration { get; set; } = BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl; + private bool Recurse { get; set; } private bool UseCertificate { get; set; } @@ -107,6 +115,15 @@ namespace WixToolset.Core.Burn.CommandLine this.BasePaths.Add(parser.GetNextArgumentAsDirectoryOrError(argument)); return true; + case "bundlepayloadgeneration": + var bundlePayloadGenerationValue = parser.GetNextArgumentOrError(argument); + if (Enum.TryParse(bundlePayloadGenerationValue, ignoreCase: true, out var bundlePayloadGeneration)) + { + this.BundlePayloadGeneration = bundlePayloadGeneration; + return true; + } + break; + case "du": case "downloadurl": this.DownloadUrl = parser.GetNextArgumentOrError(argument); @@ -175,152 +192,74 @@ namespace WixToolset.Core.Burn.CommandLine private IEnumerable HarvestRemotePayloads(IEnumerable paths) { var first = true; - var hashes = new Dictionary(); - - if (this.UseCertificate) - { - hashes = CertificateHashes.Read(paths).Where(c => !String.IsNullOrEmpty(c.PublicKey) && !String.IsNullOrEmpty(c.Thumbprint) && c.Exception is null).ToDictionary(c => c.Path); - } + var hashes = this.GetHashes(paths); foreach (var path in paths) { - var element = this.CreateRemotePayloadElement(path, first); + var harvestedFile = this.HarvestFile(path, first, hashes); first = false; - if (element == null) + if (harvestedFile == null) { continue; } - var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id")) + if (harvestedFile.PackagePayloads.Any()) { - SourceFile = new IntermediateFieldPathValue { Path = path }, - }; - - this.PayloadHarvester.HarvestStandardInformation(payloadSymbol); - - element.Add(new XAttribute("Name", Path.GetFileName(path))); - - if (!String.IsNullOrEmpty(payloadSymbol.DisplayName)) - { - element.Add(new XAttribute("ProductName", payloadSymbol.DisplayName)); - } + var packageHashes = this.GetHashes(harvestedFile.PackagePayloads.Select(x => x.SourceFile.Path)); - if (!String.IsNullOrEmpty(payloadSymbol.Description)) - { - element.Add(new XAttribute("Description", payloadSymbol.Description)); - } - - if (!String.IsNullOrEmpty(this.DownloadUrl)) - { - var filename = this.GetRelativeFileName(payloadSymbol.SourceFile.Path); - var formattedUrl = String.Format(this.DownloadUrl, filename); - - if (Uri.TryCreate(formattedUrl, UriKind.Absolute, out var url)) + foreach (var payloadSymbol in harvestedFile.PackagePayloads) { - element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri)); + var harvestedPackageFile = this.HarvestFile(payloadSymbol.SourceFile.Path, false, packageHashes); + yield return harvestedPackageFile.Element; } } - if (hashes.TryGetValue(path, out var certificateHashes)) - { - element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey)); - element.Add(new XAttribute("CertificateThumbprint", certificateHashes.Thumbprint)); - } - - if (!String.IsNullOrEmpty(payloadSymbol.Hash)) - { - element.Add(new XAttribute("Hash", payloadSymbol.Hash)); - } - - if (payloadSymbol.FileSize.HasValue) - { - element.Add(new XAttribute("Size", payloadSymbol.FileSize.Value)); - } - - if (!String.IsNullOrEmpty(payloadSymbol.Version)) - { - element.Add(new XAttribute("Version", payloadSymbol.Version)); - } - - if (element.Name == BundlePackagePayloadName) - { - var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.IntermediateFolder, payloadSymbol); - command.Execute(); - - if (!this.Messaging.EncounteredError) - { - var bundleElement = new XElement(RemoteBundleName); - - bundleElement.Add(new XAttribute("BundleId", command.HarvestedBundlePackage.BundleId)); - - if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.DisplayName)) - { - bundleElement.Add(new XAttribute("DisplayName", command.HarvestedBundlePackage.DisplayName)); - } - - if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.EngineVersion)) - { - bundleElement.Add(new XAttribute("EngineVersion", command.HarvestedBundlePackage.EngineVersion)); - } - - bundleElement.Add(new XAttribute("InstallSize", command.HarvestedBundlePackage.InstallSize)); - bundleElement.Add(new XAttribute("ManifestNamespace", command.HarvestedBundlePackage.ManifestNamespace)); - bundleElement.Add(new XAttribute("PerMachine", command.HarvestedBundlePackage.PerMachine ? "yes" : "no")); - bundleElement.Add(new XAttribute("ProviderKey", command.HarvestedDependencyProvider.ProviderKey)); - bundleElement.Add(new XAttribute("ProtocolVersion", command.HarvestedBundlePackage.ProtocolVersion)); - - if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.Version)) - { - bundleElement.Add(new XAttribute("Version", command.HarvestedBundlePackage.Version)); - } - - bundleElement.Add(new XAttribute("Win64", command.HarvestedBundlePackage.Win64 ? "yes" : "no")); - - var setUpgradeCode = false; - foreach (var relatedBundle in command.RelatedBundles) - { - if (!setUpgradeCode && relatedBundle.Action == RelatedBundleActionType.Upgrade) - { - setUpgradeCode = true; - bundleElement.Add(new XAttribute("UpgradeCode", relatedBundle.BundleId)); - continue; - } - - var relatedBundleElement = new XElement(RemoteRelatedBundleName); - - relatedBundleElement.Add(new XAttribute("Id", relatedBundle.BundleId)); - relatedBundleElement.Add(new XAttribute("Action", relatedBundle.Action.ToString())); - - bundleElement.Add(relatedBundleElement); - } + yield return harvestedFile.Element; + } + } - element.Add(bundleElement); - } - } + private Dictionary GetHashes(IEnumerable paths) + { + var hashes = new Dictionary(); - yield return element; + if (this.UseCertificate) + { + hashes = CertificateHashes.Read(paths) + .Where(c => !String.IsNullOrEmpty(c.PublicKey) && !String.IsNullOrEmpty(c.Thumbprint) && c.Exception is null) + .ToDictionary(c => c.Path); } + + return hashes; } - private XElement CreateRemotePayloadElement(string path, bool firstHarvest) + private HarvestedFile HarvestFile(string path, bool isPackage, Dictionary hashes) { - if (firstHarvest) + XElement element; + WixBundlePackageType? packageType = null; + + if (isPackage) { var extension = this.PackageType.HasValue ? this.PackageType.ToString() : Path.GetExtension(path); switch (extension.ToUpperInvariant()) { case "BUNDLE": - return new XElement(BundlePackagePayloadName); + packageType = WixBundlePackageType.Bundle; + element = new XElement(BundlePackagePayloadName); + break; case "EXE": case ".EXE": - return new XElement(ExePackagePayloadName); + packageType = WixBundlePackageType.Exe; + element = new XElement(ExePackagePayloadName); + break; case "MSU": case ".MSU": - return new XElement(MsuPackagePayloadName); + packageType = WixBundlePackageType.Msu; + element = new XElement(MsuPackagePayloadName); + break; default: this.Messaging.Write(BurnBackendErrors.UnsupportedRemotePackagePayload(extension, path)); @@ -329,8 +268,75 @@ namespace WixToolset.Core.Burn.CommandLine } else { - return new XElement(PayloadName); + element = new XElement(PayloadName); } + + var payloadSymbol = new WixBundlePayloadSymbol(null, new Identifier(AccessModifier.Section, "id")) + { + SourceFile = new IntermediateFieldPathValue { Path = path }, + Name = Path.GetFileName(path), + }; + + this.PayloadHarvester.HarvestStandardInformation(payloadSymbol); + + element.Add(new XAttribute("Name", payloadSymbol.Name)); + + if (!String.IsNullOrEmpty(payloadSymbol.DisplayName)) + { + element.Add(new XAttribute("ProductName", payloadSymbol.DisplayName)); + } + + if (!String.IsNullOrEmpty(payloadSymbol.Description)) + { + element.Add(new XAttribute("Description", payloadSymbol.Description)); + } + + if (!String.IsNullOrEmpty(this.DownloadUrl)) + { + var filename = this.GetRelativeFileName(payloadSymbol.SourceFile.Path); + var formattedUrl = String.Format(this.DownloadUrl, filename); + + if (Uri.TryCreate(formattedUrl, UriKind.Absolute, out var url)) + { + element.Add(new XAttribute("DownloadUrl", url.AbsoluteUri)); + } + } + + if (hashes.TryGetValue(path, out var certificateHashes)) + { + element.Add(new XAttribute("CertificatePublicKey", certificateHashes.PublicKey)); + element.Add(new XAttribute("CertificateThumbprint", certificateHashes.Thumbprint)); + } + + if (!String.IsNullOrEmpty(payloadSymbol.Hash)) + { + element.Add(new XAttribute("Hash", payloadSymbol.Hash)); + } + + if (payloadSymbol.FileSize.HasValue) + { + element.Add(new XAttribute("Size", payloadSymbol.FileSize.Value)); + } + + if (!String.IsNullOrEmpty(payloadSymbol.Version)) + { + element.Add(new XAttribute("Version", payloadSymbol.Version)); + } + + var harvestedFile = new HarvestedFile + { + Element = element, + PayloadSymbol = payloadSymbol, + }; + + switch (packageType) + { + case WixBundlePackageType.Bundle: + this.HarvestBundle(harvestedFile); + break; + } + + return harvestedFile; } private string GetRelativeFileName(string path) @@ -345,5 +351,75 @@ namespace WixToolset.Core.Burn.CommandLine return Path.GetFileName(path); } + + private void HarvestBundle(HarvestedFile harvestedFile) + { + var packagePayloadSymbol = new WixBundleBundlePackagePayloadSymbol(null, new Identifier(AccessModifier.Section, harvestedFile.PayloadSymbol.Id.Id)) + { + PayloadGeneration = this.BundlePayloadGeneration, + }; + + var command = new HarvestBundlePackageCommand(this.ServiceProvider, this.BackendExtensions, this.IntermediateFolder, harvestedFile.PayloadSymbol, packagePayloadSymbol, new Dictionary()); + command.Execute(); + + if (!this.Messaging.EncounteredError) + { + var bundleElement = new XElement(RemoteBundleName); + + bundleElement.Add(new XAttribute("BundleId", command.HarvestedBundlePackage.BundleId)); + + if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.DisplayName)) + { + bundleElement.Add(new XAttribute("DisplayName", command.HarvestedBundlePackage.DisplayName)); + } + + if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.EngineVersion)) + { + bundleElement.Add(new XAttribute("EngineVersion", command.HarvestedBundlePackage.EngineVersion)); + } + + bundleElement.Add(new XAttribute("InstallSize", command.HarvestedBundlePackage.InstallSize)); + bundleElement.Add(new XAttribute("ManifestNamespace", command.HarvestedBundlePackage.ManifestNamespace)); + bundleElement.Add(new XAttribute("PerMachine", command.HarvestedBundlePackage.PerMachine ? "yes" : "no")); + bundleElement.Add(new XAttribute("ProviderKey", command.HarvestedDependencyProvider.ProviderKey)); + bundleElement.Add(new XAttribute("ProtocolVersion", command.HarvestedBundlePackage.ProtocolVersion)); + + if (!String.IsNullOrEmpty(command.HarvestedBundlePackage.Version)) + { + bundleElement.Add(new XAttribute("Version", command.HarvestedBundlePackage.Version)); + } + + bundleElement.Add(new XAttribute("Win64", command.HarvestedBundlePackage.Win64 ? "yes" : "no")); + + var setUpgradeCode = false; + foreach (var relatedBundle in command.RelatedBundles) + { + if (!setUpgradeCode && relatedBundle.Action == RelatedBundleActionType.Upgrade) + { + setUpgradeCode = true; + bundleElement.Add(new XAttribute("UpgradeCode", relatedBundle.BundleId)); + continue; + } + + var relatedBundleElement = new XElement(RemoteRelatedBundleName); + + relatedBundleElement.Add(new XAttribute("Id", relatedBundle.BundleId)); + relatedBundleElement.Add(new XAttribute("Action", relatedBundle.Action.ToString())); + + bundleElement.Add(relatedBundleElement); + } + + harvestedFile.PackagePayloads.AddRange(command.Payloads); + + harvestedFile.Element.Add(bundleElement); + } + } + + private class HarvestedFile + { + public XElement Element { get; set; } + public WixBundlePayloadSymbol PayloadSymbol { get; set; } + public List PackagePayloads { get; } = new List(); + } } } 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 @@ -12,6 +12,17 @@ namespace WixToolset.Core.TestPackage /// public static class XmlNodeExtensions { + /// + /// Adds root element around XML then returns it using single quotes and stripping all namespaces. + /// + /// + /// Attributes for which the value should be set to '*'. + /// + public static string GetFragmentTestXml(this string xmlFragment, Dictionary> ignoredAttributesByElementName = null) + { + return $"{xmlFragment}".GetTestXml(ignoredAttributesByElementName); + } + /// /// Returns the node's outer XML using single quotes and stripping all namespaces. /// 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 @@ +// 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. + +namespace WixToolset.Core +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + + internal class CompilerPackagePayload + { + public CompilerPackagePayload(CompilerPayload compilerPayload, WixBundlePackageType packageType) + { + this.CompilerPayload = compilerPayload; + this.PackageType = packageType; + } + + private CompilerCore Core => this.CompilerPayload.Core; + + private XElement Element => this.CompilerPayload.Element; + + private SourceLineNumber SourceLineNumbers => this.CompilerPayload.SourceLineNumbers; + + public CompilerPayload CompilerPayload { get; } + + private WixBundlePackageType PackageType { get; } + + public BundlePackagePayloadGenerationType? PayloadGenerationType { get; set; } + + public IntermediateSymbol CreatePackagePayloadSymbol(ComplexReferenceParentType parentType, string parentId) + { + var payload = this.CompilerPayload.CreatePayloadSymbol(parentType, parentId); + if (payload == null) + { + return null; + } + + IntermediateSymbol packagePayload; + + switch (this.PackageType) + { + case WixBundlePackageType.Bundle: + packagePayload = this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(payload.SourceLineNumbers, payload.Id) + { + PayloadGeneration = this.PayloadGenerationType ?? BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl, + }); + break; + + case WixBundlePackageType.Exe: + packagePayload = this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(payload.SourceLineNumbers, payload.Id)); + break; + + case WixBundlePackageType.Msi: + packagePayload = this.Core.AddSymbol(new WixBundleMsiPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id)); + break; + + case WixBundlePackageType.Msp: + packagePayload = this.Core.AddSymbol(new WixBundleMspPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id)); + break; + + case WixBundlePackageType.Msu: + packagePayload = this.Core.AddSymbol(new WixBundleMsuPackagePayloadSymbol(payload.SourceLineNumbers, payload.Id)); + break; + + default: + throw new NotImplementedException(); + } + + this.Core.CreateGroupAndOrderingRows(payload.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackagePayload, payload.Id?.Id, ComplexReferenceChildType.Unknown, null); + + return packagePayload; + } + + public bool ParsePayloadGeneration(XAttribute attrib) + { + if (this.PackageType != WixBundlePackageType.Bundle) + { + return false; + } + + var value = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib); + switch (value) + { + case "none": + this.PayloadGenerationType = BundlePackagePayloadGenerationType.None; + break; + case "externalWithoutDownloadUrl": + this.PayloadGenerationType = BundlePackagePayloadGenerationType.ExternalWithoutDownloadUrl; + break; + case "external": + this.PayloadGenerationType = BundlePackagePayloadGenerationType.External; + break; + case "all": + this.PayloadGenerationType = BundlePackagePayloadGenerationType.All; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(this.SourceLineNumbers, this.Element.Name.LocalName, attrib.Name.LocalName, value, "none", "externalWithoutDownloadUrl", "external", "all")); + break; + } + + return true; + } + } +} 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 internal class CompilerPayload { - public string Version { get; set; } - public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element) { this.Core = core; @@ -20,11 +18,11 @@ namespace WixToolset.Core this.SourceLineNumbers = sourceLineNumbers; } - private CompilerCore Core { get; } + public CompilerCore Core { get; } - private XElement Element { get; } + public XElement Element { get; } - private SourceLineNumber SourceLineNumbers { get; } + public SourceLineNumber SourceLineNumbers { get; } public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default; @@ -52,6 +50,8 @@ namespace WixToolset.Core public string SourceFile { get; set; } + public string Version { get; set; } + private void CalculateAndVerifyFields() { 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 if (packageType.HasValue) { - var compilerPayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null); - var payloadSymbol = compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id); - if (payloadSymbol != null) - { - this.CreatePackagePayloadSymbol(payloadSymbol.SourceLineNumbers, packageType.Value, payloadSymbol.Id, ComplexReferenceParentType.PayloadGroup, id); - } + var compilerPackagePayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null); + compilerPackagePayload.CreatePackagePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id); } } else @@ -2030,7 +2026,7 @@ namespace WixToolset.Core string msuKB = null; var enableFeatureSelection = YesNoType.NotSet; var forcePerMachine = YesNoType.NotSet; - CompilerPayload childPackageCompilerPayload = null; + CompilerPackagePayload childCompilerPackagePayload = null; var bundle = YesNoType.NotSet; var slipstream = YesNoType.NotSet; var hasPayloadInfo = false; @@ -2192,7 +2188,7 @@ namespace WixToolset.Core { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - if (childPackageCompilerPayload != null) + if (childCompilerPackagePayload != null) { this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); } @@ -2201,12 +2197,12 @@ namespace WixToolset.Core this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed")); } - childPackageCompilerPayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id); + childCompilerPackagePayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id); } - if (compilerPayload.Id == null && childPackageCompilerPayload != null) + if (compilerPayload.Id == null && childCompilerPackagePayload != null) { - compilerPayload.Id = childPackageCompilerPayload.Id; + compilerPayload.Id = childCompilerPackagePayload.CompilerPayload.Id; } compilerPayload.FinishCompilingPackage(); @@ -2427,13 +2423,8 @@ namespace WixToolset.Core if (!this.Core.EncounteredError) { - var packageCompilerPayload = childPackageCompilerPayload ?? (hasPayloadInfo ? compilerPayload : null); - if (packageCompilerPayload != null) - { - var payload = packageCompilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Package, id.Id); - - this.CreatePackagePayloadSymbol(sourceLineNumbers, packageType, payload.Id, ComplexReferenceParentType.Package, id); - } + var compilerPackagePayload = childCompilerPackagePayload ?? (hasPayloadInfo ? new CompilerPackagePayload(compilerPayload, packageType) : null); + compilerPackagePayload?.CreatePackagePayloadSymbol(ComplexReferenceParentType.Package, id.Id); this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id)); @@ -2536,35 +2527,7 @@ namespace WixToolset.Core return id.Id; } - private void CreatePackagePayloadSymbol(SourceLineNumber sourceLineNumbers, WixBundlePackageType packageType, Identifier payloadId, ComplexReferenceParentType parentType, Identifier parentId) - { - switch (packageType) - { - case WixBundlePackageType.Bundle: - this.Core.AddSymbol(new WixBundleBundlePackagePayloadSymbol(sourceLineNumbers, payloadId)); - break; - - case WixBundlePackageType.Exe: - this.Core.AddSymbol(new WixBundleExePackagePayloadSymbol(sourceLineNumbers, payloadId)); - break; - - case WixBundlePackageType.Msi: - this.Core.AddSymbol(new WixBundleMsiPackagePayloadSymbol(sourceLineNumbers, payloadId)); - break; - - case WixBundlePackageType.Msp: - this.Core.AddSymbol(new WixBundleMspPackagePayloadSymbol(sourceLineNumbers, payloadId)); - break; - - case WixBundlePackageType.Msu: - this.Core.AddSymbol(new WixBundleMsuPackagePayloadSymbol(sourceLineNumbers, payloadId)); - break; - } - - this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PackagePayload, payloadId?.Id, ComplexReferenceChildType.Unknown, null); - } - - private CompilerPayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId) + private CompilerPackagePayload ParsePackagePayloadElement(SourceLineNumber sourceLineNumbers, XElement node, WixBundlePackageType packageType, Identifier defaultId) { sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node); var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node) @@ -2572,6 +2535,7 @@ namespace WixToolset.Core Id = defaultId, IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu, }; + var compilerPackagePayload = new CompilerPackagePayload(compilerPayload, packageType); // This list lets us evaluate extension attributes *after* all core attributes // have been parsed and dealt with, regardless of authoring order. @@ -2627,6 +2591,9 @@ namespace WixToolset.Core compilerPayload.ParseHash(attrib); } break; + case "PayloadGeneration": + allowed = compilerPackagePayload.ParsePayloadGeneration(attrib); + break; case "ProductName": allowed = compilerPayload.IsRemoteAllowed; if (allowed) @@ -2691,11 +2658,18 @@ namespace WixToolset.Core if (allowed) { + if (compilerPackagePayload.PayloadGenerationType.HasValue) + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle", "PayloadGeneration")); + } + if (remoteBundleSeen) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "RemoteBundle")); } + this.ParseRemoteBundleElement(child, compilerPayload.Id.Id); remoteBundleSeen = true; } @@ -2723,7 +2697,7 @@ namespace WixToolset.Core this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "RemoteBundle")); } - return compilerPayload; + return compilerPackagePayload; } 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 { "build", Path.Combine(folder, "BundlePackage", "BundlePackage.wxs"), + "-bindpath", Path.Combine(folder, ".Data"), "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), "-bindpath", binFolder, "-intermediateFolder", parentIntermediateFolder, @@ -105,6 +106,7 @@ namespace WixToolsetTest.CoreIntegration "" + "" + "" + + "" + "", }, bundlePackages); @@ -129,7 +131,7 @@ namespace WixToolsetTest.CoreIntegration .ToArray(); WixAssert.CompareLineByLine(new string[] { - "", + "", }, packageElements); // grandparent.exe @@ -178,6 +180,20 @@ namespace WixToolsetTest.CoreIntegration "", }, bundlePackages); + ignoreAttributesByElementName = new Dictionary> + { + { "Payload", new List { "FileSize", "Hash" } }, + }; + var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload") + .Cast() + .Select(e => e.GetTestXml(ignoreAttributesByElementName)) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + "", + "", + }, payloads); + registrations = grandparentExtractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration") .Cast() .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 @@ namespace WixToolsetTest.CoreIntegration { + using System.Collections.Generic; using System.IO; using System.Linq; using WixBuildTools.TestSupport; @@ -10,6 +11,142 @@ namespace WixToolsetTest.CoreIntegration public class RemotePayloadFixture { + [Fact] + public void CanGetRemoteBundlePayload() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var outputFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(outputFolder, "obj"); + var exePath = Path.Combine(outputFolder, @"bin\test.exe"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "RemotePayload", "DiversePayloadsBundle.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-bindpath", Path.Combine(folder, ".Data"), + "-intermediateFolder", intermediateFolder, + "-o", exePath, + }); + + result.AssertSuccess(); + + File.Copy(Path.Combine(folder, ".Data", "signed_bundle_engine.exe"), Path.Combine(outputFolder, "bin", "signed_bundle_engine.exe")); + + // None + var noneOutFile = Path.Combine(outputFolder, "none_out.xml"); + + result = WixRunner.Execute(new[] + { + "burn", "remotepayload", + exePath, + "-o", noneOutFile, + "-packagetype", "bundle", + "-bundlepayloadgeneration", "none", + }); + + result.AssertSuccess(); + + var xml = File.ReadAllText(noneOutFile); + var ignoreAttributesByElementName = new Dictionary> + { + { "BundlePackagePayload", new List { "Size", "Hash" } }, + { "RemoteBundle", new List { "BundleId", "EngineVersion", "ProviderKey" } }, + { "Payload", new List { "Size", "Hash" } }, + }; + WixAssert.StringEqual( + "" + + "" + + "" + + "" + + "", xml.GetFragmentTestXml(ignoreAttributesByElementName)); + + // ExternalWithoutDownloadUrl + var externalWithoutDownloadUrlOutFile = Path.Combine(outputFolder, "externalWithoutDownloadUrl_out.xml"); + + result = WixRunner.Execute(new[] + { + "burn", "remotepayload", + exePath, + "-o", externalWithoutDownloadUrlOutFile, + "-packagetype", "bundle", + "-bundlepayloadgeneration", "externalWithoutDownloadUrl", + }); + + result.AssertSuccess(); + + xml = File.ReadAllText(externalWithoutDownloadUrlOutFile); + WixAssert.StringEqual( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", xml.GetFragmentTestXml(ignoreAttributesByElementName)); + + // External + var externalOutFile = Path.Combine(outputFolder, "external_out.xml"); + + result = WixRunner.Execute(new[] + { + "burn", "remotepayload", + exePath, + "-o", externalOutFile, + "-packagetype", "bundle", + "-bundlepayloadgeneration", "external", + }); + + result.AssertSuccess(); + + xml = File.ReadAllText(externalOutFile); + WixAssert.StringEqual( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", xml.GetFragmentTestXml(ignoreAttributesByElementName)); + + // All + var allOutFile = Path.Combine(outputFolder, "all_out.xml"); + + result = WixRunner.Execute(new[] + { + "burn", "remotepayload", + exePath, + "-o", allOutFile, + "-packagetype", "bundle", + "-bundlepayloadgeneration", "all", + }); + + result.AssertSuccess(); + + xml = File.ReadAllText(allOutFile); + WixAssert.StringEqual( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "", xml.GetFragmentTestXml(ignoreAttributesByElementName)); + } + } + [Fact] public void CanGetRemoteV3BundlePayload() { @@ -181,7 +318,7 @@ namespace WixToolsetTest.CoreIntegration "-recurse", "-du", "https://www.example.com/files/{0}", Path.Combine(folder, ".Data", "burn.exe"), - Path.Combine(folder, "RemotePayload", "*"), + Path.Combine(folder, "RemotePayload", "recurse", "*"), "-basepath", folder, "-bp", Path.Combine(folder, ".Data"), "-o", outFile @@ -195,9 +332,9 @@ namespace WixToolsetTest.CoreIntegration WixAssert.CompareLineByLine(new[] { @"", - @"", - @"", - @"", + @"", + @"", + @"", }, elements); } } 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 @@ - + - + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat deleted file mode 100644 index a96b19b3..00000000 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/a.dat +++ /dev/null @@ -1 +0,0 @@ -This is a.dat. diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat new file mode 100644 index 00000000..a96b19b3 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/a.dat @@ -0,0 +1 @@ +This is a.dat. diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat new file mode 100644 index 00000000..35c4c043 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/b.dat @@ -0,0 +1 @@ +This is b.dat. A little bit longer. diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat new file mode 100644 index 00000000..937aa91f --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/recurse/subfolder/c.dat @@ -0,0 +1 @@ +This is c.dat. A little bit longer, now! diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat deleted file mode 100644 index 35c4c043..00000000 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/b.dat +++ /dev/null @@ -1 +0,0 @@ -This is b.dat. A little bit longer. diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat deleted file mode 100644 index 937aa91f..00000000 --- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/RemotePayload/subfolder/c.dat +++ /dev/null @@ -1 +0,0 @@ -This is c.dat. A little bit longer, now! -- cgit v1.2.3-55-g6feb