diff options
Diffstat (limited to 'src')
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); |
