From 8b3488c8c77959f425d0e5f70d27c5b2b1c86125 Mon Sep 17 00:00:00 2001 From: Sean Hall <r.sean.hall@gmail.com> Date: Sun, 28 Feb 2021 21:05:49 -0600 Subject: Use new PackagePayload symbols for the package's payload. #4183 --- src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 7 +- .../Bind/GenerateManifestDataFromIRCommand.cs | 4 + .../Bundles/CreateBurnManifestCommand.cs | 5 +- .../Bundles/GetPackageFacadesCommand.cs | 111 ++++++++- src/WixToolset.Core/Compile/CompilerPayload.cs | 93 +++++-- src/WixToolset.Core/Compiler_Bundle.cs | 268 ++++++++++++++------- src/WixToolset.Core/Linker.cs | 2 +- .../PackagePayloadFixture.cs | 207 ++++++++++++++++ .../PackagePayload/MissingSourceFileAndHash.wxs | 10 + .../PackagePayload/MissingSourceFileAndName.wxs | 10 + .../PackagePayloadInPayloadGroup.wxs | 15 ++ .../TestData/PackagePayload/SpecifiedHash.wxs | 10 + .../SpecifiedHashAndMissingDownloadUrl.wxs | 10 + .../PackagePayload/SpecifiedSourceFileAndHash.wxs | 10 + .../WrongPackagePayloadInPayloadGroup.wxs | 15 ++ .../SingleExeBundle/SingleExeRemotePayload.wxs | 9 +- 16 files changed, 670 insertions(+), 116 deletions(-) create mode 100644 src/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndHash.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndName.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/PackagePayloadInPayloadGroup.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHash.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndMissingDownloadUrl.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndHash.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/WrongPackagePayloadInPayloadGroup.wxs diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index 724dd7ff..12a530ae 100644 --- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs @@ -180,12 +180,17 @@ namespace WixToolset.Core.Burn IDictionary<string, PackageFacade> facades; { - var command = new GetPackageFacadesCommand(chainPackageSymbols, section); + var command = new GetPackageFacadesCommand(this.Messaging, chainPackageSymbols, section); command.Execute(); facades = command.PackageFacades; } + if (this.Messaging.EncounteredError) + { + return; + } + // Process each package facade. Note this is likely to add payloads and other symbols so // note that any indexes created above may be out of date now. foreach (var facade in facades.Values) diff --git a/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs index 36ced6cf..d4a69513 100644 --- a/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs @@ -66,12 +66,16 @@ namespace WixToolset.Core.Burn.Bind case SymbolDefinitionType.WixBundleContainer: case SymbolDefinitionType.WixBundleCustomDataAttribute: case SymbolDefinitionType.WixBundleExePackage: + case SymbolDefinitionType.WixBundleExePackagePayload: case SymbolDefinitionType.WixBundleExtension: case SymbolDefinitionType.WixBundleMsiFeature: case SymbolDefinitionType.WixBundleMsiPackage: + case SymbolDefinitionType.WixBundleMsiPackagePayload: case SymbolDefinitionType.WixBundleMsiProperty: case SymbolDefinitionType.WixBundleMspPackage: + case SymbolDefinitionType.WixBundleMspPackagePayload: case SymbolDefinitionType.WixBundleMsuPackage: + case SymbolDefinitionType.WixBundleMsuPackagePayload: case SymbolDefinitionType.WixBundlePackage: case SymbolDefinitionType.WixBundlePackageCommandLine: case SymbolDefinitionType.WixBundlePackageExitCode: diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index 3bc6bf1b..1559a646 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -564,15 +564,16 @@ namespace WixToolset.Core.Burn.Bundles } // Write any contained Payloads with the PackagePayload being first + var packagePayloadId = package.PackageSymbol.PayloadRef; writer.WriteStartElement("PayloadRef"); - writer.WriteAttributeString("Id", package.PackageSymbol.PayloadRef); + writer.WriteAttributeString("Id", packagePayloadId); writer.WriteEndElement(); var packagePayloads = payloadsByPackage[package.PackageId]; foreach (var payload in packagePayloads) { - if (payload.Id.Id != package.PackageSymbol.PayloadRef) + if (payload.Id.Id != packagePayloadId) { writer.WriteStartElement("PayloadRef"); writer.WriteAttributeString("Id", payload.Id.Id); diff --git a/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs index 24d1e8d8..dacff364 100644 --- a/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs @@ -6,17 +6,21 @@ namespace WixToolset.Core.Burn.Bundles using System.Linq; using WixToolset.Data; using WixToolset.Data.Symbols; + using WixToolset.Extensibility.Services; internal class GetPackageFacadesCommand { - public GetPackageFacadesCommand(IEnumerable<WixBundlePackageSymbol> chainPackageSymbols, IntermediateSection section) + public GetPackageFacadesCommand(IMessaging messaging, IEnumerable<WixBundlePackageSymbol> chainPackageSymbols, IntermediateSection section) { + this.Messaging = messaging; this.ChainPackageSymbols = chainPackageSymbols; this.Section = section; } private IEnumerable<WixBundlePackageSymbol> ChainPackageSymbols { get; } + private IMessaging Messaging { get; } + private IntermediateSection Section { get; } public IDictionary<string, PackageFacade> PackageFacades { get; private set; } @@ -27,12 +31,101 @@ namespace WixToolset.Core.Burn.Bundles var msiPackages = this.Section.Symbols.OfType<WixBundleMsiPackageSymbol>().ToDictionary(t => t.Id.Id); var mspPackages = this.Section.Symbols.OfType<WixBundleMspPackageSymbol>().ToDictionary(t => t.Id.Id); var msuPackages = this.Section.Symbols.OfType<WixBundleMsuPackageSymbol>().ToDictionary(t => t.Id.Id); + var exePackagePayloads = this.Section.Symbols.OfType<WixBundleExePackagePayloadSymbol>().ToDictionary(t => t.Id.Id); + var msiPackagePayloads = this.Section.Symbols.OfType<WixBundleMsiPackagePayloadSymbol>().ToDictionary(t => t.Id.Id); + var mspPackagePayloads = this.Section.Symbols.OfType<WixBundleMspPackagePayloadSymbol>().ToDictionary(t => t.Id.Id); + var msuPackagePayloads = this.Section.Symbols.OfType<WixBundleMsuPackagePayloadSymbol>().ToDictionary(t => t.Id.Id); var facades = new Dictionary<string, PackageFacade>(); foreach (var package in this.ChainPackageSymbols) { var id = package.Id.Id; + + IntermediateSymbol packagePayload = null; + foreach (var wixGroup in this.Section.Symbols.OfType<WixGroupSymbol>().Where(g => g.ParentType == ComplexReferenceParentType.Package && g.ParentId == id)) + { + if (wixGroup.ChildType == ComplexReferenceChildType.PackagePayload) + { + IntermediateSymbol tempPackagePayload = null; + if (exePackagePayloads.TryGetValue(wixGroup.ChildId, out var exePackagePayload)) + { + if (package.Type == WixBundlePackageType.Exe) + { + tempPackagePayload = exePackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(exePackagePayload.SourceLineNumbers, "Exe")); + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers)); + } + } + else if (msiPackagePayloads.TryGetValue(wixGroup.ChildId, out var msiPackagePayload)) + { + if (package.Type == WixBundlePackageType.Msi) + { + tempPackagePayload = msiPackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(msiPackagePayload.SourceLineNumbers, "Msi")); + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers)); + } + } + else if (mspPackagePayloads.TryGetValue(wixGroup.ChildId, out var mspPackagePayload)) + { + if (package.Type == WixBundlePackageType.Msp) + { + tempPackagePayload = mspPackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(mspPackagePayload.SourceLineNumbers, "Msp")); + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers)); + } + } + else if (msuPackagePayloads.TryGetValue(wixGroup.ChildId, out var msuPackagePayload)) + { + if (package.Type == WixBundlePackageType.Msu) + { + tempPackagePayload = msuPackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported(msuPackagePayload.SourceLineNumbers, "Msu")); + this.Messaging.Write(ErrorMessages.PackagePayloadUnsupported2(package.SourceLineNumbers)); + } + } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound(package.Type + "PackagePayload", wixGroup.ChildId)); + } + + if (tempPackagePayload != null) + { + if (packagePayload == null) + { + packagePayload = tempPackagePayload; + } + else + { + this.Messaging.Write(ErrorMessages.MultiplePackagePayloads(tempPackagePayload.SourceLineNumbers, id, packagePayload.Id.Id, tempPackagePayload.Id.Id)); + this.Messaging.Write(ErrorMessages.MultiplePackagePayloads2(packagePayload.SourceLineNumbers)); + this.Messaging.Write(ErrorMessages.MultiplePackagePayloads3(package.SourceLineNumbers)); + } + } + } + } + + if (packagePayload == null) + { + this.Messaging.Write(ErrorMessages.MissingPackagePayload(package.SourceLineNumbers, id, package.Type.ToString())); + } + else + { + package.PayloadRef = packagePayload.Id.Id; + } + switch (package.Type) { case WixBundlePackageType.Exe: @@ -40,6 +133,10 @@ namespace WixToolset.Core.Burn.Bundles { facades.Add(id, new PackageFacade(package, exePackage)); } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleExePackage", id)); + } break; case WixBundlePackageType.Msi: @@ -47,6 +144,10 @@ namespace WixToolset.Core.Burn.Bundles { facades.Add(id, new PackageFacade(package, msiPackage)); } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsiPackage", id)); + } break; case WixBundlePackageType.Msp: @@ -54,6 +155,10 @@ namespace WixToolset.Core.Burn.Bundles { facades.Add(id, new PackageFacade(package, mspPackage)); } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMspPackage", id)); + } break; case WixBundlePackageType.Msu: @@ -61,6 +166,10 @@ namespace WixToolset.Core.Burn.Bundles { facades.Add(id, new PackageFacade(package, msuPackage)); } + else + { + this.Messaging.Write(ErrorMessages.IdentifierNotFound("WixBundleMsuPackage", id)); + } break; } } diff --git a/src/WixToolset.Core/Compile/CompilerPayload.cs b/src/WixToolset.Core/Compile/CompilerPayload.cs index 4eda56f8..7a5fd1b2 100644 --- a/src/WixToolset.Core/Compile/CompilerPayload.cs +++ b/src/WixToolset.Core/Compile/CompilerPayload.cs @@ -23,6 +23,8 @@ namespace WixToolset.Core public Identifier Id { get; set; } + public bool IsRemoteAllowed { get; set; } + public bool IsRequired { get; set; } = true; public string Name { get; set; } @@ -48,26 +50,17 @@ namespace WixToolset.Core private SourceLineNumber SourceLineNumbers { get; } - private void CalculateAndVerifyFields(CompilerPayload remotePayload = null) + private void CalculateAndVerifyFields() { + var isRemote = this.IsRemoteAllowed && !String.IsNullOrEmpty(this.Hash); + if (String.IsNullOrEmpty(this.SourceFile)) { - if (String.IsNullOrEmpty(this.Name)) - { - if (this.IsRequired) - { - this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile")); - } - } - else if (remotePayload == null) + if (!String.IsNullOrEmpty(this.Name) && !isRemote) { this.SourceFile = Path.Combine("SourceDir", this.Name); } } - else if (remotePayload != null) - { - this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "RemotePayload", "SourceFile")); - } else if (this.SourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { if (String.IsNullOrEmpty(this.Name)) @@ -80,24 +73,67 @@ namespace WixToolset.Core } } - if (remotePayload != null) + if (String.IsNullOrEmpty(this.SourceFile) && !isRemote) { - if (this.DownloadUrl == null) + if (this.IsRequired) { - this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "RemotePayload")); + if (!this.IsRemoteAllowed) + { + this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile")); + } + else + { + this.Core.Write(ErrorMessages.ExpectedAttributes(this.SourceLineNumbers, this.Element.Name.LocalName, "SourceFile", "Hash")); + } } + } + else if (this.IsRemoteAllowed) + { + var isLocal = !String.IsNullOrEmpty(this.SourceFile); - if (YesNoDefaultType.No != this.Compressed) + if (isLocal) + { + if (isRemote) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Hash", "SourceFile")); + } + } + else { + if (String.IsNullOrEmpty(this.DownloadUrl)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "Hash")); + } + + if (String.IsNullOrEmpty(this.Name)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "Hash")); + } + + if (YesNoDefaultType.Yes == this.Compressed) + { + this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(this.SourceLineNumbers, this.Element.Name.LocalName)); + } + this.Compressed = YesNoDefaultType.No; - this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(this.SourceLineNumbers, this.Element.Name.LocalName)); } - this.Description = remotePayload.Description; - this.DisplayName = remotePayload.DisplayName; - this.Hash = remotePayload.Hash; - this.Size = remotePayload.Size; - this.Version = remotePayload.Version; + VerifyValidValue("Description", !String.IsNullOrEmpty(this.Description)); + VerifyValidValue("ProductName", !String.IsNullOrEmpty(this.ProductName)); + VerifyValidValue("Size", this.Size.HasValue); + VerifyValidValue("Version", !String.IsNullOrEmpty(this.Version)); + + void VerifyValidValue(string attributeName, bool isSpecified) + { + if (isLocal && isSpecified) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, attributeName, "SourceFile")); + } + else if (!isLocal && !isSpecified) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, attributeName, "Hash")); + } + } } } @@ -143,9 +179,9 @@ namespace WixToolset.Core return symbol; } - public void FinishCompilingPackage(CompilerPayload remotePayload) + public void FinishCompilingPackage() { - this.CalculateAndVerifyFields(remotePayload); + this.CalculateAndVerifyFields(); this.GenerateIdFromFilename(); if (this.Id == null) @@ -155,6 +191,13 @@ namespace WixToolset.Core } } + public void FinishCompilingPackagePayload() + { + this.CalculateAndVerifyFields(); + this.GenerateIdFromFilename(); + this.GenerateIdFromPrefix("ppy"); + } + public void FinishCompilingPayload() { this.CalculateAndVerifyFields(); diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs index 64fe2acc..60db75d6 100644 --- a/src/WixToolset.Core/Compiler_Bundle.cs +++ b/src/WixToolset.Core/Compiler_Bundle.cs @@ -1451,71 +1451,6 @@ namespace WixToolset.Core return compilerPayload.Id; } - private CompilerPayload ParseRemotePayloadElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - var remotePayload = new CompilerPayload(this.Core, sourceLineNumbers, node); - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Description": - remotePayload.ParseDescription(attrib); - break; - case "Hash": - remotePayload.ParseHash(attrib); - break; - case "ProductName": - remotePayload.ParseProductName(attrib); - break; - case "Size": - remotePayload.ParseSize(attrib); - break; - case "Version": - remotePayload.ParseVersion(attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (String.IsNullOrEmpty(remotePayload.ProductName)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductName")); - } - - if (String.IsNullOrEmpty(remotePayload.Description)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description")); - } - - if (String.IsNullOrEmpty(remotePayload.Hash)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Hash")); - } - - if (0 == remotePayload.Size) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Size")); - } - - if (String.IsNullOrEmpty(remotePayload.Version)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); - } - - return remotePayload; - } - /// <summary> /// Parse PayloadGroup element. /// </summary> @@ -1561,8 +1496,21 @@ namespace WixToolset.Core { if (CompilerCore.WixNamespace == child.Name.Namespace) { + WixBundlePackageType? packageType = null; switch (child.Name.LocalName) { + case "ExePackagePayload": + packageType = WixBundlePackageType.Exe; + break; + case "MsiPackagePayload": + packageType = WixBundlePackageType.Msi; + break; + case "MspPackagePayload": + packageType = WixBundlePackageType.Msp; + break; + case "MsuPackagePayload": + packageType = WixBundlePackageType.Msu; + break; case "Payload": previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id, previousType, previousId); previousType = ComplexReferenceChildType.Payload; @@ -1575,6 +1523,19 @@ namespace WixToolset.Core this.Core.UnexpectedElement(node, child); break; } + + if (packageType.HasValue) + { + var compilerPayload = this.ParsePackagePayloadElement(null, child, packageType.Value, null); + var payloadSymbol = compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.PayloadGroup, id?.Id, previousType, previousId?.Id); + if (payloadSymbol != null) + { + previousId = payloadSymbol.Id; + previousType = ComplexReferenceChildType.Payload; + + this.CreatePackagePayloadSymbol(payloadSymbol.SourceLineNumbers, packageType.Value, payloadSymbol.Id, ComplexReferenceParentType.PayloadGroup, id); + } + } } else { @@ -1984,7 +1945,7 @@ namespace WixToolset.Core Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType); Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType); - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);; + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node) { IsRequired = false, @@ -2008,8 +1969,9 @@ namespace WixToolset.Core string msuKB = null; var enableFeatureSelection = YesNoType.NotSet; var forcePerMachine = YesNoType.NotSet; - CompilerPayload remotePayload = null; + CompilerPayload childPackageCompilerPayload = null; var slipstream = YesNoType.NotSet; + var hasPayloadInfo = false; var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" }; @@ -2029,12 +1991,15 @@ namespace WixToolset.Core break; case "Name": compilerPayload.ParseName(attrib); + hasPayloadInfo = true; break; case "SourceFile": compilerPayload.ParseSourceFile(attrib); + hasPayloadInfo = true; break; case "DownloadUrl": compilerPayload.ParseDownloadUrl(attrib); + hasPayloadInfo = true; break; case "After": after = this.Core.GetAttributeValue(sourceLineNumbers, attrib); @@ -2128,6 +2093,7 @@ namespace WixToolset.Core break; case "Compressed": compilerPayload.ParseCompressed(attrib); + hasPayloadInfo = true; break; case "Slipstream": slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); @@ -2150,26 +2116,30 @@ namespace WixToolset.Core } } - // We need to handle RemotePayload up front because it effects value of sourceFile which is used in Id generation. Id is needed by other child elements. - foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload")) + // We need to handle the package payload up front because it affects Id generation. Id is needed by other child elements. + var packagePayloadElementName = packageType + "PackagePayload"; + foreach (var child in node.Elements(CompilerCore.WixNamespace + packagePayloadElementName)) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage" && node.Name.LocalName != "MsuPackage") + if (childPackageCompilerPayload != null) { - this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers)); - continue; + this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); } - - if (null != remotePayload) + else if (hasPayloadInfo) { - this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); + this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "SourceFile", "Name", "DownloadUrl", "Compressed")); } - remotePayload = this.ParseRemotePayloadElement(child); + childPackageCompilerPayload = this.ParsePackagePayloadElement(childSourceLineNumbers, child, packageType, compilerPayload.Id); + } + + if (compilerPayload.Id == null && childPackageCompilerPayload != null) + { + compilerPayload.Id = childPackageCompilerPayload.Id; } - compilerPayload.FinishCompilingPackage(remotePayload); + compilerPayload.FinishCompilingPackage(); var id = compilerPayload.Id; if (null == logPathVariable) @@ -2279,7 +2249,11 @@ namespace WixToolset.Core this.ParseCommandLineElement(child, id.Id); } break; - case "RemotePayload": + case "ExePackagePayload": + case "MsiPackagePayload": + case "MspPackagePayload": + case "MsuPackagePayload": + allowed = packagePayloadElementName == child.Name.LocalName; // Handled previously break; default: @@ -2301,8 +2275,13 @@ namespace WixToolset.Core if (!this.Core.EncounteredError) { - // We create the package contents as a payload with this package as the parent - compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Package, id.Id); + 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); + } this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id)); @@ -2313,7 +2292,6 @@ namespace WixToolset.Core var chainPackageSymbol = this.Core.AddSymbol(new WixBundlePackageSymbol(sourceLineNumbers, id) { Type = packageType, - PayloadRef = id.Id, Attributes = attributes, InstallCondition = installCondition, CacheId = cacheId, @@ -2391,6 +2369,134 @@ namespace WixToolset.Core return id.Id; } + private void CreatePackagePayloadSymbol(SourceLineNumber sourceLineNumbers, WixBundlePackageType packageType, Identifier payloadId, ComplexReferenceParentType parentType, Identifier parentId) + { + switch (packageType) + { + 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) + { + sourceLineNumbers = sourceLineNumbers ?? Preprocessor.GetSourceLineNumbers(node); + var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node) + { + Id = defaultId, + IsRemoteAllowed = packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu, + }; + + // This list lets us evaluate extension attributes *after* all core attributes + // have been parsed and dealt with, regardless of authoring order. + var extensionAttributes = new List<XAttribute>(); + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + var allowed = true; + switch (attrib.Name.LocalName) + { + case "Id": + compilerPayload.ParseId(attrib); + break; + case "Compressed": + compilerPayload.ParseCompressed(attrib); + break; + case "Name": + compilerPayload.ParseName(attrib); + break; + case "SourceFile": + compilerPayload.ParseSourceFile(attrib); + break; + case "DownloadUrl": + compilerPayload.ParseDownloadUrl(attrib); + break; + case "Description": + allowed = compilerPayload.IsRemoteAllowed; + if (allowed) + { + compilerPayload.ParseDescription(attrib); + } + break; + case "Hash": + allowed = compilerPayload.IsRemoteAllowed; + if (allowed) + { + compilerPayload.ParseHash(attrib); + } + break; + case "ProductName": + allowed = compilerPayload.IsRemoteAllowed; + if (allowed) + { + compilerPayload.ParseProductName(attrib); + } + break; + case "Size": + allowed = compilerPayload.IsRemoteAllowed; + if (allowed) + { + compilerPayload.ParseSize(attrib); + } + break; + case "Version": + allowed = compilerPayload.IsRemoteAllowed; + if (allowed) + { + compilerPayload.ParseVersion(attrib); + } + break; + default: + allowed = false; + break; + } + + if (!allowed) + { + this.Core.UnexpectedAttribute(node, attrib); + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + compilerPayload.FinishCompilingPackagePayload(); + + // Now that the PayloadId is known, we can parse the extension attributes. + var context = new Dictionary<string, string> + { + ["Id"] = compilerPayload.Id.Id, + }; + + foreach (var extensionAttribute in extensionAttributes) + { + this.Core.ParseExtensionAttribute(node, extensionAttribute, context); + } + + this.Core.ParseForExtensionElements(node); + + return compilerPayload; + } + /// <summary> /// Parse CommandLine element. /// </summary> diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs index e0af89ba..41e0db7d 100644 --- a/src/WixToolset.Core/Linker.cs +++ b/src/WixToolset.Core/Linker.cs @@ -1242,7 +1242,7 @@ namespace WixToolset.Core var groups = new WixGroupingOrdering(entrySection, this.Messaging); // Create UX payloads and Package payloads - groups.UseTypes(new[] { ComplexReferenceParentType.Container, ComplexReferenceParentType.Layout, ComplexReferenceParentType.PackageGroup, ComplexReferenceParentType.PayloadGroup, ComplexReferenceParentType.Package }, new[] { ComplexReferenceChildType.PackageGroup, ComplexReferenceChildType.Package, ComplexReferenceChildType.PayloadGroup, ComplexReferenceChildType.Payload }); + groups.UseTypes(new[] { ComplexReferenceParentType.Container, ComplexReferenceParentType.Layout, ComplexReferenceParentType.PackageGroup, ComplexReferenceParentType.PayloadGroup, ComplexReferenceParentType.Package }, new[] { ComplexReferenceChildType.PackageGroup, ComplexReferenceChildType.Package, ComplexReferenceChildType.PackagePayload, ComplexReferenceChildType.PayloadGroup, ComplexReferenceChildType.Payload }); groups.FlattenAndRewriteGroups(ComplexReferenceParentType.Package, false); groups.FlattenAndRewriteGroups(ComplexReferenceParentType.Container, false); groups.FlattenAndRewriteGroups(ComplexReferenceParentType.Layout, false); diff --git a/src/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs new file mode 100644 index 00000000..77a21f61 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs @@ -0,0 +1,207 @@ +// 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 WixToolsetTest.CoreIntegration +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using Xunit; + + public class PackagePayloadFixture + { + [Fact] + public void CanSpecifyPackagePayloadInPayloadGroup() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var bundlePath = Path.Combine(baseFolder, @"bin\test.exe"); + var baFolderPath = Path.Combine(baseFolder, "ba"); + var extractFolderPath = Path.Combine(baseFolder, "extract"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "PackagePayload", "PackagePayloadInPayloadGroup.wxs"), + Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-bindpath", Path.Combine(folder, ".Data"), + "-intermediateFolder", intermediateFolder, + "-o", bundlePath, + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(bundlePath)); + + var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); + extractResult.AssertSuccess(); + + var exePackageElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Chain/burn:ExePackage"); + var ignoreAttributesByElementName = new Dictionary<string, List<string>> + { + { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } }, + }; + Assert.Equal(1, exePackageElements.Count); + Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='yes' CacheId='*' InstallSize='*' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_PackagePayloadInPayloadGroup' RollbackLogPathVariable='WixBundleRollbackLog_PackagePayloadInPayloadGroup' DetectCondition='none' InstallArguments='' UninstallArguments='' RepairArguments='' Repairable='no'><PayloadRef Id='burn.exe' /></ExePackage>", exePackageElements[0].GetTestXml(ignoreAttributesByElementName)); + } + } + + [Fact] + public void ErrorWhenMissingSourceFileAndHash() + { + var folder = TestData.Get(@"TestData", "PackagePayload"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(false, new[] + { + "build", + Path.Combine(folder, "MissingSourceFileAndHash.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + Assert.Equal(44, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The MsuPackagePayload element's SourceFile or Hash attribute was not found; one of these is required.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + + [Fact] + public void ErrorWhenMissingSourceFileAndName() + { + var folder = TestData.Get(@"TestData", "PackagePayload"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(false, new[] + { + "build", + Path.Combine(folder, "MissingSourceFileAndName.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + Assert.Equal(44, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The MsiPackagePayload element's Name or SourceFile attribute was not found; one of these is required.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + + [Fact] + public void ErrorWhenSpecifiedHash() + { + var folder = TestData.Get(@"TestData", "PackagePayload"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "SpecifiedHash.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + Assert.Equal(4, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The MspPackagePayload element contains an unexpected attribute 'Hash'.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + + [Fact] + public void ErrorWhenSpecifiedHashAndMissingDownloadUrl() + { + var folder = TestData.Get(@"TestData", "PackagePayload"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "SpecifiedHashAndMissingDownloadUrl.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + Assert.Equal(10, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The MsuPackagePayload/@DownloadUrl attribute was not found; it is required when attribute Hash is specified.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + + [Fact] + public void ErrorWhenSpecifiedSourceFileAndHash() + { + var folder = TestData.Get(@"TestData", "PackagePayload"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "SpecifiedSourceFileAndHash.wxs"), + "-o", Path.Combine(baseFolder, "test.wixlib") + }); + + Assert.Equal(35, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The ExePackagePayload/@Hash attribute cannot be specified when attribute SourceFile is present.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + + [Fact] + public void ErrorWhenWrongPackagePayloadInPayloadGroup() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var bundlePath = Path.Combine(baseFolder, @"bin\test.exe"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "PackagePayload", "WrongPackagePayloadInPayloadGroup.wxs"), + Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-bindpath", Path.Combine(folder, ".Data"), + "-intermediateFolder", intermediateFolder, + "-o", bundlePath, + }); + + Assert.Equal(407, result.ExitCode); + WixAssert.CompareLineByLine(new[] + { + "The ExePackagePayload element can only be used for ExePackages.", + "The location of the package related to previous error.", + "There is no payload defined for package 'WrongPackagePayloadInPayloadGroup'. This is specified on the MsiPackage element or a child MsiPackagePayload element.", + }, result.Messages.Select(m => m.ToString()).ToArray()); + } + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndHash.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndHash.wxs new file mode 100644 index 00000000..5e1b99ff --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndHash.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <MsuPackage Id="MissingSourceFileAndHash" Permanent="yes" DetectCondition="none"> + <MsuPackagePayload DownloadUrl="example.com" /> + </MsuPackage> + </PackageGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndName.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndName.wxs new file mode 100644 index 00000000..f220d81a --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/MissingSourceFileAndName.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <MsiPackage Id="MissingSourceFileAndName"> + <MsiPackagePayload DownloadUrl="example.com" /> + </MsiPackage> + </PackageGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/PackagePayloadInPayloadGroup.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/PackagePayloadInPayloadGroup.wxs new file mode 100644 index 00000000..149870a4 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/PackagePayloadInPayloadGroup.wxs @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <ExePackage Id="PackagePayloadInPayloadGroup" Permanent="yes" DetectCondition="none"> + <PayloadGroupRef Id="PackagePayloadGroup" /> + </ExePackage> + </PackageGroup> + </Fragment> + <Fragment> + <PayloadGroup Id="PackagePayloadGroup"> + <ExePackagePayload SourceFile="burn.exe" /> + </PayloadGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHash.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHash.wxs new file mode 100644 index 00000000..3c361c49 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHash.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <MspPackage Id="SpecifiedHash"> + <MspPackagePayload SourceFile="example.msp" DownloadUrl="example.com" Hash="abcd" /> + </MspPackage> + </PackageGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndMissingDownloadUrl.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndMissingDownloadUrl.wxs new file mode 100644 index 00000000..8e62f660 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedHashAndMissingDownloadUrl.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <MsuPackage Id="SpecifiedHashAndMissingDownloadUrl" Permanent="yes" DetectCondition="none"> + <MsuPackagePayload Name="example.msu" Hash="abcd" Size="1" Version="1.0.0.0" ProductName="KB1234567" Description="fake msu" /> + </MsuPackage> + </PackageGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndHash.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndHash.wxs new file mode 100644 index 00000000..f79da874 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/SpecifiedSourceFileAndHash.wxs @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <ExePackage Id="SpecifiedSourceFileAndHash" Permanent="yes" DetectCondition="none"> + <ExePackagePayload SourceFile="example.exe" Hash="abcd" /> + </ExePackage> + </PackageGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/WrongPackagePayloadInPayloadGroup.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/WrongPackagePayloadInPayloadGroup.wxs new file mode 100644 index 00000000..dda306cf --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PackagePayload/WrongPackagePayloadInPayloadGroup.wxs @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Fragment> + <PackageGroup Id="BundlePackages"> + <MsiPackage Id="WrongPackagePayloadInPayloadGroup"> + <PayloadGroupRef Id="WrongPackagePayloadGroup" /> + </MsiPackage> + </PackageGroup> + </Fragment> + <Fragment> + <PayloadGroup Id="WrongPackagePayloadGroup"> + <ExePackagePayload SourceFile="burn.exe" /> + </PayloadGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SingleExeBundle/SingleExeRemotePayload.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/SingleExeBundle/SingleExeRemotePayload.wxs index 79ba52d2..56f08ba9 100644 --- a/src/test/WixToolsetTest.CoreIntegration/TestData/SingleExeBundle/SingleExeRemotePayload.wxs +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SingleExeBundle/SingleExeRemotePayload.wxs @@ -13,11 +13,10 @@ Vital="yes" Permanent="yes" Protocol="netfx4" - DownloadUrl="C" - LogPathVariable="NetFx462FullLog" - Compressed="no" - Name="NDP462-KB3151802-Web.exe"> - <RemotePayload + LogPathVariable="NetFx462FullLog"> + <ExePackagePayload + DownloadUrl="C" + Name="NDP462-KB3151802-Web.exe" Description="Microsoft .NET Framework 4.6.2 Setup" Hash="C42E6ED280290648BBD59F664008852F4CFE4548" ProductName="Microsoft .NET Framework 4.6.2" -- cgit v1.2.3-55-g6feb