From dfcd6728a9d56ac37a5daa8cbedabbf10c333773 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 27 Sep 2022 15:03:47 -0700 Subject: Introduce PatchFilterMap to remove Row.SectionId A Row's SectionId is not set correctly in most scenarios. It was only really needed for the old section-based patch filtering. As section-base patch filtering was replaced in favor of the more logical filter generation, Row.SectionId was archaic and mostly outdated/wrong data. --- .../wix/WixToolset.Data/WindowsInstaller/Row.cs | 31 --- .../WindowsInstaller/Xsd/objects.xsd | 2 - .../BaseWindowsInstallerBackendBinderExtension.cs | 7 + .../IWindowsInstallerBackendBinderExtension.cs | 8 + .../Bind/CreateIdtFileCommand.cs | 5 - .../Bind/CreatePatchTransformsCommand.cs | 19 +- .../Bind/GeneratePatchFilterIdsCommand.cs | 233 +++++++++++++++++++++ .../Bind/GenerateSectionIdsCommand.cs | 225 -------------------- .../Bind/GenerateTransformCommand.cs | 18 +- .../Bind/PatchFilterMap.cs | 80 +++++++ .../Bind/ReduceTransformCommand.cs | 142 ++++++------- src/wix/WixToolset.Core.WindowsInstaller/Differ.cs | 9 +- .../WindowsInstallerBackendHelper.cs | 5 +- .../WixToolset.Core.WindowsInstaller/MspBackend.cs | 10 +- 14 files changed, 426 insertions(+), 368 deletions(-) create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/Bind/GeneratePatchFilterIdsCommand.cs delete mode 100644 src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateSectionIdsCommand.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs diff --git a/src/api/wix/WixToolset.Data/WindowsInstaller/Row.cs b/src/api/wix/WixToolset.Data/WindowsInstaller/Row.cs index f44082d3..cbb47492 100644 --- a/src/api/wix/WixToolset.Data/WindowsInstaller/Row.cs +++ b/src/api/wix/WixToolset.Data/WindowsInstaller/Row.cs @@ -52,17 +52,6 @@ namespace WixToolset.Data.WindowsInstaller /// The row transform operation. public RowOperation Operation { get; set; } - /// - /// Gets or sets wether the row is a duplicate of another row thus redundant. - /// - public bool Redundant { get; set; } - - /// - /// Gets or sets the SectionId property on the row. - /// - /// The SectionId property on the row. - public string SectionId { get; set; } - /// /// Gets the source file and line number for the row. /// @@ -276,8 +265,6 @@ namespace WixToolset.Data.WindowsInstaller bool empty = reader.IsEmptyElement; RowOperation operation = RowOperation.None; - bool redundant = false; - string sectionId = null; SourceLineNumber sourceLineNumbers = null; while (reader.MoveToNextAttribute()) @@ -287,12 +274,6 @@ namespace WixToolset.Data.WindowsInstaller case "op": operation = (RowOperation)Enum.Parse(typeof(RowOperation), reader.Value, true); break; - case "redundant": - redundant = reader.Value.Equals("yes"); - break; - case "sectionId": - sectionId = reader.Value; - break; case "sourceLineNumber": sourceLineNumbers = SourceLineNumber.CreateFromEncoded(reader.Value); break; @@ -301,8 +282,6 @@ namespace WixToolset.Data.WindowsInstaller var row = table.CreateRow(sourceLineNumbers); row.Operation = operation; - row.Redundant = redundant; - row.SectionId = sectionId; // loop through all the fields in a row if (!empty) @@ -364,16 +343,6 @@ namespace WixToolset.Data.WindowsInstaller writer.WriteAttributeString("op", this.Operation.ToString().ToLowerInvariant()); } - if (this.Redundant) - { - writer.WriteAttributeString("redundant", "yes"); - } - - if (null != this.SectionId) - { - writer.WriteAttributeString("sectionId", this.SectionId); - } - if (null != this.SourceLineNumbers) { writer.WriteAttributeString("sourceLineNumber", this.SourceLineNumbers.GetEncoded()); diff --git a/src/api/wix/WixToolset.Data/WindowsInstaller/Xsd/objects.xsd b/src/api/wix/WixToolset.Data/WindowsInstaller/Xsd/objects.xsd index 5d95a59c..94909032 100644 --- a/src/api/wix/WixToolset.Data/WindowsInstaller/Xsd/objects.xsd +++ b/src/api/wix/WixToolset.Data/WindowsInstaller/Xsd/objects.xsd @@ -96,8 +96,6 @@ - - diff --git a/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs index a54f05fc..0b31cdd7 100644 --- a/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs +++ b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs @@ -62,6 +62,13 @@ namespace WixToolset.Extensibility { } + /// + /// See + /// + public virtual void FinalizePatchFilterIds(WindowsInstallerData data, IDictionary rowToFilterId, string filterIdPrefix) + { + } + /// /// See /// diff --git a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendBinderExtension.cs b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendBinderExtension.cs index 067745c2..fdf753c7 100644 --- a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendBinderExtension.cs +++ b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendBinderExtension.cs @@ -29,6 +29,14 @@ namespace WixToolset.Extensibility /// The finalized intermediate section. void SymbolsFinalized(IntermediateSection section); + /// + /// Extension can process the filter ids applied to rows when processing patches. + /// + /// The WindowsInstallerData with rows to apply filters to. + /// The mapping that applies a filter id to a row. + /// The prefix to use applying additional filters to rows. + void FinalizePatchFilterIds(WindowsInstallerData data, IDictionary rowToFilterId, string filterIdPrefix); + /// /// Finds an existing cabinet that contains the provided files. /// diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs index 1bed65d5..89fc81db 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs @@ -71,11 +71,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind foreach (Row row in table.Rows) { - if (row.Redundant) - { - continue; - } - string rowString = this.RowToIdtDefinition(row, keepAddedColumns); byte[] rowBytes; diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs index 5cdafe7e..0d88cfd1 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs @@ -16,13 +16,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind internal class CreatePatchTransformsCommand { - public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection resolverExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection bindPaths) + public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection resolverExtensions, IReadOnlyCollection backendExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection bindPaths) { this.Messaging = messaging; this.BackendHelper = backendHelper; this.PathResolver = pathResolver; this.FileResolver = fileResolver; this.ResolverExtensions = resolverExtensions; + this.BackendExtensions = backendExtensions; this.Intermediate = intermediate; this.IntermediateFolder = intermediateFolder; this.BindPaths = bindPaths; @@ -38,16 +39,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind private IReadOnlyCollection ResolverExtensions { get; } + private IReadOnlyCollection BackendExtensions { get; } + private Intermediate Intermediate { get; } private string IntermediateFolder { get; } private IReadOnlyCollection BindPaths { get; } + public PatchFilterMap PatchFilterMap { get; private set; } + public IEnumerable PatchTransforms { get; private set; } public IEnumerable Execute() { + var patchFilterMap = new PatchFilterMap(); var patchTransforms = new List(); var symbols = this.Intermediate.Sections.SelectMany(s => s.Symbols); @@ -63,19 +69,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind if (patchRefSymbols.Count > 0) { - var targetCommand = new GenerateSectionIdsCommand(targetData); + var targetCommand = new GeneratePatchFilterIdsCommand(this.BackendExtensions, targetData, "target:"); targetCommand.Execute(); - var updatedCommand = new GenerateSectionIdsCommand(updatedData); + patchFilterMap.AddTargetRowFilterIds(targetCommand.RowToFilterId); + + var updatedCommand = new GeneratePatchFilterIdsCommand(this.BackendExtensions, updatedData, "updated:"); updatedCommand.Execute(); + + patchFilterMap.AddUpdatedRowFilterIds(updatedCommand.RowToFilterId); } - var command = new GenerateTransformCommand(this.Messaging, targetData, updatedData, preserveUnchangedRows: true, showPedanticMessages: false); + var command = new GenerateTransformCommand(this.Messaging, targetData, updatedData, patchFilterMap, preserveUnchangedRows: true, showPedanticMessages: false); var transform = command.Execute(); patchTransforms.Add(new PatchTransform(symbol.Id.Id, transform)); } + this.PatchFilterMap = patchFilterMap; this.PatchTransforms = patchTransforms; return this.PatchTransforms; diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GeneratePatchFilterIdsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GeneratePatchFilterIdsCommand.cs new file mode 100644 index 00000000..caddc5fa --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GeneratePatchFilterIdsCommand.cs @@ -0,0 +1,233 @@ +// 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.Globalization; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + /// + /// Creates section ids on rows which form logical groupings of resources. + /// + internal class GeneratePatchFilterIdsCommand + { + public GeneratePatchFilterIdsCommand(IReadOnlyCollection backendExtensions, WindowsInstallerData data, string filterIdPrefix) + { + this.BackendExtensions = backendExtensions; + this.Data = data; + this.FilterIdPrefix = filterIdPrefix; + } + + private IReadOnlyCollection BackendExtensions { get; } + + private WindowsInstallerData Data { get; } + + private string FilterIdPrefix { get; } + + public IDictionary RowToFilterId { get; private set; } + + public void Execute() + { + this.RowToFilterId = new Dictionary(); + + var output = this.Data; + + // First assign and index section ids for the tables that are in their own sections. + this.AssignFilterIdsToTable(output.Tables["Binary"], 0); + var componentSectionIdIndex = this.AssignFilterIdsToTable(output.Tables["Component"], 0); + var customActionSectionIdIndex = this.AssignFilterIdsToTable(output.Tables["CustomAction"], 0); + this.AssignFilterIdsToTable(output.Tables["Directory"], 0); + var featureSectionIdIndex = this.AssignFilterIdsToTable(output.Tables["Feature"], 0); + this.AssignFilterIdsToTable(output.Tables["Icon"], 0); + var digitalCertificateSectionIdIndex = this.AssignFilterIdsToTable(output.Tables["MsiDigitalCertificate"], 0); + this.AssignFilterIdsToTable(output.Tables["Property"], 0); + + // Now handle all the tables that rely on the first set of indexes but also produce their own indexes. Order matters here. + var fileFilterIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["File"], componentSectionIdIndex, 1, 0); + var appIdFilterIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["Class"], componentSectionIdIndex, 2, 5); + var odbcDataSourceFilterIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["ODBCDataSource"], componentSectionIdIndex, 1, 0); + var odbcDriverSectionIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["ODBCDriver"], componentSectionIdIndex, 1, 0); + var registrySectionIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["Registry"], componentSectionIdIndex, 5, 0); + var serviceInstallSectionIdIndex = this.ConnectTableToSectionAndIndex(output.Tables["ServiceInstall"], componentSectionIdIndex, 11, 0); + + // Now handle all the tables which only rely on previous indexes and order does not matter. + foreach (var table in output.Tables) + { + switch (table.Name) + { + case "MsiFileHash": + this.ConnectTableToFilterId(table, fileFilterIdIndex, 0); + break; + case "MsiAssembly": + case "MsiAssemblyName": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 0); + break; + case "MsiPackageCertificate": + case "MsiPatchCertificate": + this.ConnectTableToFilterId(table, digitalCertificateSectionIdIndex, 1); + break; + case "CreateFolder": + case "FeatureComponents": + case "MoveFile": + case "ReserveCost": + case "ODBCTranslator": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 1); + break; + case "TypeLib": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 2); + break; + case "Shortcut": + case "Environment": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 3); + break; + case "RemoveRegistry": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 4); + break; + case "ServiceControl": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 5); + break; + case "IniFile": + case "RemoveIniFile": + this.ConnectTableToFilterId(table, componentSectionIdIndex, 7); + break; + case "AppId": + this.ConnectTableToFilterId(table, appIdFilterIdIndex, 0); + break; + case "Condition": + this.ConnectTableToFilterId(table, featureSectionIdIndex, 0); + break; + case "ODBCSourceAttribute": + this.ConnectTableToFilterId(table, odbcDataSourceFilterIdIndex, 0); + break; + case "ODBCAttribute": + this.ConnectTableToFilterId(table, odbcDriverSectionIdIndex, 0); + break; + case "AdminExecuteSequence": + case "AdminUISequence": + case "AdvtExecuteSequence": + case "AdvtUISequence": + case "InstallExecuteSequence": + case "InstallUISequence": + this.ConnectTableToFilterId(table, customActionSectionIdIndex, 0); + break; + case "LockPermissions": + case "MsiLockPermissions": + foreach (var row in table.Rows) + { + var lockObject = row.FieldAsString(0); + var tableName = row.FieldAsString(1); + + var filterId = String.Empty; + switch (tableName) + { + case "File": + filterId = fileFilterIdIndex[lockObject]; + break; + case "Registry": + filterId = registrySectionIdIndex[lockObject]; + break; + case "ServiceInstall": + filterId = serviceInstallSectionIdIndex[lockObject]; + break; + } + + if (!String.IsNullOrEmpty(filterId)) + { + this.RowToFilterId.Add(row, filterId); + } + } + break; + } + } + + // Now pass the data to each backend extension to allow them to analyze the data and determine their proper filter ids. + foreach (var extension in this.BackendExtensions) + { + extension.FinalizePatchFilterIds(this.Data, this.RowToFilterId, this.FilterIdPrefix); + } + } + + private Dictionary AssignFilterIdsToTable(Table table, int rowPrimaryKeyIndex) + { + var primaryKeyToFilterId = new Dictionary(); + + if (null != table) + { + foreach (var row in table.Rows) + { + var filterId = this.GetNewFilterId(row); + + this.RowToFilterId.Add(row, filterId); + + primaryKeyToFilterId.Add(row.FieldAsString(rowPrimaryKeyIndex), filterId); + } + } + + return primaryKeyToFilterId; + } + + /// + /// Connects a table's rows to an already sectioned table. + /// + /// The table containing rows that need to be connected to sections. + /// A hashtable containing keys to map table to its section. + /// The index of the column which is used as the foreign key in to the sectionIdIndex. + private void ConnectTableToFilterId(Table table, Dictionary filterIdByPrimaryKey, int rowIndex) + { + if (null != table) + { + foreach (var row in table.Rows) + { + if (filterIdByPrimaryKey.TryGetValue(row.FieldAsString(rowIndex), out var filterId)) + { + this.RowToFilterId.Add(row, filterId); + } + } + } + } + + /// + /// Connects a table's rows to a table with filter ids already assigned and produces an index for other tables to connect to it. + /// + /// The table containing rows that need to be connected to sections. + /// A dictionary containing keys to map table to its section. + /// The index of the column which is used as the foreign key in to the sectionIdIndex. + /// The index of the column which is used by other tables to reference this table. + /// A dictionary containing the tables key for each row paired with its assigned section id. + private Dictionary ConnectTableToSectionAndIndex(Table table, Dictionary filterIdsByPrimaryKey, int rowIndex, int rowPrimaryKeyIndex) + { + var newPrimaryKeyToSectionId = new Dictionary(); + + if (null != table) + { + foreach (var row in table.Rows) + { + var foreignKey = row.FieldAsString(rowIndex); + + if (!filterIdsByPrimaryKey.TryGetValue(foreignKey, out var filterId)) + { + continue; + } + + this.RowToFilterId.Add(row, filterId); + + var primaryKey = row.FieldAsString(rowPrimaryKeyIndex); + + if (!String.IsNullOrEmpty(primaryKey) && filterIdsByPrimaryKey.ContainsKey(primaryKey)) + { + newPrimaryKeyToSectionId.Add(primaryKey, filterId); + } + } + } + + return newPrimaryKeyToSectionId; + } + + private string GetNewFilterId(Row row) + { + return this.FilterIdPrefix + row.Number.ToString(CultureInfo.InvariantCulture); + } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateSectionIdsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateSectionIdsCommand.cs deleted file mode 100644 index c7bebbed..00000000 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateSectionIdsCommand.cs +++ /dev/null @@ -1,225 +0,0 @@ -// 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.Globalization; - using WixToolset.Data.WindowsInstaller; - - /// - /// Creates section ids on rows which form logical groupings of resources. - /// - internal class GenerateSectionIdsCommand - { - private int sectionCount; - - public GenerateSectionIdsCommand(WindowsInstallerData data) - { - this.Data = data; - } - - private WindowsInstallerData Data { get; } - - public void Execute() - { - var output = this.Data; - - this.sectionCount = 0; - - // First assign and index section ids for the tables that are in their own sections. - this.AssignSectionIdsToTable(output.Tables["Binary"], 0); - var componentSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["Component"], 0); - var customActionSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["CustomAction"], 0); - this.AssignSectionIdsToTable(output.Tables["Directory"], 0); - var featureSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["Feature"], 0); - this.AssignSectionIdsToTable(output.Tables["Icon"], 0); - var digitalCertificateSectionIdIndex = this.AssignSectionIdsToTable(output.Tables["MsiDigitalCertificate"], 0); - this.AssignSectionIdsToTable(output.Tables["Property"], 0); - - // Now handle all the tables that rely on the first set of indexes but also produce their own indexes. Order matters here. - var fileSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["File"], componentSectionIdIndex, 1, 0); - var appIdSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Class"], componentSectionIdIndex, 2, 5); - var odbcDataSourceSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDataSource"], componentSectionIdIndex, 1, 0); - var odbcDriverSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ODBCDriver"], componentSectionIdIndex, 1, 0); - var registrySectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["Registry"], componentSectionIdIndex, 5, 0); - var serviceInstallSectionIdIndex = ConnectTableToSectionAndIndex(output.Tables["ServiceInstall"], componentSectionIdIndex, 11, 0); - - // Now handle all the tables which only rely on previous indexes and order does not matter. - foreach (var table in output.Tables) - { - switch (table.Name) - { - case "MsiFileHash": - ConnectTableToSection(table, fileSectionIdIndex, 0); - break; - case "MsiAssembly": - case "MsiAssemblyName": - ConnectTableToSection(table, componentSectionIdIndex, 0); - break; - case "MsiPackageCertificate": - case "MsiPatchCertificate": - ConnectTableToSection(table, digitalCertificateSectionIdIndex, 1); - break; - case "CreateFolder": - case "FeatureComponents": - case "MoveFile": - case "ReserveCost": - case "ODBCTranslator": - ConnectTableToSection(table, componentSectionIdIndex, 1); - break; - case "TypeLib": - ConnectTableToSection(table, componentSectionIdIndex, 2); - break; - case "Shortcut": - case "Environment": - ConnectTableToSection(table, componentSectionIdIndex, 3); - break; - case "RemoveRegistry": - ConnectTableToSection(table, componentSectionIdIndex, 4); - break; - case "ServiceControl": - ConnectTableToSection(table, componentSectionIdIndex, 5); - break; - case "IniFile": - case "RemoveIniFile": - ConnectTableToSection(table, componentSectionIdIndex, 7); - break; - case "AppId": - ConnectTableToSection(table, appIdSectionIdIndex, 0); - break; - case "Condition": - ConnectTableToSection(table, featureSectionIdIndex, 0); - break; - case "ODBCSourceAttribute": - ConnectTableToSection(table, odbcDataSourceSectionIdIndex, 0); - break; - case "ODBCAttribute": - ConnectTableToSection(table, odbcDriverSectionIdIndex, 0); - break; - case "AdminExecuteSequence": - case "AdminUISequence": - case "AdvtExecuteSequence": - case "AdvtUISequence": - case "InstallExecuteSequence": - case "InstallUISequence": - ConnectTableToSection(table, customActionSectionIdIndex, 0); - break; - case "LockPermissions": - case "MsiLockPermissions": - foreach (var row in table.Rows) - { - var lockObject = row.FieldAsString(0); - var tableName = row.FieldAsString(1); - switch (tableName) - { - case "File": - row.SectionId = fileSectionIdIndex[lockObject]; - break; - case "Registry": - row.SectionId = registrySectionIdIndex[lockObject]; - break; - case "ServiceInstall": - row.SectionId = serviceInstallSectionIdIndex[lockObject]; - break; - } - } - break; - } - } - - // Now pass the output to each unbinder extension to allow them to analyze the output and determine their proper section ids. - //foreach (IUnbinderExtension extension in this.unbinderExtensions) - //{ - // extension.GenerateSectionIds(output); - //} - } - - /// - /// Creates new section ids on all the rows in a table. - /// - /// The table to add sections to. - /// The index of the column which is used by other tables to reference this table. - /// A dictionary containing the tables key for each row paired with its assigned section id. - private Dictionary AssignSectionIdsToTable(Table table, int rowPrimaryKeyIndex) - { - var primaryKeyToSectionId = new Dictionary(); - - if (null != table) - { - foreach (var row in table.Rows) - { - row.SectionId = this.GetNewSectionId(); - - primaryKeyToSectionId.Add(row.FieldAsString(rowPrimaryKeyIndex), row.SectionId); - } - } - - return primaryKeyToSectionId; - } - - /// - /// Connects a table's rows to an already sectioned table. - /// - /// The table containing rows that need to be connected to sections. - /// A hashtable containing keys to map table to its section. - /// The index of the column which is used as the foreign key in to the sectionIdIndex. - private static void ConnectTableToSection(Table table, Dictionary sectionIdIndex, int rowIndex) - { - if (null != table) - { - foreach (var row in table.Rows) - { - if (sectionIdIndex.TryGetValue(row.FieldAsString(rowIndex), out var sectionId)) - { - row.SectionId = sectionId; - } - } - } - } - - /// - /// Connects a table's rows to an already sectioned table and produces an index for other tables to connect to it. - /// - /// The table containing rows that need to be connected to sections. - /// A dictionary containing keys to map table to its section. - /// The index of the column which is used as the foreign key in to the sectionIdIndex. - /// The index of the column which is used by other tables to reference this table. - /// A dictionary containing the tables key for each row paired with its assigned section id. - private static Dictionary ConnectTableToSectionAndIndex(Table table, Dictionary sectionIdIndex, int rowIndex, int rowPrimaryKeyIndex) - { - var newPrimaryKeyToSectionId = new Dictionary(); - - if (null != table) - { - foreach (var row in table.Rows) - { - var foreignKey = row.FieldAsString(rowIndex); - - if (!sectionIdIndex.TryGetValue(foreignKey, out var sectionId)) - { - continue; - } - - row.SectionId = sectionId; - - var primaryKey = row.FieldAsString(rowPrimaryKeyIndex); - - if (!String.IsNullOrEmpty(primaryKey) && sectionIdIndex.ContainsKey(primaryKey)) - { - newPrimaryKeyToSectionId.Add(primaryKey, row.SectionId); - } - } - } - - return newPrimaryKeyToSectionId; - } - - private string GetNewSectionId() - { - this.sectionCount++; - - return "wix.section." + this.sectionCount.ToString(CultureInfo.InvariantCulture); - } - } -} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs index 92a0e11f..4efc6a11 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs @@ -6,6 +6,7 @@ namespace WixToolset.Core.WindowsInstaller using System.Collections.Generic; using System.Globalization; using WixToolset.Core.Native.Msi; + using WixToolset.Core.WindowsInstaller.Bind; using WixToolset.Data; using WixToolset.Data.Symbols; using WixToolset.Data.WindowsInstaller; @@ -16,18 +17,18 @@ namespace WixToolset.Core.WindowsInstaller /// internal class GenerateTransformCommand { - private const char SectionDelimiter = '/'; private readonly IMessaging messaging; private SummaryInformationStreams transformSummaryInfo; /// /// Instantiates a new Differ class. /// - public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool preserveUnchangedRows, bool showPedanticMessages) + public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, PatchFilterMap patchFilterMap, bool preserveUnchangedRows, bool showPedanticMessages) { this.messaging = messaging; this.TargetOutput = targetOutput; this.UpdatedOutput = updatedOutput; + this.PatchFilterMap = patchFilterMap; this.PreserveUnchangedRows = preserveUnchangedRows; this.ShowPedanticMessages = showPedanticMessages; } @@ -36,6 +37,8 @@ namespace WixToolset.Core.WindowsInstaller private WindowsInstallerData UpdatedOutput { get; } + public PatchFilterMap PatchFilterMap { get; } + private TransformFlags ValidationFlags { get; } private bool ShowPedanticMessages { get; } @@ -112,7 +115,6 @@ namespace WixToolset.Core.WindowsInstaller foreach (var updatedRow in updatedTable.Rows) { updatedRow.Operation = RowOperation.Add; - updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; addedTable.Rows.Add(updatedRow); } } @@ -177,7 +179,6 @@ namespace WixToolset.Core.WindowsInstaller else if (null == updatedRow) { targetRow.Operation = RowOperation.Delete; - targetRow.SectionId += SectionDelimiter; comparedRow = targetRow; keepRow = true; @@ -189,9 +190,10 @@ namespace WixToolset.Core.WindowsInstaller if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) { // Include only summary information rows that are allowed in a transform. - if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) + if (Enum.IsDefined(typeof(SummaryInformation.Transform), updatedRow.FieldAsInteger(0))) { - updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; + this.PatchFilterMap.AddTargetRowFilterToUpdatedRowFilter(targetRow, updatedRow); + comparedRow = updatedRow; keepRow = true; } @@ -273,8 +275,9 @@ namespace WixToolset.Core.WindowsInstaller if (keepRow) { + this.PatchFilterMap.AddTargetRowFilterToUpdatedRowFilter(targetRow, updatedRow); + comparedRow = updatedRow; - comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; } } } @@ -340,7 +343,6 @@ namespace WixToolset.Core.WindowsInstaller var updatedRow = updatedPrimaryKeyEntry.Value; updatedRow.Operation = RowOperation.Add; - updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; rows.Add(updatedRow); } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs new file mode 100644 index 00000000..4822f3a5 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/PatchFilterMap.cs @@ -0,0 +1,80 @@ +// 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 WixToolset.Data.WindowsInstaller; + + internal class PatchFilterMap + { + private readonly Dictionary filterMap = new Dictionary(); + + public void AddTargetRowFilterIds(IEnumerable> rowFilterIds) + { + foreach (var kvp in rowFilterIds) + { + this.filterMap.Add(kvp.Key, new PatchFilter(kvp.Key, kvp.Value, null)); + } + } + + public void AddUpdatedRowFilterIds(IEnumerable> rowFilterIds) + { + foreach (var kvp in rowFilterIds) + { + this.filterMap.Add(kvp.Key, new PatchFilter(kvp.Key, null, kvp.Value)); + } + } + + public void AddTargetRowFilterToUpdatedRowFilter(Row targetRow, Row updatedRow) + { + if (this.filterMap.TryGetValue(targetRow, out var targetPatchFilter) && !String.IsNullOrEmpty(targetPatchFilter.TargetFilterId)) + { + // If the updated row didn't have a patch filter, it gets one now because the target patch has + // a target filter id to add. + if (!this.filterMap.TryGetValue(updatedRow, out var updatedPatchFilter)) + { + updatedPatchFilter = new PatchFilter(updatedRow, null, null); + this.filterMap.Add(updatedRow, updatedPatchFilter); + } + + updatedPatchFilter.SetTargetFilterId(targetPatchFilter); + } + } + + internal bool ContainsPatchFilterForRow(Row row) + { + return this.filterMap.ContainsKey(row); + } + + internal bool TryGetPatchFiltersForRow(Row row, out string targetFilterId, out string updatedFilterId) + { + this.filterMap.TryGetValue(row, out var patchFilter); + + targetFilterId = patchFilter?.TargetFilterId; + updatedFilterId = patchFilter?.UpdatedFilterId; + return patchFilter != null; + } + + private class PatchFilter + { + public PatchFilter(Row row, string targetFilterId, string updatedFilterId) + { + this.Row = row; + this.TargetFilterId = targetFilterId; + this.UpdatedFilterId = updatedFilterId; + } + + public Row Row { get; } + + public string TargetFilterId { get; private set; } + + public string UpdatedFilterId { get; } + + public void SetTargetFilterId(PatchFilter targetPatchFilter) + { + this.TargetFilterId = targetPatchFilter.TargetFilterId; + } + } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs index 4966a0b4..e7d93660 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ReduceTransformCommand.cs @@ -11,18 +11,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind internal class ReduceTransformCommand { - private const char SectionDelimiter = '/'; - - public ReduceTransformCommand(Intermediate intermediate, IEnumerable patchTransforms) + public ReduceTransformCommand(Intermediate intermediate, IEnumerable patchTransforms, PatchFilterMap patchFilterMap) { this.Intermediate = intermediate; this.PatchTransforms = patchTransforms; + this.PatchFilterMap = patchFilterMap; } private Intermediate Intermediate { get; } private IEnumerable PatchTransforms { get; } + private PatchFilterMap PatchFilterMap { get; } + public void Execute() { var symbols = this.Intermediate.Sections.SelectMany(s => s.Symbols).ToList(); @@ -51,8 +52,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind private bool ReduceTransform(WindowsInstallerData transform, IEnumerable patchRefSymbols) { // identify sections to keep - var oldSections = new Dictionary(); - var newSections = new Dictionary(); + var targetFilterIdsToKeep = new Dictionary(); + var updatedFilterIdsToKeep = new Dictionary(); var tableKeyRows = new Dictionary>(); var sequenceList = new List(); var componentFeatureAddsIndex = new Dictionary>(); @@ -72,10 +73,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind foreach (var patchRefSymbol in patchRefSymbols) { var tableName = patchRefSymbol.Table; - var key = patchRefSymbol.PrimaryKeys; + var primaryKey = patchRefSymbol.PrimaryKeys; // Short circuit filtering if all changes should be included. - if ("*" == tableName && "*" == key) + if ("*" == tableName && "*" == primaryKey) { RemoveProductCodeFromTransform(transform); return true; @@ -88,22 +89,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind } // Index the table. - if (!tableKeyRows.TryGetValue(tableName, out var keyRows)) + if (!tableKeyRows.TryGetValue(tableName, out var rowsByPrimaryKey)) { - keyRows = table.Rows.ToDictionary(r => r.GetPrimaryKey()); - tableKeyRows.Add(tableName, keyRows); + rowsByPrimaryKey = table.Rows.ToDictionary(r => r.GetPrimaryKey()); + tableKeyRows.Add(tableName, rowsByPrimaryKey); } - if (!keyRows.TryGetValue(key, out var row)) + if (!rowsByPrimaryKey.TryGetValue(primaryKey, out var row)) { // Row not found. continue; } // Differ.sectionDelimiter - var sections = row.SectionId.Split(SectionDelimiter); - oldSections[sections[0]] = row; - newSections[sections[1]] = row; + if (this.PatchFilterMap.TryGetPatchFiltersForRow(row, out var targetFilterId, out var updatedFilterId)) + { + targetFilterIdsToKeep[targetFilterId] = row; + updatedFilterIdsToKeep[updatedFilterId] = row; + } } // throw away sections not referenced @@ -221,49 +224,34 @@ namespace WixToolset.Core.WindowsInstaller.Bind } } - if (null == row.SectionId) + if (this.IsInPatchFamily(row, targetFilterIdsToKeep, updatedFilterIdsToKeep)) { - table.Rows.RemoveAt(i); - i--; - } - else - { - var sections = row.SectionId.Split(SectionDelimiter); - // ignore the row without section id. - if (0 == sections[0].Length && 0 == sections[1].Length) + if ("Component" == table.Name) { - table.Rows.RemoveAt(i); - i--; + keptComponents.Add(row.FieldAsString(0), row); } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) - { - if ("Component" == table.Name) - { - keptComponents.Add(row.FieldAsString(0), row); - } - if ("Directory" == table.Name) - { - keptDirectories.Add(row.FieldAsString(0), row); - } - - if ("Feature" == table.Name) - { - keptFeatures.Add(row.FieldAsString(0), row); - } - - keptRows++; + if ("Directory" == table.Name) + { + keptDirectories.Add(row.FieldAsString(0), row); } - else + + if ("Feature" == table.Name) { - table.Rows.RemoveAt(i); - i--; + keptFeatures.Add(row.FieldAsString(0), row); } + + keptRows++; + } + else + { + table.Rows.RemoveAt(i); + i--; } } } - keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable); + keptRows += ReduceTransformSequenceTable(sequenceList, targetFilterIdsToKeep, updatedFilterIdsToKeep, customActionTable); if (null != directoryTable) { @@ -345,7 +333,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } } - keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable); + keptRows += ReduceTransformSequenceTable(sequenceList, targetFilterIdsToKeep, updatedFilterIdsToKeep, customActionTable); // Delete tables that are empty. var tablesToDelete = transform.Tables.Where(t => t.Rows.Count == 0).Select(t => t.Name).ToList(); @@ -358,6 +346,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind return keptRows > 0; } + private bool IsInPatchFamily(Row row, Dictionary oldSections, Dictionary newSections) + { + var result = false; + + if (this.PatchFilterMap.TryGetPatchFiltersForRow(row, out var targetFilterId, out var updatedFilterId)) + { + if ((String.IsNullOrEmpty(targetFilterId) && newSections.ContainsKey(updatedFilterId)) || (String.IsNullOrEmpty(updatedFilterId) && oldSections.ContainsKey(targetFilterId))) + { + result = true; + } + else if (!String.IsNullOrEmpty(targetFilterId) && !String.IsNullOrEmpty(updatedFilterId) && (oldSections.ContainsKey(targetFilterId) || newSections.ContainsKey(updatedFilterId))) + { + result = true; + } + } + + return result; + } + /// /// Check if the section is in a PatchFamily. /// @@ -415,7 +422,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind /// Hashtable contains section id should be kept in the target wixout. /// Hashtable contains all the rows in the CustomAction table. /// Number of rows left - private static int ReduceTransformSequenceTable(List
sequenceList, Dictionary oldSections, Dictionary newSections, Dictionary customAction) + private int ReduceTransformSequenceTable(List
sequenceList, Dictionary oldSections, Dictionary newSections, Dictionary customAction) { var keptRows = 0; @@ -424,19 +431,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind for (var i = 0; i < currentTable.Rows.Count; i++) { var row = currentTable.Rows[i]; - var actionName = row.Fields[0].Data.ToString(); - var sections = row.SectionId.Split(SectionDelimiter); - var isSectionIdEmpty = (sections[0].Length == 0 && sections[1].Length == 0); + var actionName = row.FieldAsString(0); if (row.Operation == RowOperation.None) { - // Ignore the rows without section id. - if (isSectionIdEmpty) - { - currentTable.Rows.RemoveAt(i); - i--; - } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) + if (this.IsInPatchFamily(row, oldSections, newSections)) { keptRows++; } @@ -457,12 +456,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else if (!sequenceChanged && conditionChanged) { - if (isSectionIdEmpty) - { - currentTable.Rows.RemoveAt(i); - i--; - } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) + if (this.IsInPatchFamily(row, oldSections, newSections)) { keptRows++; } @@ -474,12 +468,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else if (sequenceChanged && conditionChanged) { - if (isSectionIdEmpty) - { - row.Fields[1].Modified = false; - keptRows++; - } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) + if (this.IsInPatchFamily(row, oldSections, newSections)) { keptRows++; } @@ -492,13 +481,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else if (row.Operation == RowOperation.Delete) { - if (isSectionIdEmpty) - { - // it is a stardard action which is added by wix, we should keep this action. - row.Operation = RowOperation.None; - keptRows++; - } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) + if (this.IsInPatchFamily(row, oldSections, newSections)) { keptRows++; } @@ -519,11 +502,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind } else if (row.Operation == RowOperation.Add) { - if (isSectionIdEmpty) + // Keep unfiltered added rows. + if (!this.PatchFilterMap.ContainsPatchFilterForRow(row)) { keptRows++; } - else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) + else if (this.IsInPatchFamily(row, oldSections, newSections)) { keptRows++; } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs index f4e4a1fc..e4cfe22d 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Differ.cs @@ -17,7 +17,6 @@ namespace WixToolset.Core.WindowsInstaller /// public sealed class Differ { - private const char SectionDelimiter = '/'; private readonly IMessaging messaging; private SummaryInformationStreams transformSummaryInfo; @@ -111,7 +110,6 @@ namespace WixToolset.Core.WindowsInstaller foreach (var updatedRow in updatedTable.Rows) { updatedRow.Operation = RowOperation.Add; - updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; addedTable.Rows.Add(updatedRow); } } @@ -200,7 +198,6 @@ namespace WixToolset.Core.WindowsInstaller else if (null == updatedRow) { operation = targetRow.Operation = RowOperation.Delete; - targetRow.SectionId += SectionDelimiter; comparedRow = targetRow; keepRow = true; } @@ -211,9 +208,8 @@ namespace WixToolset.Core.WindowsInstaller if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) { // ignore rows that shouldn't be in a transform - if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) + if (Enum.IsDefined(typeof(SummaryInformation.Transform), updatedRow.FieldAsInteger(0))) { - updatedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; comparedRow = updatedRow; keepRow = true; operation = RowOperation.Modify; @@ -297,7 +293,7 @@ namespace WixToolset.Core.WindowsInstaller if (keepRow) { comparedRow = updatedRow; - comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; + //comparedRow.SectionId = targetRow.SectionId + SectionDelimiter + updatedRow.SectionId; } } } @@ -361,7 +357,6 @@ namespace WixToolset.Core.WindowsInstaller var updatedRow = (Row)updatedPrimaryKeyEntry.Value; updatedRow.Operation = RowOperation.Add; - updatedRow.SectionId = SectionDelimiter + updatedRow.SectionId; rows.Add(updatedRow); } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs index f372af82..4ade5b1d 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerBackendHelper.cs @@ -141,10 +141,7 @@ namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices { var table = data.EnsureTable(tableDefinition); - var row = table.CreateRow(symbol.SourceLineNumbers); - row.SectionId = section.Id; - - return row; + return table.CreateRow(symbol.SourceLineNumbers); } public bool TryAddSymbolToMatchingTableDefinitions(IntermediateSection section, IntermediateSymbol symbol, WindowsInstallerData data, TableDefinitionCollection tableDefinitions) diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs index d1d7c19b..ace382de 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs @@ -34,14 +34,18 @@ namespace WixToolset.Core.WindowsInstaller // Create transforms named in patch transforms. IEnumerable patchTransforms; + PatchFilterMap patchFilterMap; { - var command = new CreatePatchTransformsCommand(messaging, backendHelper, pathResolver, fileResolver, resolveExtensions, context.IntermediateRepresentation, context.IntermediateFolder, context.BindPaths); - patchTransforms = command.Execute(); + var command = new CreatePatchTransformsCommand(messaging, backendHelper, pathResolver, fileResolver, resolveExtensions, backendExtensions, context.IntermediateRepresentation, context.IntermediateFolder, context.BindPaths); + command.Execute(); + + patchTransforms = command.PatchTransforms; + patchFilterMap = command.PatchFilterMap; } // Reduce transforms. { - var command = new ReduceTransformCommand(context.IntermediateRepresentation, patchTransforms); + var command = new ReduceTransformCommand(context.IntermediateRepresentation, patchTransforms, patchFilterMap); command.Execute(); } -- cgit v1.2.3-55-g6feb