diff options
Diffstat (limited to 'src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs')
| -rw-r--r-- | src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | 400 |
1 files changed, 169 insertions, 231 deletions
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index 2516a6fa..31198837 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs | |||
| @@ -4,13 +4,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Globalization; | ||
| 8 | using System.IO; | 7 | using System.IO; |
| 9 | using System.Linq; | 8 | using System.Linq; |
| 10 | using System.Runtime.InteropServices; | ||
| 11 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 12 | using WixToolset.Data.Symbols; | 10 | using WixToolset.Data.Symbols; |
| 13 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 14 | using WixToolset.Extensibility; | 13 | using WixToolset.Extensibility; |
| 15 | using WixToolset.Extensibility.Data; | 14 | using WixToolset.Extensibility.Data; |
| 16 | using WixToolset.Extensibility.Services; | 15 | using WixToolset.Extensibility.Services; |
| @@ -20,70 +19,63 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 20 | /// </summary> | 19 | /// </summary> |
| 21 | internal class CreateCabinetsCommand | 20 | internal class CreateCabinetsCommand |
| 22 | { | 21 | { |
| 23 | public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB | 22 | public const int DefaultMaximumUncompressedMediaSize = 200; // Default value is 200 MB |
| 24 | public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) | 23 | public const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) |
| 25 | 24 | ||
| 25 | private readonly CabinetResolver cabinetResolver; | ||
| 26 | private readonly List<IFileTransfer> fileTransfers; | 26 | private readonly List<IFileTransfer> fileTransfers; |
| 27 | |||
| 28 | private readonly List<ITrackedFile> trackedFiles; | 27 | private readonly List<ITrackedFile> trackedFiles; |
| 29 | 28 | ||
| 30 | private readonly FileSplitCabNamesCallback newCabNamesCallBack; | 29 | public CreateCabinetsCommand(IServiceProvider serviceProvider, IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions, IntermediateSection section, string cabCachePath, int cabbingThreadCount, string outputPath, string intermediateFolder, CompressionLevel? defaultCompressionLevel, bool compressed, string modularizationSuffix, Dictionary<MediaSymbol, IEnumerable<IFileFacade>> filesByCabinetMedia, WindowsInstallerData data, TableDefinitionCollection tableDefinitions, Func<MediaSymbol, string, string, string> resolveMedia) |
| 31 | |||
| 32 | private Dictionary<string, string> lastCabinetAddedToMediaTable; // Key is First Cabinet Name, Value is Last Cabinet Added in the Split Sequence | ||
| 33 | |||
| 34 | public CreateCabinetsCommand(IServiceProvider serviceProvider, IBackendHelper backendHelper, WixMediaTemplateSymbol mediaTemplate) | ||
| 35 | { | 30 | { |
| 36 | this.fileTransfers = new List<IFileTransfer>(); | 31 | this.Messaging = messaging; |
| 37 | 32 | ||
| 38 | this.trackedFiles = new List<ITrackedFile>(); | 33 | this.BackendHelper = backendHelper; |
| 39 | |||
| 40 | this.newCabNamesCallBack = this.NewCabNamesCallBack; | ||
| 41 | 34 | ||
| 42 | this.ServiceProvider = serviceProvider; | 35 | this.Section = section; |
| 43 | 36 | ||
| 44 | this.BackendHelper = backendHelper; | 37 | this.CabbingThreadCount = cabbingThreadCount; |
| 45 | 38 | ||
| 46 | this.MediaTemplate = mediaTemplate; | 39 | this.IntermediateFolder = intermediateFolder; |
| 47 | } | 40 | this.LayoutDirectory = Path.GetDirectoryName(outputPath); |
| 48 | 41 | ||
| 49 | private IServiceProvider ServiceProvider { get; } | 42 | this.DefaultCompressionLevel = defaultCompressionLevel; |
| 43 | this.ModularizationSuffix = modularizationSuffix; | ||
| 44 | this.FileFacadesByCabinet = filesByCabinetMedia; | ||
| 50 | 45 | ||
| 51 | private IBackendHelper BackendHelper { get; } | 46 | this.Data = data; |
| 47 | this.TableDefinitions = tableDefinitions; | ||
| 52 | 48 | ||
| 53 | private WixMediaTemplateSymbol MediaTemplate { get; } | 49 | this.ResolveMedia = resolveMedia; |
| 54 | 50 | ||
| 55 | /// <summary> | 51 | this.cabinetResolver = new CabinetResolver(serviceProvider, cabCachePath, backendExtensions); |
| 56 | /// Sets the number of threads to use for cabinet creation. | 52 | this.fileTransfers = new List<IFileTransfer>(); |
| 57 | /// </summary> | 53 | this.trackedFiles = new List<ITrackedFile>(); |
| 58 | public int CabbingThreadCount { private get; set; } | 54 | } |
| 59 | 55 | ||
| 60 | public string CabCachePath { private get; set; } | 56 | private IMessaging Messaging { get; } |
| 61 | 57 | ||
| 62 | public IMessaging Messaging { private get; set; } | 58 | private IBackendHelper BackendHelper { get; } |
| 63 | 59 | ||
| 64 | public string IntermediateFolder { private get; set; } | 60 | private IntermediateSection Section { get; } |
| 65 | 61 | ||
| 66 | /// <summary> | 62 | private int CabbingThreadCount { get; set; } |
| 67 | /// Sets the default compression level to use for cabinets | ||
| 68 | /// that don't have their compression level explicitly set. | ||
| 69 | /// </summary> | ||
| 70 | public CompressionLevel? DefaultCompressionLevel { private get; set; } | ||
| 71 | 63 | ||
| 72 | public IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { private get; set; } | 64 | private string IntermediateFolder { get; } |
| 73 | 65 | ||
| 74 | public WindowsInstallerData Data { private get; set; } | 66 | private string LayoutDirectory { get; } |
| 75 | 67 | ||
| 76 | public string LayoutDirectory { private get; set; } | 68 | private CompressionLevel? DefaultCompressionLevel { get; } |
| 77 | 69 | ||
| 78 | public bool Compressed { private get; set; } | 70 | private string ModularizationSuffix { get; } |
| 79 | 71 | ||
| 80 | public string ModularizationSuffix { private get; set; } | 72 | private Dictionary<MediaSymbol, IEnumerable<IFileFacade>> FileFacadesByCabinet { get; } |
| 81 | 73 | ||
| 82 | public Dictionary<MediaSymbol, IEnumerable<IFileFacade>> FileFacadesByCabinet { private get; set; } | 74 | private WindowsInstallerData Data { get; } |
| 83 | 75 | ||
| 84 | public Func<MediaSymbol, string, string, string> ResolveMedia { private get; set; } | 76 | private TableDefinitionCollection TableDefinitions { get; } |
| 85 | 77 | ||
| 86 | public TableDefinitionCollection TableDefinitions { private get; set; } | 78 | private Func<MediaSymbol, string, string, string> ResolveMedia { get; } |
| 87 | 79 | ||
| 88 | public IEnumerable<IFileTransfer> FileTransfers => this.fileTransfers; | 80 | public IEnumerable<IFileTransfer> FileTransfers => this.fileTransfers; |
| 89 | 81 | ||
| @@ -91,8 +83,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 91 | 83 | ||
| 92 | public void Execute() | 84 | public void Execute() |
| 93 | { | 85 | { |
| 94 | this.lastCabinetAddedToMediaTable = new Dictionary<string, string>(); | ||
| 95 | |||
| 96 | // If the cabbing thread count wasn't provided, default the number of cabbing threads to the number of processors. | 86 | // If the cabbing thread count wasn't provided, default the number of cabbing threads to the number of processors. |
| 97 | if (this.CabbingThreadCount <= 0) | 87 | if (this.CabbingThreadCount <= 0) |
| 98 | { | 88 | { |
| @@ -101,13 +91,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 101 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); | 91 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); |
| 102 | } | 92 | } |
| 103 | 93 | ||
| 104 | // Send Binder object to Facilitate NewCabNamesCallBack Callback | ||
| 105 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); | ||
| 106 | |||
| 107 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | ||
| 108 | this.GetMediaTemplateAttributes(out var maximumCabinetSizeForLargeFileSplitting, out var maximumUncompressedMediaSize); | 94 | this.GetMediaTemplateAttributes(out var maximumCabinetSizeForLargeFileSplitting, out var maximumUncompressedMediaSize); |
| 109 | cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = maximumCabinetSizeForLargeFileSplitting; | 95 | |
| 110 | cabinetBuilder.MaximumUncompressedMediaSize = maximumUncompressedMediaSize; | 96 | var cabinetBuilder = new CabinetBuilder(this.Messaging, this.CabbingThreadCount, maximumCabinetSizeForLargeFileSplitting, maximumUncompressedMediaSize); |
| 111 | 97 | ||
| 112 | foreach (var entry in this.FileFacadesByCabinet) | 98 | foreach (var entry in this.FileFacadesByCabinet) |
| 113 | { | 99 | { |
| @@ -129,12 +115,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 129 | return; | 115 | return; |
| 130 | } | 116 | } |
| 131 | 117 | ||
| 132 | // create queued cabinets with multiple threads | 118 | // Create queued cabinets with multiple threads. |
| 133 | cabinetBuilder.CreateQueuedCabinets(); | 119 | cabinetBuilder.CreateQueuedCabinets(); |
| 120 | |||
| 134 | if (this.Messaging.EncounteredError) | 121 | if (this.Messaging.EncounteredError) |
| 135 | { | 122 | { |
| 136 | return; | 123 | return; |
| 137 | } | 124 | } |
| 125 | |||
| 126 | this.UpdateMediaWithSpannedCabinets(cabinetBuilder.CompletedCabinets); | ||
| 138 | } | 127 | } |
| 139 | 128 | ||
| 140 | private int CalculateCabbingThreadCount() | 129 | private int CalculateCabbingThreadCount() |
| @@ -163,7 +152,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 163 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades) | 152 | private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData data, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable<IFileFacade> fileFacades) |
| 164 | { | 153 | { |
| 165 | CabinetWorkItem cabinetWorkItem = null; | 154 | CabinetWorkItem cabinetWorkItem = null; |
| 166 | var tempCabinetFileX = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet); | 155 | |
| 156 | var intermediateCabinetPath = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet); | ||
| 167 | 157 | ||
| 168 | // check for an empty cabinet | 158 | // check for an empty cabinet |
| 169 | if (!fileFacades.Any()) | 159 | if (!fileFacades.Any()) |
| @@ -172,25 +162,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 172 | var cabinetName = mediaSymbol.Cabinet.TrimStart('#'); | 162 | var cabinetName = mediaSymbol.Cabinet.TrimStart('#'); |
| 173 | 163 | ||
| 174 | // If building a patch, remind them to run -p for torch. | 164 | // If building a patch, remind them to run -p for torch. |
| 175 | if (OutputType.Patch == data.Type) | 165 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, OutputType.Patch == data.Type)); |
| 176 | { | ||
| 177 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, true)); | ||
| 178 | } | ||
| 179 | else | ||
| 180 | { | ||
| 181 | this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName)); | ||
| 182 | } | ||
| 183 | } | 166 | } |
| 184 | 167 | ||
| 185 | var cabinetResolver = new CabinetResolver(this.ServiceProvider, this.CabCachePath, this.BackendExtensions); | 168 | var resolvedCabinet = this.cabinetResolver.ResolveCabinet(intermediateCabinetPath, fileFacades); |
| 186 | |||
| 187 | var resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades); | ||
| 188 | 169 | ||
| 189 | // create a cabinet work item if it's not being skipped | 170 | // Create a cabinet work item if it's not being skipped. |
| 190 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) | 171 | if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) |
| 191 | { | 172 | { |
| 192 | // Default to the threshold for best smartcabbing (makes smallest cabinet). | 173 | // Default to the threshold for best smartcabbing (makes smallest cabinet). |
| 193 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, resolvedCabinet.Path, fileFacades, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); | 174 | cabinetWorkItem = new CabinetWorkItem(mediaSymbol.SourceLineNumbers, mediaSymbol.DiskId, resolvedCabinet.Path, fileFacades, maxThreshold: 0, compressionLevel: compressionLevel, modularizationSuffix: this.ModularizationSuffix); |
| 194 | } | 175 | } |
| 195 | else // reuse the cabinet from the cabinet cache. | 176 | else // reuse the cabinet from the cabinet cache. |
| 196 | { | 177 | { |
| @@ -235,185 +216,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 235 | return cabinetWorkItem; | 216 | return cabinetWorkItem; |
| 236 | } | 217 | } |
| 237 | 218 | ||
| 238 | //private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable<FileFacade> fileFacades) | ||
| 239 | //{ | ||
| 240 | // ResolvedCabinet resolved = null; | ||
| 241 | |||
| 242 | // List<BindFileWithPath> filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList(); | ||
| 243 | |||
| 244 | // foreach (var extension in this.BackendExtensions) | ||
| 245 | // { | ||
| 246 | // resolved = extension.ResolveCabinet(cabinetPath, filesWithPath); | ||
| 247 | // if (null != resolved) | ||
| 248 | // { | ||
| 249 | // break; | ||
| 250 | // } | ||
| 251 | // } | ||
| 252 | |||
| 253 | // return resolved; | ||
| 254 | //} | ||
| 255 | |||
| 256 | /// <summary> | ||
| 257 | /// Delegate for Cabinet Split Callback | ||
| 258 | /// </summary> | ||
| 259 | [UnmanagedFunctionPointer(CallingConvention.StdCall)] | ||
| 260 | internal delegate void FileSplitCabNamesCallback([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken); | ||
| 261 | |||
| 262 | /// <summary> | ||
| 263 | /// Call back to Add File Transfer for new Cab and add new Cab to Media table | ||
| 264 | /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe | ||
| 265 | /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored | ||
| 266 | /// </summary> | ||
| 267 | /// <param name="firstCabName">The name of splitting cabinet without extention e.g. "cab1".</param> | ||
| 268 | /// <param name="newCabinetName">The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"</param> | ||
| 269 | /// <param name="fileToken">The file token of the first file present in the splitting cabinet</param> | ||
| 270 | internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabinetName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken) | ||
| 271 | { | ||
| 272 | throw new NotImplementedException(); | ||
| 273 | #if TODO_CAB_SPANNING | ||
| 274 | // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads | ||
| 275 | var mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); | ||
| 276 | try | ||
| 277 | { | ||
| 278 | if (!mutex.WaitOne(0, false)) // Check if you can get the lock | ||
| 279 | { | ||
| 280 | // Cound not get the Lock | ||
| 281 | this.Messaging.Write(VerboseMessages.CabinetsSplitInParallel()); | ||
| 282 | mutex.WaitOne(); // Wait on other thread | ||
| 283 | } | ||
| 284 | |||
| 285 | var firstCabinetName = firstCabName + ".cab"; | ||
| 286 | var transferAdded = false; // Used for Error Handling | ||
| 287 | |||
| 288 | // Create File Transfer for new Cabinet using transfer of Base Cabinet | ||
| 289 | foreach (var transfer in this.FileTransfers) | ||
| 290 | { | ||
| 291 | if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) | ||
| 292 | { | ||
| 293 | var newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); | ||
| 294 | var newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); | ||
| 295 | |||
| 296 | var trackSource = this.BackendHelper.TrackFile(newCabSourcePath, TrackedFileType.Intermediate, transfer.SourceLineNumbers); | ||
| 297 | this.trackedFiles.Add(trackSource); | ||
| 298 | |||
| 299 | var trackTarget = this.BackendHelper.TrackFile(newCabTargetPath, TrackedFileType.Final, transfer.SourceLineNumbers); | ||
| 300 | this.trackedFiles.Add(trackTarget); | ||
| 301 | |||
| 302 | var newTransfer = this.BackendHelper.CreateFileTransfer(trackSource.Path, trackTarget.Path, transfer.Move, transfer.SourceLineNumbers); | ||
| 303 | this.fileTransfers.Add(newTransfer); | ||
| 304 | |||
| 305 | transferAdded = true; | ||
| 306 | break; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | // Check if File Transfer was added | ||
| 311 | if (!transferAdded) | ||
| 312 | { | ||
| 313 | throw new WixException(ErrorMessages.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); | ||
| 314 | } | ||
| 315 | |||
| 316 | // Add the new Cabinets to media table using LastSequence of Base Cabinet | ||
| 317 | var mediaTable = this.Output.Tables["Media"]; | ||
| 318 | var wixFileTable = this.Output.Tables["WixFile"]; | ||
| 319 | var diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain | ||
| 320 | var lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain | ||
| 321 | var lastSplitCabinetFound = false; // Used for Error Handling | ||
| 322 | |||
| 323 | var lastCabinetOfThisSequence = String.Empty; | ||
| 324 | // Get the Value of Last Cabinet Added in this split Sequence from Dictionary | ||
| 325 | if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) | ||
| 326 | { | ||
| 327 | // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence | ||
| 328 | lastCabinetOfThisSequence = firstCabinetName; | ||
| 329 | } | ||
| 330 | |||
| 331 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
| 332 | { | ||
| 333 | // Get details for the Last Cabinet Added in this Split Sequence | ||
| 334 | if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 335 | { | ||
| 336 | lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; | ||
| 337 | diskIDForLastSplitCabAdded = mediaRow.DiskId; | ||
| 338 | lastSplitCabinetFound = true; | ||
| 339 | } | ||
| 340 | |||
| 341 | // Check for Name Collision for the new Cabinet added | ||
| 342 | if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 343 | { | ||
| 344 | // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row | ||
| 345 | throw new WixException(ErrorMessages.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | // Check if the last Split Cabinet was found in the Media Table | ||
| 350 | if (!lastSplitCabinetFound) | ||
| 351 | { | ||
| 352 | throw new WixException(ErrorMessages.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); | ||
| 353 | } | ||
| 354 | |||
| 355 | // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort | ||
| 356 | // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with | ||
| 357 | // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction | ||
| 358 | MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); | ||
| 359 | newMediaRow.Cabinet = newCabinetName; | ||
| 360 | newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion | ||
| 361 | newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; | ||
| 362 | |||
| 363 | // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique | ||
| 364 | foreach (MediaRow mediaRow in mediaTable.Rows) | ||
| 365 | { | ||
| 366 | // Check if this row comes after inserted row and it is not the new cabinet inserted row | ||
| 367 | if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) | ||
| 368 | { | ||
| 369 | mediaRow.DiskId++; // Increment DiskID | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | // Now Increment DiskID for All files Rows so that they refer to the right Media Row | ||
| 374 | foreach (WixFileRow wixFileRow in wixFileTable.Rows) | ||
| 375 | { | ||
| 376 | // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet | ||
| 377 | // This check will work as we have only one large file in every splitting cabinet | ||
| 378 | // If we want to support splitting cabinet with more large files we need to update this code | ||
| 379 | if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) | ||
| 380 | { | ||
| 381 | wixFileRow.DiskId++; // Increment DiskID | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback | ||
| 386 | this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; | ||
| 387 | |||
| 388 | mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails | ||
| 389 | } | ||
| 390 | finally | ||
| 391 | { | ||
| 392 | // Releasing the Mutex here | ||
| 393 | mutex.ReleaseMutex(); | ||
| 394 | } | ||
| 395 | #endif | ||
| 396 | } | ||
| 397 | |||
| 398 | |||
| 399 | /// <summary> | 219 | /// <summary> |
| 400 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides | 220 | /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides |
| 401 | /// </summary> | 221 | /// </summary> |
| 402 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) | 222 | private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) |
| 403 | { | 223 | { |
| 404 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | 224 | var mediaTemplate = this.Section.Symbols.OfType<WixMediaTemplateSymbol>().FirstOrDefault(); |
| 405 | var mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
| 406 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
| 407 | 225 | ||
| 408 | // Supply Compile MediaTemplate Attributes to Cabinet Builder | 226 | // Supply Compile MediaTemplate Attributes to Cabinet Builder |
| 409 | if (this.MediaTemplate != null) | 227 | if (mediaTemplate != null) |
| 410 | { | 228 | { |
| 229 | // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size | ||
| 230 | var mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); | ||
| 231 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | ||
| 232 | |||
| 411 | // Get the Value for Max Cab Size for File Splitting | 233 | // Get the Value for Max Cab Size for File Splitting |
| 412 | var maxCabSizeForLargeFileInMB = 0; | 234 | var maxCabSizeForLargeFileInMB = 0; |
| 413 | try | 235 | try |
| 414 | { | 236 | { |
| 415 | // Override authored mcslfs value if environment variable is authored. | 237 | // Override authored mcslfs value if environment variable is authored. |
| 416 | maxCabSizeForLargeFileInMB = !String.IsNullOrEmpty(mcslfsString) ? Int32.Parse(mcslfsString) : this.MediaTemplate.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; | 238 | maxCabSizeForLargeFileInMB = !String.IsNullOrEmpty(mcslfsString) ? Int32.Parse(mcslfsString) : mediaTemplate.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; |
| 417 | 239 | ||
| 418 | var testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; | 240 | var testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; |
| 419 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; | 241 | maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; |
| @@ -431,7 +253,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 431 | try | 253 | try |
| 432 | { | 254 | { |
| 433 | // Override authored mums value if environment variable is authored. | 255 | // Override authored mums value if environment variable is authored. |
| 434 | maxPreCompressedSizeInMB = !String.IsNullOrEmpty(mumsString) ? Int32.Parse(mumsString) : this.MediaTemplate.MaximumUncompressedMediaSize ?? DefaultMaximumUncompressedMediaSize; | 256 | maxPreCompressedSizeInMB = !String.IsNullOrEmpty(mumsString) ? Int32.Parse(mumsString) : mediaTemplate.MaximumUncompressedMediaSize ?? DefaultMaximumUncompressedMediaSize; |
| 435 | 257 | ||
| 436 | var testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; | 258 | var testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; |
| 437 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; | 259 | maxUncompressedMediaSize = maxPreCompressedSizeInMB; |
| @@ -451,5 +273,121 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 451 | maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize; | 273 | maxUncompressedMediaSize = DefaultMaximumUncompressedMediaSize; |
| 452 | } | 274 | } |
| 453 | } | 275 | } |
| 276 | |||
| 277 | private void UpdateMediaWithSpannedCabinets(IReadOnlyCollection<CompletedCabinetWorkItem> completedCabinetWorkItems) | ||
| 278 | { | ||
| 279 | var completedCabinetsSpanned = completedCabinetWorkItems.Where(c => c.CreatedCabinets.Count > 1).OrderBy(c => c.DiskId).ToList(); | ||
| 280 | |||
| 281 | if (completedCabinetsSpanned.Count == 0) | ||
| 282 | { | ||
| 283 | return; | ||
| 284 | } | ||
| 285 | |||
| 286 | var fileTransfersByName = this.fileTransfers.ToDictionary(t => Path.GetFileName(t.Source), StringComparer.OrdinalIgnoreCase); | ||
| 287 | var mediaTable = this.Data.Tables["Media"]; | ||
| 288 | var fileTable = this.Data.Tables["File"]; | ||
| 289 | var mediaRows = mediaTable.Rows.Cast<MediaRow>().OrderBy(m => m.DiskId).ToList(); | ||
| 290 | var fileRows = fileTable.Rows.Cast<FileRow>().OrderBy(f => f.Sequence).ToList(); | ||
| 291 | |||
| 292 | var mediaRowsByOriginalDiskId = mediaRows.ToDictionary(m => m.DiskId); | ||
| 293 | var addedMediaRows = new List<MediaRow>(); | ||
| 294 | |||
| 295 | foreach (var completedCabinetSpanned in completedCabinetsSpanned) | ||
| 296 | { | ||
| 297 | var cabinet = completedCabinetSpanned.CreatedCabinets.First(); | ||
| 298 | var spannedCabinets = completedCabinetSpanned.CreatedCabinets.Skip(1); | ||
| 299 | |||
| 300 | if (!fileTransfersByName.TryGetValue(cabinet.CabinetName, out var transfer) || | ||
| 301 | !mediaRowsByOriginalDiskId.TryGetValue(completedCabinetSpanned.DiskId, out var mediaRow)) | ||
| 302 | { | ||
| 303 | throw new WixException(ErrorMessages.SplitCabinetCopyRegistrationFailed(spannedCabinets.First().CabinetName, cabinet.CabinetName)); | ||
| 304 | } | ||
| 305 | |||
| 306 | var lastDiskId = mediaRow.DiskId; | ||
| 307 | var mediaRowsThatWillNeedDiskIdUpdated = mediaRows.OrderBy(m => m.DiskId).Where(m => m.DiskId > mediaRow.DiskId).ToList(); | ||
| 308 | |||
| 309 | foreach (var spannedCabinet in spannedCabinets) | ||
| 310 | { | ||
| 311 | var spannedCabinetSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), spannedCabinet.CabinetName); | ||
| 312 | var spannedCabinetTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), spannedCabinet.CabinetName); | ||
| 313 | |||
| 314 | var trackSource = this.BackendHelper.TrackFile(spannedCabinetSourcePath, TrackedFileType.Intermediate, transfer.SourceLineNumbers); | ||
| 315 | this.trackedFiles.Add(trackSource); | ||
| 316 | |||
| 317 | var trackTarget = this.BackendHelper.TrackFile(spannedCabinetTargetPath, TrackedFileType.BuiltOutput, transfer.SourceLineNumbers); | ||
| 318 | this.trackedFiles.Add(trackTarget); | ||
| 319 | |||
| 320 | var newTransfer = this.BackendHelper.CreateFileTransfer(trackSource.Path, trackTarget.Path, transfer.Move, transfer.SourceLineNumbers); | ||
| 321 | this.fileTransfers.Add(newTransfer); | ||
| 322 | |||
| 323 | // FDI Extract requires DiskID of Split Cabinets to be continuous. So a new Media row must inserted just | ||
| 324 | // after the previous spanned cabinet according to DiskID sort order, otherwise Windows Installer will | ||
| 325 | // encounter Error 2350 (FDI Server Error). | ||
| 326 | var newMediaRow = (MediaRow)mediaTable.CreateRow(mediaRow.SourceLineNumbers); | ||
| 327 | newMediaRow.Cabinet = spannedCabinet.CabinetName; | ||
| 328 | newMediaRow.DiskId = ++lastDiskId; | ||
| 329 | newMediaRow.LastSequence = mediaRow.LastSequence; | ||
| 330 | |||
| 331 | addedMediaRows.Add(newMediaRow); | ||
| 332 | } | ||
| 333 | |||
| 334 | // Increment the DiskId for all Media rows that come after the newly inserted row to ensure that the DiskId is unique | ||
| 335 | // and the Media rows stay in order based on last sequence. | ||
| 336 | foreach (var updateMediaRow in mediaRowsThatWillNeedDiskIdUpdated) | ||
| 337 | { | ||
| 338 | updateMediaRow.DiskId = ++lastDiskId; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | mediaTable.ValidateRows(); | ||
| 343 | |||
| 344 | var oldDiskIdToNewDiskId = mediaRowsByOriginalDiskId.Where(originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Value.DiskId != originalDiskIdWithMediaRow.Key) | ||
| 345 | .ToDictionary(originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Key, originalDiskIdWithMediaRow => originalDiskIdWithMediaRow.Value.DiskId); | ||
| 346 | |||
| 347 | // Update the File row and FileSymbols so the DiskIds are correct in the WixOutput, even if this | ||
| 348 | // data doesn't show up in the Windows Installer database. | ||
| 349 | foreach (var fileRow in fileRows) | ||
| 350 | { | ||
| 351 | if (oldDiskIdToNewDiskId.TryGetValue(fileRow.DiskId, out var newDiskId)) | ||
| 352 | { | ||
| 353 | fileRow.DiskId = newDiskId; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | foreach (var fileSymbol in this.Section.Symbols.OfType<FileSymbol>()) | ||
| 358 | { | ||
| 359 | if (fileSymbol.DiskId.HasValue && oldDiskIdToNewDiskId.TryGetValue(fileSymbol.DiskId.Value, out var newDiskId)) | ||
| 360 | { | ||
| 361 | fileSymbol.DiskId = newDiskId; | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | // Update the MediaSymbol DiskIds to the correct DiskId. Note that the MediaSymbol Id | ||
| 366 | // is not changed because symbol ids are not allowed to change after they are created. | ||
| 367 | foreach (var mediaSymbol in this.Section.Symbols.OfType<MediaSymbol>()) | ||
| 368 | { | ||
| 369 | if (oldDiskIdToNewDiskId.TryGetValue(mediaSymbol.DiskId, out var newDiskId)) | ||
| 370 | { | ||
| 371 | mediaSymbol.DiskId = newDiskId; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | // Now that the existing MediaSymbol DiskIds are updated, add the newly created Media rows | ||
| 376 | // as symbols. Notice that the new MediaSymbols do not have an Id because they very likely | ||
| 377 | // would conflict with MediaSymbols that had their DiskIds updated but Ids could not be updated. | ||
| 378 | // The newly created MediaSymbols will rename anonymous. | ||
| 379 | foreach (var mediaRow in addedMediaRows) | ||
| 380 | { | ||
| 381 | this.Section.AddSymbol(new MediaSymbol(mediaRow.SourceLineNumbers) | ||
| 382 | { | ||
| 383 | Cabinet = mediaRow.Cabinet, | ||
| 384 | DiskId = mediaRow.DiskId, | ||
| 385 | DiskPrompt = mediaRow.DiskPrompt, | ||
| 386 | LastSequence = mediaRow.LastSequence, | ||
| 387 | Source = mediaRow.Source, | ||
| 388 | VolumeLabel = mediaRow.VolumeLabel | ||
| 389 | }); | ||
| 390 | } | ||
| 391 | } | ||
| 454 | } | 392 | } |
| 455 | } | 393 | } |
