diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs')
| -rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs new file mode 100644 index 00000000..23c481b7 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | |||
| @@ -0,0 +1,314 @@ | |||
| 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.Databases | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Globalization; | ||
| 8 | using WixToolset.Core.Bind; | ||
| 9 | using WixToolset.Data; | ||
| 10 | using WixToolset.Data.Rows; | ||
| 11 | |||
| 12 | /// <summary> | ||
| 13 | /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. | ||
| 14 | /// </summary> | ||
| 15 | public class AssignMediaCommand | ||
| 16 | { | ||
| 17 | public AssignMediaCommand() | ||
| 18 | { | ||
| 19 | this.CabinetNameTemplate = "Cab{0}.cab"; | ||
| 20 | } | ||
| 21 | |||
| 22 | public Output Output { private get; set; } | ||
| 23 | |||
| 24 | public bool FilesCompressed { private get; set; } | ||
| 25 | |||
| 26 | public string CabinetNameTemplate { private get; set; } | ||
| 27 | |||
| 28 | public IEnumerable<FileFacade> FileFacades { private get; set; } | ||
| 29 | |||
| 30 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
| 31 | |||
| 32 | /// <summary> | ||
| 33 | /// Gets cabinets with their file rows. | ||
| 34 | /// </summary> | ||
| 35 | public Dictionary<MediaRow, IEnumerable<FileFacade>> FileFacadesByCabinetMedia { get; private set; } | ||
| 36 | |||
| 37 | /// <summary> | ||
| 38 | /// Get media rows. | ||
| 39 | /// </summary> | ||
| 40 | public RowDictionary<MediaRow> MediaRows { get; private set; } | ||
| 41 | |||
| 42 | /// <summary> | ||
| 43 | /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no. | ||
| 44 | /// This contains all the files when Package element is marked with compression=no | ||
| 45 | /// </summary> | ||
| 46 | public IEnumerable<FileFacade> UncompressedFileFacades { get; private set; } | ||
| 47 | |||
| 48 | public void Execute() | ||
| 49 | { | ||
| 50 | Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia = new Dictionary<MediaRow, List<FileFacade>>(); | ||
| 51 | |||
| 52 | RowDictionary<MediaRow> mediaRows = new RowDictionary<MediaRow>(); | ||
| 53 | |||
| 54 | List<FileFacade> uncompressedFiles = new List<FileFacade>(); | ||
| 55 | |||
| 56 | MediaRow mergeModuleMediaRow = null; | ||
| 57 | Table mediaTable = this.Output.Tables["Media"]; | ||
| 58 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
| 59 | |||
| 60 | // If both tables are authored, it is an error. | ||
| 61 | if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) | ||
| 62 | { | ||
| 63 | throw new WixException(WixErrors.MediaTableCollision(null)); | ||
| 64 | } | ||
| 65 | |||
| 66 | // When building merge module, all the files go to "#MergeModule.CABinet". | ||
| 67 | if (OutputType.Module == this.Output.Type) | ||
| 68 | { | ||
| 69 | Table mergeModuleMediaTable = new Table(null, this.TableDefinitions["Media"]); | ||
| 70 | mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null); | ||
| 71 | mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; | ||
| 72 | |||
| 73 | filesByCabinetMedia.Add(mergeModuleMediaRow, new List<FileFacade>()); | ||
| 74 | } | ||
| 75 | |||
| 76 | if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) | ||
| 77 | { | ||
| 78 | this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); | ||
| 79 | } | ||
| 80 | else | ||
| 81 | { | ||
| 82 | this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); | ||
| 83 | } | ||
| 84 | |||
| 85 | this.FileFacadesByCabinetMedia = new Dictionary<MediaRow, IEnumerable<FileFacade>>(); | ||
| 86 | |||
| 87 | foreach (var mediaRowWithFiles in filesByCabinetMedia) | ||
| 88 | { | ||
| 89 | this.FileFacadesByCabinetMedia.Add(mediaRowWithFiles.Key, mediaRowWithFiles.Value); | ||
| 90 | } | ||
| 91 | |||
| 92 | this.MediaRows = mediaRows; | ||
| 93 | |||
| 94 | this.UncompressedFileFacades = uncompressedFiles; | ||
| 95 | } | ||
| 96 | |||
| 97 | /// <summary> | ||
| 98 | /// Assign files to cabinets based on MediaTemplate authoring. | ||
| 99 | /// </summary> | ||
| 100 | /// <param name="fileFacades">FileRowCollection</param> | ||
| 101 | private void AutoAssignFiles(Table mediaTable, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) | ||
| 102 | { | ||
| 103 | const int MaxCabIndex = 999; | ||
| 104 | |||
| 105 | ulong currentPreCabSize = 0; | ||
| 106 | ulong maxPreCabSizeInBytes; | ||
| 107 | int maxPreCabSizeInMB = 0; | ||
| 108 | int currentCabIndex = 0; | ||
| 109 | |||
| 110 | MediaRow currentMediaRow = null; | ||
| 111 | |||
| 112 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
| 113 | |||
| 114 | // Auto assign files to cabinets based on maximum uncompressed media size | ||
| 115 | mediaTable.Rows.Clear(); | ||
| 116 | WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; | ||
| 117 | |||
| 118 | if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) | ||
| 119 | { | ||
| 120 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; | ||
| 121 | } | ||
| 122 | |||
| 123 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
| 124 | |||
| 125 | try | ||
| 126 | { | ||
| 127 | // Override authored mums value if environment variable is authored. | ||
| 128 | if (!String.IsNullOrEmpty(mumsString)) | ||
| 129 | { | ||
| 130 | maxPreCabSizeInMB = Int32.Parse(mumsString); | ||
| 131 | } | ||
| 132 | else | ||
| 133 | { | ||
| 134 | maxPreCabSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; | ||
| 135 | } | ||
| 136 | |||
| 137 | maxPreCabSizeInBytes = (ulong)maxPreCabSizeInMB * 1024 * 1024; | ||
| 138 | } | ||
| 139 | catch (FormatException) | ||
| 140 | { | ||
| 141 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); | ||
| 142 | } | ||
| 143 | catch (OverflowException) | ||
| 144 | { | ||
| 145 | throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCabSizeInMB)); | ||
| 146 | } | ||
| 147 | |||
| 148 | foreach (FileFacade facade in this.FileFacades) | ||
| 149 | { | ||
| 150 | // When building a product, if the current file is not to be compressed or if | ||
| 151 | // the package set not to be compressed, don't cab it. | ||
| 152 | if (OutputType.Product == this.Output.Type && | ||
| 153 | (YesNoType.No == facade.File.Compressed || | ||
| 154 | (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) | ||
| 155 | { | ||
| 156 | uncompressedFiles.Add(facade); | ||
| 157 | continue; | ||
| 158 | } | ||
| 159 | |||
| 160 | if (currentCabIndex == MaxCabIndex) | ||
| 161 | { | ||
| 162 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. | ||
| 163 | List<FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; | ||
| 164 | facade.WixFile.DiskId = currentCabIndex; | ||
| 165 | cabinetFiles.Add(facade); | ||
| 166 | continue; | ||
| 167 | } | ||
| 168 | |||
| 169 | // Update current cab size. | ||
| 170 | currentPreCabSize += (ulong)facade.File.FileSize; | ||
| 171 | |||
| 172 | if (currentPreCabSize > maxPreCabSizeInBytes) | ||
| 173 | { | ||
| 174 | // Overflow due to current file | ||
| 175 | currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); | ||
| 176 | mediaRows.Add(currentMediaRow); | ||
| 177 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | ||
| 178 | |||
| 179 | List<FileFacade> cabinetFileRows = filesByCabinetMedia[currentMediaRow]; | ||
| 180 | facade.WixFile.DiskId = currentCabIndex; | ||
| 181 | cabinetFileRows.Add(facade); | ||
| 182 | // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize | ||
| 183 | currentPreCabSize = (ulong)facade.File.FileSize; | ||
| 184 | } | ||
| 185 | else | ||
| 186 | { | ||
| 187 | // File fits in the current cab. | ||
| 188 | if (currentMediaRow == null) | ||
| 189 | { | ||
| 190 | // Create new cab and MediaRow | ||
| 191 | currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); | ||
| 192 | mediaRows.Add(currentMediaRow); | ||
| 193 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | ||
| 194 | } | ||
| 195 | |||
| 196 | // Associate current file with current cab. | ||
| 197 | List<FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; | ||
| 198 | facade.WixFile.DiskId = currentCabIndex; | ||
| 199 | cabinetFiles.Add(facade); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | // If there are uncompressed files and no MediaRow, create a default one. | ||
| 204 | if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) | ||
| 205 | { | ||
| 206 | MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
| 207 | defaultMediaRow.DiskId = 1; | ||
| 208 | mediaRows.Add(defaultMediaRow); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /// <summary> | ||
| 213 | /// Assign files to cabinets based on Media authoring. | ||
| 214 | /// </summary> | ||
| 215 | /// <param name="mediaTable"></param> | ||
| 216 | /// <param name="mergeModuleMediaRow"></param> | ||
| 217 | /// <param name="fileFacades"></param> | ||
| 218 | private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) | ||
| 219 | { | ||
| 220 | if (OutputType.Module != this.Output.Type) | ||
| 221 | { | ||
| 222 | if (null != mediaTable) | ||
| 223 | { | ||
| 224 | Dictionary<string, MediaRow> cabinetMediaRows = new Dictionary<string, MediaRow>(StringComparer.InvariantCultureIgnoreCase); | ||
| 225 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
| 226 | { | ||
| 227 | // If the Media row has a cabinet, make sure it is unique across all Media rows. | ||
| 228 | if (!String.IsNullOrEmpty(mediaRow.Cabinet)) | ||
| 229 | { | ||
| 230 | MediaRow existingRow; | ||
| 231 | if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) | ||
| 232 | { | ||
| 233 | Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); | ||
| 234 | Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); | ||
| 235 | } | ||
| 236 | else | ||
| 237 | { | ||
| 238 | cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | mediaRows.Add(mediaRow); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | foreach (MediaRow mediaRow in mediaRows.Values) | ||
| 247 | { | ||
| 248 | if (null != mediaRow.Cabinet) | ||
| 249 | { | ||
| 250 | filesByCabinetMedia.Add(mediaRow, new List<FileFacade>()); | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | foreach (FileFacade facade in fileFacades) | ||
| 256 | { | ||
| 257 | if (OutputType.Module == this.Output.Type) | ||
| 258 | { | ||
| 259 | filesByCabinetMedia[mergeModuleMediaRow].Add(facade); | ||
| 260 | } | ||
| 261 | else | ||
| 262 | { | ||
| 263 | MediaRow mediaRow; | ||
| 264 | if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow)) | ||
| 265 | { | ||
| 266 | Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId)); | ||
| 267 | continue; | ||
| 268 | } | ||
| 269 | |||
| 270 | // When building a product, if the current file is not to be compressed or if | ||
| 271 | // the package set not to be compressed, don't cab it. | ||
| 272 | if (OutputType.Product == this.Output.Type && | ||
| 273 | (YesNoType.No == facade.File.Compressed || | ||
| 274 | (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) | ||
| 275 | { | ||
| 276 | uncompressedFiles.Add(facade); | ||
| 277 | } | ||
| 278 | else // file is marked compressed. | ||
| 279 | { | ||
| 280 | List<FileFacade> cabinetFiles; | ||
| 281 | if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles)) | ||
| 282 | { | ||
| 283 | cabinetFiles.Add(facade); | ||
| 284 | } | ||
| 285 | else | ||
| 286 | { | ||
| 287 | Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId)); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /// <summary> | ||
| 295 | /// Adds a row to the media table with cab name template filled in. | ||
| 296 | /// </summary> | ||
| 297 | /// <param name="mediaTable"></param> | ||
| 298 | /// <param name="cabIndex"></param> | ||
| 299 | /// <returns></returns> | ||
| 300 | private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) | ||
| 301 | { | ||
| 302 | MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); | ||
| 303 | currentMediaRow.DiskId = cabIndex; | ||
| 304 | currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); | ||
| 305 | |||
| 306 | Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); | ||
| 307 | WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); | ||
| 308 | row.DiskId = cabIndex; | ||
| 309 | row.CompressionLevel = mediaTemplateRow.CompressionLevel; | ||
| 310 | |||
| 311 | return currentMediaRow; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
