@joebigelow / wix-1 / commits / faa9777b

Add support for remote BundlePackagePayload.

Sean Hall committed Apr 13, 2022 at 12:16 UTC faa9777b7720a935b28c33c3fa1088cd7bcc2f86
9 files changed +407 -10
src/api/wix/WixToolset.Extensibility/Services/IParseHelper.cs
+2 -1
@@ -422,7 +422,8 @@ namespace WixToolset.Extensibility.Services
422 /// <param name="intermediate">Parent intermediate.</param>
423 /// <param name="section">Parent section.</param>
424 /// <param name="element">Element to parse children.</param>
425 - void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element);
425 + /// <param name="context">Extra information about the context in which this element is being parsed.</param>
426 + void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element, IDictionary<string, string> context = null);
427
428 /// <summary>
429 /// Schedules an action symbol.
src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+1 -1
@@ -57,7 +57,7 @@ namespace WixToolset.Core.Burn.Bundles
57 public void Execute()
58 {
59 var harvestedBundlePackage = this.Section.Symbols.OfType<WixBundleHarvestedBundlePackageSymbol>()
60 - .Where(h => h.Id == this.PackagePayload.Id)
60 + .Where(h => h.Id.Id == this.PackagePayload.Id.Id)
61 .SingleOrDefault();
62
63 if (harvestedBundlePackage == null)
src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+1 -1
@@ -65,7 +65,7 @@ namespace WixToolset.Core.Burn.Bundles
65 public void Execute()
66 {
67 var harvestedMsiPackage = this.Section.Symbols.OfType<WixBundleHarvestedMsiPackageSymbol>()
68 - .Where(h => h.Id == this.PackagePayload.Id)
68 + .Where(h => h.Id.Id == this.PackagePayload.Id.Id)
69 .SingleOrDefault();
70
71 if (harvestedMsiPackage == null)
src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
+1 -1
@@ -53,7 +53,7 @@ namespace WixToolset.Core.Burn.Bundles
53 public void Execute()
54 {
55 var harvestedMspPackage = this.Section.Symbols.OfType<WixBundleHarvestedMspPackageSymbol>()
56 - .Where(h => h.Id == this.PackagePayload.Id)
56 + .Where(h => h.Id.Id == this.PackagePayload.Id.Id)
57 .SingleOrDefault();
58
59 if (harvestedMspPackage == null)
src/wix/WixToolset.Core/CompilerCore.cs
+3 -2
@@ -849,9 +849,10 @@ namespace WixToolset.Core
849 /// Process all children of the element looking for extensions and erroring on the unexpected.
850 /// </summary>
851 /// <param name="element">Element to parse children.</param>
852 - public void ParseForExtensionElements(XElement element)
852 + /// <param name="context">Extra information about the context in which this element is being parsed.</param>
853 + public void ParseForExtensionElements(XElement element, IDictionary<string, string> context = null)
854 {
854 - this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element);
855 + this.parseHelper.ParseForExtensionElements(this.extensions.Values, this.intermediate, this.ActiveSection, element, context);
856 }
857
858 /// <summary>
src/wix/WixToolset.Core/Compiler_Bundle.cs
+282 -2
@@ -2552,7 +2552,7 @@ namespace WixToolset.Core
2552 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
2553 {
2554 Id = defaultId,
2555 - IsRemoteAllowed = packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu,
2555 + IsRemoteAllowed = packageType == WixBundlePackageType.Bundle || packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu,
2556 };
2557
2558 // This list lets us evaluate extension attributes *after* all core attributes
@@ -2659,11 +2659,291 @@ namespace WixToolset.Core
2659 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
2660 }
2661
2662 - this.Core.ParseForExtensionElements(node);
2662 + foreach (var child in node.Elements())
2663 + {
2664 + if (CompilerCore.WixNamespace == child.Name.Namespace)
2665 + {
2666 + bool allowed;
2667 + switch (child.Name.LocalName)
2668 + {
2669 + case "RemoteBundle":
2670 + allowed = packageType == WixBundlePackageType.Bundle;
2671 +
2672 + if (allowed)
2673 + {
2674 + this.ParseRemoteBundleElement(child, compilerPayload.Id.Id);
2675 + }
2676 +
2677 + break;
2678 + default:
2679 + allowed = false;
2680 + break;
2681 + }
2682 +
2683 + if (!allowed)
2684 + {
2685 + this.Core.UnexpectedElement(node, child);
2686 + }
2687 + }
2688 + else
2689 + {
2690 + this.Core.ParseExtensionElement(node, child, context);
2691 + }
2692 + }
2693
2694 return compilerPayload;
2695 }
2696
2697 + private void ParseRemoteBundleElement(XElement node, string packagePayloadId)
2698 + {
2699 + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2700 + string bundleId = null;
2701 + string displayName = null;
2702 + string engineVersion = null;
2703 + long? installSize = null;
2704 + string manifestNamespace = null;
2705 + var perMachine = YesNoType.NotSet;
2706 + var protocolVersion = -1;
2707 + string providerKey = null;
2708 + string upgradeCode = null;
2709 + string version = null;
2710 + var win64 = YesNoType.NotSet;
2711 +
2712 + foreach (var attrib in node.Attributes())
2713 + {
2714 + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2715 + {
2716 + switch (attrib.Name.LocalName)
2717 + {
2718 + case "BundleId":
2719 + bundleId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib);
2720 + break;
2721 + case "DisplayName":
2722 + displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2723 + break;
2724 + case "EngineVersion":
2725 + engineVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
2726 + break;
2727 + case "InstallSize":
2728 + installSize = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, Int64.MaxValue);
2729 + break;
2730 + case "ManifestNamespace":
2731 + manifestNamespace = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2732 + break;
2733 + case "PerMachine":
2734 + perMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2735 + break;
2736 + case "ProtocolVersion":
2737 + protocolVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
2738 + break;
2739 + case "ProviderKey":
2740 + providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2741 + break;
2742 + case "UpgradeCode":
2743 + upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib);
2744 + break;
2745 + case "Version":
2746 + version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
2747 + break;
2748 + case "Win64":
2749 + win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2750 + break;
2751 + default:
2752 + this.Core.UnexpectedAttribute(node, attrib);
2753 + break;
2754 + }
2755 + }
2756 + else
2757 + {
2758 + this.Core.ParseExtensionAttribute(node, attrib);
2759 + }
2760 + }
2761 +
2762 + if (String.IsNullOrEmpty(bundleId))
2763 + {
2764 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BundleId"));
2765 + }
2766 +
2767 + if (String.IsNullOrEmpty(manifestNamespace))
2768 + {
2769 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManifestNamespace"));
2770 + }
2771 +
2772 + if (perMachine == YesNoType.NotSet)
2773 + {
2774 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PerMachine"));
2775 + }
2776 +
2777 + if (protocolVersion == -1)
2778 + {
2779 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProtocolVersion"));
2780 + }
2781 +
2782 + if (String.IsNullOrEmpty(providerKey))
2783 + {
2784 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey"));
2785 + }
2786 +
2787 + if (String.IsNullOrEmpty(upgradeCode))
2788 + {
2789 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
2790 + }
2791 +
2792 + if (String.IsNullOrEmpty(version))
2793 + {
2794 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
2795 + }
2796 +
2797 + if (win64 == YesNoType.NotSet)
2798 + {
2799 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Win64"));
2800 + }
2801 +
2802 + if (!this.Messaging.EncounteredError)
2803 + {
2804 + WixBundleHarvestedBundlePackageAttributes bundleAttributes = 0;
2805 + bundleAttributes |= (YesNoType.Yes == perMachine) ? WixBundleHarvestedBundlePackageAttributes.PerMachine : 0;
2806 + bundleAttributes |= (YesNoType.Yes == win64) ? WixBundleHarvestedBundlePackageAttributes.Win64 : 0;
2807 +
2808 + var symbol = this.Core.AddSymbol(new WixBundleHarvestedBundlePackageSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, packagePayloadId))
2809 + {
2810 + Attributes = bundleAttributes,
2811 + BundleId = bundleId,
2812 + DisplayName = displayName,
2813 + EngineVersion = engineVersion,
2814 + ManifestNamespace = manifestNamespace,
2815 + ProtocolVersion = protocolVersion,
2816 + Version = version,
2817 + });
2818 +
2819 + if (installSize.HasValue)
2820 + {
2821 + symbol.InstallSize = installSize.Value;
2822 + }
2823 +
2824 + this.Core.AddSymbol(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
2825 + {
2826 + PackagePayloadRef = packagePayloadId,
2827 + BundleId = upgradeCode,
2828 + Action = RelatedBundleActionType.Upgrade,
2829 + });
2830 +
2831 + var depId = new Identifier(AccessModifier.Section, this.Core.CreateIdentifier("dep", packagePayloadId, providerKey));
2832 + this.Core.AddSymbol(new WixBundleHarvestedDependencyProviderSymbol(sourceLineNumbers, depId)
2833 + {
2834 + PackagePayloadRef = packagePayloadId,
2835 + ProviderKey = providerKey,
2836 + Version = version,
2837 + });
2838 + }
2839 +
2840 + var context = new Dictionary<string, string>
2841 + {
2842 + ["Id"] = packagePayloadId,
2843 + };
2844 +
2845 + foreach (var child in node.Elements())
2846 + {
2847 + if (CompilerCore.WixNamespace == child.Name.Namespace)
2848 + {
2849 + switch (child.Name.LocalName)
2850 + {
2851 + case "RemoteRelatedBundle":
2852 + this.ParseRemoteRelatedBundleElement(child, packagePayloadId);
2853 + break;
2854 + default:
2855 + this.Core.UnexpectedElement(node, child);
2856 + break;
2857 + }
2858 + }
2859 + else
2860 + {
2861 + this.Core.ParseExtensionElement(node, child, context);
2862 + }
2863 + }
2864 + }
2865 +
2866 + private void ParseRemoteRelatedBundleElement(XElement node, string payloadId)
2867 + {
2868 + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2869 + string id = null;
2870 + RelatedBundleActionType? actionType = null;
2871 +
2872 + foreach (var attrib in node.Attributes())
2873 + {
2874 + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2875 + {
2876 + switch (attrib.Name.LocalName)
2877 + {
2878 + case "Id":
2879 + id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2880 + break;
2881 + case "Action":
2882 + var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2883 + switch (action)
2884 + {
2885 + case "Detect":
2886 + case "detect":
2887 + actionType = RelatedBundleActionType.Detect;
2888 + break;
2889 + case "Upgrade":
2890 + case "upgrade":
2891 + actionType = RelatedBundleActionType.Upgrade;
2892 + break;
2893 + case "Addon":
2894 + case "addon":
2895 + actionType = RelatedBundleActionType.Addon;
2896 + break;
2897 + case "Patch":
2898 + case "patch":
2899 + actionType = RelatedBundleActionType.Patch;
2900 + break;
2901 + case "":
2902 + break;
2903 + default:
2904 + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
2905 + break;
2906 + }
2907 + break;
2908 + default:
2909 + this.Core.UnexpectedAttribute(node, attrib);
2910 + break;
2911 + }
2912 + }
2913 + else
2914 + {
2915 + this.Core.ParseExtensionAttribute(node, attrib);
2916 + }
2917 + }
2918 +
2919 + if (null == id)
2920 + {
2921 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2922 + }
2923 +
2924 + if (!actionType.HasValue)
2925 + {
2926 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
2927 + }
2928 +
2929 + var context = new Dictionary<string, string>
2930 + {
2931 + ["PayloadId"] = payloadId,
2932 + };
2933 +
2934 + this.Core.ParseForExtensionElements(node, context);
2935 +
2936 + if (!this.Core.EncounteredError)
2937 + {
2938 + this.Core.AddSymbol(new WixBundlePackageRelatedBundleSymbol(sourceLineNumbers)
2939 + {
2940 + PackagePayloadRef = payloadId,
2941 + BundleId = id,
2942 + Action = actionType.Value,
2943 + });
2944 + }
2945 + }
2946 +
2947 /// <summary>
2948 /// Parse CommandLine element.
2949 /// </summary>
src/wix/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+2 -2
@@ -714,7 +714,7 @@ namespace WixToolset.Core.ExtensibilityServices
714 return keyPath;
715 }
716
717 - public void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element)
717 + public void ParseForExtensionElements(IEnumerable<ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement element, IDictionary<string, string> context = null)
718 {
719 var checkInnerText = false;
720
@@ -728,7 +728,7 @@ namespace WixToolset.Core.ExtensibilityServices
728 }
729 else
730 {
731 - this.ParseExtensionElement(extensions, intermediate, section, element, childElement);
731 + this.ParseExtensionElement(extensions, intermediate, section, element, childElement, context);
732 }
733 }
734 else
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+87
@@ -204,6 +204,93 @@ namespace WixToolsetTest.CoreIntegration
204 }
205 }
206
207 + [Fact]
208 + public void CanBuildBundleWithRemoteBundlePackage()
209 + {
210 + var folder = TestData.Get(@"TestData");
211 +
212 + using (var fs = new DisposableFileSystem())
213 + {
214 + var baseFolder = fs.GetFolder();
215 + var parentIntermediateFolder = Path.Combine(baseFolder, "obj", "Parent");
216 + var binFolder = Path.Combine(baseFolder, "bin");
217 + var parentBundlePath = Path.Combine(binFolder, "parent.exe");
218 + var parentPdbPath = Path.Combine(binFolder, "parent.wixpdb");
219 + var parentBaFolderPath = Path.Combine(baseFolder, "parentba");
220 + var extractFolderPath = Path.Combine(baseFolder, "extract");
221 +
222 + var result = WixRunner.Execute(new[]
223 + {
224 + "build",
225 + Path.Combine(folder, "BundlePackage", "RemoteBundlePackage.wxs"),
226 + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
227 + "-bindpath", binFolder,
228 + "-intermediateFolder", parentIntermediateFolder,
229 + "-o", parentBundlePath,
230 + });
231 +
232 + result.AssertSuccess();
233 +
234 + Assert.True(File.Exists(parentBundlePath));
235 +
236 + var chainBundleId = "{216BDA7F-74BD-45E8-957B-500552F05629}";
237 + string parentBundleId;
238 + using (var wixOutput = WixOutput.Read(parentPdbPath))
239 + {
240 +
241 + var intermediate = Intermediate.Load(wixOutput);
242 + var section = intermediate.Sections.Single();
243 +
244 + var bundleSymbol = section.Symbols.OfType<WixBundleSymbol>().Single();
245 + parentBundleId = bundleSymbol.BundleId;
246 + }
247 +
248 + var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, parentBaFolderPath, extractFolderPath);
249 + extractResult.AssertSuccess();
250 +
251 + var ignoreAttributesByElementName = new Dictionary<string, List<string>>
252 + {
253 + { "BundlePackage", new List<string> { "Size" } },
254 + };
255 + var bundlePackages = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Chain/burn:BundlePackage")
256 + .Cast<XmlElement>()
257 + .Select(e => e.GetTestXml(ignoreAttributesByElementName))
258 + .ToArray();
259 + WixAssert.CompareLineByLine(new string[]
260 + {
261 + $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0.0' InstallSize='34' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' Version='1.0.0.0' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'>" +
262 + "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
263 + "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
264 + "<PayloadRef Id='chain.exe' />" +
265 + "</BundlePackage>",
266 + }, bundlePackages);
267 +
268 + var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
269 + .Cast<XmlElement>()
270 + .Select(e => e.GetTestXml())
271 + .ToArray();
272 + WixAssert.CompareLineByLine(new string[]
273 + {
274 + $"<Registration Id='{parentBundleId}' ExecutableName='parent.exe' PerMachine='yes' Tag='' Version='1.0.1.0' ProviderKey='{parentBundleId}'>" +
275 + "<Arp DisplayName='RemoteBundlePackageBundle' DisplayVersion='1.0.1.0' Publisher='Example Corporation' />" +
276 + "</Registration>"
277 + }, registrations);
278 +
279 + ignoreAttributesByElementName = new Dictionary<string, List<string>>
280 + {
281 + { "WixPackageProperties", new List<string> { "DownloadSize", "PackageSize" } },
282 + };
283 + var packageElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:WixPackageProperties")
284 + .Cast<XmlElement>()
285 + .Select(e => e.GetTestXml(ignoreAttributesByElementName))
286 + .ToArray();
287 + WixAssert.CompareLineByLine(new string[]
288 + {
289 + "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundleDescription' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='yes' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='no' Version='1.0.0.0' Cache='keep' />",
290 + }, packageElements);
291 + }
292 + }
293 +
294 [Fact]
295 public void CanBuildBundleWithV3BundlePackage()
296 {
src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/RemoteBundlePackage.wxs new
+28
@@ -0,0 +1,28 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Bundle Name="RemoteBundlePackageBundle" Version="1.0.1.0" Manufacturer="Example Corporation" UpgradeCode="{9BE68A41-FD0E-4E25-B607-D636E42AFEF6}">
3 + <BootstrapperApplication>
4 + <BootstrapperApplicationDll SourceFile="fakeba.dll" />
5 + </BootstrapperApplication>
6 + <Chain>
7 + <BundlePackage Permanent="yes">
8 + <BundlePackagePayload Name="chain.exe"
9 + DownloadUrl="http://example.com/chain.exe"
10 + Description="BurnBundleDescription"
11 + Size="123456789"
12 + Hash="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA">
13 + <RemoteBundle BundleId="{216BDA7F-74BD-45E8-957B-500552F05629}"
14 + DisplayName="BurnBundle"
15 + InstallSize="34"
16 + ManifestNamespace="http://wixtoolset.org/schemas/v4/2008/Burn"
17 + PerMachine="yes"
18 + ProtocolVersion="1"
19 + ProviderKey="MyProviderKey,v1.0"
20 + UpgradeCode="{B94478B1-E1F3-4700-9CE8-6AA090854AEC}"
21 + Version="1.0.0.0"
22 + Win64="no"
23 + />
24 + </BundlePackagePayload>
25 + </BundlePackage>
26 + </Chain>
27 + </Bundle>
28 +</Wix>