From 8faa28db427119b3541733290d87783dd699d425 Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 1 Apr 2022 19:28:51 -0500 Subject: Support v3 bundles in BundlePackage and "burn extract" command. --- .../V3BundlePackageBundle.wixproj | 22 ++++ .../V3BundlePackageBundle.wxs | 10 ++ .../WixToolsetTest.BurnE2E/BundlePackageTests.cs | 12 +++ src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs | 21 ++++ src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs | 2 +- .../Bundles/ProcessBundlePackageCommand.cs | 116 ++++++++++++++------- src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs | 6 ++ .../WixToolset.Core.Burn/BurnBackendWarnings.cs | 12 +++ .../BundlePackageFixture.cs | 87 ++++++++++++++++ .../TestData/.Data/v3bundle.exe | Bin 0 -> 648397 bytes .../TestData/BundlePackage/V3BundlePackage.wxs | 10 ++ 11 files changed, 257 insertions(+), 41 deletions(-) create mode 100644 src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wixproj create mode 100644 src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wxs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/v3bundle.exe create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs diff --git a/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wixproj b/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wixproj new file mode 100644 index 00000000..d57ac072 --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wixproj @@ -0,0 +1,22 @@ + + + + Bundle + TestBA_x64 + {B6CAE45D-A7E5-4302-9FCF-4D05632F9FD7} + x64 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wxs b/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wxs new file mode 100644 index 00000000..131a1b4d --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/V3BundlePackageBundle/V3BundlePackageBundle.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs index 1e6cda9c..4686875a 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs @@ -3,6 +3,7 @@ namespace WixToolsetTest.BurnE2E { using System.IO; + using WixTestTools; using Xunit; using Xunit.Abstractions; @@ -63,6 +64,17 @@ namespace WixToolsetTest.BurnE2E bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(); } + [Fact] + public void CanInstallV3BundlePackage() + { + var v3BundlePackageBundle = this.CreateBundleInstaller("V3BundlePackageBundle"); + + var logPath = v3BundlePackageBundle.Install(); + v3BundlePackageBundle.VerifyRegisteredAndInPackageCache(); + + Assert.True(LogVerifier.MessageInLogFile(logPath, "Applied execute package: v3bundle.exe, result: 0x0, restart: None")); + } + [Fact] public void CanSkipObsoleteBundlePackage() { diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs index 7b34f4ae..25c22361 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs @@ -58,11 +58,32 @@ namespace WixToolset.Core.Burn.Bundles protected const uint IMAGE_NT_SIGNATURE = 0x00004550; protected const ulong IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. + public const ushort IMAGE_FILE_MACHINE_AM33 = 0x1D3; public const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664; public const ushort IMAGE_FILE_MACHINE_ARM = 0x1C0; public const ushort IMAGE_FILE_MACHINE_ARM64 = 0xAA64; public const ushort IMAGE_FILE_MACHINE_ARMNT = 0x1C4; + public const ushort IMAGE_FILE_MACHINE_EBC = 0xEBC; public const ushort IMAGE_FILE_MACHINE_I386 = 0x14C; + public const ushort IMAGE_FILE_MACHINE_IA64 = 0x200; + public const ushort IMAGE_FILE_MACHINE_LOONGARCH32 = 0x6232; + public const ushort IMAGE_FILE_MACHINE_LOONGARCH64 = 0x6264; + public const ushort IMAGE_FILE_MACHINE_M32R = 0x9041; + public const ushort IMAGE_FILE_MACHINE_MIPS16 = 0x266; + public const ushort IMAGE_FILE_MACHINE_MIPSFPU = 0x366; + public const ushort IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466; + public const ushort IMAGE_FILE_MACHINE_POWERPC = 0x1F0; + public const ushort IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1; + public const ushort IMAGE_FILE_MACHINE_R4000 = 0x166; + public const ushort IMAGE_FILE_MACHINE_RISCV32 = 0x5032; + public const ushort IMAGE_FILE_MACHINE_RISCV64 = 0x5064; + public const ushort IMAGE_FILE_MACHINE_RISCV128 = 0x5128; + public const ushort IMAGE_FILE_MACHINE_SH3 = 0x1A2; + public const ushort IMAGE_FILE_MACHINE_SH3DSP = 0x1A3; + public const ushort IMAGE_FILE_MACHINE_SH4 = 0x1A6; + public const ushort IMAGE_FILE_MACHINE_SH5 = 0x1A8; + public const ushort IMAGE_FILE_MACHINE_THUMB = 0x1C2; + public const ushort IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169; // The ".wixburn" section contains: // 0- 3: magic number diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs index ac6a2b8f..e3d0f0af 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs @@ -102,7 +102,7 @@ namespace WixToolset.Core.Burn.Bundles var document = new XmlDocument(); document.Load(manifestPath); var namespaceManager = new XmlNamespaceManager(document.NameTable); - namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); + namespaceManager.AddNamespace("burn", document.DocumentElement.NamespaceURI); var uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager); var payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager); diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs index e8c68faa..ff4a74a7 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs @@ -71,16 +71,37 @@ namespace WixToolset.Core.Burn.Bundles switch (burnReader.MachineType) { + case BurnCommon.IMAGE_FILE_MACHINE_AM33: case BurnCommon.IMAGE_FILE_MACHINE_ARM: case BurnCommon.IMAGE_FILE_MACHINE_ARMNT: case BurnCommon.IMAGE_FILE_MACHINE_I386: + case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH32: + case BurnCommon.IMAGE_FILE_MACHINE_M32R: break; case BurnCommon.IMAGE_FILE_MACHINE_AMD64: case BurnCommon.IMAGE_FILE_MACHINE_ARM64: + case BurnCommon.IMAGE_FILE_MACHINE_IA64: + case BurnCommon.IMAGE_FILE_MACHINE_LOONGARCH64: bundlePackage.Win64 = true; break; + case BurnCommon.IMAGE_FILE_MACHINE_EBC: + case BurnCommon.IMAGE_FILE_MACHINE_MIPS16: + case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU: + case BurnCommon.IMAGE_FILE_MACHINE_MIPSFPU16: + case BurnCommon.IMAGE_FILE_MACHINE_POWERPC: + case BurnCommon.IMAGE_FILE_MACHINE_POWERPCFP: + case BurnCommon.IMAGE_FILE_MACHINE_R4000: + case BurnCommon.IMAGE_FILE_MACHINE_RISCV32: + case BurnCommon.IMAGE_FILE_MACHINE_RISCV64: + case BurnCommon.IMAGE_FILE_MACHINE_RISCV128: + case BurnCommon.IMAGE_FILE_MACHINE_SH3: + case BurnCommon.IMAGE_FILE_MACHINE_SH3DSP: + case BurnCommon.IMAGE_FILE_MACHINE_SH4: + case BurnCommon.IMAGE_FILE_MACHINE_SH5: + case BurnCommon.IMAGE_FILE_MACHINE_THUMB: + case BurnCommon.IMAGE_FILE_MACHINE_WCEMIPSV2: default: - Debug.Assert(false, "Unknown machine type"); + this.Messaging.Write(BurnBackendWarnings.UnknownCoffMachineType(packagePayload.SourceLineNumbers, sourcePath, burnReader.MachineType)); break; } @@ -90,46 +111,60 @@ namespace WixToolset.Core.Burn.Bundles // This should be a safe assumption since we will need to add the protocol version to the section to support this harvesting. bundlePackage.SupportsBurnProtocol = burnReader.Version == 2; - var document = new XmlDocument(); - document.Load(Path.Combine(baFolderPath, "manifest.xml")); - var namespaceManager = new XmlNamespaceManager(document.NameTable); - namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); // TODO: support v3 bundles - var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement; - var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement; - - var perMachine = registrationElement.GetAttribute("PerMachine") == "yes"; - this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; - - var version = registrationElement.GetAttribute("Version"); - packagePayload.Version = version; - bundlePackage.Version = version; - this.Facade.PackageSymbol.Version = version; - - if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) + try { - this.Facade.PackageSymbol.CacheId = String.Format("{0}v{1}", bundlePackage.BundleId, version); + var document = new XmlDocument(); + document.Load(Path.Combine(baFolderPath, "manifest.xml")); + var namespaceManager = new XmlNamespaceManager(document.NameTable); + + if (document.DocumentElement.LocalName != "BurnManifest") + { + this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(packagePayload.SourceLineNumbers, sourcePath, $"Expected root element to be 'Manifest' but was '{document.DocumentElement.LocalName}'.")); + return; + } + + namespaceManager.AddNamespace("burn", document.DocumentElement.NamespaceURI); + var registrationElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration", namespaceManager) as XmlElement; + var arpElement = document.SelectSingleNode("/burn:BurnManifest/burn:Registration/burn:Arp", namespaceManager) as XmlElement; + + var perMachine = registrationElement.GetAttribute("PerMachine") == "yes"; + this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; + + var version = registrationElement.GetAttribute("Version"); + packagePayload.Version = version; + bundlePackage.Version = version; + this.Facade.PackageSymbol.Version = version; + + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) + { + this.Facade.PackageSymbol.CacheId = String.Format("{0}v{1}", bundlePackage.BundleId, version); + } + + var providerKey = registrationElement.GetAttribute("ProviderKey"); + var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", bundlePackage.Id.Id, providerKey)); + this.Section.AddSymbol(new WixDependencyProviderSymbol(packagePayload.SourceLineNumbers, depId) + { + ParentRef = bundlePackage.Id.Id, + ProviderKey = providerKey, + Version = version, + Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported, + }); + + if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) + { + this.Facade.PackageSymbol.DisplayName = arpElement.GetAttribute("DisplayName"); + } + + this.ProcessPackages(document, namespaceManager); + + this.ProcessRelatedBundles(document, namespaceManager, packagePayload, sourcePath); + + // TODO: Add payloads? } - - var providerKey = registrationElement.GetAttribute("ProviderKey"); - var depId = new Identifier(AccessModifier.Section, this.BackendHelper.GenerateIdentifier("dep", bundlePackage.Id.Id, providerKey)); - this.Section.AddSymbol(new WixDependencyProviderSymbol(packagePayload.SourceLineNumbers, depId) - { - ParentRef = bundlePackage.Id.Id, - ProviderKey = providerKey, - Version = version, - Attributes = WixDependencyProviderAttributes.ProvidesAttributesImported, - }); - - if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) + catch (Exception e) { - this.Facade.PackageSymbol.DisplayName = arpElement.GetAttribute("DisplayName"); + this.Messaging.Write(BurnBackendErrors.InvalidBundleManifest(packagePayload.SourceLineNumbers, sourcePath, e.ToString())); } - - this.ProcessPackages(document, namespaceManager); - - this.ProcessRelatedBundles(document, namespaceManager); - - // TODO: Add payloads? } } @@ -153,15 +188,16 @@ namespace WixToolset.Core.Burn.Bundles this.Facade.PackageSymbol.InstallSize = packageInstallSize; } - private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager) + private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager, WixBundlePayloadSymbol packagePayload, string sourcePath) { foreach (XmlElement relatedBundleElement in document.SelectNodes("/burn:BurnManifest/burn:RelatedBundle", namespaceManager)) { var id = relatedBundleElement.GetAttribute("Id"); + var actionValue = relatedBundleElement.GetAttribute("Action"); - if (!Enum.TryParse(relatedBundleElement.GetAttribute("Action"), out RelatedBundleActionType action)) + if (!Enum.TryParse(actionValue, out RelatedBundleActionType action)) { - // TODO: warning + this.Messaging.Write(BurnBackendWarnings.UnknownBundleRelationAction(packagePayload.SourceLineNumbers, sourcePath, actionValue)); continue; } diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs index a710333e..bf07fad2 100644 --- a/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs +++ b/src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs @@ -85,6 +85,11 @@ namespace WixToolset.Core.Burn return Message(null, Ids.UnsupportedRemotePackagePayload, "The first remote payload must be a supported package type of .exe or .msu. Use the -packageType switch to override the inferred extension: {0} from file: {1}", extension, path); } + public static Message InvalidBundleManifest(SourceLineNumber sourceLineNumbers, string bundleExecutable, string reason) + { + return Message(sourceLineNumbers, Ids.InvalidBundleManifest, "Unable to harvest bundle executable '{0}'. The manifest was invalid. {1}", bundleExecutable, reason); + } + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) { return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); @@ -104,6 +109,7 @@ namespace WixToolset.Core.Burn IncompatibleWixBurnSection = 8009, UnsupportedRemotePackagePayload = 8010, FailedToAddIconOrSplashScreenToBundle = 8011, + InvalidBundleManifest = 8012, } // last available is 8499. 8500 is BurnBackendWarnings. } } diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs index 033b755a..6d45ff84 100644 --- a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs +++ b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs @@ -26,6 +26,16 @@ namespace WixToolset.Core.Burn return Message(sourceLineNumbers, Ids.FailedToExtractAttachedContainers, "Failed to extract attached container. This most often happens when extracting a stripped bundle from the package cache, which is not supported."); } + public static Message UnknownCoffMachineType(SourceLineNumber sourceLineNumbers, string bundleExecutable, ushort machineType) + { + return Message(sourceLineNumbers, Ids.UnknownCoffMachineType, "The bundle '{0}' has an unknown COFF machine type: {1}. It is assumed to be 32-bit.", bundleExecutable, machineType); + } + + public static Message UnknownBundleRelationAction(SourceLineNumber sourceLineNumbers, string bundleExecutable, string action) + { + return Message(sourceLineNumbers, Ids.UnknownBundleRelationAction, "The manifest for the bundle '{0}' contains an unknown related bundle action '{1}'. It will be ignored.", bundleExecutable, action); + } + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) { return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args); @@ -37,6 +47,8 @@ namespace WixToolset.Core.Burn AttachedContainerPayloadCollision2 = 8501, EmptyContainer = 8502, FailedToExtractAttachedContainers = 8503, + UnknownCoffMachineType = 8504, + UnknownBundleRelationAction = 8505, } // last available is 8999. 9000 is VerboseMessages. } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs index 6ccddbb7..5f30760e 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs @@ -127,5 +127,92 @@ namespace WixToolsetTest.CoreIntegration }, packageElements); } } + + [Fact] + public void CanBuildBundleWithV3BundlePackage() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var parentIntermediateFolder = Path.Combine(baseFolder, "obj", "Parent"); + var binFolder = Path.Combine(baseFolder, "bin"); + var parentBundlePath = Path.Combine(binFolder, "parent.exe"); + var parentPdbPath = Path.Combine(binFolder, "parent.wixpdb"); + var baFolderPath = Path.Combine(baseFolder, "ba"); + var extractFolderPath = Path.Combine(baseFolder, "extract"); + string chainBundleId = "{215A70DB-AB35-48C7-BE51-D66EAAC87177}"; + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "BundlePackage", "V3BundlePackage.wxs"), + "-bindpath", Path.Combine(folder, ".Data"), + "-bindpath", Path.Combine(folder, "SimpleBundle", "data"), + "-intermediateFolder", parentIntermediateFolder, + "-o", parentBundlePath, + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(parentBundlePath)); + + string parentBundleId; + using (var wixOutput = WixOutput.Read(parentPdbPath)) + { + + var intermediate = Intermediate.Load(wixOutput); + var section = intermediate.Sections.Single(); + + var bundleSymbol = section.Symbols.OfType().Single(); + parentBundleId = bundleSymbol.BundleId; + } + + var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, baFolderPath, extractFolderPath); + extractResult.AssertSuccess(); + + var ignoreAttributesByElementName = new Dictionary> + { + { "BundlePackage", new List { "Size" } }, + }; + var bundlePackages = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Chain/burn:BundlePackage") + .Cast() + .Select(e => e.GetTestXml(ignoreAttributesByElementName)) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + $"" + + "" + + "" + + "" + + "", + }, bundlePackages); + + var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration") + .Cast() + .Select(e => e.GetTestXml()) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + $"" + + "" + + "" + }, registrations); + + ignoreAttributesByElementName = new Dictionary> + { + { "WixPackageProperties", new List { "DownloadSize", "PackageSize" } }, + }; + var packageElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:WixPackageProperties") + .Cast() + .Select(e => e.GetTestXml(ignoreAttributesByElementName)) + .ToArray(); + WixAssert.CompareLineByLine(new string[] + { + "", + }, packageElements); + } + } } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/v3bundle.exe b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/v3bundle.exe new file mode 100644 index 00000000..13f3e3d9 Binary files /dev/null and b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/v3bundle.exe differ diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs new file mode 100644 index 00000000..192bb6d7 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/BundlePackage/V3BundlePackage.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + -- cgit v1.2.3-55-g6feb