/// <param name="output">Output to generate image for.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> /// <param name="compressed">Flag if source image should be compressed.</param> /// <returns>The uncompressed file rows.</returns> public void Execute() { RowDictionary <WixMediaRow> wixMediaRows = new RowDictionary <WixMediaRow>(this.WixMediaTable); this.lastCabinetAddedToMediaTable = new Dictionary <string, string>(); this.SetCabbingThreadCount(); // Send Binder object to Facilitate NewCabNamesCallBack Callback CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); // Supply Compile MediaTemplate Attributes to Cabinet Builder int MaximumCabinetSizeForLargeFileSplitting; int MaximumUncompressedMediaSize; this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; foreach (var entry in this.FileRowsByCabinet) { MediaRow mediaRow = entry.Key; IEnumerable <FileFacade> files = entry.Value; CompressionLevel compressionLevel = this.DefaultCompressionLevel; WixMediaRow wixMediaRow = null; string mediaLayoutFolder = null; if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) { mediaLayoutFolder = wixMediaRow.Layout; if (wixMediaRow.CompressionLevel.HasValue) { compressionLevel = wixMediaRow.CompressionLevel.Value; } } string cabinetDir = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaRow, compressionLevel, files, this.fileTransfers); if (null != cabinetWorkItem) { cabinetBuilder.Enqueue(cabinetWorkItem); } } // stop processing if an error previously occurred if (Messaging.Instance.EncounteredError) { return; } // create queued cabinets with multiple threads cabinetBuilder.CreateQueuedCabinets(); if (Messaging.Instance.EncounteredError) { return; } }
/// <summary> /// Creates a cabinet using the wixcab.dll interop layer. /// </summary> /// <param name="cabinetWorkItem">CabinetWorkItem containing information about the cabinet to create.</param> private void CreateCabinet(CabinetWorkItem cabinetWorkItem) { Messaging.Instance.OnMessage(WixVerboses.CreateCabinet(cabinetWorkItem.CabinetFile)); int maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting ulong maxPreCompressedSizeInBytes = 0; if (MaximumCabinetSizeForLargeFileSplitting != 0) { // User Specified Max Cab Size for File Splitting, So Check if this cabinet has a single file larger than MaximumUncompressedFileSize // If a file is larger than MaximumUncompressedFileSize, then the cabinet containing it will have only this file if (1 == cabinetWorkItem.FileFacades.Count()) { // Cabinet has Single File, Check if this is Large File than needs Splitting into Multiple cabs // Get the Value for Max Uncompressed Media Size maxPreCompressedSizeInBytes = (ulong)MaximumUncompressedMediaSize * 1024 * 1024; foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row { if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) { // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting maxCabinetSize = MaximumCabinetSizeForLargeFileSplitting; } } } } // create the cabinet file string cabinetFileName = Path.GetFileName(cabinetWorkItem.CabinetFile); string cabinetDirectory = Path.GetDirectoryName(cabinetWorkItem.CabinetFile); using (WixCreateCab cab = new WixCreateCab(cabinetFileName, cabinetDirectory, cabinetWorkItem.FileFacades.Count(), maxCabinetSize, cabinetWorkItem.MaxThreshold, cabinetWorkItem.CompressionLevel)) { foreach (FileFacade facade in cabinetWorkItem.FileFacades) { cab.AddFile(facade); } cab.Complete(newCabNamesCallBackAddress); } }
/// <summary> /// Creates a work item to create a cabinet. /// </summary> /// <param name="output">Output for the current database.</param> /// <param name="cabinetDir">Directory to create cabinet in.</param> /// <param name="mediaRow">MediaRow containing information about the cabinet.</param> /// <param name="fileFacades">Collection of files in this cabinet.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <returns>created CabinetWorkItem object</returns> private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable <FileFacade> fileFacades, List <FileTransfer> fileTransfers) { CabinetWorkItem cabinetWorkItem = null; string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); // check for an empty cabinet if (!fileFacades.Any()) { string cabinetName = mediaRow.Cabinet; // remove the leading '#' from the embedded cabinet name to make the warning easier to understand if (cabinetName.StartsWith("#", StringComparison.Ordinal)) { cabinetName = cabinetName.Substring(1); } // If building a patch, remind them to run -p for torch. if (OutputType.Patch == output.Type) { Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true)); } else { Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName)); } } ResolvedCabinet resolvedCabinet = this.ResolveCabinet(tempCabinetFileX, fileFacades); // create a cabinet work item if it's not being skipped if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) { int maxThreshold = 0; // default to the threshold for best smartcabbing (makes smallest cabinet). cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold, compressionLevel /*, this.FileManager*/); } else // reuse the cabinet from the cabinet cache. { Messaging.Instance.OnMessage(WixVerboses.ReusingCabCache(mediaRow.SourceLineNumbers, mediaRow.Cabinet, resolvedCabinet.Path)); try { // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) // causing the project to look like it perpetually needs a rebuild until all of the reused // cabinets get newer timestamps. File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); } catch (Exception e) { Messaging.Instance.OnMessage(WixWarnings.CannotUpdateCabCache(mediaRow.SourceLineNumbers, resolvedCabinet.Path, e.Message)); } } if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) { Table streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); Row streamRow = streamsTable.CreateRow(mediaRow.SourceLineNumbers); streamRow[0] = mediaRow.Cabinet.Substring(1); streamRow[1] = resolvedCabinet.Path; } else { string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); FileTransfer transfer; if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer)) { transfer.Built = true; fileTransfers.Add(transfer); } } return(cabinetWorkItem); }
/// <summary> /// Enqueues a CabinetWorkItem to the queue. /// </summary> /// <param name="cabinetWorkItem">cabinet work item</param> public void Enqueue(CabinetWorkItem cabinetWorkItem) { this.cabinetWorkItems.Enqueue(cabinetWorkItem); }