Beispiel #1
0
        /// <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()
        {
            var wixMediaTuples = this.WixMediaTuples.ToDictionary(t => t.DiskId_);

            this.lastCabinetAddedToMediaTable = new Dictionary <string, string>();

            this.SetCabbingThreadCount();

            // Send Binder object to Facilitate NewCabNamesCallBack Callback
            CabinetBuilder cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack));

            // Supply Compile MediaTemplate Attributes to Cabinet Builder
            this.GetMediaTemplateAttributes(out var MaximumCabinetSizeForLargeFileSplitting, out var MaximumUncompressedMediaSize);
            cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting;
            cabinetBuilder.MaximumUncompressedMediaSize            = MaximumUncompressedMediaSize;

            foreach (var entry in this.FileRowsByCabinet)
            {
                var mediaTuple = entry.Key;
                IEnumerable <FileFacade> files            = entry.Value;
                CompressionLevel         compressionLevel = this.DefaultCompressionLevel ?? CompressionLevel.Medium;

                string mediaLayoutFolder = null;

                if (wixMediaTuples.TryGetValue(mediaTuple.DiskId, out var wixMediaRow))
                {
                    mediaLayoutFolder = wixMediaRow.Layout;

                    if (wixMediaRow.CompressionLevel.HasValue)
                    {
                        compressionLevel = wixMediaRow.CompressionLevel.Value;
                    }
                }

                string cabinetDir = this.ResolveMedia(mediaTuple, mediaLayoutFolder, this.LayoutDirectory);

                CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaTuple, compressionLevel, files, this.fileTransfers);
                if (null != cabinetWorkItem)
                {
                    cabinetBuilder.Enqueue(cabinetWorkItem);
                }
            }

            // stop processing if an error previously occurred
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            // create queued cabinets with multiple threads
            cabinetBuilder.CreateQueuedCabinets();
            if (this.Messaging.EncounteredError)
            {
                return;
            }
        }
Beispiel #2
0
        /// <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)
        {
            this.Messaging.Write(VerboseMessages.CreateCabinet(cabinetWorkItem.CabinetFile));

            int   maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting
            ulong maxPreCompressedSizeInBytes = 0;

            if (this.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 = this.MaximumCabinetSizeForLargeFileSplitting;
                        }
                    }
                }
            }

            // create the cabinet file
            var    cabinetPath      = Path.GetFullPath(cabinetWorkItem.CabinetFile);
            string cabinetFileName  = Path.GetFileName(cabinetWorkItem.CabinetFile);
            string cabinetDirectory = Path.GetDirectoryName(cabinetWorkItem.CabinetFile);

            var files = cabinetWorkItem.FileFacades
                        .Select(facade => facade.Hash == null ?
                                new CabinetCompressFile(facade.WixFile.Source.Path, facade.File.File) :
                                new CabinetCompressFile(facade.WixFile.Source.Path, facade.File.File, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4))
                        .ToList();

            var cabinetCompressionLevel = (CabinetCompressionLevel)cabinetWorkItem.CompressionLevel;

            var cab = new Cabinet(cabinetPath);

            cab.Compress(files, cabinetCompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold);

            // TODO: Handle newCabNamesCallBackAddress from compression.
        }
        /// <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)
        {
            this.Messaging.Write(VerboseMessages.CreateCabinet(cabinetWorkItem.CabinetFile));

            var   maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting
            ulong maxPreCompressedSizeInBytes = 0;

            if (this.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)this.MaximumUncompressedMediaSize * 1024 * 1024;

                    var facade = cabinetWorkItem.FileFacades.First();

                    // If the file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting
                    if ((ulong)facade.FileSize >= maxPreCompressedSizeInBytes)
                    {
                        maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting;
                    }
                }
            }

            // create the cabinet file
            var cabinetPath = Path.GetFullPath(cabinetWorkItem.CabinetFile);

            var files = cabinetWorkItem.FileFacades
                        .OrderBy(f => f.Sequence)
                        .Select(facade => facade.Hash == null ?
                                new CabinetCompressFile(facade.SourcePath, facade.Id + cabinetWorkItem.ModularizationSuffix) :
                                new CabinetCompressFile(facade.SourcePath, facade.Id + cabinetWorkItem.ModularizationSuffix, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4))
                        .ToList();

            var cab = new Cabinet(cabinetPath);

            cab.Compress(files, cabinetWorkItem.CompressionLevel, maxCabinetSize, cabinetWorkItem.MaxThreshold);

            // TODO: Handle newCabNamesCallBackAddress from compression.
        }
Beispiel #4
0
        /// <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, MediaTuple 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)
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true));
                }
                else
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName));
                }
            }

            var cabinetResolver = new CabinetResolver(this.CabCachePath, this.BackendExtensions);

            ResolvedCabinet resolvedCabinet = cabinetResolver.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.
            {
                this.Messaging.Write(VerboseMessages.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)
                {
                    this.Messaging.Write(WarningMessages.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);
                if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out var transfer))
                {
                    transfer.Built = true;
                    fileTransfers.Add(transfer);
                }
            }

            return(cabinetWorkItem);
        }
Beispiel #5
0
 /// <summary>
 /// Enqueues a CabinetWorkItem to the queue.
 /// </summary>
 /// <param name="cabinetWorkItem">cabinet work item</param>
 public void Enqueue(CabinetWorkItem cabinetWorkItem) => this.cabinetWorkItems.Enqueue(cabinetWorkItem);
Beispiel #6
0
        /// <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="mediaSymbol">Media symbol containing information about the cabinet.</param>
        /// <param name="fileFacades">Collection of files in this cabinet.</param>
        /// <returns>created CabinetWorkItem object</returns>
        private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData output, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable <FileFacade> fileFacades)
        {
            CabinetWorkItem cabinetWorkItem  = null;
            var             tempCabinetFileX = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet);

            // check for an empty cabinet
            if (!fileFacades.Any())
            {
                // Remove the leading '#' from the embedded cabinet name to make the warning easier to understand
                var cabinetName = mediaSymbol.Cabinet.TrimStart('#');

                // If building a patch, remind them to run -p for torch.
                if (OutputType.Patch == output.Type)
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, true));
                }
                else
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName));
                }
            }

            var cabinetResolver = new CabinetResolver(this.ServiceProvider, this.CabCachePath, this.BackendExtensions);

            var resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades);

            // create a cabinet work item if it's not being skipped
            if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption)
            {
                // Default to the threshold for best smartcabbing (makes smallest cabinet).
                cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold: 0, compressionLevel, this.ModularizationSuffix /*, this.FileManager*/);
            }
            else // reuse the cabinet from the cabinet cache.
            {
                this.Messaging.Write(VerboseMessages.ReusingCabCache(mediaSymbol.SourceLineNumbers, mediaSymbol.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)
                {
                    this.Messaging.Write(WarningMessages.CannotUpdateCabCache(mediaSymbol.SourceLineNumbers, resolvedCabinet.Path, e.Message));
                }
            }

            var trackResolvedCabinet = this.BackendHelper.TrackFile(resolvedCabinet.Path, TrackedFileType.Intermediate, mediaSymbol.SourceLineNumbers);

            this.trackedFiles.Add(trackResolvedCabinet);

            if (mediaSymbol.Cabinet.StartsWith("#", StringComparison.Ordinal))
            {
                var streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]);

                var streamRow = streamsTable.CreateRow(mediaSymbol.SourceLineNumbers);
                streamRow[0] = mediaSymbol.Cabinet.Substring(1);
                streamRow[1] = resolvedCabinet.Path;
            }
            else
            {
                var trackDestination = this.BackendHelper.TrackFile(Path.Combine(cabinetDir, mediaSymbol.Cabinet), TrackedFileType.Final, mediaSymbol.SourceLineNumbers);
                this.trackedFiles.Add(trackDestination);

                var transfer = this.BackendHelper.CreateFileTransfer(resolvedCabinet.Path, trackDestination.Path, resolvedCabinet.BuildOption == CabinetBuildOption.BuildAndMove, mediaSymbol.SourceLineNumbers);
                this.fileTransfers.Add(transfer);
            }

            return(cabinetWorkItem);
        }