public Row CreateRow(IntermediateSection section, IntermediateSymbol symbol, WindowsInstallerData output, TableDefinition tableDefinition) { var table = output.EnsureTable(tableDefinition); var row = table.CreateRow(symbol.SourceLineNumbers); row.SectionId = section.Id; return row; }
/// <summary> /// Generates the WixFile table based on a path to an admin image msi and an Output. /// </summary> /// <param name="databaseFile">The path to the msi database file in an admin image.</param> /// <param name="output">The Output that represents the msi database.</param> private void GenerateWixFileTable(string databaseFile, WindowsInstallerData output) { throw new NotImplementedException(); #if TODO_FIX_UNBINDING_FILES var adminRootPath = Path.GetDirectoryName(databaseFile); var componentDirectoryIndex = new Hashtable(); var componentTable = output.Tables["Component"]; foreach (var row in componentTable.Rows) { componentDirectoryIndex.Add(row[0], row[2]); } // Index full source paths for all directories var directoryDirectoryParentIndex = new Hashtable(); var directoryFullPathIndex = new Hashtable(); var directorySourceNameIndex = new Hashtable(); var directoryTable = output.Tables["Directory"]; foreach (var row in directoryTable.Rows) { directoryDirectoryParentIndex.Add(row[0], row[1]); if (null == row[1]) { directoryFullPathIndex.Add(row[0], adminRootPath); } else { directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2])); } } foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex) { if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key)) { this.GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex); } } var fileTable = output.Tables["File"]; var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]); foreach (var row in fileTable.Rows) { var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]); wixFileRow.File = (string)row[0]; wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]]; wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2])); if (!File.Exists(wixFileRow.Source)) { throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source)); } wixFileTable.Rows.Add(wixFileRow); } #endif }
public void Execute() { var transformFlags = 0; var targetOutput = new WindowsInstallerData(null); var updatedOutput = new WindowsInstallerData(null); // TODO: handle added columns // to generate a localized transform, both the target and updated // databases need to have the same code page. the only reason to // set different code pages is to support localized primary key // columns, but that would only support deleting rows. if this // becomes necessary, define a PreviousCodepage property on the // Output class and persist this throughout transform generation. targetOutput.Codepage = this.Transform.Codepage; updatedOutput.Codepage = this.Transform.Codepage; // remove certain Property rows which will be populated from summary information values string targetUpgradeCode = null; string updatedUpgradeCode = null; if (this.Transform.TryGetTable("Property", out var propertyTable)) { for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) { Row row = propertyTable.Rows[i]; if ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0]) { propertyTable.Rows.RemoveAt(i); if ("UpgradeCode" == (string)row[0]) { updatedUpgradeCode = (string)row[1]; } } } } var targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); var updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); var targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); // process special summary information values foreach (var row in this.Transform.Tables["_SummaryInformation"].Rows) { var summaryId = row.FieldAsInteger(0); var summaryData = row.FieldAsString(1); if ((int)SummaryInformation.Transform.CodePage == summaryId) { // convert from a web name if provided var codePage = summaryData; if (null == codePage) { codePage = "0"; } else { codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); } var previousCodePage = row.Fields[1].PreviousData; if (null == previousCodePage) { previousCodePage = "0"; } else { previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); } var targetCodePageRow = targetSummaryInfo.CreateRow(null); targetCodePageRow[0] = 1; // PID_CODEPAGE targetCodePageRow[1] = previousCodePage; var updatedCodePageRow = updatedSummaryInfo.CreateRow(null); updatedCodePageRow[0] = 1; // PID_CODEPAGE updatedCodePageRow[1] = codePage; } else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId || (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId) { // the target language var propertyData = summaryData.Split(';'); var lang = 2 == propertyData.Length ? propertyData[1] : "0"; var tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo; var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable; var productLanguageRow = tempPropertyTable.CreateRow(null); productLanguageRow[0] = "ProductLanguage"; productLanguageRow[1] = lang; // set the platform;language on the MSI to be generated var templateRow = tempSummaryInfo.CreateRow(null); templateRow[0] = 7; // PID_TEMPLATE templateRow[1] = summaryData; } else if ((int)SummaryInformation.Transform.ProductCodes == summaryId) { var propertyData = summaryData.Split(';'); var targetProductCodeRow = targetPropertyTable.CreateRow(null); targetProductCodeRow[0] = "ProductCode"; targetProductCodeRow[1] = propertyData[0].Substring(0, 38); var targetProductVersionRow = targetPropertyTable.CreateRow(null); targetProductVersionRow[0] = "ProductVersion"; targetProductVersionRow[1] = propertyData[0].Substring(38); var updatedProductCodeRow = updatedPropertyTable.CreateRow(null); updatedProductCodeRow[0] = "ProductCode"; updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); var updatedProductVersionRow = updatedPropertyTable.CreateRow(null); updatedProductVersionRow[0] = "ProductVersion"; updatedProductVersionRow[1] = propertyData[1].Substring(38); // UpgradeCode is optional and may not exists in the target // or upgraded databases, so do not include a null-valued // UpgradeCode property. targetUpgradeCode = propertyData[2]; if (!String.IsNullOrEmpty(targetUpgradeCode)) { var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); targetUpgradeCodeRow[0] = "UpgradeCode"; targetUpgradeCodeRow[1] = targetUpgradeCode; // If the target UpgradeCode is specified, an updated // UpgradeCode is required. if (String.IsNullOrEmpty(updatedUpgradeCode)) { updatedUpgradeCode = targetUpgradeCode; } } if (!String.IsNullOrEmpty(updatedUpgradeCode)) { var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); updatedUpgradeCodeRow[0] = "UpgradeCode"; updatedUpgradeCodeRow[1] = updatedUpgradeCode; } } else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId) { transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture); } else if ((int)SummaryInformation.Transform.Reserved11 == summaryId) { // PID_LASTPRINTED should be null for transforms row.Operation = RowOperation.None; } else { // add everything else as is var targetRow = targetSummaryInfo.CreateRow(null); targetRow[0] = row[0]; targetRow[1] = row[1]; var updatedRow = updatedSummaryInfo.CreateRow(null); updatedRow[0] = row[0]; updatedRow[1] = row[1]; } } // Validate that both databases have an UpgradeCode if the // authoring transform will validate the UpgradeCode; otherwise, // MsiCreateTransformSummaryinfo() will fail with 1620. if (((int)TransformFlags.ValidateUpgradeCode & transformFlags) != 0 && (String.IsNullOrEmpty(targetUpgradeCode) || String.IsNullOrEmpty(updatedUpgradeCode))) { this.Messaging.Write(ErrorMessages.BothUpgradeCodesRequired()); } string emptyFile = null; foreach (var table in this.Transform.Tables) { // Ignore unreal tables when building transforms except the _Stream table. // These tables are ignored when generating the database so there is no reason // to process them here. if (table.Definition.Unreal && "_Streams" != table.Name) { continue; } // process table operations switch (table.Operation) { case TableOperation.Add: updatedOutput.EnsureTable(table.Definition); break; case TableOperation.Drop: targetOutput.EnsureTable(table.Definition); continue; default: targetOutput.EnsureTable(table.Definition); updatedOutput.EnsureTable(table.Definition); break; } // process row operations foreach (var row in table.Rows) { switch (row.Operation) { case RowOperation.Add: var updatedTable = updatedOutput.EnsureTable(table.Definition); updatedTable.Rows.Add(row); continue; case RowOperation.Delete: var targetTable = targetOutput.EnsureTable(table.Definition); targetTable.Rows.Add(row); // fill-in non-primary key values foreach (var field in row.Fields) { if (!field.Column.PrimaryKey) { if (ColumnType.Number == field.Column.Type && !field.Column.IsLocalizable) { field.Data = field.Column.MinValue; } else if (ColumnType.Object == field.Column.Type) { if (null == emptyFile) { emptyFile = Path.Combine(this.IntermediateFolder, "empty"); } field.Data = emptyFile; } else { field.Data = "0"; } } } continue; } // Assure that the file table's sequence is populated if ("File" == table.Name) { foreach (var fileRow in table.Rows) { if (null == fileRow[7]) { if (RowOperation.Add == fileRow.Operation) { this.Messaging.Write(ErrorMessages.InvalidAddedFileRowWithoutSequence(fileRow.SourceLineNumbers, (string)fileRow[0])); break; } // Set to 1 to prevent invalid IDT file from being generated fileRow[7] = 1; } } } // process modified and unmodified rows var modifiedRow = false; var targetRow = table.Definition.CreateRow(null); var updatedRow = row; for (var i = 0; i < row.Fields.Length; i++) { var updatedField = row.Fields[i]; if (updatedField.Modified) { // set a different value in the target row to ensure this value will be modified during transform generation if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) { var data = updatedField.AsNullableInteger(); targetRow[i] = (data == 1) ? 2 : 1; } else if (ColumnType.Object == updatedField.Column.Type) { if (null == emptyFile) { emptyFile = Path.Combine(this.IntermediateFolder, "empty"); } targetRow[i] = emptyFile; } else { var data = updatedField.AsString(); targetRow[i] = (data == "0") ? "1" : "0"; } modifiedRow = true; } else if (ColumnType.Object == updatedField.Column.Type) { var objectField = (ObjectField)updatedField; // create an empty file for comparing against if (null == objectField.PreviousData) { if (null == emptyFile) { emptyFile = Path.Combine(this.IntermediateFolder, "empty"); } targetRow[i] = emptyFile; modifiedRow = true; } else if (!this.FileSystemManager.CompareFiles(objectField.PreviousData, (string)objectField.Data)) { targetRow[i] = objectField.PreviousData; modifiedRow = true; } } else // unmodified { if (null != updatedField.Data) { targetRow[i] = updatedField.Data; } } } // modified rows and certain special rows go in the target and updated msi databases if (modifiedRow || ("Property" == table.Name && ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0]))) { var targetTable = targetOutput.EnsureTable(table.Definition); targetTable.Rows.Add(targetRow); var updatedTable = updatedOutput.EnsureTable(table.Definition); updatedTable.Rows.Add(updatedRow); } } } //foreach (BinderExtension extension in this.Extensions) //{ // extension.PostBind(this.Context); //} // Any errors encountered up to this point can cause errors during generation. if (this.Messaging.EncounteredError) { return; } var transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); var targetDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi")); var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi")); try { if (!String.IsNullOrEmpty(emptyFile)) { using (var fileStream = File.Create(emptyFile)) { } } this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false); this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true); // make sure the directory exists Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); // create the transform file using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly)) using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) { if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) { updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); } else { this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); } } } finally { if (!String.IsNullOrEmpty(emptyFile)) { File.Delete(emptyFile); } } }
public WindowsInstallerData Execute() { var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); transform.Type = OutputType.Transform; // get the summary information table using (var summaryInformation = new SummaryInformation(this.TransformFile)) { var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); for (var i = 1; 19 >= i; i++) { var value = summaryInformation.GetProperty(i); if (0 < value.Length) { var row = table.CreateRow(transform.SourceLineNumbers); row[0] = i; row[1] = value; } } } // create a schema msi which hopefully matches the table schemas in the transform var schemaOutput = new WindowsInstallerData(null); var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); foreach (var tableDefinition in this.TableDefinitions) { // skip unreal tables and the Patch table if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) { schemaOutput.EnsureTable(tableDefinition); } } var addedRows = new Dictionary <string, Row>(); Table transformViewTable; // Bind the schema msi. this.GenerateDatabase(schemaOutput, msiDatabaseFile); // apply the transform to the database and retrieve the modifications using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { // apply the transform with the ViewTransform option to collect all the modifications msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); // unbind the database var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); var transformViewOutput = unbindCommand.Execute(); // index the added and possibly modified rows (added rows may also appears as modified rows) transformViewTable = transformViewOutput.Tables["_TransformView"]; var modifiedRows = new Hashtable(); foreach (var row in transformViewTable.Rows) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; if ("INSERT" == columnName) { var index = String.Concat(tableName, ':', primaryKeys); addedRows.Add(index, null); } else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row { var index = String.Concat(tableName, ':', primaryKeys); modifiedRows[index] = row; } } // create placeholder rows for modified rows to make the transform insert the updated values when its applied foreach (Row row in modifiedRows.Values) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; var index = String.Concat(tableName, ':', primaryKeys); // ignore information for added rows if (!addedRows.ContainsKey(index)) { var table = schemaOutput.Tables[tableName]; this.CreateRow(table, primaryKeys, true); } } } // Re-bind the schema output with the placeholder rows. this.GenerateDatabase(schemaOutput, msiDatabaseFile); // apply the transform to the database and retrieve the modifications using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { try { // apply the transform msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All); // commit the database to guard against weird errors with streams msiDatabase.Commit(); } catch (Win32Exception ex) { if (0x65B == ex.NativeErrorCode) { // this commonly happens when the transform was built // against a database schema different from the internal // table definitions throw new WixException(ErrorMessages.TransformSchemaMismatch()); } } // unbind the database var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); var output = unbindCommand.Execute(); // index all the rows to easily find modified rows var rows = new Dictionary <string, Row>(); foreach (var table in output.Tables) { foreach (var row in table.Rows) { rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); } } // process the _TransformView rows into transform rows foreach (var row in transformViewTable.Rows) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; var table = transform.EnsureTable(this.TableDefinitions[tableName]); if ("CREATE" == columnName) // added table { table.Operation = TableOperation.Add; } else if ("DELETE" == columnName) // deleted row { var deletedRow = this.CreateRow(table, primaryKeys, false); deletedRow.Operation = RowOperation.Delete; } else if ("DROP" == columnName) // dropped table { table.Operation = TableOperation.Drop; } else if ("INSERT" == columnName) // added row { var index = String.Concat(tableName, ':', primaryKeys); var addedRow = rows[index]; addedRow.Operation = RowOperation.Add; table.Rows.Add(addedRow); } else if (null != primaryKeys) // modified row { var index = String.Concat(tableName, ':', primaryKeys); // the _TransformView table includes information for added rows // that looks like modified rows so it sometimes needs to be ignored if (!addedRows.ContainsKey(index)) { var modifiedRow = rows[index]; // mark the field as modified var indexOfModifiedValue = -1; for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) { if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) { indexOfModifiedValue = i; break; } } modifiedRow.Fields[indexOfModifiedValue].Modified = true; // move the modified row into the transform the first time its encountered if (RowOperation.None == modifiedRow.Operation) { modifiedRow.Operation = RowOperation.Modify; table.Rows.Add(modifiedRow); } } } else // added column { var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal)); column.Added = true; } } } return(transform); }
/// <summary> /// Creates a transform by diffing two outputs. /// </summary> public WindowsInstallerData Execute() { var targetOutput = this.TargetOutput; var updatedOutput = this.UpdatedOutput; var validationFlags = this.ValidationFlags; var transform = new WindowsInstallerData(null) { Type = OutputType.Transform, Codepage = updatedOutput.Codepage }; this.transformSummaryInfo = new SummaryInformationStreams(); // compare the codepages if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags)) { this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage)); if (null != updatedOutput.SourceLineNumbers) { this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers)); } } // compare the output types if (targetOutput.Type != updatedOutput.Type) { throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString())); } // compare the contents of the tables foreach (var targetTable in targetOutput.Tables) { var updatedTable = updatedOutput.Tables[targetTable.Name]; var operation = TableOperation.None; var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation); if (TableOperation.Drop == operation) { var droppedTable = transform.EnsureTable(targetTable.Definition); droppedTable.Operation = TableOperation.Drop; } else if (TableOperation.None == operation) { var modifiedTable = transform.EnsureTable(updatedTable.Definition); foreach (var row in rows) { modifiedTable.Rows.Add(row); } } } // added tables foreach (var updatedTable in updatedOutput.Tables) { if (null == targetOutput.Tables[updatedTable.Name]) { var addedTable = transform.EnsureTable(updatedTable.Definition); addedTable.Operation = TableOperation.Add; foreach (var updatedRow in updatedTable.Rows) { updatedRow.Operation = RowOperation.Add; updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; addedTable.Rows.Add(updatedRow); } } } // set summary information properties if (!this.SuppressKeepingSpecialRows) { var summaryInfoTable = transform.Tables["_SummaryInformation"]; this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); } this.Transform = transform; return(this.Transform); }
/// <summary> /// Adds the PatchFiles action to the sequence table if it does not already exist. /// </summary> /// <param name="table">The sequence table to check or modify.</param> /// <param name="mainTransform">The primary authoring transform.</param> /// <param name="pairedTransform">The secondary patch transform.</param> /// <param name="mainFileRow">The file row that contains information about the patched file.</param> private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow) { var tableName = table.ToString(); // Find/add PatchFiles action (also determine sequence for it). // Search mainTransform first, then pairedTransform (pairedTransform overrides). var hasPatchFilesAction = false; var installFilesSequence = 0; var duplicateFilesSequence = 0; TestSequenceTableForPatchFilesAction( mainTransform.Tables[tableName], ref hasPatchFilesAction, ref installFilesSequence, ref duplicateFilesSequence); TestSequenceTableForPatchFilesAction( pairedTransform.Tables[tableName], ref hasPatchFilesAction, ref installFilesSequence, ref duplicateFilesSequence); if (!hasPatchFilesAction) { WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionSymbol); var sequence = patchFilesActionSymbol.Sequence; // Test for default sequence value's appropriateness if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence)) { if (0 != duplicateFilesSequence) { if (duplicateFilesSequence < installFilesSequence) { throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action)); } else { sequence = (duplicateFilesSequence + installFilesSequence) / 2; if (installFilesSequence == sequence || duplicateFilesSequence == sequence) { throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action)); } } } else { sequence = installFilesSequence + 1; } } var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); if (0 == sequenceTable.Rows.Count) { sequenceTable.Operation = TableOperation.Add; } var patchAction = sequenceTable.CreateRow(null); patchAction[0] = patchFilesActionSymbol.Action; patchAction[1] = patchFilesActionSymbol.Condition; patchAction[2] = sequence; patchAction.Operation = RowOperation.Add; } }
/// <summary> /// Creates a work item to create a cabinet. /// </summary> /// <param name="output">Output for the current database.</param> /// <param name="cabinetDir">Directory to create cabinet in.</param> /// <param name="mediaSymbol">Media symbol containing information about the cabinet.</param> /// <param name="fileFacades">Collection of files in this cabinet.</param> /// <returns>created CabinetWorkItem object</returns> private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData output, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable <FileFacade> fileFacades) { CabinetWorkItem cabinetWorkItem = null; var tempCabinetFileX = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet); // check for an empty cabinet if (!fileFacades.Any()) { // Remove the leading '#' from the embedded cabinet name to make the warning easier to understand var cabinetName = mediaSymbol.Cabinet.TrimStart('#'); // If building a patch, remind them to run -p for torch. if (OutputType.Patch == output.Type) { this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, true)); } else { this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName)); } } var cabinetResolver = new CabinetResolver(this.ServiceProvider, this.CabCachePath, this.BackendExtensions); var resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades); // create a cabinet work item if it's not being skipped if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) { // Default to the threshold for best smartcabbing (makes smallest cabinet). cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold: 0, compressionLevel, this.ModularizationSuffix /*, this.FileManager*/); } else // reuse the cabinet from the cabinet cache. { this.Messaging.Write(VerboseMessages.ReusingCabCache(mediaSymbol.SourceLineNumbers, mediaSymbol.Cabinet, resolvedCabinet.Path)); try { // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) // causing the project to look like it perpetually needs a rebuild until all of the reused // cabinets get newer timestamps. File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); } catch (Exception e) { this.Messaging.Write(WarningMessages.CannotUpdateCabCache(mediaSymbol.SourceLineNumbers, resolvedCabinet.Path, e.Message)); } } var trackResolvedCabinet = this.BackendHelper.TrackFile(resolvedCabinet.Path, TrackedFileType.Intermediate, mediaSymbol.SourceLineNumbers); this.trackedFiles.Add(trackResolvedCabinet); if (mediaSymbol.Cabinet.StartsWith("#", StringComparison.Ordinal)) { var streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); var streamRow = streamsTable.CreateRow(mediaSymbol.SourceLineNumbers); streamRow[0] = mediaSymbol.Cabinet.Substring(1); streamRow[1] = resolvedCabinet.Path; } else { var trackDestination = this.BackendHelper.TrackFile(Path.Combine(cabinetDir, mediaSymbol.Cabinet), TrackedFileType.Final, mediaSymbol.SourceLineNumbers); this.trackedFiles.Add(trackDestination); var transfer = this.BackendHelper.CreateFileTransfer(resolvedCabinet.Path, trackDestination.Path, resolvedCabinet.BuildOption == CabinetBuildOption.BuildAndMove, mediaSymbol.SourceLineNumbers); this.fileTransfers.Add(transfer); } return(cabinetWorkItem); }
public void Execute() { // Create and add substorages for instance transforms. var wixInstanceTransformsSymbols = this.Section.Symbols.OfType <WixInstanceTransformsSymbol>(); if (wixInstanceTransformsSymbols.Any()) { string targetProductCode = null; string targetUpgradeCode = null; string targetProductVersion = null; var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"]; var targetPropertyTable = this.Output.Tables["Property"]; // Get the data from target database foreach (var propertyRow in targetPropertyTable.Rows) { if ("ProductCode" == (string)propertyRow[0]) { targetProductCode = (string)propertyRow[1]; } else if ("ProductVersion" == (string)propertyRow[0]) { targetProductVersion = (string)propertyRow[1]; } else if ("UpgradeCode" == (string)propertyRow[0]) { targetUpgradeCode = (string)propertyRow[1]; } } // Index the Instance Component Rows, we'll get the Components rows from the real Component table. var targetInstanceComponentTable = this.Section.Symbols.OfType <WixInstanceComponentSymbol>(); var instanceComponentGuids = targetInstanceComponentTable.ToDictionary(t => t.Id.Id, t => (ComponentRow)null); if (instanceComponentGuids.Any()) { var targetComponentTable = this.Output.Tables["Component"]; foreach (ComponentRow componentRow in targetComponentTable.Rows) { var component = (string)componentRow[0]; if (instanceComponentGuids.ContainsKey(component)) { instanceComponentGuids[component] = componentRow; } } } // Generate the instance transforms foreach (var instanceSymbol in wixInstanceTransformsSymbols) { var instanceId = instanceSymbol.Id.Id; var instanceTransform = new WindowsInstallerData(instanceSymbol.SourceLineNumbers); instanceTransform.Type = OutputType.Transform; instanceTransform.Codepage = this.Output.Codepage; var instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); string targetPlatformAndLanguage = null; foreach (var summaryInformationRow in targetSummaryInformationTable.Rows) { if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE { targetPlatformAndLanguage = (string)summaryInformationRow[1]; } // Copy the row's data to the transform. var copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(summaryInformationRow.SourceLineNumbers); copyOfSummaryRow[0] = summaryInformationRow[0]; copyOfSummaryRow[1] = summaryInformationRow[1]; } // Modify the appropriate properties. var propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]); // Change the ProductCode property var productCode = instanceSymbol.ProductCode; if ("*" == productCode) { productCode = this.BackendHelper.CreateGuid(); } var productCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); productCodeRow.Operation = RowOperation.Modify; productCodeRow.Fields[1].Modified = true; productCodeRow[0] = "ProductCode"; productCodeRow[1] = productCode; // Change the instance property var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); instanceIdRow.Operation = RowOperation.Modify; instanceIdRow.Fields[1].Modified = true; instanceIdRow[0] = instanceSymbol.PropertyId; instanceIdRow[1] = instanceId; if (!String.IsNullOrEmpty(instanceSymbol.ProductName)) { // Change the ProductName property var productNameRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); productNameRow.Operation = RowOperation.Modify; productNameRow.Fields[1].Modified = true; productNameRow[0] = "ProductName"; productNameRow[1] = instanceSymbol.ProductName; } if (!String.IsNullOrEmpty(instanceSymbol.UpgradeCode)) { // Change the UpgradeCode property var upgradeCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); upgradeCodeRow.Operation = RowOperation.Modify; upgradeCodeRow.Fields[1].Modified = true; upgradeCodeRow[0] = "UpgradeCode"; upgradeCodeRow[1] = instanceSymbol.UpgradeCode; // Change the Upgrade table var targetUpgradeTable = this.Output.Tables["Upgrade"]; if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) { var upgradeId = instanceSymbol.UpgradeCode; var upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]); foreach (var row in targetUpgradeTable.Rows) { // In case they are upgrading other codes to this new product, leave the ones that don't match the // Product.UpgradeCode intact. if (targetUpgradeCode == (string)row[0]) { var upgradeRow = upgradeTable.CreateRow(row.SourceLineNumbers); upgradeRow.Operation = RowOperation.Add; upgradeRow.Fields[0].Modified = true; // I was hoping to be able to RowOperation.Modify, but that didn't appear to function. // upgradeRow.Fields[0].PreviousData = (string)row[0]; // Inserting a new Upgrade record with the updated UpgradeCode upgradeRow[0] = upgradeId; upgradeRow[1] = row[1]; upgradeRow[2] = row[2]; upgradeRow[3] = row[3]; upgradeRow[4] = row[4]; upgradeRow[5] = row[5]; upgradeRow[6] = row[6]; // Delete the old row var upgradeRemoveRow = upgradeTable.CreateRow(row.SourceLineNumbers); upgradeRemoveRow.Operation = RowOperation.Delete; upgradeRemoveRow[0] = row[0]; upgradeRemoveRow[1] = row[1]; upgradeRemoveRow[2] = row[2]; upgradeRemoveRow[3] = row[3]; upgradeRemoveRow[4] = row[4]; upgradeRemoveRow[5] = row[5]; upgradeRemoveRow[6] = row[6]; } } } } // If there are instance Components generate new GUIDs for them. if (0 < instanceComponentGuids.Count) { var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); foreach (var targetComponentRow in instanceComponentGuids.Values) { var guid = targetComponentRow.Guid; if (!String.IsNullOrEmpty(guid)) { var instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers); instanceComponentRow.Operation = RowOperation.Modify; instanceComponentRow.Fields[1].Modified = true; instanceComponentRow[0] = targetComponentRow[0]; instanceComponentRow[1] = this.BackendHelper.CreateGuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)); instanceComponentRow[2] = targetComponentRow[2]; instanceComponentRow[3] = targetComponentRow[3]; instanceComponentRow[4] = targetComponentRow[4]; instanceComponentRow[5] = targetComponentRow[5]; } } } // Update the summary information var summaryRows = new Dictionary <int, Row>(instanceSummaryInformationTable.Rows.Count); foreach (var row in instanceSummaryInformationTable.Rows) { summaryRows[(int)row[0]] = row; if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) { row[1] = targetPlatformAndLanguage; } else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) { row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode); } else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) { row[1] = 0; } else if ((int)SummaryInformation.Transform.Security == (int)row[0]) { row[1] = "4"; } } if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) { var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; summaryRow[1] = targetPlatformAndLanguage; } else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags)) { var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; summaryRow[1] = "0"; } else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security)) { var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); summaryRow[0] = (int)SummaryInformation.Transform.Security; summaryRow[1] = "4"; } this.Output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); } } }
/// <summary> /// Creates a transform by diffing two outputs. /// </summary> /// <param name="targetOutput">The target output.</param> /// <param name="updatedOutput">The updated output.</param> /// <param name="validationFlags"></param> /// <returns>The transform.</returns> public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags) { WindowsInstallerData transform = new WindowsInstallerData(null); transform.Type = OutputType.Transform; transform.Codepage = updatedOutput.Codepage; this.transformSummaryInfo = new SummaryInformationStreams(); // compare the codepages if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags)) { this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage)); if (null != updatedOutput.SourceLineNumbers) { this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers)); } } // compare the output types if (targetOutput.Type != updatedOutput.Type) { throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString())); } // compare the contents of the tables foreach (Table targetTable in targetOutput.Tables) { Table updatedTable = updatedOutput.Tables[targetTable.Name]; TableOperation operation = TableOperation.None; List <Row> rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation); if (TableOperation.Drop == operation) { Table droppedTable = transform.EnsureTable(targetTable.Definition); droppedTable.Operation = TableOperation.Drop; } else if (TableOperation.None == operation) { Table modified = transform.EnsureTable(updatedTable.Definition); rows.ForEach(r => modified.Rows.Add(r)); } } // added tables foreach (Table updatedTable in updatedOutput.Tables) { if (null == targetOutput.Tables[updatedTable.Name]) { Table addedTable = transform.EnsureTable(updatedTable.Definition); addedTable.Operation = TableOperation.Add; foreach (Row updatedRow in updatedTable.Rows) { updatedRow.Operation = RowOperation.Add; updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; addedTable.Rows.Add(updatedRow); } } } // set summary information properties if (!this.suppressKeepingSpecialRows) { Table summaryInfoTable = transform.Tables["_SummaryInformation"]; this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); } return(transform); }