From c4497aa78b2d85b2613af64311bf282756aff43a Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 23 May 2020 01:57:15 -0700 Subject: Support instance transforms --- .../Bind/BindDatabaseCommand.cs | 243 +------------------ .../Bind/CreateInstanceTransformsCommand.cs | 260 +++++++++++++++++++++ .../WixToolsetTest.CoreIntegration/MsiFixture.cs | 37 ++- 3 files changed, 297 insertions(+), 243 deletions(-) create mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index e09c12da..8e901d30 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -373,9 +373,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else // we can create instance transforms since Component Guids are set. { -#if TODO_FIX_INSTANCE_TRANSFORM - this.CreateInstanceTransforms(this.Output); -#endif + var command = new CreateInstanceTransformsCommand(section, output, tableDefinitions, this.BackendHelper); + command.Execute(); } #if TODO_FINISH_UPDATE @@ -640,244 +639,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind } #endif - -#if TODO_FIX_INSTANCE_TRANSFORM - /// - /// Creates instance transform substorages in the output. - /// - /// Output containing instance transform definitions. - private void CreateInstanceTransforms(Output output) - { - // Create and add substorages for instance transforms. - Table wixInstanceTransformsTable = output.Tables["WixInstanceTransforms"]; - if (null != wixInstanceTransformsTable && 0 <= wixInstanceTransformsTable.Rows.Count) - { - string targetProductCode = null; - string targetUpgradeCode = null; - string targetProductVersion = null; - - Table targetSummaryInformationTable = output.Tables["_SummaryInformation"]; - Table targetPropertyTable = output.Tables["Property"]; - - // Get the data from target database - foreach (Row 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. - Dictionary instanceComponentGuids = new Dictionary(); - Table targetInstanceComponentTable = output.Tables["WixInstanceComponent"]; - if (null != targetInstanceComponentTable && 0 < targetInstanceComponentTable.Rows.Count) - { - foreach (Row row in targetInstanceComponentTable.Rows) - { - // Build up all the instances, we'll get the Components rows from the real Component table. - instanceComponentGuids.Add((string)row[0], null); - } - - Table targetComponentTable = output.Tables["Component"]; - foreach (ComponentRow componentRow in targetComponentTable.Rows) - { - string component = (string)componentRow[0]; - if (instanceComponentGuids.ContainsKey(component)) - { - instanceComponentGuids[component] = componentRow; - } - } - } - - // Generate the instance transforms - foreach (Row instanceRow in wixInstanceTransformsTable.Rows) - { - string instanceId = (string)instanceRow[0]; - - Output instanceTransform = new Output(instanceRow.SourceLineNumbers); - instanceTransform.Type = OutputType.Transform; - instanceTransform.Codepage = output.Codepage; - - Table instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); - string targetPlatformAndLanguage = null; - - foreach (Row summaryInformationRow in targetSummaryInformationTable.Rows) - { - if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE - { - targetPlatformAndLanguage = (string)summaryInformationRow[1]; - } - - // Copy the row's data to the transform. - Row copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(null); - copyOfSummaryRow[0] = summaryInformationRow[0]; - copyOfSummaryRow[1] = summaryInformationRow[1]; - } - - // Modify the appropriate properties. - Table propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]); - - // Change the ProductCode property - string productCode = (string)instanceRow[2]; - if ("*" == productCode) - { - productCode = Common.GenerateGuid(); - } - - Row productCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - productCodeRow.Operation = RowOperation.Modify; - productCodeRow.Fields[1].Modified = true; - productCodeRow[0] = "ProductCode"; - productCodeRow[1] = productCode; - - // Change the instance property - Row instanceIdRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - instanceIdRow.Operation = RowOperation.Modify; - instanceIdRow.Fields[1].Modified = true; - instanceIdRow[0] = (string)instanceRow[1]; - instanceIdRow[1] = instanceId; - - if (null != instanceRow[3]) - { - // Change the ProductName property - Row productNameRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - productNameRow.Operation = RowOperation.Modify; - productNameRow.Fields[1].Modified = true; - productNameRow[0] = "ProductName"; - productNameRow[1] = (string)instanceRow[3]; - } - - if (null != instanceRow[4]) - { - // Change the UpgradeCode property - Row upgradeCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - upgradeCodeRow.Operation = RowOperation.Modify; - upgradeCodeRow.Fields[1].Modified = true; - upgradeCodeRow[0] = "UpgradeCode"; - upgradeCodeRow[1] = instanceRow[4]; - - // Change the Upgrade table - Table targetUpgradeTable = output.Tables["Upgrade"]; - if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) - { - string upgradeId = (string)instanceRow[4]; - Table upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]); - foreach (Row 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]) - { - Row upgradeRow = upgradeTable.CreateRow(null); - 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 - Row upgradeRemoveRow = upgradeTable.CreateRow(null); - 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) - { - Table componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); - foreach (ComponentRow targetComponentRow in instanceComponentGuids.Values) - { - string guid = targetComponentRow.Guid; - if (!String.IsNullOrEmpty(guid)) - { - Row instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers); - instanceComponentRow.Operation = RowOperation.Modify; - instanceComponentRow.Fields[1].Modified = true; - instanceComponentRow[0] = targetComponentRow[0]; - instanceComponentRow[1] = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)).ToString("B").ToUpper(CultureInfo.InvariantCulture); - instanceComponentRow[2] = targetComponentRow[2]; - instanceComponentRow[3] = targetComponentRow[3]; - instanceComponentRow[4] = targetComponentRow[4]; - instanceComponentRow[5] = targetComponentRow[5]; - } - } - } - - // Update the summary information - Hashtable summaryRows = new Hashtable(instanceSummaryInformationTable.Rows.Count); - foreach (Row row in instanceSummaryInformationTable.Rows) - { - summaryRows[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.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; - summaryRow[1] = targetPlatformAndLanguage; - } - else if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; - summaryRow[1] = "0"; - } - else if (!summaryRows.Contains((int)SummaryInformation.Transform.Security)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.Security; - summaryRow[1] = "4"; - } - - output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); - } - } - } -#endif - /// /// Validate that there are no duplicate GUIDs in the output. /// diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs new file mode 100644 index 00000000..772100ca --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs @@ -0,0 +1,260 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.WindowsInstaller.Bind +{ + using System; + using System.Collections.Generic; + using System.Linq; + using WixToolset.Core.WindowsInstaller.Msi; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Data.WindowsInstaller.Rows; + using WixToolset.Extensibility.Services; + + internal class CreateInstanceTransformsCommand + { + public CreateInstanceTransformsCommand(IntermediateSection section, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, IBackendHelper backendHelper) + { + this.Section = section; + this.Output = output; + this.TableDefinitions = tableDefinitions; + this.BackendHelper = backendHelper; + } + + private IntermediateSection Section { get; } + + private WindowsInstallerData Output { get; } + + public TableDefinitionCollection TableDefinitions { get; } + + private IBackendHelper BackendHelper { get; } + + public void Execute() + { + // Create and add substorages for instance transforms. + var wixInstanceTransformsTuples = this.Section.Tuples.OfType(); + + if (wixInstanceTransformsTuples.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.Tuples.OfType(); + 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 instanceTuple in wixInstanceTransformsTuples) + { + var instanceId = instanceTuple.Id.Id; + + var instanceTransform = new WindowsInstallerData(instanceTuple.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 = instanceTuple.ProductCode; + if ("*" == productCode) + { + productCode = Common.GenerateGuid(); + } + + var productCodeRow = propertyTable.CreateRow(instanceTuple.SourceLineNumbers); + productCodeRow.Operation = RowOperation.Modify; + productCodeRow.Fields[1].Modified = true; + productCodeRow[0] = "ProductCode"; + productCodeRow[1] = productCode; + + // Change the instance property + var instanceIdRow = propertyTable.CreateRow(instanceTuple.SourceLineNumbers); + instanceIdRow.Operation = RowOperation.Modify; + instanceIdRow.Fields[1].Modified = true; + instanceIdRow[0] = instanceTuple.PropertyId; + instanceIdRow[1] = instanceId; + + if (!String.IsNullOrEmpty(instanceTuple.ProductName)) + { + // Change the ProductName property + var productNameRow = propertyTable.CreateRow(instanceTuple.SourceLineNumbers); + productNameRow.Operation = RowOperation.Modify; + productNameRow.Fields[1].Modified = true; + productNameRow[0] = "ProductName"; + productNameRow[1] = instanceTuple.ProductName; + } + + if (!String.IsNullOrEmpty(instanceTuple.UpgradeCode)) + { + // Change the UpgradeCode property + var upgradeCodeRow = propertyTable.CreateRow(instanceTuple.SourceLineNumbers); + upgradeCodeRow.Operation = RowOperation.Modify; + upgradeCodeRow.Fields[1].Modified = true; + upgradeCodeRow[0] = "UpgradeCode"; + upgradeCodeRow[1] = instanceTuple.UpgradeCode; + + // Change the Upgrade table + var targetUpgradeTable = this.Output.Tables["Upgrade"]; + if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) + { + var upgradeId = instanceTuple.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(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(instanceTuple.SourceLineNumbers); + summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; + summaryRow[1] = targetPlatformAndLanguage; + } + else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags)) + { + var summaryRow = instanceSummaryInformationTable.CreateRow(instanceTuple.SourceLineNumbers); + summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; + summaryRow[1] = "0"; + } + else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security)) + { + var summaryRow = instanceSummaryInformationTable.CreateRow(instanceTuple.SourceLineNumbers); + summaryRow[0] = (int)SummaryInformation.Transform.Security; + summaryRow[1] = "4"; + } + + this.Output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); + } + } + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs b/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs index 075f7733..69258ae4 100644 --- a/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/MsiFixture.cs @@ -790,7 +790,7 @@ namespace WixToolsetTest.CoreIntegration } } - [Fact(Skip = "Not implemented yet.")] + [Fact] public void CanBuildInstanceTransform() { var folder = TestData.Get(@"TestData\InstanceTransform"); @@ -813,7 +813,30 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); var output = WindowsInstallerData.Load(Path.Combine(intermediateFolder, @"bin\test.wixpdb"), false); - Assert.NotEmpty(output.SubStorages); + var substorage = output.SubStorages.Single(); + Assert.Equal("I1", substorage.Name); + + var data = substorage.Data; + Assert.Equal(new[] + { + "_SummaryInformation", + "Property", + "Upgrade" + }, data.Tables.Select(t => t.Name).ToArray()); + + Assert.Equal(new[] + { + "INSTANCEPROPERTY\tI1", + "ProductName\tMsiPackage (Instance 1)", + }, JoinRows(data.Tables["Property"])); + + Assert.Equal(new[] + { + "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t\tWIX_UPGRADE_DETECTED", + "{047730A5-30FE-4A62-A520-DA9381B8226A}\t\t1.0.0.0\t1033\t1\t0\t0", + "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t\tWIX_DOWNGRADE_DETECTED", + "{047730A5-30FE-4A62-A520-DA9381B8226A}\t1.0.0.0\t\t1033\t2\t0\t0" + }, JoinRows(data.Tables["Upgrade"])); } } @@ -850,5 +873,15 @@ namespace WixToolsetTest.CoreIntegration Assert.False(File.Exists(msiPath)); } } + + private static string[] JoinRows(Table table) + { + return table.Rows.Select(r => JoinFields(r.Fields)).ToArray(); + + string JoinFields(Field[] fields) + { + return String.Join('\t', fields.Select(f => f.ToString())); + } + } } } -- cgit v1.2.3-55-g6feb