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>()