private static IList <SoftwareTag> CollectPackageTags(Output bundle) { List <SoftwareTag> tags = new List <SoftwareTag>(); Table packageTable = bundle.Tables["WixBundlePackage"]; if (null != packageTable) { Table payloadTable = bundle.Tables["WixBundlePayload"]; RowDictionary <WixBundlePayloadRow> payloads = new RowDictionary <WixBundlePayloadRow>(payloadTable); foreach (WixBundlePackageRow row in packageTable.RowsAs <WixBundlePackageRow>()) { if (WixBundlePackageType.Msi == row.Type) { string packagePayloadId = row.PackagePayload; WixBundlePayloadRow payload = payloads.Get(packagePayloadId); using (Database db = new Database(payload.FullFileName)) { if (db.Tables.Contains("SoftwareIdentificationTag")) { using (View view = db.OpenView("SELECT `Regid`, `UniqueId`, `Type` FROM `SoftwareIdentificationTag`")) { view.Execute(); while (true) { using (Record record = view.Fetch()) { if (null == record) { break; } TagType type = String.IsNullOrEmpty(record.GetString(3)) ? TagType.Unknown : (TagType)Enum.Parse(typeof(TagType), record.GetString(3)); tags.Add(new SoftwareTag() { Regid = record.GetString(1), Id = record.GetString(2), Type = type }); } } } } } } } } return(tags); }
public void Execute() { if (null != this.WixBBControlTable) { RowDictionary <BBControlRow> bbControlRows = new RowDictionary <BBControlRow>(this.BBControlTable); foreach (Row wixRow in this.WixBBControlTable.Rows) { BBControlRow bbControlRow = bbControlRows.Get(wixRow.GetPrimaryKey()); bbControlRow.Text = this.ReadTextFile(bbControlRow.SourceLineNumbers, wixRow.FieldAsString(2)); } } if (null != this.WixControlTable) { RowDictionary <ControlRow> controlRows = new RowDictionary <ControlRow>(this.ControlTable); foreach (Row wixRow in this.WixControlTable.Rows) { ControlRow controlRow = controlRows.Get(wixRow.GetPrimaryKey()); controlRow.Text = this.ReadTextFile(controlRow.SourceLineNumbers, wixRow.FieldAsString(2)); } } }
public void Execute() { RowDictionary <WixBundleExePackageRow> exePackages = new RowDictionary <WixBundleExePackageRow>(this.ExePackageTable); RowDictionary <WixBundleMsiPackageRow> msiPackages = new RowDictionary <WixBundleMsiPackageRow>(this.MsiPackageTable); RowDictionary <WixBundleMspPackageRow> mspPackages = new RowDictionary <WixBundleMspPackageRow>(this.MspPackageTable); RowDictionary <WixBundleMsuPackageRow> msuPackages = new RowDictionary <WixBundleMsuPackageRow>(this.MsuPackageTable); Dictionary <string, PackageFacade> facades = new Dictionary <string, PackageFacade>(this.PackageTable.Rows.Count); foreach (WixBundlePackageRow package in this.PackageTable.Rows) { string id = package.WixChainItemId; PackageFacade facade = null; switch (package.Type) { case WixBundlePackageType.Exe: facade = new PackageFacade(package, exePackages.Get(id)); break; case WixBundlePackageType.Msi: facade = new PackageFacade(package, msiPackages.Get(id)); break; case WixBundlePackageType.Msp: facade = new PackageFacade(package, mspPackages.Get(id)); break; case WixBundlePackageType.Msu: facade = new PackageFacade(package, msuPackages.Get(id)); break; } facades.Add(id, facade); } this.PackageFacades = facades; }
public void Execute() { Debug.Assert(OutputType.Patch != this.Output.Type); var allFileRows = this.CopyOutFileRows ? new List <FileFacade>() : null; var copyToPatch = (allFileRows != null); var copyFromPatch = !copyToPatch; var patchMediaRows = new RowDictionary <MediaRow>(); var patchMediaFileRows = new Dictionary <int, RowDictionary <WixFileRow> >(); var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); if (copyFromPatch) { // index patch files by diskId+fileId foreach (WixFileRow patchFileRow in patchFileTable.Rows) { int diskId = patchFileRow.DiskId; if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } mediaFileRows.Add(patchFileRow); } var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); patchMediaRows = new RowDictionary <MediaRow>(patchMediaTable); } // Index paired transforms by name without the "#" prefix. var pairedTransforms = this.Output.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); //Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); //foreach (SubStorage substorage in this.Output.SubStorages) //{ // if (substorage.Name.StartsWith("#")) // { // pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); // } //} try { // Copy File bind data into substorages foreach (var substorage in this.Output.SubStorages) { if (substorage.Name.StartsWith("#")) { // no changes necessary for paired transforms continue; } var mainTransform = substorage.Data; var mainWixFileTable = mainTransform.Tables["WixFile"]; var mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; this.FileManagerCore.ActiveSubStorage = substorage; var mainWixFiles = new RowDictionary <WixFileRow>(mainWixFileTable); var mainMsiFileHashIndex = new RowDictionary <Row>(); var mainFileTable = mainTransform.Tables["File"]; var pairedTransform = pairedTransforms[substorage.Name]; // copy Media.LastSequence and index the MsiFileHash table if it exists. if (copyFromPatch) { var pairedMediaTable = pairedTransform.Tables["Media"]; foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) { var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; } if (null != mainMsiFileHashTable) { mainMsiFileHashIndex = new RowDictionary <Row>(mainMsiFileHashTable); } // Validate file row changes for keypath-related issues this.ValidateFileRowChanges(mainTransform); } // Index File table of pairedTransform var pairedFileTable = pairedTransform.Tables["File"]; var pairedFileRows = new RowDictionary <FileRow>(pairedFileTable); if (null != mainFileTable) { if (copyFromPatch) { // Remove the MsiFileHash table because it will be updated later with the final file hash for each file mainTransform.Tables.Remove("MsiFileHash"); } foreach (FileRow mainFileRow in mainFileTable.Rows) { if (RowOperation.Delete == mainFileRow.Operation) { continue; } else if (RowOperation.None == mainFileRow.Operation && !copyToPatch) { continue; } var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. { var objectField = (ObjectField)mainWixFileRow.Fields[6]; var pairedFileRow = pairedFileRows.Get(mainFileRow.File); // If the file is new, we always need to add it to the patch. if (mainFileRow.Operation != RowOperation.Add) { // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. if (null == objectField.PreviousData) { if (mainFileRow.Operation == RowOperation.None) { continue; } } else { // TODO: should this entire condition be placed in the binder file manager? if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) { // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. mainFileRow.Operation = RowOperation.Modify; if (null != pairedFileRow) { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Modify; } } else { // The File is same. We need mark all the attributes as unchanged. mainFileRow.Operation = RowOperation.None; foreach (var field in mainFileRow.Fields) { field.Modified = false; } if (null != pairedFileRow) { pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Fields[6].Modified = false; pairedFileRow.Operation = RowOperation.None; } continue; } } } else if (null != pairedFileRow) // RowOperation.Add { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Add; } } // index patch files by diskId+fileId int diskId = mainWixFileRow.DiskId; if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } var fileId = mainFileRow.File; var patchFileRow = mediaFileRows.Get(fileId); if (copyToPatch) { if (null == patchFileRow) { var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); patchActualFileRow.CopyFrom(mainFileRow); patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); patchFileRow.CopyFrom(mainWixFileRow); mediaFileRows.Add(patchFileRow); allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right? } else { // TODO: confirm the rest of data is identical? // make sure Source is same. Otherwise we are silently ignoring a file. if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) { this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); } // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. patchFileRow.AppendPreviousDataFrom(mainWixFileRow); } } else { // copy data from the patch back to the transform if (null != patchFileRow) { var pairedFileRow = pairedFileRows.Get(fileId); for (var i = 0; i < patchFileRow.Fields.Length; i++) { var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i); var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i); if (1 == i) { // File.Component_ changes should not come from the shared file rows // that contain the file information as each individual transform might // have different changes (or no changes at all). } // File.Attributes should not changed for binary deltas else if (6 == i) { if (null != patchFileRow.Patch) { // File.Attribute should not change for binary deltas pairedFileRow.Attributes = mainFileRow.Attributes; mainFileRow.Fields[i].Modified = false; } } // File.Sequence is updated in pairedTransform, not mainTransform else if (7 == i) { // file sequence is updated in Patch table instead of File table for delta patches if (null != patchFileRow.Patch) { pairedFileRow.Fields[i].Modified = false; } else { pairedFileRow[i] = patchFileRow[i]; pairedFileRow.Fields[i].Modified = true; } mainFileRow.Fields[i].Modified = false; } else if (patchValue != mainValue) { mainFileRow[i] = patchFileRow[i]; mainFileRow.Fields[i].Modified = true; if (mainFileRow.Operation == RowOperation.None) { mainFileRow.Operation = RowOperation.Modify; } } } // copy MsiFileHash row for this File if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) { patchHashRow = patchFileRow.Hash; } if (null != patchHashRow) { var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); for (var i = 0; i < patchHashRow.Fields.Length; i++) { mainHashRow[i] = patchHashRow[i]; if (i > 1) { // assume all hash fields have been modified mainHashRow.Fields[i].Modified = true; } } // assume the MsiFileHash operation follows the File one mainHashRow.Operation = mainFileRow.Operation; } // copy MsiAssemblyName rows for this File List <Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; if (null != patchAssemblyNameRows) { var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); foreach (var patchAssemblyNameRow in patchAssemblyNameRows) { // Copy if there isn't an identical modified/added row already in the transform. var foundMatchingModifiedRow = false; foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) { if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) { foundMatchingModifiedRow = true; break; } } if (!foundMatchingModifiedRow) { var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) { mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; } // assume value field has been modified mainAssemblyNameRow.Fields[2].Modified = true; mainAssemblyNameRow.Operation = mainFileRow.Operation; } } } // Add patch header for this file if (null != patchFileRow.Patch) { // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); // Add to Patch table var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); if (0 == patchTable.Rows.Count) { patchTable.Operation = TableOperation.Add; } var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); patchRow[0] = patchFileRow.File; patchRow[1] = patchFileRow.Sequence; var patchFile = new FileInfo(patchFileRow.Source); patchRow[2] = (int)patchFile.Length; patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) { streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); if (0 == patchHeadersTable.Rows.Count) { patchHeadersTable.Operation = TableOperation.Add; } var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); patchHeadersRow[0] = streamName; patchHeadersRow[1] = patchFileRow.Patch; patchRow[5] = streamName; patchHeadersRow.Operation = RowOperation.Add; } else { patchRow[4] = patchFileRow.Patch; } patchRow.Operation = RowOperation.Add; } } else { // TODO: throw because all transform rows should have made it into the patch } } } } if (copyFromPatch) { this.Output.Tables.Remove("Media"); this.Output.Tables.Remove("File"); this.Output.Tables.Remove("MsiFileHash"); this.Output.Tables.Remove("MsiAssemblyName"); } } } finally { this.FileManagerCore.ActiveSubStorage = null; } this.FileFacades = allFileRows; }
public void Execute() { var allFileRows = new List <IFileFacade>(); var patchMediaFileRows = new Dictionary <int, RowDictionary <FileRow> >(); //var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); // Index paired transforms by name without their "#" prefix. var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name, s => s.Data); // Enumerate through main transforms. foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith("#"))) { var mainTransform = substorage.Data; var mainFileTable = mainTransform.Tables["File"]; if (null == mainFileTable) { continue; } // Index File table of pairedTransform var pairedTransform = pairedTransforms["#" + substorage.Name]; var pairedFileRows = new RowDictionary <FileRow>(pairedTransform.Tables["File"]); foreach (FileRow mainFileRow in mainFileTable.Rows.Where(f => f.Operation != RowOperation.Delete)) { var mainFileId = mainFileRow.File; // We need compare the underlying files and include all file changes. var objectField = (ObjectField)mainFileRow.Fields[9]; var pairedFileRow = pairedFileRows.Get(mainFileId); // If the file is new, we always need to add it to the patch. if (mainFileRow.Operation == RowOperation.Add) { if (null != pairedFileRow) // RowOperation.Add { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Add; } } else { // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. if (null == objectField.PreviousData) { if (mainFileRow.Operation == RowOperation.None) { continue; } } else { // TODO: should this entire condition be placed in the binder file manager? if (/*(0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&*/ !this.FileSystemManager.CompareFiles(objectField.PreviousData, objectField.Data.ToString())) { // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. mainFileRow.Operation = RowOperation.Modify; if (null != pairedFileRow) { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Modify; } } else { // The File is same. We need mark all the attributes as unchanged. mainFileRow.Operation = RowOperation.None; foreach (var field in mainFileRow.Fields) { field.Modified = false; } if (null != pairedFileRow) { pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Fields[6].Modified = false; pairedFileRow.Operation = RowOperation.None; } continue; } } } // index patch files by diskId+fileId var diskId = mainFileRow.DiskId; if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) { mediaFileRows = new RowDictionary <FileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } var patchFileRow = mediaFileRows.Get(mainFileId); if (null == patchFileRow) { //patchFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers); mainFileRow.CopyTo(patchFileRow); mediaFileRows.Add(patchFileRow); #if TODO_PATCHING_DELTA // TODO: should we be passing along delta information to the file facade? Probably, right? #endif var fileFacade = this.BackendHelper.CreateFileFacade(patchFileRow); allFileRows.Add(fileFacade); } else { // TODO: confirm the rest of data is identical? // make sure Source is same. Otherwise we are silently ignoring a file. if (0 != String.Compare(patchFileRow.Source, mainFileRow.Source, StringComparison.OrdinalIgnoreCase)) { this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, mainFileId, patchFileRow.Source, mainFileRow.Source)); } #if TODO_PATCHING_DELTA // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. patchFileRow.AppendPreviousDataFrom(mainFileRow); #endif } } } this.FileFacades = allFileRows; }
public void Execute() { throw new NotImplementedException(); #if TODO this.FileTransfers = Enumerable.Empty <FileTransfer>(); this.ContentFilePaths = Enumerable.Empty <string>(); // First look for data we expect to find... Chain, WixGroups, etc. // We shouldn't really get past the linker phase if there are // no group items... that means that there's no UX, no Chain, // *and* no Containers! Table chainPackageTable = this.GetRequiredTable("WixBundlePackage"); Table wixGroupTable = this.GetRequiredTable("WixGroup"); // Ensure there is one and only one row in the WixBundle table. // The compiler and linker behavior should have colluded to get // this behavior. WixBundleRow bundleRow = (WixBundleRow)this.GetSingleRowTable("WixBundle"); bundleRow.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user. // Ensure there is one and only one row in the WixBootstrapperApplication table. // The compiler and linker behavior should have colluded to get // this behavior. Row baRow = this.GetSingleRowTable("WixBootstrapperApplication"); // Ensure there is one and only one row in the WixChain table. // The compiler and linker behavior should have colluded to get // this behavior. WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain"); if (Messaging.Instance.EncounteredError) { return; } // If there are any fields to resolve later, create the cache to populate during bind. IDictionary <string, string> variableCache = null; if (this.DelayedFields.Any()) { variableCache = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase); } // TODO: Although the WixSearch tables are defined in the Util extension, // the Bundle Binder has to know all about them. We hope to revisit all // of this in the 4.0 timeframe. IEnumerable <WixSearchInfo> orderedSearches = this.OrderSearches(); // Extract files that come from cabinet files (this does not extract files from merge modules). { var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand(); extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles; extractEmbeddedFilesCommand.Execute(); } // Get the explicit payloads. RowDictionary <WixBundlePayloadRow> payloads = new RowDictionary <WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]); // Update explicitly authored payloads with their parent package and container (as appropriate) // to make it easier to gather the payloads later. foreach (WixGroupRow row in wixGroupTable.RowsAs <WixGroupRow>()) { if (ComplexReferenceChildType.Payload == row.ChildType) { WixBundlePayloadRow payload = payloads.Get(row.ChildId); if (ComplexReferenceParentType.Package == row.ParentType) { Debug.Assert(String.IsNullOrEmpty(payload.Package)); payload.Package = row.ParentId; } else if (ComplexReferenceParentType.Container == row.ParentType) { Debug.Assert(String.IsNullOrEmpty(payload.Container)); payload.Container = row.ParentId; } else if (ComplexReferenceParentType.Layout == row.ParentType) { payload.LayoutOnly = true; } } } List <FileTransfer> fileTransfers = new List <FileTransfer>(); string layoutDirectory = Path.GetDirectoryName(this.OutputPath); // Process the explicitly authored payloads. ISet <string> processedPayloads; { ProcessPayloadsCommand command = new ProcessPayloadsCommand(); command.Payloads = payloads.Values; command.DefaultPackaging = bundleRow.DefaultPackagingType; command.LayoutDirectory = layoutDirectory; command.Execute(); fileTransfers.AddRange(command.FileTransfers); processedPayloads = new HashSet <string>(payloads.Keys); } IDictionary <string, PackageFacade> facades; { GetPackageFacadesCommand command = new GetPackageFacadesCommand(); command.PackageTable = chainPackageTable; command.ExePackageTable = this.Output.Tables["WixBundleExePackage"]; command.MsiPackageTable = this.Output.Tables["WixBundleMsiPackage"]; command.MspPackageTable = this.Output.Tables["WixBundleMspPackage"]; command.MsuPackageTable = this.Output.Tables["WixBundleMsuPackage"]; command.Execute(); facades = command.PackageFacades; } // Process each package facade. Note this is likely to add payloads and other rows to tables so // note that any indexes created above may be out of date now. foreach (PackageFacade facade in facades.Values) { switch (facade.Package.Type) { case WixBundlePackageType.Exe: { ProcessExePackageCommand command = new ProcessExePackageCommand(); command.AuthoredPayloads = payloads; command.Facade = facade; command.Execute(); // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer); } break; case WixBundlePackageType.Msi: { var command = new ProcessMsiPackageCommand(); command.AuthoredPayloads = payloads; command.Facade = facade; command.BackendExtensions = this.BackendExtensions; command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]); command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]); command.PayloadTable = this.Output.Tables["WixBundlePayload"]; command.RelatedPackageTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleRelatedPackage"]); command.Execute(); if (null != variableCache) { variableCache.Add(String.Concat("packageLanguage.", facade.Package.WixChainItemId), facade.MsiPackage.ProductLanguage.ToString()); if (null != facade.MsiPackage.Manufacturer) { variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.MsiPackage.Manufacturer); } } } break; case WixBundlePackageType.Msp: { ProcessMspPackageCommand command = new ProcessMspPackageCommand(); command.AuthoredPayloads = payloads; command.Facade = facade; command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); command.Execute(); } break; case WixBundlePackageType.Msu: { ProcessMsuPackageCommand command = new ProcessMsuPackageCommand(); command.Facade = facade; command.Execute(); } break; } if (null != variableCache) { BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache); } } // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later) // are present. payloads = new RowDictionary <WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]); // Process the payloads that were added by processing the packages. { ProcessPayloadsCommand command = new ProcessPayloadsCommand(); command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList(); command.DefaultPackaging = bundleRow.DefaultPackagingType; command.LayoutDirectory = layoutDirectory; command.Execute(); fileTransfers.AddRange(command.FileTransfers); processedPayloads = null; } // Set the package metadata from the payloads now that we have the complete payload information. ILookup <string, WixBundlePayloadRow> payloadsByPackage = payloads.Values.ToLookup(p => p.Package); { foreach (PackageFacade facade in facades.Values) { facade.Package.Size = 0; IEnumerable <WixBundlePayloadRow> packagePayloads = payloadsByPackage[facade.Package.WixChainItemId]; foreach (WixBundlePayloadRow payload in packagePayloads) { facade.Package.Size += payload.FileSize; } if (!facade.Package.InstallSize.HasValue) { facade.Package.InstallSize = facade.Package.Size; } WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload]; if (String.IsNullOrEmpty(facade.Package.Description)) { facade.Package.Description = packagePayload.Description; } if (String.IsNullOrEmpty(facade.Package.DisplayName)) { facade.Package.DisplayName = packagePayload.DisplayName; } } } // Give the UX payloads their embedded IDs... int uxPayloadIndex = 0; { foreach (WixBundlePayloadRow payload in payloads.Values.Where(p => Compiler.BurnUXContainerId == p.Container)) { // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even // downloaded. The current engine requires the UX to be fully present before any downloading starts, // so that rules out downloading. Also, the burn engine does not currently copy external UX payloads // into the temporary UX directory correctly, so we don't allow external either. if (PackagingType.Embedded != payload.Packaging) { Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName)); payload.Packaging = PackagingType.Embedded; } payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloadIndex); ++uxPayloadIndex; } if (0 == uxPayloadIndex) { // If we didn't get any UX payloads, it's an error! throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication")); } // Give the embedded payloads without an embedded id yet an embedded id. int payloadIndex = 0; foreach (WixBundlePayloadRow payload in payloads.Values) { Debug.Assert(PackagingType.Unknown != payload.Packaging); if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId)) { payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAttachedContainerEmbeddedIdFormat, payloadIndex); ++payloadIndex; } } } // Determine patches to automatically slipstream. { AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand(); command.PackageFacades = facades.Values; command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]); command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); command.Execute(); } // If catalog files exist, non-embedded payloads should validate with the catalogs. IEnumerable <WixBundleCatalogRow> catalogs = this.Output.Tables["WixBundleCatalog"].RowsAs <WixBundleCatalogRow>(); if (catalogs.Any()) { VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand(); command.Catalogs = catalogs; command.Payloads = payloads.Values; command.Execute(); } if (Messaging.Instance.EncounteredError) { return; } IEnumerable <PackageFacade> orderedFacades; IEnumerable <WixBundleRollbackBoundaryRow> boundaries; { OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand(); command.Boundaries = new RowDictionary <WixBundleRollbackBoundaryRow>(this.Output.Tables["WixBundleRollbackBoundary"]); command.PackageFacades = facades; command.WixGroupTable = wixGroupTable; command.Execute(); orderedFacades = command.OrderedPackageFacades; boundaries = command.UsedRollbackBoundaries; } // Resolve any delayed fields before generating the manifest. if (this.DelayedFields.Any()) { var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(); resolveDelayedFieldsCommand.OutputType = this.Output.Type; resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields; resolveDelayedFieldsCommand.ModularizationGuid = null; resolveDelayedFieldsCommand.VariableCache = variableCache; resolveDelayedFieldsCommand.Execute(); } // Set the overridable bundle provider key. this.SetBundleProviderKey(this.Output, bundleRow); // Import or generate dependency providers for packages in the manifest. this.ProcessDependencyProviders(this.Output, facades); // Update the bundle per-machine/per-user scope based on the chained packages. this.ResolveBundleInstallScope(bundleRow, orderedFacades); // Generate the core-defined BA manifest tables... { CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand(); command.BundleRow = bundleRow; command.ChainPackages = orderedFacades; command.LastUXPayloadIndex = uxPayloadIndex; command.MsiFeatures = this.Output.Tables["WixBundleMsiFeature"].RowsAs <WixBundleMsiFeatureRow>(); command.Output = this.Output; command.Payloads = payloads; command.TableDefinitions = this.TableDefinitions; command.TempFilesLocation = this.IntermediateFolder; command.Execute(); WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; payloads.Add(baManifestPayload); } //foreach (BinderExtension extension in this.Extensions) //{ // extension.PostBind(this.Context); //} // Create all the containers except the UX container first so the manifest (that goes in the UX container) // can contain all size and hash information about the non-UX containers. RowDictionary <WixBundleContainerRow> containers = new RowDictionary <WixBundleContainerRow>(this.Output.Tables["WixBundleContainer"]); ILookup <string, WixBundlePayloadRow> payloadsByContainer = payloads.Values.ToLookup(p => p.Container); int attachedContainerIndex = 1; // count starts at one because UX container is "0". IEnumerable <WixBundlePayloadRow> uxContainerPayloads = Enumerable.Empty <WixBundlePayloadRow>(); foreach (WixBundleContainerRow container in containers.Values) { IEnumerable <WixBundlePayloadRow> containerPayloads = payloadsByContainer[container.Id]; if (!containerPayloads.Any()) { if (container.Id != Compiler.BurnDefaultAttachedContainerId) { // TODO: display warning that we're ignoring container that ended up with no paylods in it. } } else if (Compiler.BurnUXContainerId == container.Id) { container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); container.AttachedContainerIndex = 0; // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first // in the list since that is the Payload that Burn attempts to load. List <WixBundlePayloadRow> uxPayloads = new List <WixBundlePayloadRow>(); string baPayloadId = baRow.FieldAsString(0); foreach (WixBundlePayloadRow uxPayload in containerPayloads) { if (uxPayload.Id == baPayloadId) { uxPayloads.Insert(0, uxPayload); } else { uxPayloads.Add(uxPayload); } } uxContainerPayloads = uxPayloads; } else { container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); // Add detached containers to the list of file transfers. if (ContainerType.Detached == container.Type) { FileTransfer transfer; if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer)) { transfer.Built = true; fileTransfers.Add(transfer); } } else // update the attached container index. { Debug.Assert(ContainerType.Attached == container.Type); container.AttachedContainerIndex = attachedContainerIndex; ++attachedContainerIndex; } this.CreateContainer(container, containerPayloads, null); } } // Create the bundle manifest then UX container. string manifestPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml"); { var command = new CreateBurnManifestCommand(); command.BackendExtensions = this.BackendExtensions; command.Output = this.Output; command.BundleInfo = bundleRow; command.Chain = chainRow; command.Containers = containers; command.Catalogs = catalogs; command.ExecutableName = Path.GetFileName(this.OutputPath); command.OrderedPackages = orderedFacades; command.OutputPath = manifestPath; command.RollbackBoundaries = boundaries; command.OrderedSearches = orderedSearches; command.Payloads = payloads; command.UXContainerPayloads = uxContainerPayloads; command.Execute(); } WixBundleContainerRow uxContainer = containers[Compiler.BurnUXContainerId]; this.CreateContainer(uxContainer, uxContainerPayloads, manifestPath); // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note // that today, the x64 Burn uses the x86 stub. string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString(); string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe"); string bundleTempPath = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath)); Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile)); string bundleFilename = Path.GetFileName(this.OutputPath); if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) { Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename)); } FileTransfer bundleTransfer; if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer)) { bundleTransfer.Built = true; fileTransfers.Add(bundleTransfer); } File.Copy(stubFile, bundleTempPath, true); File.SetAttributes(bundleTempPath, FileAttributes.Normal); this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow); // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers // if they should be attached. using (BurnWriter writer = BurnWriter.Open(bundleTempPath)) { FileInfo burnStubFile = new FileInfo(bundleTempPath); writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId); // Always attach the UX container first writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX); // Now append all other attached containers foreach (WixBundleContainerRow container in containers.Values) { if (ContainerType.Attached == container.Type) { // The container was only created if it had payloads. if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) { writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached); } } } } if (null != this.PdbFile) { Pdb pdb = new Pdb(); pdb.Output = Output; pdb.Save(this.PdbFile); } this.FileTransfers = fileTransfers; this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList(); }
public void Execute() { var fileFacadesByDiskId = new Dictionary <int, Dictionary <string, FileFacade> >(); // Index patch file facades by diskId+fileId. foreach (var facade in this.FileFacades) { if (!fileFacadesByDiskId.TryGetValue(facade.DiskId, out var mediaFacades)) { mediaFacades = new Dictionary <string, FileFacade>(); fileFacadesByDiskId.Add(facade.DiskId, mediaFacades); } mediaFacades.Add(facade.Id, facade); } var patchMediaRows = new RowDictionary <MediaRow>(this.Output.Tables["Media"]); // Index paired transforms by name without the "#" prefix. var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name, s => s.Data); // Copy File bind data into substorages foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith("#"))) { var mainTransform = substorage.Data; var mainMsiFileHashIndex = new RowDictionary <Row>(mainTransform.Tables["MsiFileHash"]); var pairedTransform = pairedTransforms["#" + substorage.Name]; // Copy Media.LastSequence. var pairedMediaTable = pairedTransform.Tables["Media"]; foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) { var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); pairedMediaRow.LastSequence = patchMediaRow.LastSequence; } // Validate file row changes for keypath-related issues this.ValidateFileRowChanges(mainTransform); // Index File table of pairedTransform var pairedFileRows = new RowDictionary <FileRow>(pairedTransform.Tables["File"]); var mainFileTable = mainTransform.Tables["File"]; if (null != mainFileTable) { // Remove the MsiFileHash table because it will be updated later with the final file hash for each file mainTransform.Tables.Remove("MsiFileHash"); foreach (FileRow mainFileRow in mainFileTable.Rows) { if (RowOperation.Delete == mainFileRow.Operation) { continue; } else if (RowOperation.None == mainFileRow.Operation) { continue; } // Index patch files by diskId+fileId if (!fileFacadesByDiskId.TryGetValue(mainFileRow.DiskId, out var mediaFacades)) { mediaFacades = new Dictionary <string, FileFacade>(); fileFacadesByDiskId.Add(mainFileRow.DiskId, mediaFacades); } // copy data from the patch back to the transform if (mediaFacades.TryGetValue(mainFileRow.File, out var facade)) { var patchFileRow = facade.GetFileRow(); var pairedFileRow = pairedFileRows.Get(mainFileRow.File); for (var i = 0; i < patchFileRow.Fields.Length; i++) { var patchValue = patchFileRow.FieldAsString(i) ?? String.Empty; var mainValue = mainFileRow.FieldAsString(i) ?? String.Empty; if (1 == i) { // File.Component_ changes should not come from the shared file rows // that contain the file information as each individual transform might // have different changes (or no changes at all). } else if (6 == i) // File.Attributes should not changed for binary deltas { #if TODO_PATCHING_DELTA if (null != patchFileRow.Patch) { // File.Attribute should not change for binary deltas pairedFileRow.Attributes = mainFileRow.Attributes; mainFileRow.Fields[i].Modified = false; } #endif } else if (7 == i) // File.Sequence is updated in pairedTransform, not mainTransform { // file sequence is updated in Patch table instead of File table for delta patches #if TODO_PATCHING_DELTA if (null != patchFileRow.Patch) { pairedFileRow.Fields[i].Modified = false; } else #endif { pairedFileRow[i] = patchFileRow[i]; pairedFileRow.Fields[i].Modified = true; } mainFileRow.Fields[i].Modified = false; } else if (patchValue != mainValue) { mainFileRow[i] = patchFileRow[i]; mainFileRow.Fields[i].Modified = true; if (mainFileRow.Operation == RowOperation.None) { mainFileRow.Operation = RowOperation.Modify; } } } // Copy MsiFileHash row for this File. if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) { //patchHashRow = patchFileRow.Hash; throw new NotImplementedException(); } if (null != patchHashRow) { var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); for (var i = 0; i < patchHashRow.Fields.Length; i++) { mainHashRow[i] = patchHashRow[i]; if (i > 1) { // assume all hash fields have been modified mainHashRow.Fields[i].Modified = true; } } // assume the MsiFileHash operation follows the File one mainHashRow.Operation = mainFileRow.Operation; } // copy MsiAssemblyName rows for this File #if TODO_PATCHING List <Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; if (null != patchAssemblyNameRows) { var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); foreach (var patchAssemblyNameRow in patchAssemblyNameRows) { // Copy if there isn't an identical modified/added row already in the transform. var foundMatchingModifiedRow = false; foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) { if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) { foundMatchingModifiedRow = true; break; } } if (!foundMatchingModifiedRow) { var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) { mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; } // assume value field has been modified mainAssemblyNameRow.Fields[2].Modified = true; mainAssemblyNameRow.Operation = mainFileRow.Operation; } } } #endif // Add patch header for this file #if TODO_PATCHING_DELTA if (null != patchFileRow.Patch) { // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); // Add to Patch table var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); if (0 == patchTable.Rows.Count) { patchTable.Operation = TableOperation.Add; } var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); patchRow[0] = patchFileRow.File; patchRow[1] = patchFileRow.Sequence; var patchFile = new FileInfo(patchFileRow.Source); patchRow[2] = (int)patchFile.Length; patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) { streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); if (0 == patchHeadersTable.Rows.Count) { patchHeadersTable.Operation = TableOperation.Add; } var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); patchHeadersRow[0] = streamName; patchHeadersRow[1] = patchFileRow.Patch; patchRow[5] = streamName; patchHeadersRow.Operation = RowOperation.Add; } else { patchRow[4] = patchFileRow.Patch; } patchRow.Operation = RowOperation.Add; } #endif } else { // TODO: throw because all transform rows should have made it into the patch } } } this.Output.Tables.Remove("Media"); this.Output.Tables.Remove("File"); this.Output.Tables.Remove("MsiFileHash"); this.Output.Tables.Remove("MsiAssemblyName"); } }
public void Execute() { var fileRows = new RowDictionary <FileRow>(this.Output.Tables["File"]); var mediaRows = new RowDictionary <MediaRow>(this.Output.Tables["Media"]); // Calculate sequence numbers and media disk id layout for all file media information objects. if (OutputType.Module == this.Output.Type) { var lastSequence = 0; // Order by Component to group the files by directory. var optimized = this.OptimizedFileFacades(); foreach (var fileId in optimized.Select(f => f.File.File)) { var fileRow = fileRows.Get(fileId); fileRow.Sequence = ++lastSequence; } } else { int lastSequence = 0; MediaRow mediaRow = null; Dictionary <int, List <FileFacade> > patchGroups = new Dictionary <int, List <FileFacade> >(); // sequence the non-patch-added files var optimized = this.OptimizedFileFacades(); foreach (FileFacade facade in optimized) { if (null == mediaRow) { mediaRow = mediaRows.Get(facade.WixFile.DiskId); if (OutputType.Patch == this.Output.Type) { // patch Media cannot start at zero lastSequence = mediaRow.LastSequence; } } else if (mediaRow.DiskId != facade.WixFile.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows.Get(facade.WixFile.DiskId); } if (0 < facade.WixFile.PatchGroup) { if (patchGroups.TryGetValue(facade.WixFile.PatchGroup, out var patchGroup)) { patchGroup = new List <FileFacade>(); patchGroups.Add(facade.WixFile.PatchGroup, patchGroup); } patchGroup.Add(facade); } else { var fileRow = fileRows.Get(facade.File.File); fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; mediaRow = null; } // sequence the patch-added files foreach (var patchGroup in patchGroups.Values) { foreach (var facade in patchGroup) { if (null == mediaRow) { mediaRow = mediaRows.Get(facade.WixFile.DiskId); } else if (mediaRow.DiskId != facade.WixFile.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows.Get(facade.WixFile.DiskId); } var fileRow = fileRows.Get(facade.File.File); fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; } } }