diff options
21 files changed, 688 insertions, 439 deletions
diff --git a/src/api/wix/WixToolset.Data/WarningMessages.cs b/src/api/wix/WixToolset.Data/WarningMessages.cs index d39ead3d..6e2775bb 100644 --- a/src/api/wix/WixToolset.Data/WarningMessages.cs +++ b/src/api/wix/WixToolset.Data/WarningMessages.cs | |||
@@ -267,14 +267,14 @@ namespace WixToolset.Data | |||
267 | return Message(sourceLineNumbers, Ids.EmptyAttributeValue, "The {0}/@{1} attribute's value cannot be an empty string. If you want the value to be null or empty, simply remove the entire attribute.", elementName, attributeName); | 267 | return Message(sourceLineNumbers, Ids.EmptyAttributeValue, "The {0}/@{1} attribute's value cannot be an empty string. If you want the value to be null or empty, simply remove the entire attribute.", elementName, attributeName); |
268 | } | 268 | } |
269 | 269 | ||
270 | public static Message EmptyCabinet(SourceLineNumber sourceLineNumbers, string cabinetName) | ||
271 | { | ||
272 | return Message(sourceLineNumbers, Ids.EmptyCabinet, "The cabinet '{0}' does not contain any files. If this installation contains no files, this warning can likely be safely ignored. Otherwise, please add files to the cabinet or remove it.", cabinetName); | ||
273 | } | ||
274 | |||
275 | public static Message EmptyCabinet(SourceLineNumber sourceLineNumbers, string cabinetName, bool isPatch) | 270 | public static Message EmptyCabinet(SourceLineNumber sourceLineNumbers, string cabinetName, bool isPatch) |
276 | { | 271 | { |
277 | return Message(sourceLineNumbers, Ids.EmptyCabinet, "The cabinet '{0}' does not contain any files. If this patch contains no files, this warning can likely be safely ignored. Otherwise, try passing -p to torch.exe when first building the transforms, or add a ComponentRef to your PatchFamily authoring to pull changed files into the cabinet.", cabinetName, isPatch); | 272 | if (isPatch) |
273 | { | ||
274 | return Message(sourceLineNumbers, Ids.EmptyCabinet, "The cabinet '{0}' does not contain any files. If this patch contains no files, this warning can likely be safely ignored. Otherwise, try passing -p to torch.exe when first building the transforms, or add a ComponentRef to your PatchFamily authoring to pull changed files into the cabinet.", cabinetName, isPatch); | ||
275 | } | ||
276 | |||
277 | return Message(sourceLineNumbers, Ids.EmptyCabinet, "The cabinet '{0}' does not contain any files. If this installation contains no files, this warning can likely be safely ignored. Otherwise, please add files to the cabinet or remove it.", cabinetName); | ||
278 | } | 278 | } |
279 | 279 | ||
280 | public static Message ExpectedForeignRow(SourceLineNumber sourceLineNumbers, string tableName, string primaryKey, string columnName, string columnValue, string foreignTableName) | 280 | public static Message ExpectedForeignRow(SourceLineNumber sourceLineNumbers, string tableName, string primaryKey, string columnName, string columnValue, string foreignTableName) |
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h index 4f0c7b13..09784b7d 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/cabcutil.h | |||
@@ -10,7 +10,7 @@ | |||
10 | // First argument is the name of splitting cabinet without extension e.g. "cab1" | 10 | // First argument is the name of splitting cabinet without extension e.g. "cab1" |
11 | // Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" | 11 | // Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" |
12 | // Third argument is the file token of the first file present in the splitting cabinet | 12 | // Third argument is the file token of the first file present in the splitting cabinet |
13 | typedef void (__stdcall * FileSplitCabNamesCallback)(LPWSTR, LPWSTR, LPWSTR); | 13 | typedef void (__stdcall * FileSplitCabNamesCallback)(LPCWSTR, LPCWSTR, LPCWSTR); |
14 | 14 | ||
15 | #define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) | 15 | #define CAB_MAX_SIZE 0x7FFFFFFF // (see KB: Q174866) |
16 | 16 | ||
diff --git a/src/wix/WixToolset.Core.Native/Cabinet.cs b/src/wix/WixToolset.Core.Native/Cabinet.cs index e383d8fa..4d8ddb44 100644 --- a/src/wix/WixToolset.Core.Native/Cabinet.cs +++ b/src/wix/WixToolset.Core.Native/Cabinet.cs | |||
@@ -36,7 +36,8 @@ namespace WixToolset.Core.Native | |||
36 | /// <param name="compressionLevel">Level of compression to apply.</param> | 36 | /// <param name="compressionLevel">Level of compression to apply.</param> |
37 | /// <param name="maxSize">Maximum size of cabinet.</param> | 37 | /// <param name="maxSize">Maximum size of cabinet.</param> |
38 | /// <param name="maxThresh">Maximum threshold for each cabinet.</param> | 38 | /// <param name="maxThresh">Maximum threshold for each cabinet.</param> |
39 | public void Compress(IEnumerable<CabinetCompressFile> files, CompressionLevel compressionLevel, int maxSize = 0, int maxThresh = 0) | 39 | /// <returns>>List of CabinetCreated.</returns> |
40 | public IReadOnlyCollection<CabinetCreated> Compress(IEnumerable<CabinetCompressFile> files, CompressionLevel compressionLevel, int maxSize = 0, int maxThresh = 0) | ||
40 | { | 41 | { |
41 | var compressionLevelVariable = Environment.GetEnvironmentVariable(CompressionLevelVariable); | 42 | var compressionLevelVariable = Environment.GetEnvironmentVariable(CompressionLevelVariable); |
42 | 43 | ||
@@ -56,7 +57,9 @@ namespace WixToolset.Core.Native | |||
56 | wixnative.AddStdinLine(file.ToWixNativeStdinLine()); | 57 | wixnative.AddStdinLine(file.ToWixNativeStdinLine()); |
57 | } | 58 | } |
58 | 59 | ||
59 | wixnative.Run(); | 60 | var cabinetsCreated = wixnative.Run(); |
61 | |||
62 | return ParseCreatedCabinets(cabinetsCreated); | ||
60 | } | 63 | } |
61 | 64 | ||
62 | /// <summary> | 65 | /// <summary> |
@@ -103,5 +106,24 @@ namespace WixToolset.Core.Native | |||
103 | var wixnative = new WixNativeExe("extractcab", this.Path, outputFolder); | 106 | var wixnative = new WixNativeExe("extractcab", this.Path, outputFolder); |
104 | return wixnative.Run().Where(output => !String.IsNullOrWhiteSpace(output)); | 107 | return wixnative.Run().Where(output => !String.IsNullOrWhiteSpace(output)); |
105 | } | 108 | } |
109 | |||
110 | private static IReadOnlyCollection<CabinetCreated> ParseCreatedCabinets(IReadOnlyCollection<string> cabinetsCreated) | ||
111 | { | ||
112 | var created = new List<CabinetCreated>(); | ||
113 | |||
114 | foreach (var cabinetCreated in cabinetsCreated) | ||
115 | { | ||
116 | var data = cabinetCreated.Split(TextLineSplitter, StringSplitOptions.None); | ||
117 | |||
118 | if (data.Length != 3) | ||
119 | { | ||
120 | continue; | ||
121 | } | ||
122 | |||
123 | created.Add(new CabinetCreated(data[1], data[2])); | ||
124 | } | ||
125 | |||
126 | return created; | ||
127 | } | ||
106 | } | 128 | } |
107 | } | 129 | } |
diff --git a/src/wix/WixToolset.Core.Native/CabinetCreated.cs b/src/wix/WixToolset.Core.Native/CabinetCreated.cs new file mode 100644 index 00000000..635c862f --- /dev/null +++ b/src/wix/WixToolset.Core.Native/CabinetCreated.cs | |||
@@ -0,0 +1,31 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.Native | ||
4 | { | ||
5 | /// <summary> | ||
6 | /// Cabinet created by compressing files. | ||
7 | /// </summary> | ||
8 | public sealed class CabinetCreated | ||
9 | { | ||
10 | /// <summary> | ||
11 | /// Constructs CabinetCreated. | ||
12 | /// </summary> | ||
13 | /// <param name="cabinetName">Name of cabinet.</param> | ||
14 | /// <param name="firstFileToken">Token of first file compressed in cabinet.</param> | ||
15 | public CabinetCreated(string cabinetName, string firstFileToken) | ||
16 | { | ||
17 | this.CabinetName = cabinetName; | ||
18 | this.FirstFileToken = firstFileToken; | ||
19 | } | ||
20 | |||
21 | /// <summary> | ||
22 | /// Gets the name of the cabinet. | ||
23 | /// </summary> | ||
24 | public string CabinetName { get; } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Gets the token of the first file in the cabinet. | ||
28 | /// </summary> | ||
29 | public string FirstFileToken { get; } | ||
30 | } | ||
31 | } | ||
diff --git a/src/wix/WixToolset.Core.Native/WixNativeExe.cs b/src/wix/WixToolset.Core.Native/WixNativeExe.cs index 90439c5d..b3b248c0 100644 --- a/src/wix/WixToolset.Core.Native/WixNativeExe.cs +++ b/src/wix/WixToolset.Core.Native/WixNativeExe.cs | |||
@@ -49,8 +49,8 @@ namespace WixToolset.Core.Native | |||
49 | 49 | ||
50 | using (var process = Process.Start(wixNativeInfo)) | 50 | using (var process = Process.Start(wixNativeInfo)) |
51 | { | 51 | { |
52 | process.OutputDataReceived += (s, a) => outputLines.Add(a.Data); | 52 | process.OutputDataReceived += (s, a) => { if (a.Data != null) { outputLines.Add(a.Data); } }; |
53 | process.ErrorDataReceived += (s, a) => outputLines.Add(a.Data); | 53 | process.ErrorDataReceived += (s, a) => { if (a.Data != null) { outputLines.Add(a.Data); } }; |
54 | process.BeginOutputReadLine(); | 54 | process.BeginOutputReadLine(); |
55 | process.BeginErrorReadLine(); | 55 | process.BeginErrorReadLine(); |
56 | 56 | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 0e26bf6f..1589cc7a 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
@@ -410,7 +410,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
410 | var modularize = new ModularizeCommand(this.WindowsInstallerBackendHelper, data, modularizationSuffix, section.Symbols.OfType<WixSuppressModularizationSymbol>()); | 410 | var modularize = new ModularizeCommand(this.WindowsInstallerBackendHelper, data, modularizationSuffix, section.Symbols.OfType<WixSuppressModularizationSymbol>()); |
411 | modularize.Execute(); | 411 | modularize.Execute(); |
412 | 412 | ||
413 | // Ensure all sequence tables in place because, mergemod.dll requires them. | 413 | // Ensure all sequence tables in place because mergemod.dll requires them. |
414 | var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions); | 414 | var unsuppress = new AddBackSuppressedSequenceTablesCommand(data, tableDefinitions); |
415 | suppressedTableNames = unsuppress.Execute(); | 415 | suppressedTableNames = unsuppress.Execute(); |
416 | } | 416 | } |
@@ -438,28 +438,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
438 | command.Execute(); | 438 | command.Execute(); |
439 | } | 439 | } |
440 | 440 | ||
441 | // create cabinet files and process uncompressed files | 441 | // Create cabinet files. |
442 | var layoutDirectory = Path.GetDirectoryName(this.OutputPath); | ||
443 | if (!this.SuppressLayout || OutputType.Module == data.Type) | 442 | if (!this.SuppressLayout || OutputType.Module == data.Type) |
444 | { | 443 | { |
445 | this.Messaging.Write(VerboseMessages.CreatingCabinetFiles()); | 444 | this.Messaging.Write(VerboseMessages.CreatingCabinetFiles()); |
446 | 445 | ||
447 | var mediaTemplate = section.Symbols.OfType<WixMediaTemplateSymbol>().FirstOrDefault(); | 446 | var command = new CreateCabinetsCommand(this.ServiceProvider, this.Messaging, this.WindowsInstallerBackendHelper, this.BackendExtensions, section, this.CabCachePath, this.CabbingThreadCount, this.OutputPath, this.IntermediateFolder, this.DefaultCompressionLevel, compressed, modularizationSuffix, filesByCabinetMedia, data, tableDefinitions, this.ResolveMedia); |
448 | |||
449 | var command = new CreateCabinetsCommand(this.ServiceProvider, this.WindowsInstallerBackendHelper, mediaTemplate); | ||
450 | command.CabbingThreadCount = this.CabbingThreadCount; | ||
451 | command.CabCachePath = this.CabCachePath; | ||
452 | command.DefaultCompressionLevel = this.DefaultCompressionLevel; | ||
453 | command.Data = data; | ||
454 | command.Messaging = this.Messaging; | ||
455 | command.BackendExtensions = this.BackendExtensions; | ||
456 | command.LayoutDirectory = layoutDirectory; | ||
457 | command.Compressed = compressed; | ||
458 | command.ModularizationSuffix = modularizationSuffix; | ||
459 | command.FileFacadesByCabinet = filesByCabinetMedia; | ||
460 | command.ResolveMedia = this.ResolveMedia; | ||
461 | command.TableDefinitions = tableDefinitions; | ||
462 | command.IntermediateFolder = this.IntermediateFolder; | ||
463 | command.Execute(); | 447 | command.Execute(); |
464 | 448 | ||
465 | fileTransfers.AddRange(command.FileTransfers); | 449 | fileTransfers.AddRange(command.FileTransfers); |
@@ -523,13 +507,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
523 | // Process uncompressed files. | 507 | // Process uncompressed files. |
524 | if (!this.SuppressLayout && uncompressedFiles.Any()) | 508 | if (!this.SuppressLayout && uncompressedFiles.Any()) |
525 | { | 509 | { |
526 | var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver); | 510 | var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver, uncompressedFiles, this.OutputPath, compressed, longNames, this.ResolveMedia); |
527 | command.Compressed = compressed; | ||
528 | command.FileFacades = uncompressedFiles; | ||
529 | command.LayoutDirectory = layoutDirectory; | ||
530 | command.LongNamesInImage = longNames; | ||
531 | command.ResolveMedia = this.ResolveMedia; | ||
532 | command.DatabasePath = this.OutputPath; | ||
533 | command.Execute(); | 511 | command.Execute(); |
534 | 512 | ||
535 | fileTransfers.AddRange(command.FileTransfers); | 513 | fileTransfers.AddRange(command.FileTransfers); |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs index 49eaad42..9acbe475 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | |||
@@ -18,18 +18,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
18 | internal sealed class CabinetBuilder | 18 | internal sealed class CabinetBuilder |
19 | { | 19 | { |
20 | private readonly Queue<CabinetWorkItem> cabinetWorkItems; | 20 | private readonly Queue<CabinetWorkItem> cabinetWorkItems; |
21 | private int threadCount; | 21 | private readonly List<CompletedCabinetWorkItem> completedCabinets; |
22 | 22 | ||
23 | // Address of Binder's callback function for Cabinet Splitting | 23 | public CabinetBuilder(IMessaging messaging, int threadCount, int maximumCabinetSizeForLargeFileSplitting, int maximumUncompressedMediaSize) |
24 | private readonly IntPtr newCabNamesCallBackAddress; | ||
25 | |||
26 | /// <summary> | ||
27 | /// Instantiate a new CabinetBuilder. | ||
28 | /// </summary> | ||
29 | /// <param name="messaging"></param> | ||
30 | /// <param name="threadCount">number of threads to use</param> | ||
31 | /// <param name="newCabNamesCallBackAddress">Address of Binder's callback function for Cabinet Splitting</param> | ||
32 | public CabinetBuilder(IMessaging messaging, int threadCount, IntPtr newCabNamesCallBackAddress) | ||
33 | { | 24 | { |
34 | if (0 >= threadCount) | 25 | if (0 >= threadCount) |
35 | { | 26 | { |
@@ -37,24 +28,32 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
37 | } | 28 | } |
38 | 29 | ||
39 | this.cabinetWorkItems = new Queue<CabinetWorkItem>(); | 30 | this.cabinetWorkItems = new Queue<CabinetWorkItem>(); |
40 | this.Messaging = messaging; | 31 | this.completedCabinets = new List<CompletedCabinetWorkItem>(); |
41 | this.threadCount = threadCount; | ||
42 | 32 | ||
43 | // Set Address of Binder's callback function for Cabinet Splitting | 33 | this.Messaging = messaging; |
44 | this.newCabNamesCallBackAddress = newCabNamesCallBackAddress; | 34 | this.ThreadCount = threadCount; |
35 | this.MaximumCabinetSizeForLargeFileSplitting = maximumCabinetSizeForLargeFileSplitting; | ||
36 | this.MaximumUncompressedMediaSize = maximumUncompressedMediaSize; | ||
45 | } | 37 | } |
46 | 38 | ||
47 | private IMessaging Messaging { get; } | 39 | private IMessaging Messaging { get; } |
48 | 40 | ||
49 | public int MaximumCabinetSizeForLargeFileSplitting { get; set; } | 41 | private int ThreadCount { get; } |
50 | 42 | ||
51 | public int MaximumUncompressedMediaSize { get; set; } | 43 | private int MaximumCabinetSizeForLargeFileSplitting { get; } |
44 | |||
45 | private int MaximumUncompressedMediaSize { get; } | ||
46 | |||
47 | public IReadOnlyCollection<CompletedCabinetWorkItem> CompletedCabinets => this.completedCabinets; | ||
52 | 48 | ||
53 | /// <summary> | 49 | /// <summary> |
54 | /// Enqueues a CabinetWorkItem to the queue. | 50 | /// Enqueues a CabinetWorkItem to the queue. |
55 | /// </summary> | 51 | /// </summary> |
56 | /// <param name="cabinetWorkItem">cabinet work item</param> | 52 | /// <param name="cabinetWorkItem">cabinet work item</param> |
57 | public void Enqueue(CabinetWorkItem cabinetWorkItem) => this.cabinetWorkItems.Enqueue(cabinetWorkItem); | 53 | public void Enqueue(CabinetWorkItem cabinetWorkItem) |
54 | { | ||
55 | this.cabinetWorkItems.Enqueue(cabinetWorkItem); | ||
56 | } | ||
58 | 57 | ||
59 | /// <summary> | 58 | /// <summary> |
60 | /// Create the queued cabinets. | 59 | /// Create the queued cabinets. |
@@ -62,8 +61,20 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
62 | /// <returns>error message number (zero if no error)</returns> | 61 | /// <returns>error message number (zero if no error)</returns> |
63 | public void CreateQueuedCabinets() | 62 | public void CreateQueuedCabinets() |
64 | { | 63 | { |
64 | if (this.cabinetWorkItems.Count == 0) | ||
65 | { | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | var cabinetFolders = this.cabinetWorkItems.Select(c => Path.GetDirectoryName(c.CabinetFile)).Distinct(StringComparer.OrdinalIgnoreCase); | ||
70 | |||
71 | foreach (var folder in cabinetFolders) | ||
72 | { | ||
73 | Directory.CreateDirectory(folder); | ||
74 | } | ||
75 | |||
65 | // don't create more threads than the number of cabinets to build | 76 | // don't create more threads than the number of cabinets to build |
66 | var numberOfThreads = Math.Min(this.threadCount, this.cabinetWorkItems.Count); | 77 | var numberOfThreads = Math.Min(this.ThreadCount, this.cabinetWorkItems.Count); |
67 | 78 | ||
68 | if (0 < numberOfThreads) | 79 | if (0 < numberOfThreads) |
69 | { | 80 | { |
@@ -107,8 +118,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
107 | cabinetWorkItem = this.cabinetWorkItems.Dequeue(); | 118 | cabinetWorkItem = this.cabinetWorkItems.Dequeue(); |
108 | } | 119 | } |
109 | 120 | ||
110 | // create a cabinet | 121 | // Create a cabinet. |
111 | this.CreateCabinet(cabinetWorkItem); | 122 | var created = this.CreateCabinet(cabinetWorkItem); |
123 | |||
124 | // Update the cabinet work item to report back what cabinets were created. | ||
125 | lock (this.completedCabinets) | ||
126 | { | ||
127 | this.completedCabinets.Add(new CompletedCabinetWorkItem(cabinetWorkItem.DiskId, created)); | ||
128 | } | ||
112 | } | 129 | } |
113 | } | 130 | } |
114 | catch (WixException we) | 131 | catch (WixException we) |
@@ -125,7 +142,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
125 | /// Creates a cabinet using the wixcab.dll interop layer. | 142 | /// Creates a cabinet using the wixcab.dll interop layer. |
126 | /// </summary> | 143 | /// </summary> |
127 | /// <param name="cabinetWorkItem">CabinetWorkItem containing information about the cabinet to create.</param> | 144 | /// <param name="cabinetWorkItem">CabinetWorkItem containing information about the cabinet to create.</param> |
128 | private void CreateCabinet(CabinetWorkItem cabinetWorkItem) | 145 | private IReadOnlyCollection<CabinetCreated> CreateCabinet(CabinetWorkItem cabinetWorkItem) |
129 | { | 146 | { |
130 | this.Messaging.Write(VerboseMessages.CreateCabinet(cabinetWorkItem.CabinetFile)); | 147 | this.Messaging.Write(VerboseMessages.CreateCabinet(cabinetWorkItem.CabinetFile)); |
131 | 148 | ||
@@ -163,7 +180,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
163 | .ToList(); | 180 | .ToList(); |
164 | 181 | ||
165 | var cab = new Cabinet(cabinetPath); | 182 | var cab = new Cabinet(cabinetPath); |
166 | cab.Compress(files, cabinetWorkItem.CompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold); | 183 | var created = cab.Compress(files, cabinetWorkItem.CompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold); |
167 | 184 | ||
168 | // Best effort check to see if the cabinet is too large for the Windows Installer. | 185 | // Best effort check to see if the cabinet is too large for the Windows Installer. |
169 | try | 186 | try |
@@ -177,6 +194,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
177 | catch | 194 | catch |
178 | { | 195 | { |
179 | } | 196 | } |
197 | |||
198 | return created; | ||
180 | } | 199 | } |
181 | } | 200 | } |
182 | } | 201 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs index b1a29834..12332c80 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs | |||
@@ -15,14 +15,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
15 | /// Instantiate a new CabinetWorkItem. | 15 | /// Instantiate a new CabinetWorkItem. |
16 | /// </summary> | 16 | /// </summary> |
17 | /// <param name="sourceLineNumber">Source line number that requires the cabinet creation.</param> | 17 | /// <param name="sourceLineNumber">Source line number that requires the cabinet creation.</param> |
18 | /// <param name="diskId"></param> | ||
18 | /// <param name="fileFacades">The collection of files in this cabinet.</param> | 19 | /// <param name="fileFacades">The collection of files in this cabinet.</param> |
19 | /// <param name="cabinetFile">The cabinet file.</param> | 20 | /// <param name="cabinetFile">The cabinet file.</param> |
20 | /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> | 21 | /// <param name="maxThreshold">Maximum threshold for each cabinet.</param> |
21 | /// <param name="compressionLevel">The compression level of the cabinet.</param> | 22 | /// <param name="compressionLevel">The compression level of the cabinet.</param> |
22 | /// <param name="modularizationSuffix">Modularization suffix used when building a Merge Module.</param> | 23 | /// <param name="modularizationSuffix">Modularization suffix used when building a Merge Module.</param> |
23 | public CabinetWorkItem(SourceLineNumber sourceLineNumber, string cabinetFile, IEnumerable<IFileFacade> fileFacades, int maxThreshold, CompressionLevel compressionLevel, string modularizationSuffix) | 24 | public CabinetWorkItem(SourceLineNumber sourceLineNumber, int diskId, string cabinetFile, IEnumerable<IFileFacade> fileFacades, int maxThreshold, CompressionLevel compressionLevel, string modularizationSuffix) |
24 | { | 25 | { |
25 | this.SourceLineNumber = sourceLineNumber; | 26 | this.SourceLineNumber = sourceLineNumber; |
27 | this.DiskId = diskId; | ||
26 | this.CabinetFile = cabinetFile; | 28 | this.CabinetFile = cabinetFile; |
27 | this.CompressionLevel = compressionLevel; | 29 | this.CompressionLevel = compressionLevel; |
28 | this.ModularizationSuffix = modularizationSuffix; | 30 | this.ModularizationSuffix = modularizationSuffix; |
@@ -36,6 +38,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
36 | public SourceLineNumber SourceLineNumber { get; } | 38 | public SourceLineNumber SourceLineNumber { get; } |
37 | 39 | ||
38 | /// <summary> | 40 | /// <summary> |
41 | /// Gets the Media symbol's DiskId that requires the cabinet. | ||
42 | /// </summary> | ||
43 | public int DiskId { get; } | ||
44 | |||
45 | /// <summary> | ||
39 | /// Gets the cabinet file. | 46 | /// Gets the cabinet file. |
40 | /// </summary> | 47 | /// </summary> |
41 | /// <value>The cabinet file.</value> | 48 | /// <value>The cabinet file.</value> |
@@ -56,7 +63,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
56 | /// Gets the collection of files in this cabinet. | 63 | /// Gets the collection of files in this cabinet. |
57 | /// </summary> | 64 | /// </summary> |
58 | /// <value>The collection of files in this cabinet.</value> | 65 | /// <value>The collection of files in this cabinet.</value> |
59 | public IEnumerable<IFileFacade> FileFacades { get; } | 66 | public IEnumerable<IFileFacade> FileFacades { get; } |
60 | 67 | ||
61 | /// <summary> | 68 | /// <summary> |
62 | /// Gets the max threshold. | 69 | /// Gets the max threshold. |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CompletedCabinetWorkItem.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CompletedCabinetWorkItem.cs new file mode 100644 index 00000000..e916bb61 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CompletedCabinetWorkItem.cs | |||
@@ -0,0 +1,20 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | ||
4 | { | ||
5 | using System.Collections.Generic; | ||
6 | using WixToolset.Core.Native; | ||
7 | |||
8 | internal class CompletedCabinetWorkItem | ||
9 | { | ||
10 | public CompletedCabinetWorkItem(int diskId, IReadOnlyCollection<CabinetCreated> created) | ||
11 | { | ||
12 | this.DiskId = diskId; | ||
13 | this.CreatedCabinets = created; | ||
14 | } | ||
15 | |||
16 | public int DiskId { get; } | ||
17 | |||
18 | public IReadOnlyCollection<CabinetCreated> CreatedCabinets { get; } | ||
19 | } | ||
20 | } | ||
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index 2516a6fa..31198837 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | |||
@@ -4,13 +4,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.Globalization; | ||
8 | using System.IO; | 7 | using System.IO; |
9 | using System.Linq; | 8 | using System.Linq; |
10 | using System.Runtime.InteropServices; | ||
11 | using WixToolset.Data; | 9 | using WixToolset.Data; |
12 | using WixToolset.Data.Symbols; | 10 | using WixToolset.Data.Symbols; |
13 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
12 | using WixToolset.Data.WindowsInstaller.Rows; | ||
14 | using WixToolset.Extensibility; | 13 | using WixToolset.Extensibility; |
15 | using WixToolset.Extensibility.Data; | 14 | using WixToolset.Extensibility.Data; |
16 | using WixToolset.Extensibility.Services; | 15 | using WixToolset.Extensibility.Services; |
@@ -20,70 +19,63 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
20 | /// </summary> | 19 | /// </summary> |
21 | internal class CreateCabinetsCommand | 20 | internal class CreateCabinetsCommand |
22 | { | 21 | { |
23 | public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB | 22 | public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB |
24 | public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | 23 | public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) |
25 | 24 | ||
25 | private readonly CabinetResolver cabinetResolver; | ||
26 | private readonly List<IFileTransfer> fileTransfers; | 26 | private readonly List<IFileTransfer> fileTransfers; |
27 | |||
28 | private readonly List<ITrackedFile> trackedFiles; | 27 | private readonly List<ITrackedFile> trackedFiles; |
29 | 28 | ||
30 | private readonly FileSplitCabNamesCallback newCabNamesCallBack; | 29 | public CreateCabinetsCommand(IServiceProvider serviceProvider, IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IntermediateSection section, string cabCachePath, int cabbingThreadCount, string outputPath, string intermediateFolder, CompressionLevel? defaultCompressionLevel, bool compressed, string modularizationSuffix, Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia, WindowsInstallerData data, TableDefinitionCollection tableDefinitions, Func<MediaSymbol, string, string, string> resolveMedia) |
31 | |||
32 | private Dictionary<string, string> lastCabinetAddedToMediaTable; // Key is First Cabinet Name, Value is Last Cabinet Added in the Split Sequence | ||
33 | |||
34 | public CreateCabinetsCommand(IServiceProvider serviceProvider, IBackendHelper backendHelper, WixMediaTemplateSymbol mediaTemplate) | ||
35 | { | 30 | { |
36 | this.fileTransfers = new List<IFileTransfer>(); | 31 | this.Messaging = messaging; |
37 | 32 | ||
38 | this.trackedFiles = new List<ITrackedFile>(); | 33 | this.BackendHelper = backendHelper; |
39 | |||
40 | this.newCabNamesCallBack = this.NewCabNamesCallBack; | ||
41 | 34 | ||
42 | this.ServiceProvider = serviceProvider; | 35 | this.Section = section; |
43 | 36 | ||
44 | this.BackendHelper = backendHelper; | 37 | this.CabbingThreadCount = cabbingThreadCount; |
45 | 38 | ||
46 | this.MediaTemplate = mediaTemplate; | 39 | this.IntermediateFolder = intermediateFolder; |
47 | } | 40 | this.LayoutDirectory = Path.GetDirectoryName(outputPath); |
48 | 41 | ||
49 | private IServiceProvider ServiceProvider { get; } | 42 | this.DefaultCompressionLevel = defaultCompressionLevel; |
43 | this.ModularizationSuffix = modularizationSuffix; | ||
44 | this.FileFacadesByCabinet = filesByCabinetMedia; | ||
50 | 45 | ||
51 | private IBackendHelper BackendHelper { get; } | 46 | this.Data = data; |
47 | this.TableDefinitions = tableDefinitions; | ||
52 | 48 | ||
53 | private WixMediaTemplateSymbol MediaTemplate { get; } | 49 | this.ResolveMedia = resolveMedia; |
54 | 50 | ||
55 | /// <summary> | 51 | this.cabinetResolver = new CabinetResolver(serviceProvider, cabCachePath, backendExtensions); |
56 | /// Sets the number of threads to use for cabinet creation. | 52 | this.fileTransfers = new List<IFileTransfer>(); |
57 | /// </summary> | 53 | this.trackedFiles = new List<ITrackedFile>(); |
58 | public int CabbingThreadCount { private get; set; } | 54 | } |
59 | 55 | ||
60 | public string CabCachePath { private get; set; } | 56 | private IMessaging Messaging { get; } |
61 | 57 | ||
62 | public IMessaging Messaging { private get; set; } | 58 | private IBackendHelper BackendHelper { get; } |
63 | 59 | ||
64 | public string IntermediateFolder { private get; set; } | 60 | private IntermediateSection Section { get; } |
65 | 61 | ||
66 | /// <summary> | 62 | private int CabbingThreadCount { get; set; } |
67 | /// Sets the default compression level to use for cabinets | ||
68 | /// that don't have their compression level explicitly set. | ||
69 | /// </summary> | ||
70 | public CompressionLevel? DefaultCompressionLevel { private get; set; } | ||
71 | 63 | ||
72 | public IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { private get; set; } | 64 | private string IntermediateFolder { get; } |
73 | 65 | ||
74 | public WindowsInstallerData Data { private get; set; } | 66 | private string LayoutDirectory { get; } |
75 | 67 | ||
76 | public string LayoutDirectory { private get; set; } | 68 | private CompressionLevel? DefaultCompressionLevel { get; } |
77 | 69 | ||
78 | public bool Compressed { private get; set; } | 70 | private string ModularizationSuffix { get; } |
79 | 71 | ||
80 | public string ModularizationSuffix { private get; set; } | 72 | private Dictionary<MediaSymbol, IEnumerable<IFileFacade>> FileFacadesByCabinet { get; } |
81 | 73 | ||
82 | public Dictionary<MediaSymbol, IEnumerable<IFileFacade>> FileFacadesByCabinet { private get; set; } | 74 | private WindowsInstallerData Data { get; } |
83 | 75 | ||
84 | public Func<MediaSymbol, string, string, string> ResolveMedia { private get; set; } | 76 | private TableDefinitionCollection TableDefinitions { get; } |
85 | 77 | ||
86 | public TableDefinitionCollection TableDefinitions { private get; set; } | 78 | private Func<MediaSymbol, string, string, string> ResolveMedia { get; } |
87 | 79 | ||
88 | public IEnumerable<IFileTransfer> FileTransfers => this.fileTransfers; | 80 | public IEnumerable<IFileTransfer> FileTransfers => this.fileTransfers; |
89 | 81 | ||
@@ -91,8 +83,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
91 | 83 | ||
92 | public void Execute() | 84 | public void Execute() |
93 | { | 85 | { |
94 | this.lastCabinetAddedToMediaTable = new Dictionary<string, string>(); | ||
95 | |||
96 | // If the cabbing thread count wasn't provided, default the number of cabbing threads to the number of processors. | 86 | // If the cabbing thread count wasn't provided, default the number of cabbing threads to the number of processors. |
97 | if (this.CabbingThreadCount <= 0) | 87 | if (this.CabbingThreadCount <= 0) |
98 | { | 88 | { |
@@ -101,13 +91,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
101 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); | 91 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); |
102 | } | 92 | } |
103 | 93 | ||
104 | // Send Binder object to Facilitate NewCabNamesCallBack Callback | ||
105 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); | ||
106 | |||
107 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
108 | this.GetMediaTemplateAttributes(out var maximumCabinetSizeForLargeFileSplitting, out var maximumUncompressedMediaSize); | 94 | this.GetMediaTemplateAttributes(out var maximumCabinetSizeForLargeFileSplitting, out var maximumUncompressedMediaSize); |
109 | cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = maximumCabinetSizeForLargeFileSplitting; | 95 | |
110 | cabinetBuilder.MaximumUncompressedMediaSize = maximumUncompressedMediaSize; | 96 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, maximumCabinetSizeForLargeFileSplitting, maximumUncompressedMediaSize); |
111 | 97 | ||
112 | foreach (var entry in this.FileFacadesByCabinet) | 98 | foreach (var entry in this.FileFacadesByCabinet) |
113 | { | 99 | { |
@@ -129,12 +115,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
129 | return; | 115 | return; |
130 | } | 116 | } |
131 | 117 | ||
132 | // create queued cabinets with multiple threads | 118 | // Create queued cabinets with multiple threads. |
133 | cabinetBuilder.CreateQueuedCabinets(); | 119 | cabinetBuilder.CreateQueuedCabinets(); |
120 | |||
134 | if (this.Messaging.EncounteredError) | 121 | if (this.Messaging.EncounteredError) |
135 | { | 122 | { |
136 | return; | 123 | return; |
137 | } | 124 | } |
125 | |||
126 | this.UpdateMediaWithSpannedCabinets(cabinetBuilder.CompletedCabinets); | ||
138 | } | 127 | } |
139 | 128 | ||
140 | private int CalculateCabbingThreadCount() | 129 | private int CalculateCabbingThreadCount() |
@@ -163,7 +152,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
163 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades) | 152 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades) |
164 | { | 153 | { |
165 | CabinetWorkItem cabinetWorkItem = null; | 154 | CabinetWorkItem cabinetWorkItem = null; |
166 | var tempCabinetFileX = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet); | 155 | |
156 | var intermediateCabinetPath = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet); | ||
167 | 157 | ||
168 | // check for an empty cabinet | 158 | // check for an empty cabinet |
169 | if (!fileFacades.Any()) | 159 | if (!fileFacades.Any()) |
@@ -172,25 +162,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
172 | var cabinetName = mediaSymbol.Cabinet.TrimStart('#'); | 162 | var cabinetName = mediaSymbol.Cabinet.TrimStart('#'); |
173 | 163 | ||
174 | // If building a patch, remind them to run -p for torch. | 164 | // If building a patch, remind them to run -p for torch. |
175 | if (OutputType.Patch == data.Type) | 165 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, OutputType.Patch == data.Type)); |
176 | { | ||
177 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, true)); | ||
178 | } | ||
179 | else | ||
180 | { | ||
181 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName)); | ||
182 | } | ||
183 | } | 166 | } |
184 | 167 | ||
185 | var cabinetResolver = new CabinetResolver(this.ServiceProvider, this.CabCachePath, this.BackendExtensions); | 168 | var resolvedCabinet = this.cabinetResolver.ResolveCabinet(intermediateCabinetPath, fileFacades); |
186 | |||
187 | var resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades); | ||
188 | 169 | ||
189 | // create a cabinet work item if it's not being skipped | 170 | // Create a cabinet work item if it's not being skipped. |
190 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) | 171 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) |
191 | { | 172 | { |
192 | // Default to the threshold for best smartcabbing (makes smallest cabinet). | 173 | // Default to the threshold for best smartcabbing (makes smallest cabinet). |
193 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, resolvedCabinet.Path, fileFacades, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); | 174 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, mediaSymbol.DiskId, resolvedCabinet.Path, fileFacades, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); |
194 | } | 175 | } |
195 | else // reuse the cabinet from the cabinet cache. | 176 | else // reuse the cabinet from the cabinet cache. |
196 | { | 177 | { |
@@ -235,185 +216,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
235 | return cabinetWorkItem; | 216 | return cabinetWorkItem; |
236 | } | 217 | } |
237 | 218 | ||
238 | //private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades) | ||
239 | //{ | ||
240 | // ResolvedCabinet resolved = null; | ||
241 | |||
242 | // List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList(); | ||
243 | |||
244 | // foreach (var extension in this.BackendExtensions) | ||
245 | // { | ||
246 | // resolved = extension.ResolveCabinet(cabinetPath, filesWithPath); | ||
247 | // if (null != resolved) | ||
248 | // { | ||
249 | // break; | ||
250 | // } | ||
251 | // } | ||
252 | |||
253 | // return resolved; | ||
254 | //} | ||
255 | |||
256 | /// <summary> | ||
257 | /// Delegate for Cabinet Split Callback | ||
258 | /// </summary> | ||
259 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] | ||
260 | internal delegate void FileSplitCabNamesCallback([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken); | ||
261 | |||
262 | /// <summary> | ||
263 | /// Call back to Add File Transfer for new Cab and add new Cab to Media table | ||
264 | /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe | ||
265 | /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored | ||
266 | /// </summary> | ||
267 | /// <param name="firstCabName">The name of splitting cabinet without extention e.g. "cab1".</param> | ||
268 | /// <param name="newCabinetName">The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"</param> | ||
269 | /// <param name="fileToken">The file token of the first file present in the splitting cabinet</param> | ||
270 | internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabinetName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken) | ||
271 | { | ||
272 | throw new NotImplementedException(); | ||
273 | #if TODO_CAB_SPANNING | ||
274 | // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads | ||
275 | var mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); | ||
276 | try | ||
277 | { | ||
278 | if (!mutex.WaitOne(0, false)) // Check if you can get the lock | ||
279 | { | ||
280 | // Cound not get the Lock | ||
281 | this.Messaging.Write(VerboseMessages.CabinetsSplitInParallel()); | ||
282 | mutex.WaitOne(); // Wait on other thread | ||
283 | } | ||
284 | |||
285 | var firstCabinetName = firstCabName + ".cab"; | ||
286 | var transferAdded = false; // Used for Error Handling | ||
287 | |||
288 | // Create File Transfer for new Cabinet using transfer of Base Cabinet | ||
289 | foreach (var transfer in this.FileTransfers) | ||
290 | { | ||
291 | if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) | ||
292 | { | ||
293 | var newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); | ||
294 | var newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); | ||
295 | |||
296 | var trackSource = this.BackendHelper.TrackFile(newCabSourcePath, TrackedFileType.Intermediate, transfer.SourceLineNumbers); | ||
297 | this.trackedFiles.Add(trackSource); | ||
298 | |||
299 | var trackTarget = this.BackendHelper.TrackFile(newCabTargetPath, TrackedFileType.Final, transfer.SourceLineNumbers); | ||
300 | this.trackedFiles.Add(trackTarget); | ||
301 | |||
302 | var newTransfer = this.BackendHelper.CreateFileTransfer(trackSource.Path, trackTarget.Path, transfer.Move, transfer.SourceLineNumbers); | ||
303 | this.fileTransfers.Add(newTransfer); | ||
304 | |||
305 | transferAdded = true; | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | // Check if File Transfer was added | ||
311 | if (!transferAdded) | ||
312 | { | ||
313 | throw new WixException(ErrorMessages.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); | ||
314 | } | ||
315 | |||
316 | // Add the new Cabinets to media table using LastSequence of Base Cabinet | ||
317 | var mediaTable = this.Output.Tables["Media"]; | ||
318 | var wixFileTable = this.Output.Tables["WixFile"]; | ||
319 | var diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain | ||
320 | var lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain | ||
321 | var lastSplitCabinetFound = false; // Used for Error Handling | ||
322 | |||
323 | var lastCabinetOfThisSequence = String.Empty; | ||
324 | // Get the Value of Last Cabinet Added in this split Sequence from Dictionary | ||
325 | if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) | ||
326 | { | ||
327 | // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence | ||
328 | lastCabinetOfThisSequence = firstCabinetName; | ||
329 | } | ||
330 | |||
331 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
332 | { | ||
333 | // Get details for the Last Cabinet Added in this Split Sequence | ||
334 | if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
335 | { | ||
336 | lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; | ||
337 | diskIDForLastSplitCabAdded = mediaRow.DiskId; | ||
338 | lastSplitCabinetFound = true; | ||
339 | } | ||
340 | |||
341 | // Check for Name Collision for the new Cabinet added | ||
342 | if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
343 | { | ||
344 | // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row | ||
345 | throw new WixException(ErrorMessages.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | // Check if the last Split Cabinet was found in the Media Table | ||
350 | if (!lastSplitCabinetFound) | ||
351 | { | ||
352 | throw new WixException(ErrorMessages.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); | ||
353 | } | ||
354 | |||
355 | // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort | ||
356 | // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with | ||
357 | // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction | ||
358 | MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
359 | newMediaRow.Cabinet = newCabinetName; | ||
360 | newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion | ||
361 | newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; | ||
362 | |||
363 | // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique | ||
364 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
365 | { | ||
366 | // Check if this row comes after inserted row and it is not the new cabinet inserted row | ||
367 | if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
368 | { | ||
369 | mediaRow.DiskId++; // Increment DiskID | ||
370 | } | ||
371 | } | ||
372 | |||
373 | // Now Increment DiskID for All files Rows so that they refer to the right Media Row | ||
374 | foreach (WixFileRow wixFileRow in wixFileTable.Rows) | ||
375 | { | ||
376 | // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet | ||
377 | // This check will work as we have only one large file in every splitting cabinet | ||
378 | // If we want to support splitting cabinet with more large files we need to update this code | ||
379 | if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) | ||
380 | { | ||
381 | wixFileRow.DiskId++; // Increment DiskID | ||
382 | } | ||
383 | } | ||
384 | |||
385 | // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback | ||
386 | this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; | ||
387 | |||
388 | mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails | ||
389 | } | ||
390 | finally | ||
391 | { | ||
392 | // Releasing the Mutex here | ||
393 | mutex.ReleaseMutex(); | ||
394 | } | ||
395 | #endif | ||
396 | } | ||
397 | |||
398 | |||
399 | /// <summary> | 219 | /// <summary> |
400 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides | 220 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides |
401 | /// </summary> | 221 | /// </summary> |
402 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) | 222 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) |
403 | { | 223 | { |
404 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | 224 | var mediaTemplate = this.Section.Symbols.OfType<WixMediaTemplateSymbol>().FirstOrDefault(); |
405 | var mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
406 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
407 | 225 | ||
408 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | 226 | // Supply Compile MediaTemplate Attributes to Cabinet Builder |
409 | if (this.MediaTemplate != null) | 227 | if (mediaTemplate != null) |
410 | { | 228 | { |
229 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | ||
230 | var mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
231 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
232 | |||
411 | // Get the Value for Max Cab Size for File Splitting | 233 | // Get the Value for Max Cab Size for File Splitting |
412 | var maxCabSizeForLargeFileInMB = 0; | 234 | var maxCabSizeForLargeFileInMB = 0; |
413 | try | 235 | try |
414 | { | 236 | { |
415 | // Override authored mcslfs value if environment variable is authored. | 237 | // Override authored mcslfs value if environment variable is authored. |
416 | maxCabSizeForLargeFileInMB = !String.IsNullOrEmpty(mcslfsString) ? Int32.Parse(mcslfsString) : this.MediaTemplate.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; | 238 | maxCabSizeForLargeFileInMB = !String.IsNullOrEmpty(mcslfsString) ? Int32.Parse(mcslfsString) : mediaTemplate.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; |
417 | 239 | ||
418 | var testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; | 240 | var testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; |
419 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; | 241 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; |
@@ -431,7 +253,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
431 | try | 253 | try |
432 | { | 254 | { |
433 | // Override authored mums value if environment variable is authored. | 255 | // Override authored mums value if environment variable is authored. |
434 | maxPreCompressedSizeInMB = !String.IsNullOrEmpty(mumsString) ? Int32.Parse(mumsString) : this.MediaTemplate.MaximumUncompressedMediaSize ?? DefaultMaximumUncompressedMediaSize; | 256 | maxPreCompressedSizeInMB = !String.IsNullOrEmpty(mumsString) ? Int32.Parse(mumsString) : mediaTemplate.MaximumUncompressedMediaSize ?? DefaultMaximumUncompressedMediaSize; |
435 | 257 | ||
436 | var testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; | 258 | var testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; |
437 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; | 259 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; |
@@ -451,5 +273,121 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
451 | maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize; | 273 | maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize; |
452 | } | 274 | } |
453 | } | 275 | } |
276 | |||
277 | private void UpdateMediaWithSpannedCabinets(IReadOnlyCollection<CompletedCabinetWorkItem> completedCabinetWorkItems) | ||
278 | { | ||
279 | var completedCabinetsSpanned = completedCabinetWorkItems.Where(c => c.CreatedCabinets.Count > 1).OrderBy(c => c.DiskId).ToList(); | ||
280 | |||
281 | if (completedCabinetsSpanned.Count == 0) | ||
282 | { | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | var fileTransfersByName = this.fileTransfers.ToDictionary(t => Path.GetFileName(t.Source), StringComparer.OrdinalIgnoreCase); | ||
287 | var mediaTable = this.Data.Tables["Media"]; | ||
288 | var fileTable = this.Data.Tables["File"]; | ||
289 | var mediaRows = mediaTable.Rows.Cast<MediaRow>().OrderBy(m => m.DiskId).ToList(); | ||
290 | var fileRows = fileTable.Rows.Cast<FileRow>().OrderBy(f => f.Sequence).ToList(); | ||
291 | |||
292 | var mediaRowsByOriginalDiskId = mediaRows.ToDictionary(m => m.DiskId); | ||
293 | var addedMediaRows = new List<MediaRow>(); | ||
294 | |||
295 | foreach (var completedCabinetSpanned in completedCabinetsSpanned) | ||
296 | { | ||
297 | var cabinet = completedCabinetSpanned.CreatedCabinets.First(); | ||
298 | var spannedCabinets = completedCabinetSpanned.CreatedCabinets.Skip(1); | ||
299 | |||
300 | if (!fileTransfersByName.TryGetValue(cabinet.CabinetName, out var transfer) || | ||
301 | !mediaRowsByOriginalDiskId.TryGetValue(completedCabinetSpanned.DiskId, out var mediaRow)) | ||
302 | { | ||
303 | throw new WixException(ErrorMessages.SplitCabinetCopyRegistrationFailed(spannedCabinets.First().CabinetName, cabinet.CabinetName)); | ||
304 | } | ||
305 | |||
306 | var lastDiskId = mediaRow.DiskId; | ||
307 | var mediaRowsThatWillNeedDiskIdUpdated = mediaRows.OrderBy(m => m.DiskId).Where(m => m.DiskId > mediaRow.DiskId).ToList(); | ||
308 | |||
309 | foreach (var spannedCabinet in spannedCabinets) | ||
310 | { | ||
311 | var spannedCabinetSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), spannedCabinet.CabinetName); | ||
312 | var spannedCabinetTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), spannedCabinet.CabinetName); | ||
313 | |||
314 | var trackSource = this.BackendHelper.TrackFile(spannedCabinetSourcePath, TrackedFileType.Intermediate, transfer.SourceLineNumbers); | ||
315 | this.trackedFiles.Add(trackSource); | ||
316 | |||
317 | var trackTarget = this.BackendHelper.TrackFile(spannedCabinetTargetPath, TrackedFileType.BuiltOutput, transfer.SourceLineNumbers); | ||
318 | this.trackedFiles.Add(trackTarget); | ||
319 | |||
320 | var newTransfer = this.BackendHelper.CreateFileTransfer(trackSource.Path, trackTarget.Path, transfer.Move, transfer.SourceLineNumbers); | ||
321 | this.fileTransfers.Add(newTransfer); | ||
322 | |||
323 | // FDI Extract requires DiskID of Split Cabinets to be continuous. So a new Media row must inserted just | ||
324 | // after the previous spanned cabinet according to DiskID sort order, otherwise Windows Installer will | ||
325 | // encounter Error 2350 (FDI Server Error). | ||
326 | var newMediaRow = (MediaRow)mediaTable.CreateRow(mediaRow.SourceLineNumbers); | ||
327 | newMediaRow.Cabinet = spannedCabinet.CabinetName; | ||
328 | newMediaRow.DiskId = ++lastDiskId; | ||
329 | newMediaRow.LastSequence = mediaRow.LastSequence; | ||
330 | |||
331 | addedMediaRows.Add(newMediaRow); | ||
332 | } | ||
333 | |||
334 | // Increment the DiskId for all Media rows that come after the newly inserted row to ensure that the DiskId is unique | ||
335 | // and the Media rows stay in order based on last sequence. | ||
336 | foreach (var updateMediaRow in mediaRowsThatWillNeedDiskIdUpdated) | ||
337 | { | ||
338 | updateMediaRow.DiskId = ++lastDiskId; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | mediaTable.ValidateRows(); | ||
343 | |||
344 | var oldDiskIdToNewDiskId = mediaRowsByOriginalDiskId.Where(originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Value.DiskId != originalDiskIdWithMediaRow.Key) | ||
345 | .ToDictionary(originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Key, originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Value.DiskId); | ||
346 | |||
347 | // Update the File row and FileSymbols so the DiskIds are correct in the WixOutput, even if this | ||
348 | // data doesn't show up in the Windows Installer database. | ||
349 | foreach (var fileRow in fileRows) | ||
350 | { | ||
351 | if (oldDiskIdToNewDiskId.TryGetValue(fileRow.DiskId, out var newDiskId)) | ||
352 | { | ||
353 | fileRow.DiskId = newDiskId; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | foreach (var fileSymbol in this.Section.Symbols.OfType<FileSymbol>()) | ||
358 | { | ||
359 | if (fileSymbol.DiskId.HasValue && oldDiskIdToNewDiskId.TryGetValue(fileSymbol.DiskId.Value, out var newDiskId)) | ||
360 | { | ||
361 | fileSymbol.DiskId = newDiskId; | ||
362 | } | ||
363 | } | ||
364 | |||
365 | // Update the MediaSymbol DiskIds to the correct DiskId. Note that the MediaSymbol Id | ||
366 | // is not changed because symbol ids are not allowed to change after they are created. | ||
367 | foreach (var mediaSymbol in this.Section.Symbols.OfType<MediaSymbol>()) | ||
368 | { | ||
369 | if (oldDiskIdToNewDiskId.TryGetValue(mediaSymbol.DiskId, out var newDiskId)) | ||
370 | { | ||
371 | mediaSymbol.DiskId = newDiskId; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | // Now that the existing MediaSymbol DiskIds are updated, add the newly created Media rows | ||
376 | // as symbols. Notice that the new MediaSymbols do not have an Id because they very likely | ||
377 | // would conflict with MediaSymbols that had their DiskIds updated but Ids could not be updated. | ||
378 | // The newly created MediaSymbols will rename anonymous. | ||
379 | foreach (var mediaRow in addedMediaRows) | ||
380 | { | ||
381 | this.Section.AddSymbol(new MediaSymbol(mediaRow.SourceLineNumbers) | ||
382 | { | ||
383 | Cabinet = mediaRow.Cabinet, | ||
384 | DiskId = mediaRow.DiskId, | ||
385 | DiskPrompt = mediaRow.DiskPrompt, | ||
386 | LastSequence = mediaRow.LastSequence, | ||
387 | Source = mediaRow.Source, | ||
388 | VolumeLabel = mediaRow.VolumeLabel | ||
389 | }); | ||
390 | } | ||
391 | } | ||
454 | } | 392 | } |
455 | } | 393 | } |
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index 9aad3537..6662f8f7 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs | |||
@@ -17,30 +17,38 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
17 | /// </summary> | 17 | /// </summary> |
18 | internal class ProcessUncompressedFilesCommand | 18 | internal class ProcessUncompressedFilesCommand |
19 | { | 19 | { |
20 | public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper, IPathResolver pathResolver) | 20 | public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper, IPathResolver pathResolver, IEnumerable<IFileFacade> fileFacades, string outputPath, bool compressed, bool longNamesInImage, Func<MediaSymbol, string, string, string> resolveMedia) |
21 | { | 21 | { |
22 | this.Section = section; | 22 | this.Section = section; |
23 | this.BackendHelper = backendHelper; | 23 | this.BackendHelper = backendHelper; |
24 | this.PathResolver = pathResolver; | 24 | this.PathResolver = pathResolver; |
25 | |||
26 | this.DatabasePath = outputPath; | ||
27 | this.LayoutDirectory = Path.GetDirectoryName(outputPath); | ||
28 | this.Compressed = compressed; | ||
29 | this.LongNamesInImage = longNamesInImage; | ||
30 | |||
31 | this.FileFacades = fileFacades; | ||
32 | this.ResolveMedia = resolveMedia; | ||
25 | } | 33 | } |
26 | 34 | ||
27 | private IntermediateSection Section { get; } | 35 | private IntermediateSection Section { get; } |
28 | 36 | ||
29 | public IBackendHelper BackendHelper { get; } | 37 | private IBackendHelper BackendHelper { get; } |
30 | 38 | ||
31 | public IPathResolver PathResolver { get; } | 39 | private IPathResolver PathResolver { get; } |
32 | 40 | ||
33 | public string DatabasePath { private get; set; } | 41 | private string DatabasePath { get; } |
34 | 42 | ||
35 | public IEnumerable<IFileFacade> FileFacades { private get; set; } | 43 | private string LayoutDirectory { get; } |
36 | 44 | ||
37 | public string LayoutDirectory { private get; set; } | 45 | private bool Compressed { get; } |
38 | 46 | ||
39 | public bool Compressed { private get; set; } | 47 | private bool LongNamesInImage { get; } |
40 | 48 | ||
41 | public bool LongNamesInImage { private get; set; } | 49 | private IEnumerable<IFileFacade> FileFacades { get; } |
42 | 50 | ||
43 | public Func<MediaSymbol, string, string, string> ResolveMedia { private get; set; } | 51 | private Func<MediaSymbol, string, string, string> ResolveMedia { get; } |
44 | 52 | ||
45 | public IEnumerable<IFileTransfer> FileTransfers { get; private set; } | 53 | public IEnumerable<IFileTransfer> FileTransfers { get; private set; } |
46 | 54 | ||
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs index f85d4842..65043658 100644 --- a/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs +++ b/src/wix/WixToolset.Core/ExtensibilityServices/FileFacade.cs | |||
@@ -167,6 +167,9 @@ namespace WixToolset.Core.ExtensibilityServices | |||
167 | /// <summary> | 167 | /// <summary> |
168 | /// Allows direct access to the underlying FileRow as requried for patching. | 168 | /// Allows direct access to the underlying FileRow as requried for patching. |
169 | /// </summary> | 169 | /// </summary> |
170 | public FileRow GetFileRow() => this.FileRow ?? throw new NotImplementedException(); | 170 | public FileRow GetFileRow() |
171 | { | ||
172 | return this.FileRow ?? throw new NotImplementedException(); | ||
173 | } | ||
171 | } | 174 | } |
172 | } | 175 | } |
diff --git a/src/wix/test/WixToolsetTest.Core.Native/CabinetFixture.cs b/src/wix/test/WixToolsetTest.Core.Native/CabinetFixture.cs index c566339a..e1189549 100644 --- a/src/wix/test/WixToolsetTest.Core.Native/CabinetFixture.cs +++ b/src/wix/test/WixToolsetTest.Core.Native/CabinetFixture.cs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | namespace WixToolsetTest.CoreNative | 3 | namespace WixToolsetTest.CoreNative |
4 | { | 4 | { |
5 | using System; | ||
5 | using System.IO; | 6 | using System.IO; |
6 | using System.Linq; | 7 | using System.Linq; |
7 | using WixBuildTools.TestSupport; | 8 | using WixBuildTools.TestSupport; |
@@ -19,12 +20,45 @@ namespace WixToolsetTest.CoreNative | |||
19 | var intermediateFolder = fs.GetFolder(true); | 20 | var intermediateFolder = fs.GetFolder(true); |
20 | var cabPath = Path.Combine(intermediateFolder, "testout.cab"); | 21 | var cabPath = Path.Combine(intermediateFolder, "testout.cab"); |
21 | 22 | ||
22 | var files = new[] { new CabinetCompressFile(TestData.Get(@"TestData\test.txt"), "test.txt") }; | 23 | var files = new[] { new CabinetCompressFile(TestData.Get(@"TestData", "test.txt"), "test.txt") }; |
23 | 24 | ||
24 | var cabinet = new Cabinet(cabPath); | 25 | var cabinet = new Cabinet(cabPath); |
25 | cabinet.Compress(files, CompressionLevel.Low); | 26 | var created = cabinet.Compress(files, CompressionLevel.Low); |
26 | 27 | ||
27 | Assert.True(File.Exists(cabPath)); | 28 | Assert.True(File.Exists(cabPath)); |
29 | Assert.Equal(new[] | ||
30 | { | ||
31 | "testout.cab, test.txt" | ||
32 | }, created.Select(c => String.Join(", ", c.CabinetName, c.FirstFileToken)).ToArray()); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | [Fact] | ||
37 | public void CanCreateSpannedFileCabinet() | ||
38 | { | ||
39 | using (var fs = new DisposableFileSystem()) | ||
40 | { | ||
41 | var intermediateFolder = fs.GetFolder(true); | ||
42 | |||
43 | // Put more than non-zero bytes in a file sized just under 3MB since there is | ||
44 | // some overhead in cabinets that prevent perfect packing on the megabyte boundary. | ||
45 | var threeMBPath = Path.Combine(intermediateFolder, "_3mb.dat"); | ||
46 | TestData.CreateFile(threeMBPath, (long)(2.9 * 1024 * 1024), fill: true); | ||
47 | |||
48 | var cabPath = Path.Combine(intermediateFolder, "test.cab"); | ||
49 | |||
50 | var files = new[] { new CabinetCompressFile(threeMBPath, Path.GetFileNameWithoutExtension(threeMBPath)) }; | ||
51 | |||
52 | var cabinet = new Cabinet(cabPath); | ||
53 | var created = cabinet.Compress(files, CompressionLevel.None, maxSize: 1); | ||
54 | |||
55 | Assert.True(File.Exists(cabPath)); | ||
56 | Assert.Equal(new[] | ||
57 | { | ||
58 | "test.cab, _3mb", | ||
59 | "testa.cab, _3mb", | ||
60 | "testb.cab, _3mb" | ||
61 | }, created.Select(c => String.Join(", ", c.CabinetName, c.FirstFileToken)).ToArray()); | ||
28 | } | 62 | } |
29 | } | 63 | } |
30 | 64 | ||
@@ -58,8 +92,8 @@ namespace WixToolsetTest.CoreNative | |||
58 | // Compress. | 92 | // Compress. |
59 | { | 93 | { |
60 | var files = new[] { | 94 | var files = new[] { |
61 | new CabinetCompressFile(TestData.Get(@"TestData\test.txt"), "test1.txt"), | 95 | new CabinetCompressFile(TestData.Get(@"TestData", "test.txt"), "test1.txt"), |
62 | new CabinetCompressFile(TestData.Get(@"TestData\test.txt"), "test2.txt"), | 96 | new CabinetCompressFile(TestData.Get(@"TestData", "test.txt"), "test2.txt"), |
63 | }; | 97 | }; |
64 | 98 | ||
65 | var cabinet = new Cabinet(cabinetPath); | 99 | var cabinet = new Cabinet(cabinetPath); |
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiCabinetFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiCabinetFixture.cs new file mode 100644 index 00000000..8bb790d4 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiCabinetFixture.cs | |||
@@ -0,0 +1,256 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolsetTest.CoreIntegration | ||
4 | { | ||
5 | using System; | ||
6 | using System.IO; | ||
7 | using System.Linq; | ||
8 | using WixBuildTools.TestSupport; | ||
9 | using WixToolset.Core.TestPackage; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Data.Symbols; | ||
12 | using WixToolset.Data.WindowsInstaller; | ||
13 | using WixToolset.Data.WindowsInstaller.Rows; | ||
14 | using Xunit; | ||
15 | |||
16 | public class MsiCabinetFixture | ||
17 | { | ||
18 | [Fact] | ||
19 | public void CanBuildSingleFileCompressed() | ||
20 | { | ||
21 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
22 | |||
23 | using (var fs = new DisposableFileSystem()) | ||
24 | { | ||
25 | var intermediateFolder = fs.GetFolder(); | ||
26 | |||
27 | var result = WixRunner.Execute(new[] | ||
28 | { | ||
29 | "build", | ||
30 | Path.Combine(folder, "Package.wxs"), | ||
31 | Path.Combine(folder, "PackageComponents.wxs"), | ||
32 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
33 | "-bindpath", Path.Combine(folder, "data"), | ||
34 | "-intermediateFolder", intermediateFolder, | ||
35 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
36 | }); | ||
37 | |||
38 | result.AssertSuccess(); | ||
39 | |||
40 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
41 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\example.cab"))); | ||
42 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
43 | |||
44 | var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb")); | ||
45 | var section = intermediate.Sections.Single(); | ||
46 | |||
47 | var fileSymbol = section.Symbols.OfType<FileSymbol>().Single(); | ||
48 | WixAssert.StringEqual(Path.Combine(folder, @"data\test.txt"), fileSymbol[FileSymbolFields.Source].AsPath().Path); | ||
49 | WixAssert.StringEqual(@"test.txt", fileSymbol[FileSymbolFields.Source].PreviousValue.AsPath().Path); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | [Fact] | ||
54 | public void CanBuildSingleFileCompressedWithMediaTemplate() | ||
55 | { | ||
56 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
57 | |||
58 | using (var fs = new DisposableFileSystem()) | ||
59 | { | ||
60 | var intermediateFolder = fs.GetFolder(); | ||
61 | |||
62 | var result = WixRunner.Execute(new[] | ||
63 | { | ||
64 | "build", | ||
65 | Path.Combine(folder, "Package.wxs"), | ||
66 | Path.Combine(folder, "PackageComponents.wxs"), | ||
67 | "-d", "MediaTemplateCompressionLevel", | ||
68 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
69 | "-bindpath", Path.Combine(folder, "data"), | ||
70 | "-intermediateFolder", intermediateFolder, | ||
71 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
72 | }); | ||
73 | |||
74 | result.AssertSuccess(); | ||
75 | |||
76 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
77 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\cab1.cab"))); | ||
78 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | [Fact] | ||
83 | public void CanBuildSingleFileCompressedWithMediaTemplateWithLowCompression() | ||
84 | { | ||
85 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
86 | |||
87 | using (var fs = new DisposableFileSystem()) | ||
88 | { | ||
89 | var intermediateFolder = fs.GetFolder(); | ||
90 | |||
91 | var result = WixRunner.Execute(new[] | ||
92 | { | ||
93 | "build", | ||
94 | Path.Combine(folder, "Package.wxs"), | ||
95 | Path.Combine(folder, "PackageComponents.wxs"), | ||
96 | "-d", "MediaTemplateCompressionLevel=low", | ||
97 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
98 | "-bindpath", Path.Combine(folder, "data"), | ||
99 | "-intermediateFolder", intermediateFolder, | ||
100 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
101 | }); | ||
102 | |||
103 | result.AssertSuccess(); | ||
104 | |||
105 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
106 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\low1.cab"))); | ||
107 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | [Fact] | ||
112 | public void CanBuildMultipleFilesCompressed() | ||
113 | { | ||
114 | var folder = TestData.Get(@"TestData\MultiFileCompressed"); | ||
115 | |||
116 | using (var fs = new DisposableFileSystem()) | ||
117 | { | ||
118 | var intermediateFolder = fs.GetFolder(); | ||
119 | |||
120 | var result = WixRunner.Execute(new[] | ||
121 | { | ||
122 | "build", | ||
123 | "-sw1079", // TODO: why does this test need to create a second cab which is empty? | ||
124 | Path.Combine(folder, "Package.wxs"), | ||
125 | Path.Combine(folder, "PackageComponents.wxs"), | ||
126 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
127 | "-bindpath", Path.Combine(folder, "data"), | ||
128 | "-intermediateFolder", intermediateFolder, | ||
129 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
130 | }); | ||
131 | |||
132 | result.AssertSuccess(); | ||
133 | |||
134 | Assert.True(File.Exists(Path.Combine(intermediateFolder, "bin", "test.msi"))); | ||
135 | Assert.True(File.Exists(Path.Combine(intermediateFolder, "bin", "example1.cab"))); | ||
136 | Assert.True(File.Exists(Path.Combine(intermediateFolder, "bin", "example2.cab"))); | ||
137 | Assert.True(File.Exists(Path.Combine(intermediateFolder, "bin", "test.wixpdb"))); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | [Fact] | ||
142 | public void CanBuildMultipleFilesSpanningCabinets() | ||
143 | { | ||
144 | var folder = TestData.Get(@"TestData", "MsiCabinet"); | ||
145 | |||
146 | using (var fs = new DisposableFileSystem()) | ||
147 | { | ||
148 | var baseFolder = fs.GetFolder(); | ||
149 | var dataFolder = Path.Combine(folder, "data"); | ||
150 | var gendataFolder = Path.Combine(baseFolder, "generated-data"); | ||
151 | var cabFolder = Path.Combine(baseFolder, "cab"); | ||
152 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
153 | var binFolder = Path.Combine(baseFolder, "bin"); | ||
154 | var msiPath = Path.Combine(binFolder, "test.msi"); | ||
155 | var wixpdbPath = Path.ChangeExtension(msiPath, "wixpdb"); | ||
156 | |||
157 | TestData.CreateFile(Path.Combine(gendataFolder, "abc.gen"), (long)(25 * 1024 * 1024), fill: true); | ||
158 | TestData.CreateFile(Path.Combine(gendataFolder, "mno.gen"), (long)(45 * 1024 * 1024), fill: true); | ||
159 | TestData.CreateFile(Path.Combine(gendataFolder, "xyz.gen"), (long)(25 * 1024 * 1024), fill: true); | ||
160 | |||
161 | var result = WixRunner.Execute(new[] | ||
162 | { | ||
163 | "build", | ||
164 | Path.Combine(folder, "MultiFileSpanningCabinets.wxs"), | ||
165 | "-bindpath", dataFolder, | ||
166 | "-bindpath", gendataFolder, | ||
167 | "-intermediateFolder", intermediateFolder, | ||
168 | "-cc", cabFolder, | ||
169 | "-o", msiPath | ||
170 | }); | ||
171 | |||
172 | result.AssertSuccess(); | ||
173 | |||
174 | var files = Directory.GetFiles(binFolder, "*.*", SearchOption.AllDirectories).Select(s => s.Substring(binFolder.Length + 1)).OrderBy(s => s).ToArray(); | ||
175 | WixAssert.CompareLineByLine(new[] | ||
176 | { | ||
177 | "cab1.cab", | ||
178 | "cab1a.cab", | ||
179 | "cab2.cab", | ||
180 | "cab3.cab", | ||
181 | "cab3a.cab", | ||
182 | "cab3b.cab", | ||
183 | "cab4.cab", | ||
184 | "cab5.cab", | ||
185 | "cab5a.cab", | ||
186 | "test.msi", | ||
187 | "test.wixpdb" | ||
188 | }, files); | ||
189 | |||
190 | var query = Query.QueryDatabase(msiPath, new[] { "Media", "File" }); | ||
191 | WixAssert.CompareLineByLine(new[] | ||
192 | { | ||
193 | "File:fil2WOk5jeIBsvmL0db5z96JfeEZoU\tfil2WOk5jeIBsvmL0db5z96JfeEZoU\thij.txt\t18\t\t\t512\t3", | ||
194 | "File:filgErUV04C8ZBKWWWA0Zg5Fu_6NyM\tfilgErUV04C8ZBKWWWA0Zg5Fu_6NyM\tmno.gen\t47185920\t\t\t512\t4", | ||
195 | "File:filGqbzmUDXsQhijNpBL2rNX3dtCoo\tfilGqbzmUDXsQhijNpBL2rNX3dtCoo\tced.txt\t18\t\t\t512\t2", | ||
196 | "File:filj2uZN0Q5bCgR2YY6RRg1c9FqylQ\tfilj2uZN0Q5bCgR2YY6RRg1c9FqylQ\ttuv.txt\t18\t\t\t512\t6", | ||
197 | "File:filjEKfcIaBHXFyDRtZciPv4j105jQ\tfiljEKfcIaBHXFyDRtZciPv4j105jQ\tqrs.txt\t18\t\t\t512\t5", | ||
198 | "File:filKv93aSvdbL6M6UutiKdGim1UzcA\tfilKv93aSvdbL6M6UutiKdGim1UzcA\tabc.gen\t26214400\t\t\t512\t1", | ||
199 | "File:fillf1kS2G7fDKwbyrQIIw1OXPi.eY\tfillf1kS2G7fDKwbyrQIIw1OXPi.eY\txyz.gen\t26214400\t\t\t512\t7", | ||
200 | "Media:1\t1\t\tcab1.cab\t\t", | ||
201 | "Media:2\t1\t\tcab1a.cab\t\t", | ||
202 | "Media:3\t3\t\tcab2.cab\t\t", | ||
203 | "Media:4\t4\t\tcab3.cab\t\t", | ||
204 | "Media:5\t4\t\tcab3a.cab\t\t", | ||
205 | "Media:6\t4\t\tcab3b.cab\t\t", | ||
206 | "Media:7\t6\t\tcab4.cab\t\t", | ||
207 | "Media:8\t7\t\tcab5.cab\t\t", | ||
208 | "Media:9\t7\t\tcab5a.cab\t\t", | ||
209 | }, query); | ||
210 | |||
211 | var wixpdb = WixOutput.Read(wixpdbPath); | ||
212 | |||
213 | var data = WindowsInstallerData.Load(wixpdb); | ||
214 | var fileRows = data.Tables["File"].Rows.Cast<FileRow>().OrderBy(f => f.Sequence); | ||
215 | var mediaRows = data.Tables["Media"].Rows.Cast<MediaRow>().OrderBy(f => f.DiskId); | ||
216 | |||
217 | var intermediate = Intermediate.Load(wixpdb); | ||
218 | var section = intermediate.Sections.Single(); | ||
219 | var fileSymbols = section.Symbols.OfType<FileSymbol>().OrderBy(f => f.Sequence); | ||
220 | var mediaSymbols = section.Symbols.OfType<MediaSymbol>().OrderBy(f => f.DiskId); | ||
221 | |||
222 | var expectedFiles = new[] | ||
223 | { | ||
224 | "filKv93aSvdbL6M6UutiKdGim1UzcA abc.gen 1 1", | ||
225 | "filGqbzmUDXsQhijNpBL2rNX3dtCoo ced.txt 2 3", | ||
226 | "fil2WOk5jeIBsvmL0db5z96JfeEZoU hij.txt 3 3", | ||
227 | "filgErUV04C8ZBKWWWA0Zg5Fu_6NyM mno.gen 4 4", | ||
228 | "filjEKfcIaBHXFyDRtZciPv4j105jQ qrs.txt 5 7", | ||
229 | "filj2uZN0Q5bCgR2YY6RRg1c9FqylQ tuv.txt 6 7", | ||
230 | "fillf1kS2G7fDKwbyrQIIw1OXPi.eY xyz.gen 7 8", | ||
231 | }; | ||
232 | |||
233 | var expectedMedia = new[] | ||
234 | { | ||
235 | "1 cab1.cab 1", | ||
236 | "2 cab1a.cab 1", | ||
237 | "3 cab2.cab 3", | ||
238 | "4 cab3.cab 4", | ||
239 | "5 cab3a.cab 4", | ||
240 | "6 cab3b.cab 4", | ||
241 | "7 cab4.cab 6", | ||
242 | "8 cab5.cab 7", | ||
243 | "9 cab5a.cab 7", | ||
244 | }; | ||
245 | |||
246 | WixAssert.CompareLineByLine(expectedFiles, fileRows.Select(r => String.Join(" ", r.File, r.FileName, r.Sequence, r.DiskId)).ToArray()); | ||
247 | |||
248 | WixAssert.CompareLineByLine(expectedFiles, fileSymbols.Select(s => String.Join(" ", s.Id.Id, s.Name, s.Sequence, s.DiskId)).ToArray()); | ||
249 | |||
250 | WixAssert.CompareLineByLine(expectedMedia, mediaRows.Select(r => String.Join(" ", r.DiskId, r.Cabinet, r.LastSequence)).ToArray()); | ||
251 | |||
252 | WixAssert.CompareLineByLine(expectedMedia, mediaSymbols.Select(s => String.Join(" ", s.DiskId, s.Cabinet, s.LastSequence)).ToArray()); | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | } | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs index 0c01fa5e..60e9653a 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiFixture.cs | |||
@@ -58,130 +58,7 @@ namespace WixToolsetTest.CoreIntegration | |||
58 | } | 58 | } |
59 | 59 | ||
60 | [Fact] | 60 | [Fact] |
61 | public void CanBuildSingleFileCompressed() | 61 | public void CannotBuildMissingFile() |
62 | { | ||
63 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
64 | |||
65 | using (var fs = new DisposableFileSystem()) | ||
66 | { | ||
67 | var intermediateFolder = fs.GetFolder(); | ||
68 | |||
69 | var result = WixRunner.Execute(new[] | ||
70 | { | ||
71 | "build", | ||
72 | Path.Combine(folder, "Package.wxs"), | ||
73 | Path.Combine(folder, "PackageComponents.wxs"), | ||
74 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
75 | "-bindpath", Path.Combine(folder, "data"), | ||
76 | "-intermediateFolder", intermediateFolder, | ||
77 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
78 | }); | ||
79 | |||
80 | result.AssertSuccess(); | ||
81 | |||
82 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
83 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\example.cab"))); | ||
84 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
85 | |||
86 | var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb")); | ||
87 | var section = intermediate.Sections.Single(); | ||
88 | |||
89 | var fileSymbol = section.Symbols.OfType<FileSymbol>().Single(); | ||
90 | WixAssert.StringEqual(Path.Combine(folder, @"data\test.txt"), fileSymbol[FileSymbolFields.Source].AsPath().Path); | ||
91 | WixAssert.StringEqual(@"test.txt", fileSymbol[FileSymbolFields.Source].PreviousValue.AsPath().Path); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | [Fact] | ||
96 | public void CanBuildSingleFileCompressedWithMediaTemplate() | ||
97 | { | ||
98 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
99 | |||
100 | using (var fs = new DisposableFileSystem()) | ||
101 | { | ||
102 | var intermediateFolder = fs.GetFolder(); | ||
103 | |||
104 | var result = WixRunner.Execute(new[] | ||
105 | { | ||
106 | "build", | ||
107 | Path.Combine(folder, "Package.wxs"), | ||
108 | Path.Combine(folder, "PackageComponents.wxs"), | ||
109 | "-d", "MediaTemplateCompressionLevel", | ||
110 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
111 | "-bindpath", Path.Combine(folder, "data"), | ||
112 | "-intermediateFolder", intermediateFolder, | ||
113 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
114 | }); | ||
115 | |||
116 | result.AssertSuccess(); | ||
117 | |||
118 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
119 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\cab1.cab"))); | ||
120 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | [Fact] | ||
125 | public void CanBuildSingleFileCompressedWithMediaTemplateWithLowCompression() | ||
126 | { | ||
127 | var folder = TestData.Get(@"TestData\SingleFileCompressed"); | ||
128 | |||
129 | using (var fs = new DisposableFileSystem()) | ||
130 | { | ||
131 | var intermediateFolder = fs.GetFolder(); | ||
132 | |||
133 | var result = WixRunner.Execute(new[] | ||
134 | { | ||
135 | "build", | ||
136 | Path.Combine(folder, "Package.wxs"), | ||
137 | Path.Combine(folder, "PackageComponents.wxs"), | ||
138 | "-d", "MediaTemplateCompressionLevel=low", | ||
139 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
140 | "-bindpath", Path.Combine(folder, "data"), | ||
141 | "-intermediateFolder", intermediateFolder, | ||
142 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
143 | }); | ||
144 | |||
145 | result.AssertSuccess(); | ||
146 | |||
147 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
148 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\low1.cab"))); | ||
149 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | [Fact] | ||
154 | public void CanBuildMultipleFilesCompressed() | ||
155 | { | ||
156 | var folder = TestData.Get(@"TestData\MultiFileCompressed"); | ||
157 | |||
158 | using (var fs = new DisposableFileSystem()) | ||
159 | { | ||
160 | var intermediateFolder = fs.GetFolder(); | ||
161 | |||
162 | var result = WixRunner.Execute(new[] | ||
163 | { | ||
164 | "build", | ||
165 | "-sw1079", // TODO: why does this test need to create a second cab which is empty? | ||
166 | Path.Combine(folder, "Package.wxs"), | ||
167 | Path.Combine(folder, "PackageComponents.wxs"), | ||
168 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
169 | "-bindpath", Path.Combine(folder, "data"), | ||
170 | "-intermediateFolder", intermediateFolder, | ||
171 | "-o", Path.Combine(intermediateFolder, @"bin\test.msi") | ||
172 | }); | ||
173 | |||
174 | result.AssertSuccess(); | ||
175 | |||
176 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); | ||
177 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\example1.cab"))); | ||
178 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\example2.cab"))); | ||
179 | Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | [Fact] | ||
184 | public void CanFailBuildMissingFile() | ||
185 | { | 62 | { |
186 | var folder = TestData.Get(@"TestData\SingleFile"); | 63 | var folder = TestData.Get(@"TestData\SingleFile"); |
187 | 64 | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/MultiFileSpanningCabinets.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/MultiFileSpanningCabinets.wxs new file mode 100644 index 00000000..c7ac4699 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/MultiFileSpanningCabinets.wxs | |||
@@ -0,0 +1,38 @@ | |||
1 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
2 | <Package Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a" Compressed="yes" InstallerVersion="200" Scope="perMachine"> | ||
3 | <MajorUpgrade DowngradeErrorMessage="Downgrade not allowed" /> | ||
4 | |||
5 | <MediaTemplate CabinetTemplate="cab{0}.cab" CompressionLevel="none" MaximumUncompressedMediaSize="20" MaximumCabinetSizeForLargeFileSplitting="20" /> | ||
6 | |||
7 | <Feature Id="ProductFeature"> | ||
8 | <!-- The name of the files here are important to control order of the generated media and cabinets. --> | ||
9 | <Component Directory="INSTALLFOLDER"> | ||
10 | <File Source="abc.gen" /> | ||
11 | </Component> | ||
12 | <Component Directory="INSTALLFOLDER"> | ||
13 | <File Source="ced.txt" /> | ||
14 | </Component> | ||
15 | <Component Directory="INSTALLFOLDER"> | ||
16 | <File Source="hij.txt" /> | ||
17 | </Component> | ||
18 | <Component Directory="INSTALLFOLDER"> | ||
19 | <File Source="mno.gen" /> | ||
20 | </Component> | ||
21 | <Component Directory="INSTALLFOLDER"> | ||
22 | <File Source="qrs.txt" /> | ||
23 | </Component> | ||
24 | <Component Directory="INSTALLFOLDER"> | ||
25 | <File Source="tuv.txt" /> | ||
26 | </Component> | ||
27 | <Component Directory="INSTALLFOLDER"> | ||
28 | <File Source="xyz.gen" /> | ||
29 | </Component> | ||
30 | </Feature> | ||
31 | </Package> | ||
32 | |||
33 | <Fragment> | ||
34 | <StandardDirectory Id="ProgramFilesFolder"> | ||
35 | <Directory Id="INSTALLFOLDER" Name="MsiPackage" /> | ||
36 | </StandardDirectory> | ||
37 | </Fragment> | ||
38 | </Wix> | ||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/ced.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/ced.txt new file mode 100644 index 00000000..6f52fa6a --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/ced.txt | |||
@@ -0,0 +1 @@ | |||
This is ced.txt. | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/hij.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/hij.txt new file mode 100644 index 00000000..ca53b0ae --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/hij.txt | |||
@@ -0,0 +1 @@ | |||
This is hij.txt. | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/qrs.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/qrs.txt new file mode 100644 index 00000000..8e33dd06 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/qrs.txt | |||
@@ -0,0 +1 @@ | |||
This is qrs.txt. | |||
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/tuv.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/tuv.txt new file mode 100644 index 00000000..3c258bd2 --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiCabinet/data/tuv.txt | |||
@@ -0,0 +1 @@ | |||
This is tuv.txt. | |||
diff --git a/src/wix/wixnative/smartcab.cpp b/src/wix/wixnative/smartcab.cpp index b5f20736..9c441a34 100644 --- a/src/wix/wixnative/smartcab.cpp +++ b/src/wix/wixnative/smartcab.cpp | |||
@@ -2,8 +2,8 @@ | |||
2 | 2 | ||
3 | #include "precomp.h" | 3 | #include "precomp.h" |
4 | 4 | ||
5 | static HRESULT CompressFiles(__in HANDLE hCab); | 5 | static HRESULT CompressFiles(__in HANDLE hCab, __inout_z LPWSTR* psczFirstFileToken); |
6 | static void __stdcall CabNamesCallback(__in_z LPWSTR wzFirstCabName, __in_z LPWSTR wzNewCabName, __in_z LPWSTR wzFileToken); | 6 | static void __stdcall CabNamesCallback(__in_z LPCWSTR wzFirstCabName, __in_z LPCWSTR wzNewCabName, __in_z LPCWSTR wzFileToken); |
7 | 7 | ||
8 | 8 | ||
9 | HRESULT SmartCabCommand( | 9 | HRESULT SmartCabCommand( |
@@ -20,6 +20,7 @@ HRESULT SmartCabCommand( | |||
20 | UINT uiMaxThresh = 0; | 20 | UINT uiMaxThresh = 0; |
21 | COMPRESSION_TYPE ct = COMPRESSION_TYPE_NONE; | 21 | COMPRESSION_TYPE ct = COMPRESSION_TYPE_NONE; |
22 | HANDLE hCab = NULL; | 22 | HANDLE hCab = NULL; |
23 | LPWSTR sczFirstFileToken = NULL; | ||
23 | 24 | ||
24 | if (argc < 1) | 25 | if (argc < 1) |
25 | { | 26 | { |
@@ -71,16 +72,22 @@ HRESULT SmartCabCommand( | |||
71 | hr = WixNativeReadStdinPreamble(); | 72 | hr = WixNativeReadStdinPreamble(); |
72 | ExitOnFailure(hr, "failed to read stdin preamble before smartcabbing"); | 73 | ExitOnFailure(hr, "failed to read stdin preamble before smartcabbing"); |
73 | 74 | ||
74 | hr = CompressFiles(hCab); | 75 | hr = CompressFiles(hCab, &sczFirstFileToken); |
75 | ExitOnFailure(hr, "failed to compress files into cabinet: %ls", sczCabPath); | 76 | ExitOnFailure(hr, "failed to compress files into cabinet: %ls", sczCabPath); |
77 | |||
78 | CabNamesCallback(wzCabName, wzCabName, sczFirstFileToken); | ||
79 | } | ||
80 | else | ||
81 | { | ||
82 | CabNamesCallback(wzCabName, wzCabName, L""); | ||
76 | } | 83 | } |
77 | 84 | ||
78 | hr = CabCFinish(hCab, CabNamesCallback); | 85 | hr = CabCFinish(hCab, CabNamesCallback); |
79 | hCab = NULL; // once finish is called, the handle is invalid. | 86 | hCab = NULL; // once finish is called, the handle is invalid. |
80 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to compress cabinet: %ls", sczCabPath); | 87 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to compress cabinet: %ls", sczCabPath); |
81 | 88 | ||
82 | |||
83 | LExit: | 89 | LExit: |
90 | ReleaseStr(sczFirstFileToken); | ||
84 | if (hCab) | 91 | if (hCab) |
85 | { | 92 | { |
86 | CabCCancel(hCab); | 93 | CabCCancel(hCab); |
@@ -93,7 +100,8 @@ LExit: | |||
93 | 100 | ||
94 | 101 | ||
95 | static HRESULT CompressFiles( | 102 | static HRESULT CompressFiles( |
96 | __in HANDLE hCab | 103 | __in HANDLE hCab, |
104 | __inout_z LPWSTR* psczFirstFileToken | ||
97 | ) | 105 | ) |
98 | { | 106 | { |
99 | HRESULT hr = S_OK; | 107 | HRESULT hr = S_OK; |
@@ -138,6 +146,12 @@ static HRESULT CompressFiles( | |||
138 | pHashInfo = &hashInfo; | 146 | pHashInfo = &hashInfo; |
139 | } | 147 | } |
140 | 148 | ||
149 | if (psczFirstFileToken && !*psczFirstFileToken) | ||
150 | { | ||
151 | hr = StrAllocString(psczFirstFileToken, wzToken, 0); | ||
152 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to allocate first file token: %ls", wzToken); | ||
153 | } | ||
154 | |||
141 | hr = CabCAddFile(wzFilePath, wzToken, pHashInfo, hCab); | 155 | hr = CabCAddFile(wzFilePath, wzToken, pHashInfo, hCab); |
142 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to add file: %ls", wzFilePath); | 156 | ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to add file: %ls", wzFilePath); |
143 | 157 | ||
@@ -157,9 +171,9 @@ LExit: | |||
157 | // Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" | 171 | // Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" |
158 | // Third argument is the file token of the first file present in the splitting cabinet | 172 | // Third argument is the file token of the first file present in the splitting cabinet |
159 | static void __stdcall CabNamesCallback( | 173 | static void __stdcall CabNamesCallback( |
160 | __in_z LPWSTR wzFirstCabName, | 174 | __in_z LPCWSTR wzFirstCabName, |
161 | __in_z LPWSTR wzNewCabName, | 175 | __in_z LPCWSTR wzNewCabName, |
162 | __in_z LPWSTR wzFileToken | 176 | __in_z LPCWSTR wzFileToken |
163 | ) | 177 | ) |
164 | { | 178 | { |
165 | ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken); | 179 | ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken); |