From d3d3649a68cb1fa589fdd987a6690dbd5d671f0d Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 17 Sep 2017 15:35:20 -0700 Subject: Initial code commit --- .../Bind/Databases/AssignMediaCommand.cs | 314 +++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs (limited to 'src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs') diff --git a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs b/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs new file mode 100644 index 00000000..5e2650e9 --- /dev/null +++ b/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs @@ -0,0 +1,314 @@ +// 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. + +namespace WixToolset.Bind.Databases +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using WixToolset.Data; + using WixToolset.Data.Rows; + + /// + /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. + /// + public class AssignMediaCommand : ICommand + { + public AssignMediaCommand() + { + this.CabinetNameTemplate = "Cab{0}.cab"; + } + + public Output Output { private get; set; } + + public bool FilesCompressed { private get; set; } + + public string CabinetNameTemplate { private get; set; } + + public IEnumerable FileFacades { private get; set; } + + public TableDefinitionCollection TableDefinitions { private get; set; } + + /// + /// Gets cabinets with their file rows. + /// + public Dictionary> FileFacadesByCabinetMedia { get; private set; } + + /// + /// Get media rows. + /// + public RowDictionary MediaRows { get; private set; } + + /// + /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no. + /// This contains all the files when Package element is marked with compression=no + /// + public IEnumerable UncompressedFileFacades { get; private set; } + + public void Execute() + { + Dictionary> filesByCabinetMedia = new Dictionary>(); + + RowDictionary mediaRows = new RowDictionary(); + + List uncompressedFiles = new List(); + + MediaRow mergeModuleMediaRow = null; + Table mediaTable = this.Output.Tables["Media"]; + Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; + + // If both tables are authored, it is an error. + if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) + { + throw new WixException(WixErrors.MediaTableCollision(null)); + } + + // When building merge module, all the files go to "#MergeModule.CABinet". + if (OutputType.Module == this.Output.Type) + { + Table mergeModuleMediaTable = new Table(null, this.TableDefinitions["Media"]); + mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null); + mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; + + filesByCabinetMedia.Add(mergeModuleMediaRow, new List()); + } + + if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) + { + this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); + } + else + { + this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); + } + + this.FileFacadesByCabinetMedia = new Dictionary>(); + + foreach (var mediaRowWithFiles in filesByCabinetMedia) + { + this.FileFacadesByCabinetMedia.Add(mediaRowWithFiles.Key, mediaRowWithFiles.Value); + } + + this.MediaRows = mediaRows; + + this.UncompressedFileFacades = uncompressedFiles; + } + + /// + /// Assign files to cabinets based on MediaTemplate authoring. + /// + /// FileRowCollection + private void AutoAssignFiles(Table mediaTable, IEnumerable fileFacades, Dictionary> filesByCabinetMedia, RowDictionary mediaRows, List uncompressedFiles) + { + const int MaxCabIndex = 999; + + ulong currentPreCabSize = 0; + ulong maxPreCabSizeInBytes; + int maxPreCabSizeInMB = 0; + int currentCabIndex = 0; + + MediaRow currentMediaRow = null; + + Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; + + // Auto assign files to cabinets based on maximum uncompressed media size + mediaTable.Rows.Clear(); + WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; + + if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) + { + this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; + } + + string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); + + try + { + // Override authored mums value if environment variable is authored. + if (!String.IsNullOrEmpty(mumsString)) + { + maxPreCabSizeInMB = Int32.Parse(mumsString); + } + else + { + maxPreCabSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; + } + + maxPreCabSizeInBytes = (ulong)maxPreCabSizeInMB * 1024 * 1024; + } + catch (FormatException) + { + throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); + } + catch (OverflowException) + { + throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCabSizeInMB)); + } + + foreach (FileFacade facade in this.FileFacades) + { + // When building a product, if the current file is not to be compressed or if + // the package set not to be compressed, don't cab it. + if (OutputType.Product == this.Output.Type && + (YesNoType.No == facade.File.Compressed || + (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) + { + uncompressedFiles.Add(facade); + continue; + } + + if (currentCabIndex == MaxCabIndex) + { + // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. + List cabinetFiles = filesByCabinetMedia[currentMediaRow]; + facade.WixFile.DiskId = currentCabIndex; + cabinetFiles.Add(facade); + continue; + } + + // Update current cab size. + currentPreCabSize += (ulong)facade.File.FileSize; + + if (currentPreCabSize > maxPreCabSizeInBytes) + { + // Overflow due to current file + currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); + mediaRows.Add(currentMediaRow); + filesByCabinetMedia.Add(currentMediaRow, new List()); + + List cabinetFileRows = filesByCabinetMedia[currentMediaRow]; + facade.WixFile.DiskId = currentCabIndex; + cabinetFileRows.Add(facade); + // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize + currentPreCabSize = (ulong)facade.File.FileSize; + } + else + { + // File fits in the current cab. + if (currentMediaRow == null) + { + // Create new cab and MediaRow + currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); + mediaRows.Add(currentMediaRow); + filesByCabinetMedia.Add(currentMediaRow, new List()); + } + + // Associate current file with current cab. + List cabinetFiles = filesByCabinetMedia[currentMediaRow]; + facade.WixFile.DiskId = currentCabIndex; + cabinetFiles.Add(facade); + } + } + + // If there are uncompressed files and no MediaRow, create a default one. + if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) + { + MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); + defaultMediaRow.DiskId = 1; + mediaRows.Add(defaultMediaRow); + } + } + + /// + /// Assign files to cabinets based on Media authoring. + /// + /// + /// + /// + private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable fileFacades, Dictionary> filesByCabinetMedia, RowDictionary mediaRows, List uncompressedFiles) + { + if (OutputType.Module != this.Output.Type) + { + if (null != mediaTable) + { + Dictionary cabinetMediaRows = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + foreach (MediaRow mediaRow in mediaTable.Rows) + { + // If the Media row has a cabinet, make sure it is unique across all Media rows. + if (!String.IsNullOrEmpty(mediaRow.Cabinet)) + { + MediaRow existingRow; + if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) + { + Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); + Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); + } + else + { + cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow); + } + } + + mediaRows.Add(mediaRow); + } + } + + foreach (MediaRow mediaRow in mediaRows.Values) + { + if (null != mediaRow.Cabinet) + { + filesByCabinetMedia.Add(mediaRow, new List()); + } + } + } + + foreach (FileFacade facade in fileFacades) + { + if (OutputType.Module == this.Output.Type) + { + filesByCabinetMedia[mergeModuleMediaRow].Add(facade); + } + else + { + MediaRow mediaRow; + if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow)) + { + Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId)); + continue; + } + + // When building a product, if the current file is not to be compressed or if + // the package set not to be compressed, don't cab it. + if (OutputType.Product == this.Output.Type && + (YesNoType.No == facade.File.Compressed || + (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) + { + uncompressedFiles.Add(facade); + } + else // file is marked compressed. + { + List cabinetFiles; + if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles)) + { + cabinetFiles.Add(facade); + } + else + { + Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId)); + } + } + } + } + } + + /// + /// Adds a row to the media table with cab name template filled in. + /// + /// + /// + /// + private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) + { + MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); + currentMediaRow.DiskId = cabIndex; + currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); + + Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); + WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); + row.DiskId = cabIndex; + row.CompressionLevel = mediaTemplateRow.CompressionLevel; + + return currentMediaRow; + } + } +} -- cgit v1.2.3-55-g6feb