diff options
| author | Rob Mensching <rob@firegiant.com> | 2017-10-14 16:12:07 -0700 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2017-10-14 16:12:07 -0700 |
| commit | dbde9e7104b907bbbaea17e21247d8cafc8b3a4c (patch) | |
| tree | 0f5fbbb6fe12c6b2e5e622a0e18ce4c5b4eb2b96 /src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | |
| parent | fbf986eb97f68396797a89fc7d40dec07b775440 (diff) | |
| download | wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.gz wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.tar.bz2 wix-dbde9e7104b907bbbaea17e21247d8cafc8b3a4c.zip | |
Massive refactoring to introduce the concept of IBackend
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs')
| -rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs new file mode 100644 index 00000000..02015744 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | |||
| @@ -0,0 +1,499 @@ | |||
| 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 System.IO; | ||
| 9 | using System.Linq; | ||
| 10 | using System.Runtime.InteropServices; | ||
| 11 | using System.Threading; | ||
| 12 | using WixToolset.Core.Bind; | ||
| 13 | using WixToolset.Core.WindowsInstaller.Bind; | ||
| 14 | using WixToolset.Data; | ||
| 15 | using WixToolset.Data.Bind; | ||
| 16 | using WixToolset.Data.Rows; | ||
| 17 | using WixToolset.Extensibility; | ||
| 18 | |||
| 19 | /// <summary> | ||
| 20 | /// Creates cabinet files. | ||
| 21 | /// </summary> | ||
| 22 | internal class CreateCabinetsCommand | ||
| 23 | { | ||
| 24 | public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB | ||
| 25 | public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | ||
| 26 | |||
| 27 | private List<FileTransfer> fileTransfers; | ||
| 28 | |||
| 29 | private FileSplitCabNamesCallback newCabNamesCallBack; | ||
| 30 | |||
| 31 | private Dictionary<string, string> lastCabinetAddedToMediaTable; // Key is First Cabinet Name, Value is Last Cabinet Added in the Split Sequence | ||
| 32 | |||
| 33 | public CreateCabinetsCommand() | ||
| 34 | { | ||
| 35 | this.fileTransfers = new List<FileTransfer>(); | ||
| 36 | |||
| 37 | this.newCabNamesCallBack = this.NewCabNamesCallBack; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// <summary> | ||
| 41 | /// Sets the number of threads to use for cabinet creation. | ||
| 42 | /// </summary> | ||
| 43 | public int CabbingThreadCount { private get; set; } | ||
| 44 | |||
| 45 | public string CabCachePath { private get; set; } | ||
| 46 | |||
| 47 | public string TempFilesLocation { private get; set; } | ||
| 48 | |||
| 49 | /// <summary> | ||
| 50 | /// Sets the default compression level to use for cabinets | ||
| 51 | /// that don't have their compression level explicitly set. | ||
| 52 | /// </summary> | ||
| 53 | public CompressionLevel DefaultCompressionLevel { private get; set; } | ||
| 54 | |||
| 55 | public IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { private get; set; } | ||
| 56 | |||
| 57 | public Output Output { private get; set; } | ||
| 58 | |||
| 59 | public string LayoutDirectory { private get; set; } | ||
| 60 | |||
| 61 | public bool Compressed { private get; set; } | ||
| 62 | |||
| 63 | public Dictionary<MediaRow, IEnumerable<FileFacade>> FileRowsByCabinet { private get; set; } | ||
| 64 | |||
| 65 | public Func<MediaRow, string, string, string> ResolveMedia { private get; set; } | ||
| 66 | |||
| 67 | public TableDefinitionCollection TableDefinitions { private get; set; } | ||
| 68 | |||
| 69 | public Table WixMediaTable { private get; set; } | ||
| 70 | |||
| 71 | public IEnumerable<FileTransfer> FileTransfers => this.fileTransfers; | ||
| 72 | |||
| 73 | /// <param name="output">Output to generate image for.</param> | ||
| 74 | /// <param name="fileTransfers">Array of files to be transfered.</param> | ||
| 75 | /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> | ||
| 76 | /// <param name="compressed">Flag if source image should be compressed.</param> | ||
| 77 | /// <returns>The uncompressed file rows.</returns> | ||
| 78 | public void Execute() | ||
| 79 | { | ||
| 80 | RowDictionary<WixMediaRow> wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable); | ||
| 81 | |||
| 82 | this.lastCabinetAddedToMediaTable = new Dictionary<string, string>(); | ||
| 83 | |||
| 84 | this.SetCabbingThreadCount(); | ||
| 85 | |||
| 86 | // Send Binder object to Facilitate NewCabNamesCallBack Callback | ||
| 87 | CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); | ||
| 88 | |||
| 89 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
| 90 | int MaximumCabinetSizeForLargeFileSplitting; | ||
| 91 | int MaximumUncompressedMediaSize; | ||
| 92 | this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); | ||
| 93 | cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; | ||
| 94 | cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; | ||
| 95 | |||
| 96 | foreach (var entry in this.FileRowsByCabinet) | ||
| 97 | { | ||
| 98 | MediaRow mediaRow = entry.Key; | ||
| 99 | IEnumerable<FileFacade> files = entry.Value; | ||
| 100 | CompressionLevel compressionLevel = this.DefaultCompressionLevel; | ||
| 101 | |||
| 102 | WixMediaRow wixMediaRow = null; | ||
| 103 | string mediaLayoutFolder = null; | ||
| 104 | |||
| 105 | if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) | ||
| 106 | { | ||
| 107 | mediaLayoutFolder = wixMediaRow.Layout; | ||
| 108 | |||
| 109 | if (wixMediaRow.CompressionLevel.HasValue) | ||
| 110 | { | ||
| 111 | compressionLevel = wixMediaRow.CompressionLevel.Value; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | string cabinetDir = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); | ||
| 116 | |||
| 117 | CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaRow, compressionLevel, files, this.fileTransfers); | ||
| 118 | if (null != cabinetWorkItem) | ||
| 119 | { | ||
| 120 | cabinetBuilder.Enqueue(cabinetWorkItem); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | // stop processing if an error previously occurred | ||
| 125 | if (Messaging.Instance.EncounteredError) | ||
| 126 | { | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | |||
| 130 | // create queued cabinets with multiple threads | ||
| 131 | cabinetBuilder.CreateQueuedCabinets(); | ||
| 132 | if (Messaging.Instance.EncounteredError) | ||
| 133 | { | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// <summary> | ||
| 139 | /// Sets the thead count to the number of processors if the current thread count is set to 0. | ||
| 140 | /// </summary> | ||
| 141 | /// <remarks>The thread count value must be greater than 0 otherwise and exception will be thrown.</remarks> | ||
| 142 | private void SetCabbingThreadCount() | ||
| 143 | { | ||
| 144 | // default the number of cabbing threads to the number of processors if it wasn't specified | ||
| 145 | if (0 == this.CabbingThreadCount) | ||
| 146 | { | ||
| 147 | string numberOfProcessors = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); | ||
| 148 | |||
| 149 | try | ||
| 150 | { | ||
| 151 | if (null != numberOfProcessors) | ||
| 152 | { | ||
| 153 | this.CabbingThreadCount = Convert.ToInt32(numberOfProcessors, CultureInfo.InvariantCulture.NumberFormat); | ||
| 154 | |||
| 155 | if (0 >= this.CabbingThreadCount) | ||
| 156 | { | ||
| 157 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | else // default to 1 if the environment variable is not set | ||
| 161 | { | ||
| 162 | this.CabbingThreadCount = 1; | ||
| 163 | } | ||
| 164 | |||
| 165 | Messaging.Instance.OnMessage(WixVerboses.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); | ||
| 166 | } | ||
| 167 | catch (ArgumentException) | ||
| 168 | { | ||
| 169 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
| 170 | } | ||
| 171 | catch (FormatException) | ||
| 172 | { | ||
| 173 | throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | |||
| 179 | /// <summary> | ||
| 180 | /// Creates a work item to create a cabinet. | ||
| 181 | /// </summary> | ||
| 182 | /// <param name="output">Output for the current database.</param> | ||
| 183 | /// <param name="cabinetDir">Directory to create cabinet in.</param> | ||
| 184 | /// <param name="mediaRow">MediaRow containing information about the cabinet.</param> | ||
| 185 | /// <param name="fileFacades">Collection of files in this cabinet.</param> | ||
| 186 | /// <param name="fileTransfers">Array of files to be transfered.</param> | ||
| 187 | /// <returns>created CabinetWorkItem object</returns> | ||
| 188 | private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable<FileFacade> fileFacades, List<FileTransfer> fileTransfers) | ||
| 189 | { | ||
| 190 | CabinetWorkItem cabinetWorkItem = null; | ||
| 191 | string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); | ||
| 192 | |||
| 193 | // check for an empty cabinet | ||
| 194 | if (!fileFacades.Any()) | ||
| 195 | { | ||
| 196 | string cabinetName = mediaRow.Cabinet; | ||
| 197 | |||
| 198 | // remove the leading '#' from the embedded cabinet name to make the warning easier to understand | ||
| 199 | if (cabinetName.StartsWith("#", StringComparison.Ordinal)) | ||
| 200 | { | ||
| 201 | cabinetName = cabinetName.Substring(1); | ||
| 202 | } | ||
| 203 | |||
| 204 | // If building a patch, remind them to run -p for torch. | ||
| 205 | if (OutputType.Patch == output.Type) | ||
| 206 | { | ||
| 207 | Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true)); | ||
| 208 | } | ||
| 209 | else | ||
| 210 | { | ||
| 211 | Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName)); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | var cabinetResolver = new CabinetResolver(this.CabCachePath, this.BackendExtensions); | ||
| 216 | |||
| 217 | ResolvedCabinet resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades); | ||
| 218 | |||
| 219 | // create a cabinet work item if it's not being skipped | ||
| 220 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) | ||
| 221 | { | ||
| 222 | int maxThreshold = 0; // default to the threshold for best smartcabbing (makes smallest cabinet). | ||
| 223 | |||
| 224 | cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold, compressionLevel/*, this.FileManager*/); | ||
| 225 | } | ||
| 226 | else // reuse the cabinet from the cabinet cache. | ||
| 227 | { | ||
| 228 | Messaging.Instance.OnMessage(WixVerboses.ReusingCabCache(mediaRow.SourceLineNumbers, mediaRow.Cabinet, resolvedCabinet.Path)); | ||
| 229 | |||
| 230 | try | ||
| 231 | { | ||
| 232 | // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The | ||
| 233 | // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that | ||
| 234 | // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from | ||
| 235 | // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) | ||
| 236 | // causing the project to look like it perpetually needs a rebuild until all of the reused | ||
| 237 | // cabinets get newer timestamps. | ||
| 238 | File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); | ||
| 239 | } | ||
| 240 | catch (Exception e) | ||
| 241 | { | ||
| 242 | Messaging.Instance.OnMessage(WixWarnings.CannotUpdateCabCache(mediaRow.SourceLineNumbers, resolvedCabinet.Path, e.Message)); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) | ||
| 247 | { | ||
| 248 | Table streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); | ||
| 249 | |||
| 250 | Row streamRow = streamsTable.CreateRow(mediaRow.SourceLineNumbers); | ||
| 251 | streamRow[0] = mediaRow.Cabinet.Substring(1); | ||
| 252 | streamRow[1] = resolvedCabinet.Path; | ||
| 253 | } | ||
| 254 | else | ||
| 255 | { | ||
| 256 | string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); | ||
| 257 | FileTransfer transfer; | ||
| 258 | if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer)) | ||
| 259 | { | ||
| 260 | transfer.Built = true; | ||
| 261 | fileTransfers.Add(transfer); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | return cabinetWorkItem; | ||
| 266 | } | ||
| 267 | |||
| 268 | //private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades) | ||
| 269 | //{ | ||
| 270 | // ResolvedCabinet resolved = null; | ||
| 271 | |||
| 272 | // List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList(); | ||
| 273 | |||
| 274 | // foreach (var extension in this.BackendExtensions) | ||
| 275 | // { | ||
| 276 | // resolved = extension.ResolveCabinet(cabinetPath, filesWithPath); | ||
| 277 | // if (null != resolved) | ||
| 278 | // { | ||
| 279 | // break; | ||
| 280 | // } | ||
| 281 | // } | ||
| 282 | |||
| 283 | // return resolved; | ||
| 284 | //} | ||
| 285 | |||
| 286 | /// <summary> | ||
| 287 | /// Delegate for Cabinet Split Callback | ||
| 288 | /// </summary> | ||
| 289 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] | ||
| 290 | internal delegate void FileSplitCabNamesCallback([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken); | ||
| 291 | |||
| 292 | /// <summary> | ||
| 293 | /// Call back to Add File Transfer for new Cab and add new Cab to Media table | ||
| 294 | /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe | ||
| 295 | /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored | ||
| 296 | /// </summary> | ||
| 297 | /// <param name="firstCabName">The name of splitting cabinet without extention e.g. "cab1".</param> | ||
| 298 | /// <param name="newCabName">The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"</param> | ||
| 299 | /// <param name="fileToken">The file token of the first file present in the splitting cabinet</param> | ||
| 300 | internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken) | ||
| 301 | { | ||
| 302 | // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads | ||
| 303 | Mutex mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); | ||
| 304 | try | ||
| 305 | { | ||
| 306 | if (!mutex.WaitOne(0, false)) // Check if you can get the lock | ||
| 307 | { | ||
| 308 | // Cound not get the Lock | ||
| 309 | Messaging.Instance.OnMessage(WixVerboses.CabinetsSplitInParallel()); | ||
| 310 | mutex.WaitOne(); // Wait on other thread | ||
| 311 | } | ||
| 312 | |||
| 313 | string firstCabinetName = firstCabName + ".cab"; | ||
| 314 | string newCabinetName = newCabName; | ||
| 315 | bool transferAdded = false; // Used for Error Handling | ||
| 316 | |||
| 317 | // Create File Transfer for new Cabinet using transfer of Base Cabinet | ||
| 318 | foreach (FileTransfer transfer in this.FileTransfers) | ||
| 319 | { | ||
| 320 | if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) | ||
| 321 | { | ||
| 322 | string newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); | ||
| 323 | string newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); | ||
| 324 | |||
| 325 | FileTransfer newTransfer; | ||
| 326 | if (FileTransfer.TryCreate(newCabSourcePath, newCabTargetPath, transfer.Move, "Cabinet", transfer.SourceLineNumbers, out newTransfer)) | ||
| 327 | { | ||
| 328 | newTransfer.Built = true; | ||
| 329 | this.fileTransfers.Add(newTransfer); | ||
| 330 | transferAdded = true; | ||
| 331 | break; | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | // Check if File Transfer was added | ||
| 337 | if (!transferAdded) | ||
| 338 | { | ||
| 339 | throw new WixException(WixErrors.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); | ||
| 340 | } | ||
| 341 | |||
| 342 | // Add the new Cabinets to media table using LastSequence of Base Cabinet | ||
| 343 | Table mediaTable = this.Output.Tables["Media"]; | ||
| 344 | Table wixFileTable = this.Output.Tables["WixFile"]; | ||
| 345 | int diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain | ||
| 346 | int lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain | ||
| 347 | bool lastSplitCabinetFound = false; // Used for Error Handling | ||
| 348 | |||
| 349 | string lastCabinetOfThisSequence = String.Empty; | ||
| 350 | // Get the Value of Last Cabinet Added in this split Sequence from Dictionary | ||
| 351 | if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) | ||
| 352 | { | ||
| 353 | // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence | ||
| 354 | lastCabinetOfThisSequence = firstCabinetName; | ||
| 355 | } | ||
| 356 | |||
| 357 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
| 358 | { | ||
| 359 | // Get details for the Last Cabinet Added in this Split Sequence | ||
| 360 | if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 361 | { | ||
| 362 | lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; | ||
| 363 | diskIDForLastSplitCabAdded = mediaRow.DiskId; | ||
| 364 | lastSplitCabinetFound = true; | ||
| 365 | } | ||
| 366 | |||
| 367 | // Check for Name Collision for the new Cabinet added | ||
| 368 | if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 369 | { | ||
| 370 | // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row | ||
| 371 | throw new WixException(WixErrors.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | // Check if the last Split Cabinet was found in the Media Table | ||
| 376 | if (!lastSplitCabinetFound) | ||
| 377 | { | ||
| 378 | throw new WixException(WixErrors.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); | ||
| 379 | } | ||
| 380 | |||
| 381 | // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort | ||
| 382 | // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with | ||
| 383 | // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction | ||
| 384 | MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
| 385 | newMediaRow.Cabinet = newCabinetName; | ||
| 386 | newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion | ||
| 387 | newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; | ||
| 388 | |||
| 389 | // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique | ||
| 390 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
| 391 | { | ||
| 392 | // Check if this row comes after inserted row and it is not the new cabinet inserted row | ||
| 393 | if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 394 | { | ||
| 395 | mediaRow.DiskId++; // Increment DiskID | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | // Now Increment DiskID for All files Rows so that they refer to the right Media Row | ||
| 400 | foreach (WixFileRow wixFileRow in wixFileTable.Rows) | ||
| 401 | { | ||
| 402 | // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet | ||
| 403 | // This check will work as we have only one large file in every splitting cabinet | ||
| 404 | // If we want to support splitting cabinet with more large files we need to update this code | ||
| 405 | if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) | ||
| 406 | { | ||
| 407 | wixFileRow.DiskId++; // Increment DiskID | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback | ||
| 412 | this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; | ||
| 413 | |||
| 414 | mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails | ||
| 415 | } | ||
| 416 | finally | ||
| 417 | { | ||
| 418 | // Releasing the Mutex here | ||
| 419 | mutex.ReleaseMutex(); | ||
| 420 | } | ||
| 421 | } | ||
| 422 | |||
| 423 | |||
| 424 | /// <summary> | ||
| 425 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides | ||
| 426 | /// </summary> | ||
| 427 | /// <param name="output">Output to generate image for.</param> | ||
| 428 | /// <param name="fileRows">The indexed file rows.</param> | ||
| 429 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) | ||
| 430 | { | ||
| 431 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | ||
| 432 | string mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
| 433 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
| 434 | int maxCabSizeForLargeFileInMB = 0; | ||
| 435 | int maxPreCompressedSizeInMB = 0; | ||
| 436 | ulong testOverFlow = 0; | ||
| 437 | |||
| 438 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
| 439 | Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; | ||
| 440 | if (mediaTemplateTable != null) | ||
| 441 | { | ||
| 442 | WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; | ||
| 443 | |||
| 444 | // Get the Value for Max Cab Size for File Splitting | ||
| 445 | try | ||
| 446 | { | ||
| 447 | // Override authored mcslfs value if environment variable is authored. | ||
| 448 | if (!String.IsNullOrEmpty(mcslfsString)) | ||
| 449 | { | ||
| 450 | maxCabSizeForLargeFileInMB = Int32.Parse(mcslfsString); | ||
| 451 | } | ||
| 452 | else | ||
| 453 | { | ||
| 454 | maxCabSizeForLargeFileInMB = mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting; | ||
| 455 | } | ||
| 456 | testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; | ||
| 457 | } | ||
| 458 | catch (FormatException) | ||
| 459 | { | ||
| 460 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MCSLFS", mcslfsString)); | ||
| 461 | } | ||
| 462 | catch (OverflowException) | ||
| 463 | { | ||
| 464 | throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, MaxValueOfMaxCabSizeForLargeFileSplitting)); | ||
| 465 | } | ||
| 466 | |||
| 467 | try | ||
| 468 | { | ||
| 469 | // Override authored mums value if environment variable is authored. | ||
| 470 | if (!String.IsNullOrEmpty(mumsString)) | ||
| 471 | { | ||
| 472 | maxPreCompressedSizeInMB = Int32.Parse(mumsString); | ||
| 473 | } | ||
| 474 | else | ||
| 475 | { | ||
| 476 | maxPreCompressedSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; | ||
| 477 | } | ||
| 478 | testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; | ||
| 479 | } | ||
| 480 | catch (FormatException) | ||
| 481 | { | ||
| 482 | throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); | ||
| 483 | } | ||
| 484 | catch (OverflowException) | ||
| 485 | { | ||
| 486 | throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCompressedSizeInMB)); | ||
| 487 | } | ||
| 488 | |||
| 489 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; | ||
| 490 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; | ||
| 491 | } | ||
| 492 | else | ||
| 493 | { | ||
| 494 | maxCabSizeForLargeFileSplitting = 0; | ||
| 495 | maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | } | ||
| 499 | } | ||
