From c86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 23 Mar 2022 10:25:49 -0700 Subject: Make "decompile" an MSI-only command instead of a backend requirement As much as I'd like decompiling to be global functionality provided by all backends there are only two output types that support decompiling: MSI and MSM. In the future, perhaps we can invest in a generic symbols to source code converter that would reduce the redundant work backends need to do today. Until then, make decompile an MSI specific command --- .../Data/IDecompileContext.cs | 59 ------ .../Data/IDecompileResult.cs | 18 -- .../Data/IWindowsInstallerDecompileContext.cs | 95 ++++++++++ .../Data/IWindowsInstallerDecompileResult.cs | 18 ++ src/api/wix/WixToolset.Extensibility/IBackend.cs | 3 - .../WixToolset.Extensibility/IBackendFactory.cs | 6 +- .../IDecompilerExtension.cs | 22 --- .../IWindowsInstallerBackendDecompilerExtension.cs | 26 --- .../IWindowsInstallerDecompilerExtension.cs | 26 +++ .../Services/ICommandLineParser.cs | 97 ++++++++-- src/wix/WixToolset.Core.Burn/BundleBackend.cs | 6 - .../CommandLine/DecompilerSubcommand.cs | 185 +++++++++++++++++++ .../CommandLine/WindowsInstallerCommand.cs | 4 + .../Decompile/DecompileMsiOrMsmCommand.cs | 17 +- .../Decompile/Decompiler.cs | 125 +++++++------ .../IWindowsInstallerDecompiler.cs | 19 ++ .../WixToolset.Core.WindowsInstaller/MsiBackend.cs | 22 --- .../WixToolset.Core.WindowsInstaller/MsmBackend.cs | 26 --- .../WixToolset.Core.WindowsInstaller/MspBackend.cs | 5 - .../WindowsInstallerDecompileContext.cs | 50 +++++ .../WindowsInstallerDecompileResult.cs | 18 ++ .../WindowsInstallerDecompiler.cs | 50 +++++ .../WixToolsetCoreServiceProviderExtensions.cs | 6 + src/wix/WixToolset.Core/CommandLine/CommandLine.cs | 23 +-- .../CommandLine/CommandLineParser.cs | 25 ++- .../CommandLine/DecompileCommand.cs | 202 --------------------- src/wix/WixToolset.Core/DecompileContext.cs | 49 ----- src/wix/WixToolset.Core/DecompileResult.cs | 18 -- src/wix/WixToolset.Core/Decompiler.cs | 68 ------- src/wix/WixToolset.Core/IDecompiler.cs | 12 -- .../WixToolset.Core/WixToolsetServiceProvider.cs | 3 - .../ProductPackageFixture.cs | 2 +- .../CustomTableFixture.cs | 2 +- .../DecompileFixture.cs | 2 +- 34 files changed, 666 insertions(+), 643 deletions(-) delete mode 100644 src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs delete mode 100644 src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs create mode 100644 src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs create mode 100644 src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs delete mode 100644 src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs delete mode 100644 src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs create mode 100644 src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs delete mode 100644 src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs delete mode 100644 src/wix/WixToolset.Core/DecompileContext.cs delete mode 100644 src/wix/WixToolset.Core/DecompileResult.cs delete mode 100644 src/wix/WixToolset.Core/Decompiler.cs delete mode 100644 src/wix/WixToolset.Core/IDecompiler.cs (limited to 'src') diff --git a/src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs deleted file mode 100644 index fe7d0465..00000000 --- a/src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs +++ /dev/null @@ -1,59 +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.Extensibility.Data -{ - using System; - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Extensibility.Services; - -#pragma warning disable 1591 // TODO: add documentation - public interface IDecompileContext - { - IServiceProvider ServiceProvider { get; } - - string DecompilePath { get; set; } - - OutputType DecompileType { get; set; } - - IReadOnlyCollection Extensions { get; set; } - - string ExtractFolder { get; set; } - - string CabinetExtractFolder { get; set; } - - /// - /// Optional gets or sets the base path for the File/@Source. - /// - /// Default value is "SourceDir" to enable use of BindPaths. - string BaseSourcePath { get; set; } - - string IntermediateFolder { get; set; } - - bool IsAdminImage { get; set; } - - string OutputPath { get; set; } - - /// - /// Gets or sets the option to suppress custom tables. - /// - bool SuppressCustomTables { get; set; } - - /// - /// Gets or sets the option to suppress dropping empty tables. - /// - bool SuppressDroppingEmptyTables { get; set; } - - bool SuppressExtractCabinets { get; set; } - - /// - /// Gets or sets the option to suppress decompiling UI-related tables. - /// - bool SuppressUI { get; set; } - - /// - /// Gets or sets whether the decompiler should use module logic on a product output. - /// - bool TreatProductAsModule { get; set; } - } -} diff --git a/src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs b/src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs deleted file mode 100644 index cffd0976..00000000 --- a/src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs +++ /dev/null @@ -1,18 +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.Extensibility.Data -{ - using System.Collections.Generic; - using System.Xml.Linq; - using WixToolset.Data; - -#pragma warning disable 1591 // TODO: add documentation - public interface IDecompileResult - { - XDocument Document { get; set; } - - IReadOnlyCollection ExtractedFilePaths { get; set; } - - Platform? Platform { get; set; } - } -} diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs new file mode 100644 index 00000000..f744121a --- /dev/null +++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs @@ -0,0 +1,95 @@ +// 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.Extensibility.Data +{ + using System; + using System.Collections.Generic; + using WixToolset.Data; + + /// + /// The context used to decompile Windows Installer packages. + /// + public interface IWindowsInstallerDecompileContext + { + /// + /// Gets or sets the service provider. + /// + IServiceProvider ServiceProvider { get; } + + /// + /// Gets or sets the path to the file to decompile. + /// + string DecompilePath { get; set; } + + /// + /// Gets or sets the type to decompile. + /// + OutputType DecompileType { get; set; } + + /// + /// Gets or sets the decompiler extensions. + /// + IReadOnlyCollection Extensions { get; set; } + + /// + /// Gets or sets the folder where content is extracted. + /// + string ExtractFolder { get; set; } + + /// + /// Gets or sets the folder where files are extracted. + /// + string CabinetExtractFolder { get; set; } + + /// + /// Optional gets or sets the base path for the File/@Source. + /// + /// Default value is "SourceDir" to enable use of BindPaths. + string BaseSourcePath { get; set; } + + /// + /// Gets or sets the intermediate folder. + /// + string IntermediateFolder { get; set; } + + /// + /// Gets or sets whether the decompiler admin image. + /// + bool IsAdminImage { get; set; } + + /// + /// Gets or sets where to output the result. + /// + string OutputPath { get; set; } + + /// + /// Gets or sets the option to suppress custom tables. + /// + bool SuppressCustomTables { get; set; } + + /// + /// Gets or sets the option to suppress dropping empty tables. + /// + bool SuppressDroppingEmptyTables { get; set; } + + /// + /// Gets or sets whether to prevent extract cabinets. + /// + bool SuppressExtractCabinets { get; set; } + + /// + /// Gets or sets whether to suppress relative action sequencing. + /// + bool SuppressRelativeActionSequencing { get; set; } + + /// + /// Gets or sets the option to suppress decompiling UI-related tables. + /// + bool SuppressUI { get; set; } + + /// + /// Gets or sets whether the decompiler should use module logic on a product output. + /// + bool TreatProductAsModule { get; set; } + } +} diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs new file mode 100644 index 00000000..3b1dd815 --- /dev/null +++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs @@ -0,0 +1,18 @@ +// 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.Extensibility.Data +{ + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data; + +#pragma warning disable 1591 // TODO: add documentation + public interface IWindowsInstallerDecompileResult + { + XDocument Document { get; set; } + + IReadOnlyCollection ExtractedFilePaths { get; set; } + + Platform? Platform { get; set; } + } +} diff --git a/src/api/wix/WixToolset.Extensibility/IBackend.cs b/src/api/wix/WixToolset.Extensibility/IBackend.cs index cb151e05..720abe15 100644 --- a/src/api/wix/WixToolset.Extensibility/IBackend.cs +++ b/src/api/wix/WixToolset.Extensibility/IBackend.cs @@ -15,8 +15,5 @@ namespace WixToolset.Extensibility /// Bind context. /// Result of the bind operation. IBindResult Bind(IBindContext context); - -#pragma warning disable 1591 // TODO: add documentation - IDecompileResult Decompile(IDecompileContext context); } } diff --git a/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs b/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs index 7f9ef62d..8e84f5b6 100644 --- a/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs +++ b/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs @@ -3,7 +3,7 @@ namespace WixToolset.Extensibility { /// - /// Implemented by extensions to create backends. + /// Implemented by extensions that are backends. /// public interface IBackendFactory { @@ -12,8 +12,8 @@ namespace WixToolset.Extensibility /// /// Type of output being created. /// Path to the output to create. - /// The backend for the output. + /// The backend for the output. /// True if the backend was created, otherwise false. - bool TryCreateBackend(string outputType, string outputPath, out IBackend backend); + bool TryCreateBackend(string outputType, string outputPath, out IBackend binder); } } diff --git a/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs deleted file mode 100644 index 24ef3bff..00000000 --- a/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs +++ /dev/null @@ -1,22 +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.Extensibility -{ - using WixToolset.Extensibility.Data; - - /// - /// Base class for creating a decompiler extension. - /// - public interface IDecompilerExtension - { - /// - /// Called before decompiling occurs. - /// - void PreDecompile(IDecompileContext context); - - /// - /// Called after all decompiling occurs. - /// - void PostDecompile(IDecompileResult result); - } -} diff --git a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs deleted file mode 100644 index a56b63c3..00000000 --- a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs +++ /dev/null @@ -1,26 +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.Extensibility -{ - using WixToolset.Data; - using WixToolset.Data.WindowsInstaller; - using WixToolset.Extensibility.Data; - - /// - /// Interface all binder extensions implement. - /// - public interface IWindowsInstallerBackendDecompilerExtension - { - /// - /// Called before decompiling occurs. - /// - void PreBackendDecompile(IDecompileContext context); - - // TODO: Redesign this interface to be useful. - - /// - /// Called after all output changes occur and right before the output is bound into its final format. - /// - void PostBackendDecompile(IDecompileResult result); - } -} diff --git a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs new file mode 100644 index 00000000..add5f886 --- /dev/null +++ b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs @@ -0,0 +1,26 @@ +// 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.Extensibility +{ + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility.Data; + + /// + /// Interface all binder extensions implement. + /// + public interface IWindowsInstallerDecompilerExtension + { + /// + /// Called before decompiling occurs. + /// + void PreDecompile(IWindowsInstallerDecompileContext context); + + // TODO: Redesign this interface to be useful. + + /// + /// Called after all output changes occur and right before the output is bound into its final format. + /// + void PostDecompile(IWindowsInstallerDecompileResult result); + } +} diff --git a/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs b/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs index cd17f100..efd6600d 100644 --- a/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs +++ b/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs @@ -5,36 +5,111 @@ namespace WixToolset.Extensibility.Services using System.Collections.Generic; using WixToolset.Data; -#pragma warning disable 1591 // TODO: add documentation + /// + /// Provides the command-line arguments. + /// public interface ICommandLineParser { + /// + /// Gets the argument that caused the error. + /// string ErrorArgument { get; } /// /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity /// - /// The string check. + /// The string check. /// True if a valid switch, otherwise false. - bool IsSwitch(string arg); + bool IsSwitch(string argument); + /// + /// Gets the current argument as a file or displays an error. + /// + /// Current argument used in the error message if necessary. + /// Type of file displayed in the error message if necessary. + /// The fully expanded path if the argument is a file path, otherwise null. string GetArgumentAsFilePathOrError(string argument, string fileType); - void GetArgumentAsFilePathOrError(string argument, string fileType, IList paths); + /// + /// Adds the current argument as a file to the list or displays an error. + /// + /// Current argument used in the error message if necessary. + /// Type of file displayed in the error message if necessary. + /// List to add the fully expanded path if the argument is a file path. + /// True if the argument is a file path, otherwise false. + bool GetArgumentAsFilePathOrError(string argument, string fileType, IList paths); - string GetNextArgumentOrError(string commandLineSwitch); + /// + /// Gets the next argument or displays error if no argument is available. + /// + /// Current argument used in the error message if necessary. + /// The next argument if present or null + string GetNextArgumentOrError(string argument); - bool GetNextArgumentOrError(string commandLineSwitch, IList argument); + /// + /// Adds the next argument to a list or displays error if no argument is available. + /// + /// Current argument used in the error message if necessary. + /// List to add the argument to. + /// True if an argument is available, otherwise false. + bool GetNextArgumentOrError(string argument, IList arguments); - string GetNextArgumentAsDirectoryOrError(string commandLineSwitch); + /// + /// Gets the next argument as a directory or displays an error. + /// + /// Current argument used in the error message if necessary. + /// The fully expanded path if the argument is a directory, otherwise null. + string GetNextArgumentAsDirectoryOrError(string argument); - bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList directories); + /// + /// Adds the next argument as a directory to the list or displays an error. + /// + /// Current argument used in the error message if necessary. + /// List to add the fully expanded directory if the argument is a file path. + /// True if the argument is a directory, otherwise false. + bool GetNextArgumentAsDirectoryOrError(string argument, IList directories); - string GetNextArgumentAsFilePathOrError(string commandLineSwitch); + /// + /// Gets the next argument as a file or displays an error. + /// + /// Current argument used in the error message if necessary. + /// The fully expanded path if the argument is a file path, otherwise null. + string GetNextArgumentAsFilePathOrError(string argument); - bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList paths); + /// + /// Adds the next argument as a file to the list or displays an error. + /// + /// Current argument used in the error message if necessary. + /// Type of file displayed in the error message if necessary. + /// List to add the fully expanded path if the argument is a file path. + /// True if the argument is a file path, otherwise false. + bool GetNextArgumentAsFilePathOrError(string argument, string fileType, IList paths); + /// + /// Reports a command line error for the provided argument. + /// + /// Argument that caused the error. + /// Message to report. void ReportErrorArgument(string argument, Message message = null); - bool TryGetNextSwitchOrArgument(out string arg); + /// + /// Tries to get the next argument. + /// + /// Next argument if available. + /// True if argument is available, otherwise false. + bool TryGetNextSwitchOrArgument(out string argument); + + /// + /// Looks ahead to the next argument without moving to the next argument. + /// + /// Next argument if available, otherwise null. + string PeekNextArgument(); + + /// + /// Tries to looks ahead to the next argument without moving to the next argument. + /// + /// Argument found if present. + /// True if argument is found, otherwise false. + bool TryPeekNextArgument(out string argument); } } diff --git a/src/wix/WixToolset.Core.Burn/BundleBackend.cs b/src/wix/WixToolset.Core.Burn/BundleBackend.cs index a2003989..cf1971f6 100644 --- a/src/wix/WixToolset.Core.Burn/BundleBackend.cs +++ b/src/wix/WixToolset.Core.Burn/BundleBackend.cs @@ -2,7 +2,6 @@ namespace WixToolset.Core.Burn { - using System; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; @@ -35,10 +34,5 @@ namespace WixToolset.Core.Burn return result; } - - public IDecompileResult Decompile(IDecompileContext context) - { - throw new NotImplementedException(); - } } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs new file mode 100644 index 00000000..19d1c738 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs @@ -0,0 +1,185 @@ +// 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.CommandLine +{ + using System; + using System.IO; + using System.Threading; + using System.Threading.Tasks; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + internal class DecompilerSubcommand : WindowsInstallerSubcommandBase + { + public DecompilerSubcommand(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + this.Messaging = serviceProvider.GetService(); + } + + private IServiceProvider ServiceProvider { get; } + + private IMessaging Messaging { get; } + + private string InputPath { get; set; } + + private string DecompileType { get; set; } + + private string IntermediateFolder { get; set; } + + private string OutputPath { get; set; } + + private string ExportBasePath { get; set; } + + private bool SuppressCustomTables { get; set; } + + private bool SuppressDroppingEmptyTables { get; set; } + + private bool SuppressRelativeActionSequencing { get; set; } + + private bool SuppressUI { get; set; } + + public override Task ExecuteAsync(CancellationToken cancellationToken) + { + if (String.IsNullOrEmpty(this.InputPath)) + { + Console.Error.WriteLine("Input MSI or MSM database is required"); + return Task.FromResult(-1); + } + + if (!this.TryCalculateDecompileType(out var decompileType)) + { + Console.Error.WriteLine("Unknown output type '{0}' from input: {1}", decompileType, this.InputPath); + return Task.FromResult(-1); + } + + if (String.IsNullOrEmpty(this.IntermediateFolder)) + { + this.IntermediateFolder = Path.GetTempPath(); + } + + if (String.IsNullOrEmpty(this.OutputPath)) + { + this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs"); + } + + var context = this.ServiceProvider.GetService(); + context.Extensions = this.ServiceProvider.GetService().GetServices(); + context.DecompilePath = this.InputPath; + context.DecompileType = decompileType; + context.IntermediateFolder = this.IntermediateFolder; + context.OutputPath = this.OutputPath; + + context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder; + context.SuppressCustomTables = this.SuppressCustomTables; + context.SuppressDroppingEmptyTables = this.SuppressDroppingEmptyTables; + context.SuppressRelativeActionSequencing = this.SuppressRelativeActionSequencing; + context.SuppressUI = this.SuppressUI; + + try + { + var decompiler = this.ServiceProvider.GetService(); + var result = decompiler.Decompile(context); + + if (!this.Messaging.EncounteredError) + { + Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath))); + result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces); + } + } + catch (WixException e) + { + this.Messaging.Write(e.Error); + } + + return Task.FromResult(this.Messaging.LastErrorNumber); + } + + public override bool TryParseArgument(ICommandLineParser parser, string argument) + { + if (parser.IsSwitch(argument)) + { + var parameter = argument.Substring(1); + switch (parameter.ToLowerInvariant()) + { + case "intermediatefolder": + this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); + return true; + + case "o": + case "out": + this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument); + return true; + + case "sct": + this.SuppressCustomTables = true; + return true; + + case "sdet": + this.SuppressDroppingEmptyTables = true; + return true; + + case "sras": + this.SuppressRelativeActionSequencing = true; + return true; + + case "sui": + this.SuppressUI = true; + return true; + + case "type": + this.DecompileType = parser.GetNextArgumentOrError(argument); + return true; + + case "x": + // Peek ahead to get the actual value provided on the command-line so the authoring + // matches what they typed on the command-line. + var originalExportBasePath = parser.PeekNextArgument(); + parser.GetNextArgumentAsDirectoryOrError(argument); // ensure we actually got a directory. + + this.ExportBasePath = originalExportBasePath; + return true; + } + } + else if (String.IsNullOrEmpty(this.InputPath)) + { + this.InputPath = argument; + return true; + } + + return false; + } + + private bool TryCalculateDecompileType(out OutputType decompileType) + { + decompileType = OutputType.Unknown; + + if (String.IsNullOrEmpty(this.DecompileType)) + { + this.DecompileType = Path.GetExtension(this.InputPath); + } + + switch (this.DecompileType.ToLowerInvariant()) + { + case "product": + case "package": + case "msi": + case ".msi": + decompileType = OutputType.Product; + break; + + case "mergemodule": + case "module": + case "msm": + case ".msm": + decompileType = OutputType.Module; + break; + } + + return decompileType != OutputType.Unknown; + } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs index ed0c0658..f11020c8 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs @@ -45,6 +45,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine { switch (argument.ToLowerInvariant()) { + case "decompile": + this.Subcommand = new DecompilerSubcommand(this.ServiceProvider); + return true; + case "inscribe": this.Subcommand = new InscribeSubcommand(this.ServiceProvider); return true; diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs index aeda4443..5001828d 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs @@ -1,4 +1,4 @@ -// 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. + // 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.Decompile { @@ -11,28 +11,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile using WixToolset.Core.WindowsInstaller.Unbind; using WixToolset.Data; using WixToolset.Data.WindowsInstaller; - using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; internal class DecompileMsiOrMsmCommand { - public DecompileMsiOrMsmCommand(IDecompileContext context, IEnumerable backendExtensions) + public DecompileMsiOrMsmCommand(IWindowsInstallerDecompileContext context) { this.Context = context; - this.Extensions = backendExtensions; this.Messaging = context.ServiceProvider.GetService(); } - private IDecompileContext Context { get; } - - private IEnumerable Extensions { get; } + private IWindowsInstallerDecompileContext Context { get; } private IMessaging Messaging { get; } - public IDecompileResult Execute() + public IWindowsInstallerDecompileResult Execute() { - var result = this.Context.ServiceProvider.GetService(); + var result = this.Context.ServiceProvider.GetService(); try { @@ -50,7 +46,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var output = unbindCommand.Execute(); var extractedFilePaths = new List(unbindCommand.ExportedFiles); - var decompiler = new Decompiler(this.Messaging, backendHelper, this.Extensions, this.Context.BaseSourcePath, this.Context.SuppressCustomTables, this.Context.SuppressDroppingEmptyTables, this.Context.SuppressUI, this.Context.TreatProductAsModule); + var decompiler = new Decompiler(this.Messaging, backendHelper, this.Context.Extensions, this.Context.BaseSourcePath, this.Context.SuppressCustomTables, this.Context.SuppressDroppingEmptyTables, this.Context.SuppressRelativeActionSequencing, this.Context.SuppressUI, this.Context.TreatProductAsModule); result.Document = decompiler.Decompile(output); result.Platform = GetPlatformFromOutput(output); @@ -90,7 +86,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1); return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';')); - } } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs index 9c338293..4ccfaaa5 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs @@ -43,7 +43,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// /// Creates a new decompiler object with a default set of table definitions. /// - public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressUI, bool treatProductAsModule) + public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule) { this.Messaging = messaging; this.BackendHelper = backendHelper; @@ -51,10 +51,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.BaseSourcePath = baseSourcePath ?? "SourceDir"; this.SuppressCustomTables = suppressCustomTables; this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables; + this.SuppressRelativeActionSequencing = suppressRelativeActionSequencing; this.SuppressUI = suppressUI; this.TreatProductAsModule = treatProductAsModule; - this.ExtensionsByTableName = new Dictionary(); + this.ExtensionsByTableName = new Dictionary(); this.StandardActions = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id); this.TableDefinitions = new TableDefinitionCollection(); @@ -64,9 +65,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile private IBackendHelper BackendHelper { get; } - private IEnumerable Extensions { get; } + private IEnumerable Extensions { get; } - private Dictionary ExtensionsByTableName { get; } + private Dictionary ExtensionsByTableName { get; } private string BaseSourcePath { get; } @@ -238,7 +239,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// /// The row corresponding to the element. /// The indexed element. - private XElement GetIndexedElement(WixToolset.Data.WindowsInstaller.Row row) => this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); + private XElement GetIndexedElement(Row row) + { + return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); + } /// /// Gets the element corresponding to the primary key of the given table. @@ -246,7 +250,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// The table corresponding to the element. /// The primary key corresponding to the element. /// The indexed element. - private XElement GetIndexedElement(string table, params string[] primaryKey) => this.IndexedElements[String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey))]; + private XElement GetIndexedElement(string table, params string[] primaryKey) + { + return this.TryGetIndexedElement(table, out var element, primaryKey) ? element : null; + } /// /// Tries to get the element corresponding to the primary key of the given table. @@ -254,7 +261,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// The table corresponding to the element. /// The indexed element. /// Whether the element was found. - private bool TryGetIndexedElement(WixToolset.Data.WindowsInstaller.Row row, out XElement xElement) => this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); + private bool TryGetIndexedElement(Row row, out XElement xElement) + { + return this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); + } /// /// Tries to get the element corresponding to the primary key of the given table. @@ -263,14 +273,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// The indexed element. /// The primary key corresponding to the element. /// Whether the element was found. - private bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey) => this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement); + private bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey) + { + return this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement); + } /// /// Index an element by its corresponding row. /// /// The row corresponding to the element. /// The element to index. - private void IndexElement(WixToolset.Data.WindowsInstaller.Row row, XElement element) + private void IndexElement(Row row, XElement element) { this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element); } @@ -293,9 +306,15 @@ namespace WixToolset.Core.WindowsInstaller.Decompile .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList()); } - private Dictionary> IndexTableOneToMany(TableIndexedCollection tables, string tableName, int column = 0) => this.IndexTableOneToMany(tables[tableName]?.Rows ?? Enumerable.Empty(), column); + private Dictionary> IndexTableOneToMany(TableIndexedCollection tables, string tableName, int column = 0) + { + return this.IndexTableOneToMany(tables[tableName]?.Rows ?? Enumerable.Empty(), column); + } - private Dictionary> IndexTableOneToMany(Table table, int column = 0) => this.IndexTableOneToMany(table?.Rows ?? Enumerable.Empty(), column); + private Dictionary> IndexTableOneToMany(Table table, int column = 0) + { + return this.IndexTableOneToMany(table?.Rows ?? Enumerable.Empty(), column); + } private void AddChildToParent(string parentName, XElement xChild, Row row, int column) { @@ -310,7 +329,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - private static XAttribute XAttributeIfNotNull(string attributeName, Row row, int column) => row.IsColumnNull(column) ? null : new XAttribute(attributeName, row.FieldAsString(column)); + private static XAttribute XAttributeIfNotNull(string attributeName, Row row, int column) + { + return row.IsColumnNull(column) ? null : new XAttribute(attributeName, row.FieldAsString(column)); + } private static void SetAttributeIfNotNull(XElement xElement, string attributeName, string value) { @@ -1578,7 +1600,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// Nests the Permission elements below their parent elements. There are no declared foreign /// keys for the parents of the LockPermissions table. /// - private void FinalizeLockPermissionsTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "LockPermissions"); + private void FinalizeLockPermissionsTable(TableIndexedCollection tables) + { + this.FinalizePermissionsTable(tables, "LockPermissions"); + } /// /// Finalize the MsiLockPermissionsEx table. @@ -1588,7 +1613,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// Nests the PermissionEx elements below their parent elements. There are no declared foreign /// keys for the parents of the MsiLockPermissionsEx table. /// - private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "MsiLockPermissionsEx"); + private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables) + { + this.FinalizePermissionsTable(tables, "MsiLockPermissionsEx"); + } private static Dictionary> IndexTable(Table table, int keyColumn, int? dataColumn) { @@ -2462,53 +2490,44 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // index the rows from the extension libraries var indexedExtensionTables = new Dictionary>(); #if TODO_DECOMPILER_EXTENSIONS - foreach (IDecompilerExtension extension in this.extensions) + foreach (var extension in this.Extensions) { // Get the optional library from the extension with the rows to be removed. - Library library = extension.GetLibraryToRemove(this.tableDefinitions); - if (null != library) + var library = extension.GetLibraryToRemove(this.tableDefinitions); + if (library != null) { - foreach (var section in library.Sections) + foreach (var row in library.Sections.SelectMany(s => s.Tables).SelectMany(t => t.Rows)) { - foreach (Table table in section.Tables) - { - foreach (Row row in table.Rows) - { - string primaryKey; - string tableName; - - // the Actions table needs to be handled specially - if ("WixAction" == table.Name) - { - primaryKey = row.FieldAsString(1); + string primaryKey; + string tableName; - if (OutputType.Module == this.outputType) - { - tableName = String.Concat("Module", row.FieldAsString(0)); - } - else - { - tableName = row.FieldAsString(0); - } - } - else - { - primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter); - tableName = table.Name; - } + // the Actions table needs to be handled specially + if (table.Name == "WixAction") + { + primaryKey = row.FieldAsString(1); + tableName = row.FieldAsString(0); - if (null != primaryKey) - { - HashSet indexedExtensionRows; - if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows)) - { - indexedExtensionRows = new HashSet(); - indexedExtensionTables.Add(tableName, indexedExtensionRows); - } + if (this.outputType == OutputType.Module) + { + tableName = "Module" + tableName; + } + } + else + { + primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter); + tableName = table.Name; + } - indexedExtensionRows.Add(primaryKey); - } + if (primaryKey != null) + { + HashSet indexedExtensionRows; + if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows)) + { + indexedExtensionRows = new HashSet(); + indexedExtensionTables.Add(tableName, indexedExtensionRows); } + + indexedExtensionRows.Add(primaryKey); } } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs new file mode 100644 index 00000000..5bcda36c --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs @@ -0,0 +1,19 @@ +// 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 +{ + using WixToolset.Extensibility.Data; + + /// + /// Supports converting Windows Installer databases to source code. + /// + public interface IWindowsInstallerDecompiler + { + /// + /// Converts Windows Installer database back to source code. + /// + /// Context for decompiling. + /// Result of decompilation. + IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context); + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs index d1c1d3a6..f73791aa 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs @@ -47,27 +47,5 @@ namespace WixToolset.Core.WindowsInstaller } } } - - public IDecompileResult Decompile(IDecompileContext context) - { - var extensionManager = context.ServiceProvider.GetService(); - - var backendExtensions = extensionManager.GetServices(); - - foreach (var extension in backendExtensions) - { - extension.PreBackendDecompile(context); - } - - var command = new DecompileMsiOrMsmCommand(context, backendExtensions); - var result = command.Execute(); - - foreach (var extension in backendExtensions) - { - extension.PostBackendDecompile(result); - } - - return result; - } } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs index ea008c39..fa24745a 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs @@ -2,11 +2,7 @@ namespace WixToolset.Core.WindowsInstaller { - using System; using WixToolset.Core.WindowsInstaller.Bind; - using WixToolset.Core.WindowsInstaller.Decompile; - using WixToolset.Core.WindowsInstaller.Unbind; - using WixToolset.Data; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; @@ -43,27 +39,5 @@ namespace WixToolset.Core.WindowsInstaller throw; } } - - public IDecompileResult Decompile(IDecompileContext context) - { - var extensionManager = context.ServiceProvider.GetService(); - - var backendExtensions = extensionManager.GetServices(); - - foreach (var extension in backendExtensions) - { - extension.PreBackendDecompile(context); - } - - var command = new DecompileMsiOrMsmCommand(context, backendExtensions); - var result = command.Execute(); - - foreach (var extension in backendExtensions) - { - extension.PostBackendDecompile(result); - } - - return result; - } } } diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs index 398fc780..38a4ab34 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs @@ -63,11 +63,6 @@ namespace WixToolset.Core.WindowsInstaller } } - public IDecompileResult Decompile(IDecompileContext context) - { - throw new NotImplementedException(); - } - #if TODO_PATCHING public Intermediate Unbind(IUnbindContext context) { diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs new file mode 100644 index 00000000..1428a469 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs @@ -0,0 +1,50 @@ +// 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 +{ + using System; + using System.Collections.Generic; + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Data; + + internal class WindowsInstallerDecompileContext : IWindowsInstallerDecompileContext + { + internal WindowsInstallerDecompileContext(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + public IServiceProvider ServiceProvider { get; } + + public string DecompilePath { get; set; } + + public OutputType DecompileType { get; set; } + + public IReadOnlyCollection Extensions { get; set; } + + public string ExtractFolder { get; set; } + + public string CabinetExtractFolder { get; set; } + + public string BaseSourcePath { get; set; } + + public string IntermediateFolder { get; set; } + + public bool IsAdminImage { get; set; } + + public string OutputPath { get; set; } + + public bool SuppressCustomTables { get; set; } + + public bool SuppressDroppingEmptyTables { get; set; } + + public bool SuppressRelativeActionSequencing { get; set; } + + public bool SuppressExtractCabinets { get; set; } + + public bool SuppressUI { get; set; } + + public bool TreatProductAsModule { get; set; } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs new file mode 100644 index 00000000..69363286 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs @@ -0,0 +1,18 @@ +// 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 +{ + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility.Data; + + internal class WindowsInstallerDecompileResult : IWindowsInstallerDecompileResult + { + public XDocument Document { get; set; } + + public IReadOnlyCollection ExtractedFilePaths { get; set; } + + public Platform? Platform { get; set; } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs new file mode 100644 index 00000000..10420010 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs @@ -0,0 +1,50 @@ +// 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 +{ + using System; + using WixToolset.Core.WindowsInstaller.Decompile; + using WixToolset.Extensibility; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + /// + /// Decompiler of the WiX toolset. + /// + internal class WindowsInstallerDecompiler : IWindowsInstallerDecompiler + { + internal WindowsInstallerDecompiler(IServiceProvider serviceProvider) + { + this.ServiceProvider = serviceProvider; + } + + public IServiceProvider ServiceProvider { get; } + + public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context) + { + // Pre-decompile. + // + foreach (var extension in context.Extensions) + { + extension.PreDecompile(context); + } + + // Decompile. + // + var command = new DecompileMsiOrMsmCommand(context); + var result = command.Execute(); + + if (result != null) + { + // Post-decompile. + // + foreach (var extension in context.Extensions) + { + extension.PostDecompile(result); + } + } + + return result; + } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs index e686fa49..d064aed1 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs @@ -5,6 +5,7 @@ namespace WixToolset.Core.WindowsInstaller using System; using System.Collections.Generic; using WixToolset.Core.WindowsInstaller.ExtensibilityServices; + using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; /// @@ -31,6 +32,11 @@ namespace WixToolset.Core.WindowsInstaller { // Singletons. coreProvider.AddService((provider, singletons) => AddSingleton(singletons, new WindowsInstallerBackendHelper(provider))); + + // Transients. + coreProvider.AddService((provider, singletons) => new WindowsInstallerDecompiler(provider)); + coreProvider.AddService((provider, singletons) => new WindowsInstallerDecompileContext(provider)); + coreProvider.AddService((provider, singletons) => new WindowsInstallerDecompileResult()); } private static T AddSingleton(Dictionary singletons, T service) where T : class diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs index cd3b2fe4..0921f9ff 100644 --- a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs +++ b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs @@ -9,14 +9,6 @@ namespace WixToolset.Core.CommandLine using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; - internal enum CommandTypes - { - Unknown, - Build, - Preprocess, - Decompile, - } - internal class CommandLine : ICommandLine { public CommandLine(IServiceProvider serviceProvider) @@ -154,18 +146,9 @@ namespace WixToolset.Core.CommandLine } else { - if (Enum.TryParse(arg, true, out CommandTypes commandType)) + if ("build".Equals(arg, StringComparison.OrdinalIgnoreCase)) { - switch (commandType) - { - case CommandTypes.Build: - command = new BuildCommand(this.ServiceProvider); - break; - - case CommandTypes.Decompile: - command = new DecompileCommand(this.ServiceProvider); - break; - } + command = new BuildCommand(this.ServiceProvider); } else { @@ -175,8 +158,6 @@ namespace WixToolset.Core.CommandLine { break; } - - command = null; } } } diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs index 015d3e62..7e6e7cac 100644 --- a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs +++ b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs @@ -41,12 +41,16 @@ namespace WixToolset.Core.CommandLine return argument; } - public void GetArgumentAsFilePathOrError(string argument, string fileType, IList paths) + public bool GetArgumentAsFilePathOrError(string argument, string fileType, IList paths) { - foreach (var path in this.GetFiles(argument, fileType)) + var files = this.GetFiles(argument, fileType); + + foreach (var path in files) { paths.Add(path); } + + return files.Length > 0; } public string GetNextArgumentOrError(string commandLineSwitch) @@ -140,6 +144,23 @@ namespace WixToolset.Core.CommandLine return false; } + public string PeekNextArgument() + { + return this.TryPeekNextArgument(out var argument) ? argument : null; + } + + public bool TryPeekNextArgument(out string argument) + { + if (this.RemainingArguments.Count > 0) + { + argument = this.RemainingArguments.Peek(); + return true; + } + + argument = null; + return false; + } + private bool TryGetNextNonSwitchArgumentOrError(out string arg) { var result = this.TryGetNextSwitchOrArgument(out arg); diff --git a/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs b/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs deleted file mode 100644 index 22853f86..00000000 --- a/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs +++ /dev/null @@ -1,202 +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.CommandLine -{ - using System; - using System.IO; - using System.Threading; - using System.Threading.Tasks; - using System.Xml.Linq; - using WixToolset.Data; - using WixToolset.Extensibility; - using WixToolset.Extensibility.Data; - using WixToolset.Extensibility.Services; - - internal class DecompileCommand : ICommandLineCommand - { - private readonly CommandLine commandLine; - - public DecompileCommand(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - this.Messaging = serviceProvider.GetService(); - this.commandLine = new CommandLine(this.Messaging); - } - - public bool ShowHelp - { - get { return this.commandLine.ShowHelp; } - set { this.commandLine.ShowHelp = value; } - } - - public bool ShowLogo - { - get { return this.commandLine.ShowLogo; } - set { this.commandLine.ShowLogo = value; } - } - - // Stop parsing when we've decided to show help. - public bool StopParsing => this.commandLine.ShowHelp; - - private IServiceProvider ServiceProvider { get; } - - public IMessaging Messaging { get; } - - public Task ExecuteAsync(CancellationToken _) - { - if (this.commandLine.ShowHelp || String.IsNullOrEmpty(this.commandLine.DecompileFilePath)) - { - Console.WriteLine("TODO: Show decompile command help"); - return Task.FromResult(-1); - } - - var context = this.ServiceProvider.GetService(); - context.Extensions = this.ServiceProvider.GetService().GetServices(); - context.DecompilePath = this.commandLine.DecompileFilePath; - context.DecompileType = this.commandLine.CalculateDecompileType(); - context.IntermediateFolder = this.commandLine.CalculateIntermedateFolder(); - context.OutputPath = this.commandLine.CalculateOutputPath(); - - try - { - var decompiler = this.ServiceProvider.GetService(); - var result = decompiler.Decompile(context); - - if (!this.Messaging.EncounteredError) - { - Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath))); - result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces); - } - } - catch (WixException e) - { - this.Messaging.Write(e.Error); - } - - if (this.Messaging.EncounteredError) - { - return Task.FromResult(1); - } - - return Task.FromResult(0); - } - - public bool TryParseArgument(ICommandLineParser parser, string argument) - { - return this.commandLine.TryParseArgument(argument, parser); - } - - private class CommandLine - { - public CommandLine(IMessaging messaging) - { - this.Messaging = messaging; - } - - private IMessaging Messaging { get; } - - public string DecompileFilePath { get; private set; } - - public string DecompileType { get; private set; } - - public Platform Platform { get; private set; } - - public bool ShowLogo { get; set; } - - public bool ShowHelp { get; set; } - - public string IntermediateFolder { get; private set; } - - public string OutputFile { get; private set; } - - public bool TryParseArgument(string arg, ICommandLineParser parser) - { - if (parser.IsSwitch(arg)) - { - var parameter = arg.Substring(1); - switch (parameter.ToLowerInvariant()) - { - case "intermediatefolder": - this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg); - return true; - - case "o": - case "out": - this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg); - return true; - } - } - else - { - if (String.IsNullOrEmpty(this.DecompileFilePath)) - { - this.DecompileFilePath = parser.GetArgumentAsFilePathOrError(arg, "decompile file"); - return true; - } - else if (String.IsNullOrEmpty(this.OutputFile)) - { - this.OutputFile = parser.GetArgumentAsFilePathOrError(arg, "output file"); - return true; - } - } - - return false; - } - - public OutputType CalculateDecompileType() - { - if (String.IsNullOrEmpty(this.DecompileType)) - { - this.DecompileType = Path.GetExtension(this.DecompileFilePath); - } - - switch (this.DecompileType.ToLowerInvariant()) - { - case "bundle": - case ".exe": - return OutputType.Bundle; - - case "library": - case ".wixlib": - return OutputType.Library; - - case "module": - case ".msm": - return OutputType.Module; - - case "patch": - case ".msp": - return OutputType.Patch; - - case ".pcp": - return OutputType.PatchCreation; - - case "product": - case "package": - case ".msi": - return OutputType.Product; - - case "transform": - case ".mst": - return OutputType.Transform; - - case "intermediatepostlink": - case ".wixipl": - return OutputType.IntermediatePostLink; - } - - return OutputType.Unknown; - } - - public string CalculateIntermedateFolder() - { - return String.IsNullOrEmpty(this.IntermediateFolder) ? Path.GetTempPath() : this.IntermediateFolder; - } - - public string CalculateOutputPath() - { - return String.IsNullOrEmpty(this.OutputFile) ? Path.ChangeExtension(this.DecompileFilePath, ".wxs") : this.OutputFile; - } - } - } -} diff --git a/src/wix/WixToolset.Core/DecompileContext.cs b/src/wix/WixToolset.Core/DecompileContext.cs deleted file mode 100644 index a7ec03fd..00000000 --- a/src/wix/WixToolset.Core/DecompileContext.cs +++ /dev/null @@ -1,49 +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 -{ - using System; - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Extensibility; - using WixToolset.Extensibility.Data; - using WixToolset.Extensibility.Services; - - internal class DecompileContext : IDecompileContext - { - internal DecompileContext(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - public IServiceProvider ServiceProvider { get; } - - public string DecompilePath { get; set; } - - public OutputType DecompileType { get; set; } - - public IReadOnlyCollection Extensions { get; set; } - - public string ExtractFolder { get; set; } - - public string CabinetExtractFolder { get; set; } - - public string BaseSourcePath { get; set; } - - public string IntermediateFolder { get; set; } - - public bool IsAdminImage { get; set; } - - public string OutputPath { get; set; } - - public bool SuppressCustomTables { get; set; } - - public bool SuppressDroppingEmptyTables { get; set; } - - public bool SuppressExtractCabinets { get; set; } - - public bool SuppressUI { get; set; } - - public bool TreatProductAsModule { get; set; } - } -} diff --git a/src/wix/WixToolset.Core/DecompileResult.cs b/src/wix/WixToolset.Core/DecompileResult.cs deleted file mode 100644 index fc24cab7..00000000 --- a/src/wix/WixToolset.Core/DecompileResult.cs +++ /dev/null @@ -1,18 +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 -{ - using System.Collections.Generic; - using System.Xml.Linq; - using WixToolset.Data; - using WixToolset.Extensibility.Data; - - internal class DecompileResult : IDecompileResult - { - public XDocument Document { get; set; } - - public IReadOnlyCollection ExtractedFilePaths { get; set; } - - public Platform? Platform { get; set; } - } -} diff --git a/src/wix/WixToolset.Core/Decompiler.cs b/src/wix/WixToolset.Core/Decompiler.cs deleted file mode 100644 index 859f582b..00000000 --- a/src/wix/WixToolset.Core/Decompiler.cs +++ /dev/null @@ -1,68 +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 -{ - using System; - using WixToolset.Extensibility; - using WixToolset.Extensibility.Data; - using WixToolset.Extensibility.Services; - - /// - /// Decompiler of the WiX toolset. - /// - internal class Decompiler : IDecompiler - { - internal Decompiler(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - public IServiceProvider ServiceProvider { get; } - - public IDecompileResult Decompile(IDecompileContext context) - { - // Pre-decompile. - // - foreach (var extension in context.Extensions) - { - extension.PreDecompile(context); - } - - // Decompile. - // - var result = this.BackendDecompile(context); - - if (result != null) - { - // Post-decompile. - // - foreach (var extension in context.Extensions) - { - extension.PostDecompile(result); - } - } - - return result; - } - - private IDecompileResult BackendDecompile(IDecompileContext context) - { - var extensionManager = context.ServiceProvider.GetService(); - - var backendFactories = extensionManager.GetServices(); - - foreach (var factory in backendFactories) - { - if (factory.TryCreateBackend(context.DecompileType.ToString(), context.OutputPath, out var backend)) - { - var result = backend.Decompile(context); - return result; - } - } - - // TODO: messaging that a backend could not be found to decompile the decompile type? - - return null; - } - } -} diff --git a/src/wix/WixToolset.Core/IDecompiler.cs b/src/wix/WixToolset.Core/IDecompiler.cs deleted file mode 100644 index 74ec26de..00000000 --- a/src/wix/WixToolset.Core/IDecompiler.cs +++ /dev/null @@ -1,12 +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 -{ - using WixToolset.Extensibility.Data; - -#pragma warning disable 1591 // TODO: add documentation - public interface IDecompiler - { - IDecompileResult Decompile(IDecompileContext context); - } -} diff --git a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs index 9fbf6717..525b9dd9 100644 --- a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs @@ -40,14 +40,12 @@ namespace WixToolset.Core this.AddService((provider, singletons) => new LinkContext(provider)); this.AddService((provider, singletons) => new ResolveContext(provider)); this.AddService((provider, singletons) => new BindContext(provider)); - this.AddService((provider, singletons) => new DecompileContext(provider)); this.AddService((provider, singletons) => new LayoutContext(provider)); this.AddService((provider, singletons) => new BindFileWithPath()); this.AddService((provider, singletons) => new BindPath()); this.AddService((provider, singletons) => new BindResult()); this.AddService((provider, singletons) => new ComponentKeyPath()); - this.AddService((provider, singletons) => new DecompileResult()); this.AddService((provider, singletons) => new IncludedFile()); this.AddService((provider, singletons) => new PreprocessResult()); this.AddService((provider, singletons) => new ResolvedDirectory()); @@ -58,7 +56,6 @@ namespace WixToolset.Core this.AddService((provider, singletons) => new Binder(provider)); this.AddService((provider, singletons) => new Compiler(provider)); - this.AddService((provider, singletons) => new Decompiler(provider)); this.AddService((provider, singletons) => new LayoutCreator(provider)); this.AddService((provider, singletons) => new Preprocessor(provider)); this.AddService((provider, singletons) => new Librarian(provider)); diff --git a/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs index e01b9789..5ad7ef6f 100644 --- a/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs +++ b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs @@ -249,7 +249,7 @@ namespace WixToolsetTest.Converters var v3msiPath = Path.Combine(folder, "TypicalV3.msi"); var result = WixRunner.Execute(new[] { - "decompile", v3msiPath, + "msi", "decompile", v3msiPath, "-intermediateFolder", intermediateFolder, "-o", decompiledWxsPath }); diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs index b9a6185d..1be7d9ab 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs @@ -219,7 +219,7 @@ namespace WixToolsetTest.CoreIntegration result = WixRunner.Execute(new[] { - "decompile", msiPath, + "msi", "decompile", msiPath, "-sw1060", "-intermediateFolder", intermediateFolder, "-o", decompiledWxsPath diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs index e87bbfee..b43d2033 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs @@ -20,7 +20,7 @@ namespace WixToolsetTest.CoreIntegration var result = WixRunner.Execute(new[] { - "decompile", + "msi", "decompile", Path.Combine(folder, msiName), "-intermediateFolder", intermediateFolder, "-o", outputPath -- cgit v1.2.3-55-g6feb