@joebigelow / wix-1 / commits / ce3aea75

Support multiple attached containers See https://github.com/wixtoolset/issues/issues/6144

Support multiple attached containers See https://github.com/wixtoolset/issues/issues/6144

Nir Bar committed Oct 6, 2021 at 14:11 UTC ce3aea757a01f0eea906fa610501a66735ef3a15
16 files changed +170 -100
src/burn/engine/section.cpp
+2 -2
@@ -8,7 +8,7 @@
8 // If these defaults ever change, be sure to update constants in burn\stub\StubSection.cpp as well.
9 #define BURN_SECTION_NAME ".wixburn"
10 #define BURN_SECTION_MAGIC 0x00f14300
11 -#define BURN_SECTION_VERSION 0x00000002
11 +#define BURN_SECTION_VERSION 0x00000003
12 #define MANIFEST_CABINET_TOKEN L"0"
13
14 // structs
@@ -26,7 +26,7 @@ typedef struct _BURN_SECTION_HEADER
26
27 DWORD dwFormat;
28 DWORD cContainers;
29 - DWORD rgcbContainers[1];
29 + DWORD rgcbContainers[116];
30 } BURN_SECTION_HEADER;
31
32 static HRESULT VerifySectionMatchesMemoryPEHeader(
src/burn/stub/StubSection.cpp
+2 -3
@@ -7,7 +7,7 @@
7 // If these defaults ever change, be sure to update constants in burn\engine\section.cpp as well.
8 #pragma data_seg(push, ".wixburn")
9 static DWORD dwMagic = 0x00f14300;
10 -static DWORD dwVersion = 0x00000002;
10 +static DWORD dwVersion = 0x00000003;
11
12 static GUID guidBundleId = { };
13
@@ -18,6 +18,5 @@ static DWORD dwOriginalSignatureSize = 0;
18
19 static DWORD dwContainerFormat = 1;
20 static DWORD dwContainerCount = 0;
21 -static DWORD qwBootstrapperApplicationContainerSize = 0;
22 -static DWORD qwAttachedContainerSize = 0;
21 +static DWORD qwAttachedContainerSizes[116]; // Including UX container
22 #pragma data_seg(pop)
src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+1 -3
@@ -296,15 +296,13 @@ namespace WixToolset.Core.Burn
296 }
297
298 // Give the embedded payloads without an embedded id yet an embedded id.
299 - var payloadIndex = 0;
299 foreach (var payload in payloadSymbols.Values)
300 {
301 Debug.Assert(PackagingType.Unknown != payload.Packaging);
302
303 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId))
304 {
306 - payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAuthoredContainerEmbeddedIdFormat, payloadIndex);
307 - ++payloadIndex;
305 + payload.EmbeddedId = Guid.NewGuid().ToString("N");
306 }
307 }
308 }
src/wix/WixToolset.Core.Burn/BundleBackend.cs
+1 -2
@@ -62,13 +62,12 @@ namespace WixToolset.Core.Burn
62 public Intermediate Unbind(IUnbindContext context)
63 {
64 var uxExtractPath = Path.Combine(context.ExportBasePath, "UX");
65 - var acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer");
65 var messaging = context.ServiceProvider.GetService<IMessaging>();
66
67 using (var reader = BurnReader.Open(messaging, context.InputFilePath))
68 {
69 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder);
71 - reader.ExtractAttachedContainer(acExtractPath, context.IntermediateFolder);
70 + reader.ExtractAttachedContainers(context.ExportBasePath);
71 }
72
73 return null;
src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+45 -14
@@ -3,6 +3,7 @@
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 + using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using WixToolset.Data;
@@ -19,7 +20,6 @@ namespace WixToolset.Core.Burn.Bundles
20 {
21 public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn";
22 public const string BurnUXContainerEmbeddedIdFormat = "u{0}";
22 - public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}";
23
24 public const string BADataFileName = "BootstrapperApplicationData.xml";
25 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData";
@@ -67,7 +67,7 @@ namespace WixToolset.Core.Burn.Bundles
67 // 40-43: container type (1 = CAB)
68 // 44-47: container count
69 // 48-51: byte count of manifest + UX container
70 - // 52-55: byte count of attached container
70 + // 52-512: byte count of attached containers (4 bytes for each container)
71 protected const UInt32 BURN_SECTION_OFFSET_MAGIC = 0;
72 protected const UInt32 BURN_SECTION_OFFSET_VERSION = 4;
73 protected const UInt32 BURN_SECTION_OFFSET_BUNDLEGUID = 8;
@@ -78,11 +78,14 @@ namespace WixToolset.Core.Burn.Bundles
78 protected const UInt32 BURN_SECTION_OFFSET_FORMAT = 40;
79 protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44;
80 protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48;
81 - protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE = 52;
82 - protected const UInt32 BURN_SECTION_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE + 4; // last field + sizeof(DWORD)
81 + protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 = 52;
82
83 protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300;
85 - protected const UInt32 BURN_SECTION_VERSION = 0x00000002;
84 + protected const UInt32 BURN_SECTION_VERSION = 0x00000003;
85 + protected const UInt32 BURN_SECTION_COMPATIBLE_VERSION = 0x00000002;
86 + protected const UInt32 BURN_SECTION_SIZE = 512;
87 + protected const UInt32 BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT = (BURN_SECTION_SIZE - BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0) / sizeof(UInt32);
88 +
89 protected string fileExe;
90 protected UInt32 peOffset = UInt32.MaxValue;
91 protected UInt16 sections = UInt16.MaxValue;
@@ -112,6 +115,7 @@ namespace WixToolset.Core.Burn.Bundles
115 {
116 this.Messaging = messaging;
117 this.fileExe = fileExe;
118 + this.AttachedContainers = new List<ContainerSlot>();
119 }
120
121 public UInt32 Checksum { get; protected set; }
@@ -126,8 +130,7 @@ namespace WixToolset.Core.Burn.Bundles
130 public UInt32 ContainerCount { get; protected set; }
131 public UInt32 UXAddress { get; protected set; }
132 public UInt32 UXSize { get; protected set; }
129 - public UInt32 AttachedContainerAddress { get; protected set; }
130 - public UInt32 AttachedContainerSize { get; protected set; }
133 + public List<ContainerSlot> AttachedContainers { get; protected set; }
134
135 protected IMessaging Messaging { get; }
136
@@ -177,7 +180,9 @@ namespace WixToolset.Core.Burn.Bundles
180 }
181
182 reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin);
180 - byte[] bytes = reader.ReadBytes((int)BURN_SECTION_SIZE);
183 + List<byte> manifest = new List<byte>();
184 + manifest.AddRange(reader.ReadBytes((int)BURN_SECTION_SIZE));
185 + byte[] bytes = manifest.ToArray();
186 UInt32 uint32 = 0;
187
188 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC);
@@ -188,9 +193,9 @@ namespace WixToolset.Core.Burn.Bundles
193 }
194
195 this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION);
191 - if (BURN_SECTION_VERSION != this.Version)
196 + if ((BURN_SECTION_VERSION != this.Version) && (BURN_SECTION_COMPATIBLE_VERSION != this.Version))
197 {
193 - this.Messaging.Write(ErrorMessages.BundleTooNew(this.fileExe, this.Version));
198 + this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.fileExe, this.Version));
199 return false;
200 }
201
@@ -207,6 +212,11 @@ namespace WixToolset.Core.Burn.Bundles
212 this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE);
213
214 this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT);
215 + if (BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT < this.ContainerCount)
216 + {
217 + this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe));
218 + return false;
219 + }
220 this.UXAddress = this.StubSize;
221 this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE);
222
@@ -224,8 +234,18 @@ namespace WixToolset.Core.Burn.Bundles
234 this.EngineSize = this.StubSize + this.UXSize;
235 }
236
227 - this.AttachedContainerAddress = this.ContainerCount > 1 ? this.EngineSize : 0;
228 - this.AttachedContainerSize = this.ContainerCount > 1 ? BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE) : 0;
237 + this.AttachedContainers.Clear();
238 + uint nextAddress = this.EngineSize;
239 + if (this.ContainerCount > 1)
240 + {
241 + for (uint i = 0; i < (this.ContainerCount - 1 /* Excluding UX */); ++i)
242 + {
243 + uint sizeOffset = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 + (i * 4);
244 + uint size = BurnCommon.ReadUInt32(bytes, sizeOffset);
245 + this.AttachedContainers.Add(new ContainerSlot(nextAddress, size));
246 + nextAddress += size;
247 + }
248 + }
249
250 return true;
251 }
@@ -268,8 +288,7 @@ namespace WixToolset.Core.Burn.Bundles
288 return false;
289 }
290
271 - // we need 56 bytes for the manifest header, which is always going to fit in
272 - // the smallest alignment (512 bytes), but just to be paranoid...
291 + // We need 512 bytes for the manifest header
292 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA))
293 {
294 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe));
@@ -382,4 +401,16 @@ namespace WixToolset.Core.Burn.Bundles
401 return BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)BurnCommon.ReadUInt32(bytes, offset + 4) << 32);
402 }
403 }
404 +
405 + internal struct ContainerSlot
406 + {
407 + public ContainerSlot(uint address, uint size) : this()
408 + {
409 + this.Address = address;
410 + this.Size = size;
411 + }
412 +
413 + public uint Address { get; set; }
414 + public uint Size { get; set; }
415 + }
416 }
src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+20 -15
@@ -126,16 +126,19 @@ namespace WixToolset.Core.Burn.Bundles
126
127 foreach (XmlNode payload in payloads)
128 {
129 - XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath");
130 - XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath");
129 XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging");
130
133 - string sourcePath = sourcePathNode.Value;
134 - string destinationPath = filePathNode.Value;
131 string packaging = packagingNode.Value;
132
133 if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase))
134 {
135 + XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath");
136 + XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath");
137 + XmlNode containerNode = payload.Attributes.GetNamedItem("Container");
138 +
139 + string sourcePath = sourcePathNode.Value;
140 + string destinationPath = Path.Combine(containerNode.Value, filePathNode.Value);
141 +
142 this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath));
143 }
144 }
@@ -152,12 +155,11 @@ namespace WixToolset.Core.Burn.Bundles
155 /// Gets the attached container from the exe and extracts its contents to the output directory.
156 /// </summary>
157 /// <param name="outputDirectory">Directory to write extracted files to.</param>
155 - /// <param name="tempDirectory">Scratch directory.</param>
158 /// <returns>True if successful, false otherwise</returns>
157 - public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory)
159 + public bool ExtractAttachedContainers(string outputDirectory)
160 {
161 // No attached container to extract
160 - if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0)
162 + if (this.AttachedContainers.Count == 0)
163 {
164 return false;
165 }
@@ -168,16 +170,19 @@ namespace WixToolset.Core.Burn.Bundles
170 }
171
172 Directory.CreateDirectory(outputDirectory);
171 - string tempCabPath = Path.Combine(tempDirectory, "attached.cab");
172 -
173 - this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin);
174 - using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
173 + foreach (ContainerSlot cntnr in this.AttachedContainers)
174 {
176 - BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize);
177 - }
175 + string tempCabPath = Path.GetTempFileName();
176
179 - var cabinet = new Cabinet(tempCabPath);
180 - cabinet.Extract(outputDirectory);
177 + this.binaryReader.BaseStream.Seek(cntnr.Address, SeekOrigin.Begin);
178 + using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
179 + {
180 + BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
181 + }
182 +
183 + var cabinet = new Cabinet(tempCabPath);
184 + cabinet.Extract(outputDirectory);
185 + }
186
187 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames)
188 {
src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+27 -4
@@ -93,7 +93,10 @@ namespace WixToolset.Core.Burn.Bundles
93 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now.
94 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0);
95 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_UXSIZE, 0);
96 - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE, 0);
96 + for (uint i = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0; i < BURN_SECTION_SIZE; i += sizeof(UInt32))
97 + {
98 + this.WriteToBurnSectionOffset(i, 0);
99 + }
100 this.binaryWriter.BaseStream.Flush();
101
102 this.EngineSize = this.StubSize;
@@ -127,6 +130,11 @@ namespace WixToolset.Core.Burn.Bundles
130 UInt32 burnSectionCount = 0;
131 UInt32 burnSectionOffsetSize = 0;
132
133 + if (containerSize == 0)
134 + {
135 + return false;
136 + }
137 +
138 switch (container)
139 {
140 case Container.UX:
@@ -138,10 +146,19 @@ namespace WixToolset.Core.Burn.Bundles
146 break;
147
148 case Container.Attached:
141 - burnSectionCount = 2;
142 - burnSectionOffsetSize = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE;
149 // TODO: verify that the size in the section data is 0 or the same size.
144 - this.AttachedContainerSize = (uint)containerSize;
150 + uint nextAddress = this.EngineSize;
151 + foreach (ContainerSlot cntnr in this.AttachedContainers)
152 + {
153 + if (cntnr.Address >= nextAddress)
154 + {
155 + nextAddress = cntnr.Address + cntnr.Size;
156 + }
157 + }
158 +
159 + this.AttachedContainers.Add(new ContainerSlot(nextAddress, (uint)containerSize));
160 + burnSectionCount = 1 + (uint)this.AttachedContainers.Count;
161 + burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + ((uint)this.AttachedContainers.Count * 4);
162 break;
163
164 default:
@@ -208,6 +225,12 @@ namespace WixToolset.Core.Burn.Bundles
225 {
226 return false;
227 }
228 + if (burnSectionOffsetSize > (BURN_SECTION_SIZE - sizeof(UInt32)))
229 + {
230 + this.invalidBundle = true;
231 + this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT));
232 + return false;
233 + }
234
235 // Update the ".wixburn" section data
236 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount);
src/wix/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs
-8
@@ -117,14 +117,6 @@ namespace WixToolset.Core.Burn.Bundles
117 }
118 }
119
120 - foreach (var container in this.Containers.Where(c => !String.IsNullOrEmpty(c.WorkingPath) && c.Id.Id != BurnConstants.BurnUXContainerName))
121 - {
122 - if (container.Type == ContainerType.Attached && attachedContainerIndex > 2 && container.Id.Id != BurnConstants.BurnDefaultAttachedContainerName)
123 - {
124 - this.Messaging.Write(BurnBackendErrors.MultipleAttachedContainersUnsupported(container.SourceLineNumbers, container.Id.Id));
125 - }
126 - }
127 -
120 if (!this.Messaging.EncounteredError)
121 {
122 foreach (var container in this.Containers.Where(c => !String.IsNullOrEmpty(c.WorkingPath) && c.Id.Id != BurnConstants.BurnUXContainerName))
src/wix/WixToolset.Core.Burn/BurnBackendErrors.cs
+12 -6
@@ -36,11 +36,6 @@ namespace WixToolset.Core.Burn
36 return Message(sourceLineNumbers, Ids.ExternalPayloadCollision2, "The location of the symbol related to the previous error.");
37 }
38
39 - public static Message MultipleAttachedContainersUnsupported(SourceLineNumber sourceLineNumbers, string containerId)
40 - {
41 - return Message(sourceLineNumbers, Ids.MultipleAttachedContainersUnsupported, "Bundles don't currently support having more than one attached container. Either remove all authored attached containers to use the default attached container, or make sure all compressed payloads are included in this Container '{0}'.", containerId);
42 - }
43 -
39 public static Message PackageCachePayloadCollision(SourceLineNumber sourceLineNumbers, string payloadId, string payloadName, string packageId)
40 {
41 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision, "The Payload '{0}' has a duplicate Name '{1}' in package '{2}'. When caching the package, the file will get overwritten.", payloadId, payloadName, packageId);
@@ -51,6 +46,16 @@ namespace WixToolset.Core.Burn
46 return Message(sourceLineNumbers, Ids.PackageCachePayloadCollision2, "The location of the payload related to the previous error.");
47 }
48
49 + public static Message TooManyAttachedContainers(uint maxAllowed)
50 + {
51 + return Message(null, Ids.TooManyAttachedContainers, "The bundle has too many attached containers. The maximal attached container count is {0}", maxAllowed);
52 + }
53 +
54 + public static Message IncompatibleWixBurnSection(string bundleExecutable, long bundleVersion)
55 + {
56 + return Message(null, Ids.IncompatibleWixBurnSection, "Unable to read bundle executable '{0}', because this bundle was created with a different version of WiX burn (.wixburn section version '{1}'). You must use the same version of Windows Installer XML in order to read this bundle.", bundleExecutable, bundleVersion);
57 + }
58 +
59 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
60 {
61 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
@@ -66,7 +71,8 @@ namespace WixToolset.Core.Burn
71 ExternalPayloadCollision2 = 8005,
72 PackageCachePayloadCollision = 8006,
73 PackageCachePayloadCollision2 = 8007,
69 - MultipleAttachedContainersUnsupported = 8008,
74 + TooManyAttachedContainers = 8008,
75 + IncompatibleWixBurnSection = 8009,
76 } // last available is 8499. 8500 is BurnBackendWarnings.
77 }
78 }
src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+14 -8
@@ -29,17 +29,23 @@ namespace WixToolset.Core.Burn.Inscribe
29 using (var reader = BurnReader.Open(this.Context.InputFilePath))
30 {
31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false);
32 -
33 - // If there was an attached container on the original (unsigned) bundle, put it back.
34 - if (reader.AttachedContainerSize > 0)
32 + using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile))
33 {
36 - reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
34 + if (reader.Version != writer.Version)
35 + {
36 + this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.Context.InputFilePath, reader.Version));
37 + }
38
38 - using (var writer = BurnWriter.Open(this.Messaging, tempFile))
39 + writer.AttachedContainers.Clear();
40 + writer.RememberThenResetSignature();
41 + foreach (ContainerSlot cntnr in reader.AttachedContainers)
42 {
40 - writer.RememberThenResetSignature();
41 - writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
42 - inscribed = true;
43 + if (cntnr.Size > 0)
44 + {
45 + reader.Stream.Seek(cntnr.Address, SeekOrigin.Begin);
46 + writer.AppendContainer(reader.Stream, cntnr.Size, BurnCommon.Container.Attached);
47 + inscribed = true;
48 + }
49 }
50 }
51 }
src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
+2 -4
@@ -50,14 +50,12 @@ namespace WixToolset.Core.TestPackage
50 /// <param name="messaging"></param>
51 /// <param name="bundleFilePath">Path to the bundle.</param>
52 /// <param name="destinationFolderPath">Path to extract to.</param>
53 - /// <param name="tempFolderPath">Temp path for extraction.</param>
53 /// <returns>True if there was an attached container.</returns>
55 - public static bool ExtractAttachedContainer(IMessaging messaging, string bundleFilePath, string destinationFolderPath, string tempFolderPath)
54 + public static bool ExtractAttachedContainers(IMessaging messaging, string bundleFilePath, string destinationFolderPath)
55 {
57 - Directory.CreateDirectory(tempFolderPath);
56 using (var burnReader = BurnReader.Open(messaging, bundleFilePath))
57 {
60 - return burnReader.ExtractAttachedContainer(destinationFolderPath, tempFolderPath);
58 + return burnReader.ExtractAttachedContainers(destinationFolderPath);
59 }
60 }
61
src/wix/test/WixToolsetTest.CoreIntegration/BundleExtractionFixture.cs
+1 -1
@@ -26,7 +26,7 @@ namespace WixToolsetTest.CoreIntegration
26 var pdbPath = Path.Combine(baseFolder, @"bin\test.wixpdb");
27 var extractFolderPath = Path.Combine(baseFolder, "extract");
28 var baFolderPath = Path.Combine(extractFolderPath, "UX");
29 - var attachedContainerFolderPath = Path.Combine(extractFolderPath, "AttachedContainer");
29 + var attachedContainerFolderPath = Path.Combine(extractFolderPath, "WixAttachedContainer");
30
31 // TODO: use WixRunner.Execute(string[]) to always go through the command line.
32 var serviceProvider = WixToolsetServiceProviderFactory.CreateServiceProvider();
src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+2 -2
@@ -125,8 +125,8 @@ namespace WixToolsetTest.CoreIntegration
125
126 var msiPayloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='test.msi']");
127 var msiPayload = (XmlNode)Assert.Single(msiPayloads);
128 - Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />",
129 - msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash" } } }));
128 + Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />",
129 + msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }));
130 }
131
132 var manifestResource = new Resource(ResourceType.Manifest, "#1", 1033);
src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
+30 -19
@@ -2,14 +2,12 @@
2
3 namespace WixToolsetTest.CoreIntegration
4 {
5 - using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Xml;
9 using WixBuildTools.TestSupport;
11 - using WixToolset.Core;
12 - using WixToolset.Core.Burn;
10 + using WixToolset.Core.Burn.Bundles;
11 using WixToolset.Core.TestPackage;
12 using Xunit;
13
@@ -51,11 +49,11 @@ namespace WixToolsetTest.CoreIntegration
49
50 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
51 Assert.Equal(4, payloads.Count);
54 - var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
55 - Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='a0' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes));
56 - Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='a1' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes));
57 - Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a2' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes));
58 - Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a3' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes));
52 + var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
53 + Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes));
54 + Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes));
55 + Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes));
56 + Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes));
57 }
58 }
59
@@ -95,11 +93,11 @@ namespace WixToolsetTest.CoreIntegration
93
94 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
95 Assert.Equal(4, payloads.Count);
98 - var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
99 - Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes));
100 - Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a1' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes));
101 - Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes));
102 - Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a3' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes));
96 + var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
97 + Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes));
98 + Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes));
99 + Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes));
100 + Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes));
101 }
102 }
103
@@ -205,20 +203,20 @@ namespace WixToolsetTest.CoreIntegration
203 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
204 extractResult.AssertSuccess();
205
208 - var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
206 + var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
207 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
208 .Cast<XmlElement>()
209 .Select(e => e.GetTestXml(ignoreAttributes))
210 .ToArray();
211 WixAssert.CompareLineByLine(new string[]
212 {
215 - "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='a1' Container='FirstX64' />",
213 + "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='*' Container='FirstX64' />",
214 }, payloads);
215 }
216 }
217
218 [Fact]
221 - public void MultipleAttachedContainersAreNotCurrentlySupported()
219 + public void MultipleAttachedContainers()
220 {
221 var folder = TestData.Get(@"TestData");
222
@@ -230,6 +228,7 @@ namespace WixToolsetTest.CoreIntegration
228 var bundlePath = Path.Combine(binFolder, "test.exe");
229 var baFolderPath = Path.Combine(baseFolder, "ba");
230 var extractFolderPath = Path.Combine(baseFolder, "extract");
231 + var tempFolderPath = Path.Combine(baseFolder, "temp");
232
233 this.BuildMsis(folder, intermediateFolder, binFolder);
234
@@ -244,7 +243,19 @@ namespace WixToolsetTest.CoreIntegration
243 "-o", bundlePath
244 });
245
247 - Assert.Equal((int)BurnBackendErrors.Ids.MultipleAttachedContainersUnsupported, result.ExitCode);
246 + Assert.Equal(0, result.ExitCode);
247 + Assert.True(File.Exists(bundlePath));
248 +
249 + Directory.CreateDirectory(tempFolderPath);
250 + using (var burnReader = BurnReader.Open(null, bundlePath))
251 + {
252 + // Extract the BA because that loads the payload target paths from the manifest
253 + Assert.True(burnReader.ExtractUXContainer(baFolderPath, tempFolderPath));
254 + Assert.True(burnReader.ExtractAttachedContainers(extractFolderPath));
255 + }
256 +
257 + Assert.True(File.Exists(Path.Combine(extractFolderPath, "FirstX64", "FirstX64.msi")), "Expected extracted container to contain FirstX64.msi");
258 + Assert.True(File.Exists(Path.Combine(extractFolderPath, "WixAttachedContainer", "FirstX86.msi")), "Expected extracted container to contain FirstX86.msi");
259 }
260 }
261
@@ -286,14 +297,14 @@ namespace WixToolsetTest.CoreIntegration
297 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
298 extractResult.AssertSuccess();
299
289 - var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
300 + var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
301 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
302 .Cast<XmlElement>()
303 .Select(e => e.GetTestXml(ignoreAttributes))
304 .ToArray();
305 WixAssert.CompareLineByLine(new string[]
306 {
296 - "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='FirstX86' />",
307 + "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX86' />",
308 }, payloads);
309 }
310 }
src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
+2 -1
@@ -46,13 +46,14 @@ namespace WixToolsetTest.CoreIntegration
46 var ignoreAttributesByElementName = new Dictionary<string, List<string>>
47 {
48 { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } },
49 + { "Payload", new List<string> { "SourcePath" } },
50 };
51 Assert.Equal(1, exePackageElements.Count);
52 Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='keep' 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));
53
54 var payloadElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='burn.exe']");
55 Assert.Equal(1, payloadElements.Count);
55 - Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml());
56 + Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml(ignoreAttributesByElementName));
57 }
58 }
59
src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+9 -8
@@ -189,14 +189,15 @@ namespace WixToolsetTest.CoreIntegration
189 .Cast<XmlElement>()
190 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
191 .ToArray();
192 - WixAssert.CompareLineByLine(new string[]
193 - {
194 - "<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='PackagesContainer' />",
195 - "<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />",
196 - "<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />",
197 - @"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />",
198 - @"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />",
199 - }, payloads);
192 +
193 + var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash"} } };
194 + var ignoreAttributesWithSrc = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } };
195 + Assert.Equal(5, payloads.Length);
196 + Assert.Equal(@"<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='PackagesContainer' />", payloads[0].GetTestXml(ignoreAttributesWithSrc));
197 + Assert.Equal(@"<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />", payloads[1].GetTestXml(ignoreAttributes));
198 + Assert.Equal(@"<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />", payloads[2].GetTestXml(ignoreAttributes));
199 + Assert.Equal(@"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />", payloads[3].GetTestXml(ignoreAttributes));
200 + Assert.Equal(@"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />", payloads[4].GetTestXml(ignoreAttributes));
201
202 var containers = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Container")
203 .Cast<XmlElement>()