diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/WixToolset.BuildTasks/DoIt.cs | 17 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineArguments.cs | 211 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineContext.cs | 4 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineHelper.cs | 216 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineParser.cs | 593 | ||||
| -rw-r--r-- | src/WixToolset.Core/CommandLine/ParseCommandLine.cs | 257 | ||||
| -rw-r--r-- | src/WixToolset.Core/Preprocessor.cs | 2 | ||||
| -rw-r--r-- | src/WixToolset.Core/WixToolsetServiceProvider.cs | 1 | ||||
| -rw-r--r-- | src/light/LightCommandLine.cs | 196 | ||||
| -rw-r--r-- | src/light/light.cs | 49 | ||||
| -rw-r--r-- | src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs | 2 | ||||
| -rw-r--r-- | src/wix/Program.cs | 14 |
12 files changed, 740 insertions, 822 deletions
diff --git a/src/WixToolset.BuildTasks/DoIt.cs b/src/WixToolset.BuildTasks/DoIt.cs index aa01f6ec..977a2326 100644 --- a/src/WixToolset.BuildTasks/DoIt.cs +++ b/src/WixToolset.BuildTasks/DoIt.cs | |||
| @@ -171,21 +171,23 @@ namespace WixToolset.BuildTasks | |||
| 171 | 171 | ||
| 172 | var serviceProvider = new WixToolsetServiceProvider(); | 172 | var serviceProvider = new WixToolsetServiceProvider(); |
| 173 | 173 | ||
| 174 | var context = serviceProvider.GetService<ICommandLineContext>(); | ||
| 175 | |||
| 176 | var messaging = serviceProvider.GetService<IMessaging>(); | 174 | var messaging = serviceProvider.GetService<IMessaging>(); |
| 177 | messaging.SetListener(this.Listener); | 175 | messaging.SetListener(this.Listener); |
| 178 | 176 | ||
| 177 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); | ||
| 178 | arguments.Populate(commandLineString); | ||
| 179 | |||
| 180 | var context = serviceProvider.GetService<ICommandLineContext>(); | ||
| 179 | context.Messaging = messaging; | 181 | context.Messaging = messaging; |
| 180 | context.ExtensionManager = this.CreateExtensionManagerWithStandardBackends(serviceProvider); | 182 | context.ExtensionManager = this.CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions); |
| 181 | context.Arguments = commandLineString; | 183 | context.Arguments = arguments; |
| 182 | 184 | ||
| 183 | var commandLine = serviceProvider.GetService<ICommandLine>(); | 185 | var commandLine = serviceProvider.GetService<ICommandLine>(); |
| 184 | var command = commandLine.ParseStandardCommandLine(context); | 186 | var command = commandLine.ParseStandardCommandLine(context); |
| 185 | command?.Execute(); | 187 | command?.Execute(); |
| 186 | } | 188 | } |
| 187 | 189 | ||
| 188 | private IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider) | 190 | private IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, string[] extensions) |
| 189 | { | 191 | { |
| 190 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); | 192 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); |
| 191 | 193 | ||
| @@ -194,6 +196,11 @@ namespace WixToolset.BuildTasks | |||
| 194 | extensionManager.Add(type.Assembly); | 196 | extensionManager.Add(type.Assembly); |
| 195 | } | 197 | } |
| 196 | 198 | ||
| 199 | foreach (var extension in extensions) | ||
| 200 | { | ||
| 201 | extensionManager.Load(extension); | ||
| 202 | } | ||
| 203 | |||
| 197 | return extensionManager; | 204 | return extensionManager; |
| 198 | } | 205 | } |
| 199 | 206 | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineArguments.cs b/src/WixToolset.Core/CommandLine/CommandLineArguments.cs new file mode 100644 index 00000000..37adcfd3 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLineArguments.cs | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.CommandLine | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using System.Text; | ||
| 9 | using System.Text.RegularExpressions; | ||
| 10 | using WixToolset.Extensibility.Services; | ||
| 11 | |||
| 12 | internal class CommandLineArguments : ICommandLineArguments | ||
| 13 | { | ||
| 14 | public string[] OriginalArguments { get; set; } | ||
| 15 | |||
| 16 | public string[] Arguments { get; set; } | ||
| 17 | |||
| 18 | public string[] Extensions { get; set; } | ||
| 19 | |||
| 20 | public string ErrorArgument { get; set; } | ||
| 21 | |||
| 22 | private IServiceProvider ServiceProvider { get; } | ||
| 23 | |||
| 24 | public CommandLineArguments(IServiceProvider serviceProvider) | ||
| 25 | { | ||
| 26 | this.ServiceProvider = serviceProvider; | ||
| 27 | } | ||
| 28 | |||
| 29 | public void Populate(string commandLine) | ||
| 30 | { | ||
| 31 | var args = CommandLineArguments.ParseArgumentsToArray(commandLine); | ||
| 32 | |||
| 33 | this.Populate(args.ToArray()); | ||
| 34 | } | ||
| 35 | |||
| 36 | public void Populate(string[] args) | ||
| 37 | { | ||
| 38 | this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(args); | ||
| 39 | |||
| 40 | this.ProcessArgumentsAndParseExtensions(this.OriginalArguments); | ||
| 41 | } | ||
| 42 | |||
| 43 | public IParseCommandLine Parse() | ||
| 44 | { | ||
| 45 | var messaging = (IMessaging)this.ServiceProvider.GetService(typeof(IMessaging)); | ||
| 46 | |||
| 47 | return new ParseCommandLine(messaging, this.Arguments, this.ErrorArgument); | ||
| 48 | } | ||
| 49 | |||
| 50 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) | ||
| 51 | { | ||
| 52 | List<string> args = new List<string>(); | ||
| 53 | |||
| 54 | foreach (var arg in commandLineArguments) | ||
| 55 | { | ||
| 56 | if ('@' == arg[0]) | ||
| 57 | { | ||
| 58 | var responseFileArguments = CommandLineArguments.ParseResponseFile(arg.Substring(1)); | ||
| 59 | args.AddRange(responseFileArguments); | ||
| 60 | } | ||
| 61 | else | ||
| 62 | { | ||
| 63 | args.Add(arg); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | this.OriginalArguments = args.ToArray(); | ||
| 68 | } | ||
| 69 | |||
| 70 | private void ProcessArgumentsAndParseExtensions(string[] args) | ||
| 71 | { | ||
| 72 | var arguments = new List<string>(); | ||
| 73 | var extensions = new List<string>(); | ||
| 74 | |||
| 75 | for (var i = 0; i < args.Length; ++i) | ||
| 76 | { | ||
| 77 | var arg = args[i]; | ||
| 78 | |||
| 79 | if ("-ext" == arg || "/ext" == arg) | ||
| 80 | { | ||
| 81 | if (!CommandLineArguments.IsSwitchAt(args, ++i)) | ||
| 82 | { | ||
| 83 | extensions.Add(args[i]); | ||
| 84 | } | ||
| 85 | else | ||
| 86 | { | ||
| 87 | this.ErrorArgument = arg; | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | else | ||
| 92 | { | ||
| 93 | arguments.Add(arg); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | this.Arguments = arguments.ToArray(); | ||
| 98 | this.Extensions = extensions.ToArray(); | ||
| 99 | } | ||
| 100 | |||
| 101 | private static List<string> ParseResponseFile(string responseFile) | ||
| 102 | { | ||
| 103 | string arguments; | ||
| 104 | |||
| 105 | using (StreamReader reader = new StreamReader(responseFile)) | ||
| 106 | { | ||
| 107 | arguments = reader.ReadToEnd(); | ||
| 108 | } | ||
| 109 | |||
| 110 | return CommandLineArguments.ParseArgumentsToArray(arguments); | ||
| 111 | } | ||
| 112 | |||
| 113 | private static List<string> ParseArgumentsToArray(string arguments) | ||
| 114 | { | ||
| 115 | // Scan and parse the arguments string, dividing up the arguments based on whitespace. | ||
| 116 | // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed. | ||
| 117 | // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments. | ||
| 118 | // Escaped quotes and escaped backslashes also need to be unescaped by this process. | ||
| 119 | |||
| 120 | // Collects the final list of arguments to be returned. | ||
| 121 | var argsList = new List<string>(); | ||
| 122 | |||
| 123 | // True if we are inside an unescaped quote, meaning whitespace should be ignored. | ||
| 124 | var insideQuote = false; | ||
| 125 | |||
| 126 | // Index of the start of the current argument substring; either the start of the argument | ||
| 127 | // or the start of a quoted or unquoted sequence within it. | ||
| 128 | var partStart = 0; | ||
| 129 | |||
| 130 | // The current argument string being built; when completed it will be added to the list. | ||
| 131 | var arg = new StringBuilder(); | ||
| 132 | |||
| 133 | for (int i = 0; i <= arguments.Length; i++) | ||
| 134 | { | ||
| 135 | if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote)) | ||
| 136 | { | ||
| 137 | // Reached a whitespace separator or the end of the string. | ||
| 138 | |||
| 139 | // Finish building the current argument. | ||
| 140 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 141 | |||
| 142 | // Skip over the whitespace character. | ||
| 143 | partStart = i + 1; | ||
| 144 | |||
| 145 | // Add the argument to the list if it's not empty. | ||
| 146 | if (arg.Length > 0) | ||
| 147 | { | ||
| 148 | argsList.Add(CommandLineArguments.ExpandEnvironmentVariables(arg.ToString())); | ||
| 149 | arg.Length = 0; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | else if (i > partStart && arguments[i - 1] == '\\') | ||
| 153 | { | ||
| 154 | // Check the character following an unprocessed backslash. | ||
| 155 | // Unescape quotes, and backslashes followed by a quote. | ||
| 156 | if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"')) | ||
| 157 | { | ||
| 158 | // Unescape the quote or backslash by skipping the preceeding backslash. | ||
| 159 | arg.Append(arguments.Substring(partStart, i - 1 - partStart)); | ||
| 160 | arg.Append(arguments[i]); | ||
| 161 | partStart = i + 1; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | else if (arguments[i] == '"') | ||
| 165 | { | ||
| 166 | // Add the quoted or unquoted section to the argument string. | ||
| 167 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 168 | |||
| 169 | // And skip over the quote character. | ||
| 170 | partStart = i + 1; | ||
| 171 | |||
| 172 | insideQuote = !insideQuote; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | return argsList; | ||
| 177 | } | ||
| 178 | |||
| 179 | private static string ExpandEnvironmentVariables(string arguments) | ||
| 180 | { | ||
| 181 | var id = Environment.GetEnvironmentVariables(); | ||
| 182 | |||
| 183 | var regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)"); | ||
| 184 | MatchCollection matches = regex.Matches(arguments); | ||
| 185 | |||
| 186 | string value = String.Empty; | ||
| 187 | for (int i = 0; i <= (matches.Count - 1); i++) | ||
| 188 | { | ||
| 189 | try | ||
| 190 | { | ||
| 191 | var key = matches[i].Value; | ||
| 192 | regex = new Regex(String.Concat("(?i)(?:\\%)(?:", key, ")(?:\\%)")); | ||
| 193 | value = id[key].ToString(); | ||
| 194 | arguments = regex.Replace(arguments, value); | ||
| 195 | } | ||
| 196 | catch (NullReferenceException) | ||
| 197 | { | ||
| 198 | // Collapse unresolved environment variables. | ||
| 199 | arguments = regex.Replace(arguments, value); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | return arguments; | ||
| 204 | } | ||
| 205 | |||
| 206 | private static bool IsSwitchAt(string[] args, int index) | ||
| 207 | { | ||
| 208 | return args.Length > index && !String.IsNullOrEmpty(args[index]) && ('/' == args[index][0] || '-' == args[index][0]); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineContext.cs b/src/WixToolset.Core/CommandLine/CommandLineContext.cs index 2ff2c1fd..c589222d 100644 --- a/src/WixToolset.Core/CommandLine/CommandLineContext.cs +++ b/src/WixToolset.Core/CommandLine/CommandLineContext.cs | |||
| @@ -18,8 +18,6 @@ namespace WixToolset.Core.CommandLine | |||
| 18 | 18 | ||
| 19 | public IExtensionManager ExtensionManager { get; set; } | 19 | public IExtensionManager ExtensionManager { get; set; } |
| 20 | 20 | ||
| 21 | public string Arguments { get; set; } | 21 | public ICommandLineArguments Arguments { get; set; } |
| 22 | |||
| 23 | public string[] ParsedArguments { get; set; } | ||
| 24 | } | 22 | } |
| 25 | } | 23 | } |
diff --git a/src/WixToolset.Core/CommandLine/CommandLineHelper.cs b/src/WixToolset.Core/CommandLine/CommandLineHelper.cs deleted file mode 100644 index 51ece0f7..00000000 --- a/src/WixToolset.Core/CommandLine/CommandLineHelper.cs +++ /dev/null | |||
| @@ -1,216 +0,0 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.CommandLine | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.IO; | ||
| 7 | using WixToolset.Data; | ||
| 8 | using WixToolset.Extensibility.Services; | ||
| 9 | |||
| 10 | public class CommandLineHelper | ||
| 11 | { | ||
| 12 | /// <summary> | ||
| 13 | /// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not | ||
| 14 | /// </summary> | ||
| 15 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
| 16 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
| 17 | /// <param name="args">The list of strings to check.</param> | ||
| 18 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
| 19 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
| 20 | public static string GetDirectory(string commandlineSwitch, IMessaging messageHandler, string[] args, int index) | ||
| 21 | { | ||
| 22 | return GetDirectory(commandlineSwitch, messageHandler, args, index, false); | ||
| 23 | } | ||
| 24 | |||
| 25 | /// <summary> | ||
| 26 | /// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not | ||
| 27 | /// </summary> | ||
| 28 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
| 29 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
| 30 | /// <param name="args">The list of strings to check.</param> | ||
| 31 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
| 32 | /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param> | ||
| 33 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
| 34 | public static string GetDirectory(string commandlineSwitch, IMessaging messageHandler, string[] args, int index, bool allowPrefix) | ||
| 35 | { | ||
| 36 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
| 37 | |||
| 38 | if (!IsValidArg(args, index)) | ||
| 39 | { | ||
| 40 | messageHandler.Write(ErrorMessages.DirectoryPathRequired(commandlineSwitch)); | ||
| 41 | return null; | ||
| 42 | } | ||
| 43 | |||
| 44 | if (File.Exists(args[index])) | ||
| 45 | { | ||
| 46 | messageHandler.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, args[index])); | ||
| 47 | return null; | ||
| 48 | } | ||
| 49 | |||
| 50 | return VerifyPath(messageHandler, args[index], allowPrefix); | ||
| 51 | } | ||
| 52 | |||
| 53 | /// <summary> | ||
| 54 | /// Validates that a string is a valid filename, and throws appropriate warnings/errors if not | ||
| 55 | /// </summary> | ||
| 56 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
| 57 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
| 58 | /// <param name="args">The list of strings to check.</param> | ||
| 59 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
| 60 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
| 61 | public static string GetFile(string commandlineSwitch, IMessaging messageHandler, string[] args, int index) | ||
| 62 | { | ||
| 63 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
| 64 | |||
| 65 | if (!IsValidArg(args, index)) | ||
| 66 | { | ||
| 67 | messageHandler.Write(ErrorMessages.FilePathRequired(commandlineSwitch)); | ||
| 68 | return null; | ||
| 69 | } | ||
| 70 | |||
| 71 | if (Directory.Exists(args[index])) | ||
| 72 | { | ||
| 73 | messageHandler.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, args[index])); | ||
| 74 | return null; | ||
| 75 | } | ||
| 76 | |||
| 77 | return VerifyPath(messageHandler, args[index]); | ||
| 78 | } | ||
| 79 | |||
| 80 | /// <summary> | ||
| 81 | /// Get a set of files that possibly have a search pattern in the path (such as '*'). | ||
| 82 | /// </summary> | ||
| 83 | /// <param name="searchPath">Search path to find files in.</param> | ||
| 84 | /// <param name="fileType">Type of file; typically "Source".</param> | ||
| 85 | /// <returns>An array of files matching the search path.</returns> | ||
| 86 | /// <remarks> | ||
| 87 | /// This method is written in this verbose way because it needs to support ".." in the path. | ||
| 88 | /// It needs the directory path isolated from the file name in order to use Directory.GetFiles | ||
| 89 | /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since | ||
| 90 | /// Path.GetDirectoryName does not support ".." in the path. | ||
| 91 | /// </remarks> | ||
| 92 | /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception> | ||
| 93 | public static string[] GetFiles(string searchPath, string fileType) | ||
| 94 | { | ||
| 95 | if (null == searchPath) | ||
| 96 | { | ||
| 97 | throw new ArgumentNullException(nameof(searchPath)); | ||
| 98 | } | ||
| 99 | |||
| 100 | // Convert alternate directory separators to the standard one. | ||
| 101 | string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
| 102 | int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
| 103 | string[] files = null; | ||
| 104 | |||
| 105 | try | ||
| 106 | { | ||
| 107 | if (0 > lastSeparator) | ||
| 108 | { | ||
| 109 | files = Directory.GetFiles(".", filePath); | ||
| 110 | } | ||
| 111 | else // found directory separator | ||
| 112 | { | ||
| 113 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1)); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | catch (DirectoryNotFoundException) | ||
| 117 | { | ||
| 118 | // Don't let this function throw the DirectoryNotFoundException. This exception | ||
| 119 | // occurs for non-existant directories and invalid characters in the searchPattern. | ||
| 120 | } | ||
| 121 | catch (ArgumentException) | ||
| 122 | { | ||
| 123 | // Don't let this function throw the ArgumentException. This exception | ||
| 124 | // occurs in certain situations such as when passing a malformed UNC path. | ||
| 125 | } | ||
| 126 | catch (IOException) | ||
| 127 | { | ||
| 128 | throw new WixFileNotFoundException(searchPath, fileType); | ||
| 129 | } | ||
| 130 | |||
| 131 | if (null == files || 0 == files.Length) | ||
| 132 | { | ||
| 133 | throw new WixFileNotFoundException(searchPath, fileType); | ||
| 134 | } | ||
| 135 | |||
| 136 | return files; | ||
| 137 | } | ||
| 138 | |||
| 139 | /// <summary> | ||
| 140 | /// Validates that a valid string parameter (without "/" or "-"), and returns a bool indicating its validity | ||
| 141 | /// </summary> | ||
| 142 | /// <param name="args">The list of strings to check.</param> | ||
| 143 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
| 144 | /// <returns>True if a valid string parameter exists there, false if not.</returns> | ||
| 145 | public static bool IsValidArg(string[] args, int index) | ||
| 146 | { | ||
| 147 | if (args.Length <= index || String.IsNullOrEmpty(args[index]) || '/' == args[index][0] || '-' == args[index][0]) | ||
| 148 | { | ||
| 149 | return false; | ||
| 150 | } | ||
| 151 | else | ||
| 152 | { | ||
| 153 | return true; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /// <summary> | ||
| 158 | /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not | ||
| 159 | /// </summary> | ||
| 160 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
| 161 | /// <param name="path">The path to test.</param> | ||
| 162 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
| 163 | public static string VerifyPath(IMessaging messageHandler, string path) | ||
| 164 | { | ||
| 165 | return VerifyPath(messageHandler, path, false); | ||
| 166 | } | ||
| 167 | |||
| 168 | /// <summary> | ||
| 169 | /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not | ||
| 170 | /// </summary> | ||
| 171 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
| 172 | /// <param name="path">The path to test.</param> | ||
| 173 | /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param> | ||
| 174 | /// <returns>The full path if it is valid, null if it is invalid.</returns> | ||
| 175 | public static string VerifyPath(IMessaging messageHandler, string path, bool allowPrefix) | ||
| 176 | { | ||
| 177 | string fullPath; | ||
| 178 | |||
| 179 | if (0 <= path.IndexOf('\"')) | ||
| 180 | { | ||
| 181 | messageHandler.Write(ErrorMessages.PathCannotContainQuote(path)); | ||
| 182 | return null; | ||
| 183 | } | ||
| 184 | |||
| 185 | try | ||
| 186 | { | ||
| 187 | string prefix = null; | ||
| 188 | if (allowPrefix) | ||
| 189 | { | ||
| 190 | int prefixLength = path.IndexOf('=') + 1; | ||
| 191 | if (0 != prefixLength) | ||
| 192 | { | ||
| 193 | prefix = path.Substring(0, prefixLength); | ||
| 194 | path = path.Substring(prefixLength); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | if (String.IsNullOrEmpty(prefix)) | ||
| 199 | { | ||
| 200 | fullPath = Path.GetFullPath(path); | ||
| 201 | } | ||
| 202 | else | ||
| 203 | { | ||
| 204 | fullPath = String.Concat(prefix, Path.GetFullPath(path)); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | catch (Exception e) | ||
| 208 | { | ||
| 209 | messageHandler.Write(ErrorMessages.InvalidCommandLineFileName(path, e.Message)); | ||
| 210 | return null; | ||
| 211 | } | ||
| 212 | |||
| 213 | return fullPath; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/WixToolset.Core/CommandLine/CommandLineParser.cs index f4bc8ade..da0e979c 100644 --- a/src/WixToolset.Core/CommandLine/CommandLineParser.cs +++ b/src/WixToolset.Core/CommandLine/CommandLineParser.cs | |||
| @@ -5,9 +5,6 @@ namespace WixToolset.Core.CommandLine | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 7 | using System.IO; |
| 8 | using System.Linq; | ||
| 9 | using System.Text; | ||
| 10 | using System.Text.RegularExpressions; | ||
| 11 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 12 | using WixToolset.Extensibility; | 9 | using WixToolset.Extensibility; |
| 13 | using WixToolset.Extensibility.Services; | 10 | using WixToolset.Extensibility.Services; |
| @@ -22,7 +19,7 @@ namespace WixToolset.Core.CommandLine | |||
| 22 | Bind, | 19 | Bind, |
| 23 | } | 20 | } |
| 24 | 21 | ||
| 25 | internal class CommandLineParser : ICommandLine, IParseCommandLine | 22 | internal class CommandLineParser : ICommandLine |
| 26 | { | 23 | { |
| 27 | private IServiceProvider ServiceProvider { get; set; } | 24 | private IServiceProvider ServiceProvider { get; set; } |
| 28 | 25 | ||
| @@ -32,14 +29,8 @@ namespace WixToolset.Core.CommandLine | |||
| 32 | 29 | ||
| 33 | public string ActiveCommand { get; private set; } | 30 | public string ActiveCommand { get; private set; } |
| 34 | 31 | ||
| 35 | public string[] OriginalArguments { get; private set; } | ||
| 36 | |||
| 37 | public Queue<string> RemainingArguments { get; } = new Queue<string>(); | ||
| 38 | |||
| 39 | public IExtensionManager ExtensionManager { get; private set; } | 32 | public IExtensionManager ExtensionManager { get; private set; } |
| 40 | 33 | ||
| 41 | public string ErrorArgument { get; set; } | ||
| 42 | |||
| 43 | public bool ShowHelp { get; set; } | 34 | public bool ShowHelp { get; set; } |
| 44 | 35 | ||
| 45 | public ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context) | 36 | public ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context) |
| @@ -50,18 +41,6 @@ namespace WixToolset.Core.CommandLine | |||
| 50 | 41 | ||
| 51 | this.ExtensionManager = context.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>(); | 42 | this.ExtensionManager = context.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>(); |
| 52 | 43 | ||
| 53 | var args = context.ParsedArguments ?? Array.Empty<string>(); | ||
| 54 | |||
| 55 | if (!String.IsNullOrEmpty(context.Arguments)) | ||
| 56 | { | ||
| 57 | args = CommandLineParser.ParseArgumentsToArray(context.Arguments).Concat(args).ToArray(); | ||
| 58 | } | ||
| 59 | |||
| 60 | return this.ParseStandardCommandLine(context, args); | ||
| 61 | } | ||
| 62 | |||
| 63 | private ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context, string[] args) | ||
| 64 | { | ||
| 65 | var next = String.Empty; | 44 | var next = String.Empty; |
| 66 | 45 | ||
| 67 | var command = Commands.Unknown; | 46 | var command = Commands.Unknown; |
| @@ -89,99 +68,99 @@ namespace WixToolset.Core.CommandLine | |||
| 89 | var outputsFile = String.Empty; | 68 | var outputsFile = String.Empty; |
| 90 | var builtOutputsFile = String.Empty; | 69 | var builtOutputsFile = String.Empty; |
| 91 | 70 | ||
| 92 | this.Parse(context, args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) => | 71 | this.Parse(context, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, parser, arg) => |
| 93 | { | 72 | { |
| 94 | if (cmdline.IsSwitch(arg)) | 73 | if (parser.IsSwitch(arg)) |
| 95 | { | 74 | { |
| 96 | var parameter = arg.Substring(1); | 75 | var parameter = arg.Substring(1); |
| 97 | switch (parameter.ToLowerInvariant()) | 76 | switch (parameter.ToLowerInvariant()) |
| 98 | { | 77 | { |
| 99 | case "?": | 78 | case "?": |
| 100 | case "h": | 79 | case "h": |
| 101 | case "help": | 80 | case "help": |
| 102 | cmdline.ShowHelp = true; | 81 | cmdline.ShowHelp = true; |
| 103 | return true; | 82 | return true; |
| 104 | 83 | ||
| 105 | case "bindfiles": | 84 | case "bindfiles": |
| 106 | bindFiles = true; | 85 | bindFiles = true; |
| 107 | return true; | 86 | return true; |
| 108 | 87 | ||
| 109 | case "bindpath": | 88 | case "bindpath": |
| 110 | cmdline.GetNextArgumentOrError(bindPaths); | 89 | parser.GetNextArgumentOrError(arg, bindPaths); |
| 111 | return true; | 90 | return true; |
| 112 | 91 | ||
| 113 | case "cc": | 92 | case "cc": |
| 114 | cmdline.GetNextArgumentOrError(ref cabCachePath); | 93 | cabCachePath = parser.GetNextArgumentOrError(arg); |
| 115 | return true; | 94 | return true; |
| 116 | 95 | ||
| 117 | case "culture": | 96 | case "culture": |
| 118 | cmdline.GetNextArgumentOrError(cultures); | 97 | parser.GetNextArgumentOrError(arg, cultures); |
| 119 | return true; | 98 | return true; |
| 120 | case "contentsfile": | 99 | case "contentsfile": |
| 121 | cmdline.GetNextArgumentOrError(ref contentsFile); | 100 | contentsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 122 | return true; | 101 | return true; |
| 123 | case "outputsfile": | 102 | case "outputsfile": |
| 124 | cmdline.GetNextArgumentOrError(ref outputsFile); | 103 | outputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 125 | return true; | 104 | return true; |
| 126 | case "builtoutputsfile": | 105 | case "builtoutputsfile": |
| 127 | cmdline.GetNextArgumentOrError(ref builtOutputsFile); | 106 | builtOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 128 | return true; | 107 | return true; |
| 129 | 108 | ||
| 130 | case "d": | 109 | case "d": |
| 131 | case "define": | 110 | case "define": |
| 132 | cmdline.GetNextArgumentOrError(defines); | 111 | parser.GetNextArgumentOrError(arg, defines); |
| 133 | return true; | 112 | return true; |
| 134 | 113 | ||
| 135 | case "i": | 114 | case "i": |
| 136 | case "includepath": | 115 | case "includepath": |
| 137 | cmdline.GetNextArgumentOrError(includePaths); | 116 | parser.GetNextArgumentOrError(arg, includePaths); |
| 138 | return true; | 117 | return true; |
| 139 | 118 | ||
| 140 | case "intermediatefolder": | 119 | case "intermediatefolder": |
| 141 | cmdline.GetNextArgumentOrError(ref intermediateFolder); | 120 | intermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg); |
| 142 | return true; | 121 | return true; |
| 143 | 122 | ||
| 144 | case "loc": | 123 | case "loc": |
| 145 | cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files"); | 124 | parser.GetNextArgumentAsFilePathOrError(arg, "localization files", locFiles); |
| 146 | return true; | 125 | return true; |
| 147 | 126 | ||
| 148 | case "lib": | 127 | case "lib": |
| 149 | cmdline.GetNextArgumentAsFilePathOrError(libraryFiles, "library files"); | 128 | parser.GetNextArgumentAsFilePathOrError(arg, "library files", libraryFiles); |
| 150 | return true; | 129 | return true; |
| 151 | 130 | ||
| 152 | case "o": | 131 | case "o": |
| 153 | case "out": | 132 | case "out": |
| 154 | cmdline.GetNextArgumentOrError(ref outputFile); | 133 | outputFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 155 | return true; | 134 | return true; |
| 156 | 135 | ||
| 157 | case "outputtype": | 136 | case "outputtype": |
| 158 | cmdline.GetNextArgumentOrError(ref outputType); | 137 | outputType= parser.GetNextArgumentOrError(arg); |
| 159 | return true; | 138 | return true; |
| 160 | 139 | ||
| 161 | case "nologo": | 140 | case "nologo": |
| 162 | showLogo = false; | 141 | showLogo = false; |
| 163 | return true; | 142 | return true; |
| 164 | 143 | ||
| 165 | case "v": | 144 | case "v": |
| 166 | case "verbose": | 145 | case "verbose": |
| 167 | verbose = true; | 146 | verbose = true; |
| 168 | return true; | 147 | return true; |
| 169 | 148 | ||
| 170 | case "version": | 149 | case "version": |
| 171 | case "-version": | 150 | case "-version": |
| 172 | showVersion = true; | 151 | showVersion = true; |
| 173 | return true; | 152 | return true; |
| 174 | 153 | ||
| 175 | case "sval": | 154 | case "sval": |
| 176 | // todo: implement | 155 | // todo: implement |
| 177 | return true; | 156 | return true; |
| 178 | } | 157 | } |
| 179 | 158 | ||
| 180 | return false; | 159 | return false; |
| 181 | } | 160 | } |
| 182 | else | 161 | else |
| 183 | { | 162 | { |
| 184 | files.AddRange(CommandLineHelper.GetFiles(arg, "source code")); | 163 | parser.GetArgumentAsFilePathOrError(arg, "source code", files); |
| 185 | return true; | 164 | return true; |
| 186 | } | 165 | } |
| 187 | }); | 166 | }); |
| @@ -205,22 +184,22 @@ namespace WixToolset.Core.CommandLine | |||
| 205 | 184 | ||
| 206 | switch (command) | 185 | switch (command) |
| 207 | { | 186 | { |
| 208 | case Commands.Build: | 187 | case Commands.Build: |
| 209 | { | 188 | { |
| 210 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 189 | var sourceFiles = GatherSourceFiles(files, outputFolder); |
| 211 | var variables = this.GatherPreprocessorVariables(defines); | 190 | var variables = this.GatherPreprocessorVariables(defines); |
| 212 | var bindPathList = this.GatherBindPaths(bindPaths); | 191 | var bindPathList = this.GatherBindPaths(bindPaths); |
| 213 | var filterCultures = CalculateFilterCultures(cultures); | 192 | var filterCultures = CalculateFilterCultures(cultures); |
| 214 | var type = CalculateOutputType(outputType, outputFile); | 193 | var type = CalculateOutputType(outputType, outputFile); |
| 215 | return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile); | 194 | return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile); |
| 216 | } | 195 | } |
| 217 | 196 | ||
| 218 | case Commands.Compile: | 197 | case Commands.Compile: |
| 219 | { | 198 | { |
| 220 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 199 | var sourceFiles = GatherSourceFiles(files, outputFolder); |
| 221 | var variables = GatherPreprocessorVariables(defines); | 200 | var variables = GatherPreprocessorVariables(defines); |
| 222 | return new CompileCommand(this.ServiceProvider, sourceFiles, variables); | 201 | return new CompileCommand(this.ServiceProvider, sourceFiles, variables); |
| 223 | } | 202 | } |
| 224 | } | 203 | } |
| 225 | 204 | ||
| 226 | return null; | 205 | return null; |
| @@ -262,63 +241,87 @@ namespace WixToolset.Core.CommandLine | |||
| 262 | 241 | ||
| 263 | switch (outputType.ToLowerInvariant()) | 242 | switch (outputType.ToLowerInvariant()) |
| 264 | { | 243 | { |
| 265 | case "bundle": | 244 | case "bundle": |
| 266 | case ".exe": | 245 | case ".exe": |
| 267 | return OutputType.Bundle; | 246 | return OutputType.Bundle; |
| 268 | 247 | ||
| 269 | case "library": | 248 | case "library": |
| 270 | case ".wixlib": | 249 | case ".wixlib": |
| 271 | return OutputType.Library; | 250 | return OutputType.Library; |
| 272 | 251 | ||
| 273 | case "module": | 252 | case "module": |
| 274 | case ".msm": | 253 | case ".msm": |
| 275 | return OutputType.Module; | 254 | return OutputType.Module; |
| 276 | 255 | ||
| 277 | case "patch": | 256 | case "patch": |
| 278 | case ".msp": | 257 | case ".msp": |
| 279 | return OutputType.Patch; | 258 | return OutputType.Patch; |
| 280 | 259 | ||
| 281 | case ".pcp": | 260 | case ".pcp": |
| 282 | return OutputType.PatchCreation; | 261 | return OutputType.PatchCreation; |
| 283 | 262 | ||
| 284 | case "product": | 263 | case "product": |
| 285 | case "package": | 264 | case "package": |
| 286 | case ".msi": | 265 | case ".msi": |
| 287 | return OutputType.Product; | 266 | return OutputType.Product; |
| 288 | 267 | ||
| 289 | case "transform": | 268 | case "transform": |
| 290 | case ".mst": | 269 | case ".mst": |
| 291 | return OutputType.Transform; | 270 | return OutputType.Transform; |
| 292 | 271 | ||
| 293 | case "intermediatepostlink": | 272 | case "intermediatepostlink": |
| 294 | case ".wixipl": | 273 | case ".wixipl": |
| 295 | return OutputType.IntermediatePostLink; | 274 | return OutputType.IntermediatePostLink; |
| 296 | } | 275 | } |
| 297 | 276 | ||
| 298 | return OutputType.Unknown; | 277 | return OutputType.Unknown; |
| 299 | } | 278 | } |
| 300 | 279 | ||
| 301 | #if UNUSED | 280 | private ICommandLine Parse(ICommandLineContext context, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, IParseCommandLine, string, bool> parseArgument) |
| 302 | private static CommandLine Parse(string commandLineString, Func<CommandLine, string, bool> parseArgument) | ||
| 303 | { | 281 | { |
| 304 | var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray(); | 282 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); |
| 305 | |||
| 306 | return CommandLine.Parse(arguments, null, parseArgument); | ||
| 307 | } | ||
| 308 | 283 | ||
| 309 | private static CommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseArgument) | 284 | foreach (var extension in extensions) |
| 310 | { | 285 | { |
| 311 | return CommandLine.Parse(commandLineArguments, null, parseArgument); | 286 | extension.PreParse(context); |
| 312 | } | 287 | } |
| 313 | #endif | ||
| 314 | 288 | ||
| 315 | private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, string, bool> parseArgument) | 289 | var parser = context.Arguments.Parse(); |
| 316 | { | ||
| 317 | this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments); | ||
| 318 | 290 | ||
| 319 | this.QueueArgumentsAndLoadExtensions(this.OriginalArguments); | 291 | while (!this.ShowHelp && |
| 292 | String.IsNullOrEmpty(parser.ErrorArgument) && | ||
| 293 | parser.TryGetNextSwitchOrArgument(out var arg)) | ||
| 294 | { | ||
| 295 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
| 296 | { | ||
| 297 | continue; | ||
| 298 | } | ||
| 320 | 299 | ||
| 321 | this.ProcessRemainingArguments(context, parseArgument, parseCommand); | 300 | if (parser.IsSwitch(arg)) |
| 301 | { | ||
| 302 | if (!parseArgument(this, parser, arg) && | ||
| 303 | !this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions)) | ||
| 304 | { | ||
| 305 | parser.ErrorArgument = arg; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported. | ||
| 309 | { | ||
| 310 | if (parseCommand(this, arg)) | ||
| 311 | { | ||
| 312 | this.ActiveCommand = arg; | ||
| 313 | } | ||
| 314 | else | ||
| 315 | { | ||
| 316 | parser.ErrorArgument = arg; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions) && | ||
| 320 | !parseArgument(this, parser, arg)) | ||
| 321 | { | ||
| 322 | parser.ErrorArgument = arg; | ||
| 323 | } | ||
| 324 | } | ||
| 322 | 325 | ||
| 323 | return this; | 326 | return this; |
| 324 | } | 327 | } |
| @@ -358,7 +361,7 @@ namespace WixToolset.Core.CommandLine | |||
| 358 | return variables; | 361 | return variables; |
| 359 | } | 362 | } |
| 360 | 363 | ||
| 361 | private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths) | 364 | private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths) |
| 362 | { | 365 | { |
| 363 | var result = new List<BindPath>(); | 366 | var result = new List<BindPath>(); |
| 364 | 367 | ||
| @@ -379,172 +382,11 @@ namespace WixToolset.Core.CommandLine | |||
| 379 | return result; | 382 | return result; |
| 380 | } | 383 | } |
| 381 | 384 | ||
| 382 | /// <summary> | 385 | private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parse, IEnumerable<IExtensionCommandLine> extensions) |
| 383 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
| 384 | /// </summary> | ||
| 385 | /// <param name="args">The list of strings to check.</param> | ||
| 386 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
| 387 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
| 388 | public bool IsSwitch(string arg) | ||
| 389 | { | ||
| 390 | return arg != null && arg.Length > 1 && ('/' == arg[0] || '-' == arg[0]); | ||
| 391 | } | ||
| 392 | |||
| 393 | /// <summary> | ||
| 394 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
| 395 | /// </summary> | ||
| 396 | /// <param name="args">The list of strings to check.</param> | ||
| 397 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
| 398 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
| 399 | public bool IsSwitchAt(IEnumerable<string> args, int index) | ||
| 400 | { | ||
| 401 | var arg = args.ElementAtOrDefault(index); | ||
| 402 | return IsSwitch(arg); | ||
| 403 | } | ||
| 404 | |||
| 405 | public void GetNextArgumentOrError(ref string arg) | ||
| 406 | { | ||
| 407 | this.TryGetNextArgumentOrError(out arg); | ||
| 408 | } | ||
| 409 | |||
| 410 | public void GetNextArgumentOrError(IList<string> args) | ||
| 411 | { | ||
| 412 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
| 413 | { | ||
| 414 | args.Add(arg); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | public void GetNextArgumentAsFilePathOrError(IList<string> args, string fileType) | ||
| 419 | { | ||
| 420 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
| 421 | { | ||
| 422 | foreach (var path in CommandLineHelper.GetFiles(arg, fileType)) | ||
| 423 | { | ||
| 424 | args.Add(path); | ||
| 425 | } | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | public bool TryGetNextArgumentOrError(out string arg) | ||
| 430 | { | ||
| 431 | if (TryDequeue(this.RemainingArguments, out arg) && !this.IsSwitch(arg)) | ||
| 432 | { | ||
| 433 | return true; | ||
| 434 | } | ||
| 435 | |||
| 436 | this.ErrorArgument = arg ?? CommandLineParser.ExpectedArgument; | ||
| 437 | |||
| 438 | return false; | ||
| 439 | } | ||
| 440 | |||
| 441 | private static bool TryDequeue(Queue<string> q, out string arg) | ||
| 442 | { | ||
| 443 | if (q.Count > 0) | ||
| 444 | { | ||
| 445 | arg = q.Dequeue(); | ||
| 446 | return true; | ||
| 447 | } | ||
| 448 | |||
| 449 | arg = null; | ||
| 450 | return false; | ||
| 451 | } | ||
| 452 | |||
| 453 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) | ||
| 454 | { | ||
| 455 | List<string> args = new List<string>(); | ||
| 456 | |||
| 457 | foreach (var arg in commandLineArguments) | ||
| 458 | { | ||
| 459 | if ('@' == arg[0]) | ||
| 460 | { | ||
| 461 | var responseFileArguments = CommandLineParser.ParseResponseFile(arg.Substring(1)); | ||
| 462 | args.AddRange(responseFileArguments); | ||
| 463 | } | ||
| 464 | else | ||
| 465 | { | ||
| 466 | args.Add(arg); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | this.OriginalArguments = args.ToArray(); | ||
| 471 | } | ||
| 472 | |||
| 473 | private void QueueArgumentsAndLoadExtensions(string[] args) | ||
| 474 | { | 386 | { |
| 475 | for (var i = 0; i < args.Length; ++i) | ||
| 476 | { | ||
| 477 | var arg = args[i]; | ||
| 478 | |||
| 479 | if ("-ext" == arg || "/ext" == arg) | ||
| 480 | { | ||
| 481 | if (!this.IsSwitchAt(args, ++i)) | ||
| 482 | { | ||
| 483 | this.ExtensionManager.Load(args[i]); | ||
| 484 | } | ||
| 485 | else | ||
| 486 | { | ||
| 487 | this.ErrorArgument = arg; | ||
| 488 | break; | ||
| 489 | } | ||
| 490 | } | ||
| 491 | else | ||
| 492 | { | ||
| 493 | this.RemainingArguments.Enqueue(arg); | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | private void ProcessRemainingArguments(ICommandLineContext context, Func<CommandLineParser, string, bool> parseArgument, Func<CommandLineParser, string, bool> parseCommand) | ||
| 499 | { | ||
| 500 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); | ||
| 501 | |||
| 502 | foreach (var extension in extensions) | 387 | foreach (var extension in extensions) |
| 503 | { | 388 | { |
| 504 | extension.PreParse(context); | 389 | if (extension.TryParseArgument(parse, arg)) |
| 505 | } | ||
| 506 | |||
| 507 | while (!this.ShowHelp && | ||
| 508 | String.IsNullOrEmpty(this.ErrorArgument) && | ||
| 509 | TryDequeue(this.RemainingArguments, out var arg)) | ||
| 510 | { | ||
| 511 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
| 512 | { | ||
| 513 | continue; | ||
| 514 | } | ||
| 515 | |||
| 516 | if ('-' == arg[0] || '/' == arg[0]) | ||
| 517 | { | ||
| 518 | if (!parseArgument(this, arg) && | ||
| 519 | !this.TryParseCommandLineArgumentWithExtension(arg, extensions)) | ||
| 520 | { | ||
| 521 | this.ErrorArgument = arg; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported. | ||
| 525 | { | ||
| 526 | if (parseCommand(this, arg)) | ||
| 527 | { | ||
| 528 | this.ActiveCommand = arg; | ||
| 529 | } | ||
| 530 | else | ||
| 531 | { | ||
| 532 | this.ErrorArgument = arg; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, extensions) && | ||
| 536 | !parseArgument(this, arg)) | ||
| 537 | { | ||
| 538 | this.ErrorArgument = arg; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable<IExtensionCommandLine> extensions) | ||
| 544 | { | ||
| 545 | foreach (var extension in extensions) | ||
| 546 | { | ||
| 547 | if (extension.TryParseArgument(this, arg)) | ||
| 548 | { | 390 | { |
| 549 | return true; | 391 | return true; |
| 550 | } | 392 | } |
| @@ -552,110 +394,5 @@ namespace WixToolset.Core.CommandLine | |||
| 552 | 394 | ||
| 553 | return false; | 395 | return false; |
| 554 | } | 396 | } |
| 555 | |||
| 556 | private static List<string> ParseResponseFile(string responseFile) | ||
| 557 | { | ||
| 558 | string arguments; | ||
| 559 | |||
| 560 | using (StreamReader reader = new StreamReader(responseFile)) | ||
| 561 | { | ||
| 562 | arguments = reader.ReadToEnd(); | ||
| 563 | } | ||
| 564 | |||
| 565 | return CommandLineParser.ParseArgumentsToArray(arguments); | ||
| 566 | } | ||
| 567 | |||
| 568 | private static List<string> ParseArgumentsToArray(string arguments) | ||
| 569 | { | ||
| 570 | // Scan and parse the arguments string, dividing up the arguments based on whitespace. | ||
| 571 | // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed. | ||
| 572 | // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments. | ||
| 573 | // Escaped quotes and escaped backslashes also need to be unescaped by this process. | ||
| 574 | |||
| 575 | // Collects the final list of arguments to be returned. | ||
| 576 | var argsList = new List<string>(); | ||
| 577 | |||
| 578 | // True if we are inside an unescaped quote, meaning whitespace should be ignored. | ||
| 579 | var insideQuote = false; | ||
| 580 | |||
| 581 | // Index of the start of the current argument substring; either the start of the argument | ||
| 582 | // or the start of a quoted or unquoted sequence within it. | ||
| 583 | var partStart = 0; | ||
| 584 | |||
| 585 | // The current argument string being built; when completed it will be added to the list. | ||
| 586 | var arg = new StringBuilder(); | ||
| 587 | |||
| 588 | for (int i = 0; i <= arguments.Length; i++) | ||
| 589 | { | ||
| 590 | if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote)) | ||
| 591 | { | ||
| 592 | // Reached a whitespace separator or the end of the string. | ||
| 593 | |||
| 594 | // Finish building the current argument. | ||
| 595 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 596 | |||
| 597 | // Skip over the whitespace character. | ||
| 598 | partStart = i + 1; | ||
| 599 | |||
| 600 | // Add the argument to the list if it's not empty. | ||
| 601 | if (arg.Length > 0) | ||
| 602 | { | ||
| 603 | argsList.Add(CommandLineParser.ExpandEnvironmentVariables(arg.ToString())); | ||
| 604 | arg.Length = 0; | ||
| 605 | } | ||
| 606 | } | ||
| 607 | else if (i > partStart && arguments[i - 1] == '\\') | ||
| 608 | { | ||
| 609 | // Check the character following an unprocessed backslash. | ||
| 610 | // Unescape quotes, and backslashes followed by a quote. | ||
| 611 | if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"')) | ||
| 612 | { | ||
| 613 | // Unescape the quote or backslash by skipping the preceeding backslash. | ||
| 614 | arg.Append(arguments.Substring(partStart, i - 1 - partStart)); | ||
| 615 | arg.Append(arguments[i]); | ||
| 616 | partStart = i + 1; | ||
| 617 | } | ||
| 618 | } | ||
| 619 | else if (arguments[i] == '"') | ||
| 620 | { | ||
| 621 | // Add the quoted or unquoted section to the argument string. | ||
| 622 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
| 623 | |||
| 624 | // And skip over the quote character. | ||
| 625 | partStart = i + 1; | ||
| 626 | |||
| 627 | insideQuote = !insideQuote; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | return argsList; | ||
| 632 | } | ||
| 633 | |||
| 634 | private static string ExpandEnvironmentVariables(string arguments) | ||
| 635 | { | ||
| 636 | var id = Environment.GetEnvironmentVariables(); | ||
| 637 | |||
| 638 | var regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)"); | ||
| 639 | MatchCollection matches = regex.Matches(arguments); | ||
| 640 | |||
| 641 | string value = String.Empty; | ||
| 642 | for (int i = 0; i <= (matches.Count - 1); i++) | ||
| 643 | { | ||
| 644 | try | ||
| 645 | { | ||
| 646 | var key = matches[i].Value; | ||
| 647 | regex = new Regex(String.Concat("(?i)(?:\\%)(?:", key, ")(?:\\%)")); | ||
| 648 | value = id[key].ToString(); | ||
| 649 | arguments = regex.Replace(arguments, value); | ||
| 650 | } | ||
| 651 | catch (NullReferenceException) | ||
| 652 | { | ||
| 653 | // Collapse unresolved environment variables. | ||
| 654 | arguments = regex.Replace(arguments, value); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | return arguments; | ||
| 659 | } | ||
| 660 | } | 397 | } |
| 661 | } | 398 | } |
diff --git a/src/WixToolset.Core/CommandLine/ParseCommandLine.cs b/src/WixToolset.Core/CommandLine/ParseCommandLine.cs new file mode 100644 index 00000000..7d0dcbd8 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/ParseCommandLine.cs | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.CommandLine | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections.Generic; | ||
| 7 | using System.IO; | ||
| 8 | using WixToolset.Data; | ||
| 9 | using WixToolset.Extensibility.Services; | ||
| 10 | |||
| 11 | internal class ParseCommandLine : IParseCommandLine | ||
| 12 | { | ||
| 13 | private const string ExpectedArgument = "expected argument"; | ||
| 14 | |||
| 15 | public string ErrorArgument { get; set; } | ||
| 16 | |||
| 17 | private Queue<string> RemainingArguments { get; } | ||
| 18 | |||
| 19 | private IMessaging Messaging { get; } | ||
| 20 | |||
| 21 | public ParseCommandLine(IMessaging messaging, string[] arguments, string errorArgument) | ||
| 22 | { | ||
| 23 | this.Messaging = messaging; | ||
| 24 | this.RemainingArguments = new Queue<string>(arguments); | ||
| 25 | this.ErrorArgument = errorArgument; | ||
| 26 | } | ||
| 27 | |||
| 28 | public bool IsSwitch(string arg) => !String.IsNullOrEmpty(arg) && ('/' == arg[0] || '-' == arg[0]); | ||
| 29 | |||
| 30 | public void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths) | ||
| 31 | { | ||
| 32 | foreach (var path in GetFiles(argument, fileType)) | ||
| 33 | { | ||
| 34 | paths.Add(path); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | public string GetNextArgumentOrError(string commandLineSwitch) | ||
| 39 | { | ||
| 40 | if (this.TryGetNextNonSwitchArgumentOrError(out var argument)) | ||
| 41 | { | ||
| 42 | return argument; | ||
| 43 | } | ||
| 44 | |||
| 45 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 46 | return null; | ||
| 47 | } | ||
| 48 | |||
| 49 | public bool GetNextArgumentOrError(string commandLineSwitch, IList<string> args) | ||
| 50 | { | ||
| 51 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg)) | ||
| 52 | { | ||
| 53 | args.Add(arg); | ||
| 54 | return true; | ||
| 55 | } | ||
| 56 | |||
| 57 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | |||
| 61 | public string GetNextArgumentAsDirectoryOrError(string commandLineSwitch) | ||
| 62 | { | ||
| 63 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory)) | ||
| 64 | { | ||
| 65 | return directory; | ||
| 66 | } | ||
| 67 | |||
| 68 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 69 | return null; | ||
| 70 | } | ||
| 71 | |||
| 72 | public bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList<string> directories) | ||
| 73 | { | ||
| 74 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory)) | ||
| 75 | { | ||
| 76 | directories.Add(directory); | ||
| 77 | return true; | ||
| 78 | } | ||
| 79 | |||
| 80 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | |||
| 84 | public string GetNextArgumentAsFilePathOrError(string commandLineSwitch) | ||
| 85 | { | ||
| 86 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetFile(commandLineSwitch, arg, out var path)) | ||
| 87 | { | ||
| 88 | return path; | ||
| 89 | } | ||
| 90 | |||
| 91 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 92 | return null; | ||
| 93 | } | ||
| 94 | |||
| 95 | public bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList<string> paths) | ||
| 96 | { | ||
| 97 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg)) | ||
| 98 | { | ||
| 99 | foreach (var path in GetFiles(arg, fileType)) | ||
| 100 | { | ||
| 101 | paths.Add(path); | ||
| 102 | } | ||
| 103 | |||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
| 108 | return false; | ||
| 109 | } | ||
| 110 | |||
| 111 | public bool TryGetNextSwitchOrArgument(out string arg) | ||
| 112 | { | ||
| 113 | return TryDequeue(this.RemainingArguments, out arg); | ||
| 114 | } | ||
| 115 | |||
| 116 | private bool TryGetNextNonSwitchArgumentOrError(out string arg) | ||
| 117 | { | ||
| 118 | var result = this.TryGetNextSwitchOrArgument(out arg); | ||
| 119 | |||
| 120 | if (!result && !this.IsSwitch(arg)) | ||
| 121 | { | ||
| 122 | this.ErrorArgument = arg ?? ParseCommandLine.ExpectedArgument; | ||
| 123 | } | ||
| 124 | |||
| 125 | return result; | ||
| 126 | } | ||
| 127 | |||
| 128 | private static bool IsValidArg(string arg) => !(String.IsNullOrEmpty(arg) || '/' == arg[0] || '-' == arg[0]); | ||
| 129 | |||
| 130 | private static bool TryDequeue(Queue<string> q, out string arg) | ||
| 131 | { | ||
| 132 | if (q.Count > 0) | ||
| 133 | { | ||
| 134 | arg = q.Dequeue(); | ||
| 135 | return true; | ||
| 136 | } | ||
| 137 | |||
| 138 | arg = null; | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | |||
| 142 | private bool TryGetDirectory(string commandlineSwitch, IMessaging messageHandler, string arg, out string directory) | ||
| 143 | { | ||
| 144 | directory = null; | ||
| 145 | |||
| 146 | if (File.Exists(arg)) | ||
| 147 | { | ||
| 148 | this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, arg)); | ||
| 149 | return false; | ||
| 150 | } | ||
| 151 | |||
| 152 | directory = this.VerifyPath(arg); | ||
| 153 | return directory != null; | ||
| 154 | } | ||
| 155 | |||
| 156 | private bool TryGetFile(string commandlineSwitch, string arg, out string path) | ||
| 157 | { | ||
| 158 | path = null; | ||
| 159 | |||
| 160 | if (!IsValidArg(arg)) | ||
| 161 | { | ||
| 162 | this.Messaging.Write(ErrorMessages.FilePathRequired(commandlineSwitch)); | ||
| 163 | } | ||
| 164 | else if (Directory.Exists(arg)) | ||
| 165 | { | ||
| 166 | this.Messaging.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, arg)); | ||
| 167 | } | ||
| 168 | else | ||
| 169 | { | ||
| 170 | path = this.VerifyPath(arg); | ||
| 171 | } | ||
| 172 | |||
| 173 | return path != null; | ||
| 174 | } | ||
| 175 | |||
| 176 | /// <summary> | ||
| 177 | /// Get a set of files that possibly have a search pattern in the path (such as '*'). | ||
| 178 | /// </summary> | ||
| 179 | /// <param name="searchPath">Search path to find files in.</param> | ||
| 180 | /// <param name="fileType">Type of file; typically "Source".</param> | ||
| 181 | /// <returns>An array of files matching the search path.</returns> | ||
| 182 | /// <remarks> | ||
| 183 | /// This method is written in this verbose way because it needs to support ".." in the path. | ||
| 184 | /// It needs the directory path isolated from the file name in order to use Directory.GetFiles | ||
| 185 | /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since | ||
| 186 | /// Path.GetDirectoryName does not support ".." in the path. | ||
| 187 | /// </remarks> | ||
| 188 | /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception> | ||
| 189 | private string[] GetFiles(string searchPath, string fileType) | ||
| 190 | { | ||
| 191 | if (null == searchPath) | ||
| 192 | { | ||
| 193 | throw new ArgumentNullException(nameof(searchPath)); | ||
| 194 | } | ||
| 195 | |||
| 196 | // Convert alternate directory separators to the standard one. | ||
| 197 | string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
| 198 | int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
| 199 | var files = new string[0]; | ||
| 200 | |||
| 201 | try | ||
| 202 | { | ||
| 203 | if (0 > lastSeparator) | ||
| 204 | { | ||
| 205 | files = Directory.GetFiles(".", filePath); | ||
| 206 | } | ||
| 207 | else // found directory separator | ||
| 208 | { | ||
| 209 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1)); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | catch (DirectoryNotFoundException) | ||
| 213 | { | ||
| 214 | // Don't let this function throw the DirectoryNotFoundException. This exception | ||
| 215 | // occurs for non-existant directories and invalid characters in the searchPattern. | ||
| 216 | } | ||
| 217 | catch (ArgumentException) | ||
| 218 | { | ||
| 219 | // Don't let this function throw the ArgumentException. This exception | ||
| 220 | // occurs in certain situations such as when passing a malformed UNC path. | ||
| 221 | } | ||
| 222 | catch (IOException) | ||
| 223 | { | ||
| 224 | } | ||
| 225 | |||
| 226 | if (0 == files.Length) | ||
| 227 | { | ||
| 228 | this.Messaging.Write(ErrorMessages.FileNotFound(null, searchPath, fileType)); | ||
| 229 | } | ||
| 230 | |||
| 231 | return files; | ||
| 232 | } | ||
| 233 | |||
| 234 | private string VerifyPath(string path) | ||
| 235 | { | ||
| 236 | string fullPath; | ||
| 237 | |||
| 238 | if (0 <= path.IndexOf('\"')) | ||
| 239 | { | ||
| 240 | this.Messaging.Write(ErrorMessages.PathCannotContainQuote(path)); | ||
| 241 | return null; | ||
| 242 | } | ||
| 243 | |||
| 244 | try | ||
| 245 | { | ||
| 246 | fullPath = Path.GetFullPath(path); | ||
| 247 | } | ||
| 248 | catch (Exception e) | ||
| 249 | { | ||
| 250 | this.Messaging.Write(ErrorMessages.InvalidCommandLineFileName(path, e.Message)); | ||
| 251 | return null; | ||
| 252 | } | ||
| 253 | |||
| 254 | return fullPath; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
diff --git a/src/WixToolset.Core/Preprocessor.cs b/src/WixToolset.Core/Preprocessor.cs index 6733f493..23d3f205 100644 --- a/src/WixToolset.Core/Preprocessor.cs +++ b/src/WixToolset.Core/Preprocessor.cs | |||
| @@ -644,7 +644,7 @@ namespace WixToolset.Core | |||
| 644 | 644 | ||
| 645 | if (null == includeFile) | 645 | if (null == includeFile) |
| 646 | { | 646 | { |
| 647 | throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, includePath, "include")); | 647 | throw new WixFileNotFoundException(sourceLineNumbers, includePath, "include"); |
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | using (XmlReader reader = XmlReader.Create(includeFile, DocumentXmlReaderSettings)) | 650 | using (XmlReader reader = XmlReader.Create(includeFile, DocumentXmlReaderSettings)) |
diff --git a/src/WixToolset.Core/WixToolsetServiceProvider.cs b/src/WixToolset.Core/WixToolsetServiceProvider.cs index 20c6c309..7d318648 100644 --- a/src/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/WixToolset.Core/WixToolsetServiceProvider.cs | |||
| @@ -25,6 +25,7 @@ namespace WixToolset.Core | |||
| 25 | { typeof(IWindowsInstallerBackendHelper), (provider, singletons) => AddSingleton(singletons, typeof(IWindowsInstallerBackendHelper), new WindowsInstallerBackendHelper(provider)) }, | 25 | { typeof(IWindowsInstallerBackendHelper), (provider, singletons) => AddSingleton(singletons, typeof(IWindowsInstallerBackendHelper), new WindowsInstallerBackendHelper(provider)) }, |
| 26 | 26 | ||
| 27 | // Transients. | 27 | // Transients. |
| 28 | { typeof(ICommandLineArguments), (provider, singletons) => new CommandLineArguments(provider) }, | ||
| 28 | { typeof(ICommandLineContext), (provider, singletons) => new CommandLineContext(provider) }, | 29 | { typeof(ICommandLineContext), (provider, singletons) => new CommandLineContext(provider) }, |
| 29 | { typeof(ICommandLine), (provider, singletons) => new CommandLineParser() }, | 30 | { typeof(ICommandLine), (provider, singletons) => new CommandLineParser() }, |
| 30 | { typeof(IPreprocessContext), (provider, singletons) => new PreprocessContext(provider) }, | 31 | { typeof(IPreprocessContext), (provider, singletons) => new PreprocessContext(provider) }, |
diff --git a/src/light/LightCommandLine.cs b/src/light/LightCommandLine.cs index 9a90b9ce..2aa9ea59 100644 --- a/src/light/LightCommandLine.cs +++ b/src/light/LightCommandLine.cs | |||
| @@ -6,8 +6,8 @@ namespace WixToolset.Tools | |||
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Globalization; | 7 | using System.Globalization; |
| 8 | using System.IO; | 8 | using System.IO; |
| 9 | using WixToolset.Core.CommandLine; | ||
| 10 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 10 | using WixToolset.Extensibility; | ||
| 11 | using WixToolset.Extensibility.Services; | 11 | using WixToolset.Extensibility.Services; |
| 12 | 12 | ||
| 13 | public class LightCommandLine | 13 | public class LightCommandLine |
| @@ -22,7 +22,6 @@ namespace WixToolset.Tools | |||
| 22 | this.SuppressIces = new List<string>(); | 22 | this.SuppressIces = new List<string>(); |
| 23 | this.Ices = new List<string>(); | 23 | this.Ices = new List<string>(); |
| 24 | this.BindPaths = new List<BindPath>(); | 24 | this.BindPaths = new List<BindPath>(); |
| 25 | this.Extensions = new List<string>(); | ||
| 26 | this.Files = new List<string>(); | 25 | this.Files = new List<string>(); |
| 27 | this.LocalizationFiles = new List<string>(); | 26 | this.LocalizationFiles = new List<string>(); |
| 28 | this.Variables = new Dictionary<string, string>(); | 27 | this.Variables = new Dictionary<string, string>(); |
| @@ -80,8 +79,6 @@ namespace WixToolset.Tools | |||
| 80 | 79 | ||
| 81 | public List<BindPath> BindPaths { get; private set; } | 80 | public List<BindPath> BindPaths { get; private set; } |
| 82 | 81 | ||
| 83 | public List<string> Extensions { get; private set; } | ||
| 84 | |||
| 85 | public List<string> Files { get; private set; } | 82 | public List<string> Files { get; private set; } |
| 86 | 83 | ||
| 87 | public List<string> LocalizationFiles { get; private set; } | 84 | public List<string> LocalizationFiles { get; private set; } |
| @@ -96,35 +93,40 @@ namespace WixToolset.Tools | |||
| 96 | /// Parse the commandline arguments. | 93 | /// Parse the commandline arguments. |
| 97 | /// </summary> | 94 | /// </summary> |
| 98 | /// <param name="args">Commandline arguments.</param> | 95 | /// <param name="args">Commandline arguments.</param> |
| 99 | public string[] Parse(string[] args) | 96 | public string[] Parse(ICommandLineContext context) |
| 100 | { | 97 | { |
| 101 | List<string> unprocessed = new List<string>(); | 98 | var unprocessed = new List<string>(); |
| 99 | |||
| 100 | var extensions = context.ExtensionManager.Create<IExtensionCommandLine>(); | ||
| 101 | |||
| 102 | foreach (var extension in extensions) | ||
| 103 | { | ||
| 104 | extension.PreParse(context); | ||
| 105 | } | ||
| 106 | |||
| 107 | var parser = context.Arguments.Parse(); | ||
| 102 | 108 | ||
| 103 | for (int i = 0; i < args.Length; ++i) | 109 | while (!this.ShowHelp && |
| 110 | String.IsNullOrEmpty(parser.ErrorArgument) && | ||
| 111 | parser.TryGetNextSwitchOrArgument(out var arg)) | ||
| 104 | { | 112 | { |
| 105 | string arg = args[i]; | 113 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. |
| 106 | if (String.IsNullOrEmpty(arg)) // skip blank arguments | ||
| 107 | { | 114 | { |
| 108 | continue; | 115 | continue; |
| 109 | } | 116 | } |
| 110 | 117 | ||
| 111 | if (1 == arg.Length) // treat '-' and '@' as filenames when by themselves. | 118 | if (parser.IsSwitch(arg)) |
| 112 | { | 119 | { |
| 113 | unprocessed.Add(arg); | 120 | var parameter = arg.Substring(1); |
| 114 | } | ||
| 115 | else if ('-' == arg[0] || '/' == arg[0]) | ||
| 116 | { | ||
| 117 | string parameter = arg.Substring(1); | ||
| 118 | if (parameter.Equals("b", StringComparison.Ordinal)) | 121 | if (parameter.Equals("b", StringComparison.Ordinal)) |
| 119 | { | 122 | { |
| 120 | if (!CommandLineHelper.IsValidArg(args, ++i)) | 123 | var result = parser.GetNextArgumentOrError(arg); |
| 124 | if (!String.IsNullOrEmpty(result)) | ||
| 121 | { | 125 | { |
| 122 | break; | 126 | var bindPath = BindPath.Parse(result); |
| 123 | } | ||
| 124 | 127 | ||
| 125 | var bindPath = BindPath.Parse(args[i]); | 128 | this.BindPaths.Add(bindPath); |
| 126 | 129 | } | |
| 127 | this.BindPaths.Add(bindPath); | ||
| 128 | } | 130 | } |
| 129 | else if (parameter.StartsWith("cultures:", StringComparison.Ordinal)) | 131 | else if (parameter.StartsWith("cultures:", StringComparison.Ordinal)) |
| 130 | { | 132 | { |
| @@ -184,25 +186,9 @@ namespace WixToolset.Tools | |||
| 184 | this.Variables.Add(value[0], value[1]); | 186 | this.Variables.Add(value[0], value[1]); |
| 185 | } | 187 | } |
| 186 | } | 188 | } |
| 187 | else if (parameter.Equals("ext", StringComparison.Ordinal)) | ||
| 188 | { | ||
| 189 | if (!CommandLineHelper.IsValidArg(args, ++i)) | ||
| 190 | { | ||
| 191 | this.Messaging.Write(ErrorMessages.TypeSpecificationForExtensionRequired("-ext")); | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | |||
| 195 | this.Extensions.Add(args[i]); | ||
| 196 | } | ||
| 197 | else if (parameter.Equals("loc", StringComparison.Ordinal)) | 189 | else if (parameter.Equals("loc", StringComparison.Ordinal)) |
| 198 | { | 190 | { |
| 199 | string locFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 191 | parser.GetNextArgumentAsFilePathOrError(arg, "localization files", this.LocalizationFiles); |
| 200 | if (String.IsNullOrEmpty(locFile)) | ||
| 201 | { | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | |||
| 205 | this.LocalizationFiles.Add(locFile); | ||
| 206 | } | 192 | } |
| 207 | else if (parameter.Equals("nologo", StringComparison.Ordinal)) | 193 | else if (parameter.Equals("nologo", StringComparison.Ordinal)) |
| 208 | { | 194 | { |
| @@ -214,11 +200,7 @@ namespace WixToolset.Tools | |||
| 214 | } | 200 | } |
| 215 | else if ("o" == parameter || "out" == parameter) | 201 | else if ("o" == parameter || "out" == parameter) |
| 216 | { | 202 | { |
| 217 | this.OutputFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 203 | this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 218 | if (String.IsNullOrEmpty(this.OutputFile)) | ||
| 219 | { | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | 204 | } |
| 223 | else if (parameter.Equals("pedantic", StringComparison.Ordinal)) | 205 | else if (parameter.Equals("pedantic", StringComparison.Ordinal)) |
| 224 | { | 206 | { |
| @@ -230,12 +212,7 @@ namespace WixToolset.Tools | |||
| 230 | } | 212 | } |
| 231 | else if (parameter.Equals("usf", StringComparison.Ordinal)) | 213 | else if (parameter.Equals("usf", StringComparison.Ordinal)) |
| 232 | { | 214 | { |
| 233 | this.UnreferencedSymbolsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 215 | this.UnreferencedSymbolsFile = parser.GetNextArgumentAsDirectoryOrError(arg); |
| 234 | |||
| 235 | if (String.IsNullOrEmpty(this.UnreferencedSymbolsFile)) | ||
| 236 | { | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | } | 216 | } |
| 240 | else if (parameter.Equals("xo", StringComparison.Ordinal)) | 217 | else if (parameter.Equals("xo", StringComparison.Ordinal)) |
| 241 | { | 218 | { |
| @@ -243,41 +220,27 @@ namespace WixToolset.Tools | |||
| 243 | } | 220 | } |
| 244 | else if (parameter.Equals("cc", StringComparison.Ordinal)) | 221 | else if (parameter.Equals("cc", StringComparison.Ordinal)) |
| 245 | { | 222 | { |
| 246 | this.CabCachePath = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i); | 223 | this.CabCachePath = parser.GetNextArgumentAsDirectoryOrError(arg); |
| 247 | |||
| 248 | if (String.IsNullOrEmpty(this.CabCachePath)) | ||
| 249 | { | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | } | 224 | } |
| 253 | else if (parameter.Equals("ct", StringComparison.Ordinal)) | 225 | else if (parameter.Equals("ct", StringComparison.Ordinal)) |
| 254 | { | 226 | { |
| 255 | if (!CommandLineHelper.IsValidArg(args, ++i)) | 227 | var result = parser.GetNextArgumentOrError(arg); |
| 228 | if (!String.IsNullOrEmpty(result)) | ||
| 256 | { | 229 | { |
| 257 | this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(String.Empty)); | 230 | if (!Int32.TryParse(result, out var ct) || 0 >= ct) |
| 258 | break; | 231 | { |
| 259 | } | 232 | this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(result)); |
| 233 | parser.ErrorArgument = arg; | ||
| 234 | break; | ||
| 235 | } | ||
| 260 | 236 | ||
| 261 | int ct = 0; | 237 | this.CabbingThreadCount = ct; |
| 262 | if (!Int32.TryParse(args[i], out ct) || 0 >= ct) | 238 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); |
| 263 | { | ||
| 264 | this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(args[i])); | ||
| 265 | break; | ||
| 266 | } | 239 | } |
| 267 | |||
| 268 | this.CabbingThreadCount = ct; | ||
| 269 | this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); | ||
| 270 | } | 240 | } |
| 271 | else if (parameter.Equals("cub", StringComparison.Ordinal)) | 241 | else if (parameter.Equals("cub", StringComparison.Ordinal)) |
| 272 | { | 242 | { |
| 273 | string cubeFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 243 | parser.GetNextArgumentAsFilePathOrError(arg, "static validation files", this.CubeFiles); |
| 274 | |||
| 275 | if (String.IsNullOrEmpty(cubeFile)) | ||
| 276 | { | ||
| 277 | break; | ||
| 278 | } | ||
| 279 | |||
| 280 | this.CubeFiles.Add(cubeFile); | ||
| 281 | } | 244 | } |
| 282 | else if (parameter.StartsWith("ice:", StringComparison.Ordinal)) | 245 | else if (parameter.StartsWith("ice:", StringComparison.Ordinal)) |
| 283 | { | 246 | { |
| @@ -285,57 +248,27 @@ namespace WixToolset.Tools | |||
| 285 | } | 248 | } |
| 286 | else if (parameter.Equals("intermediatefolder", StringComparison.OrdinalIgnoreCase)) | 249 | else if (parameter.Equals("intermediatefolder", StringComparison.OrdinalIgnoreCase)) |
| 287 | { | 250 | { |
| 288 | this.IntermediateFolder = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i); | 251 | this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg); |
| 289 | |||
| 290 | if (String.IsNullOrEmpty(this.IntermediateFolder)) | ||
| 291 | { | ||
| 292 | break; | ||
| 293 | } | ||
| 294 | } | 252 | } |
| 295 | else if (parameter.Equals("contentsfile", StringComparison.Ordinal)) | 253 | else if (parameter.Equals("contentsfile", StringComparison.Ordinal)) |
| 296 | { | 254 | { |
| 297 | this.ContentsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 255 | this.ContentsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 298 | |||
| 299 | if (String.IsNullOrEmpty(this.ContentsFile)) | ||
| 300 | { | ||
| 301 | break; | ||
| 302 | } | ||
| 303 | } | 256 | } |
| 304 | else if (parameter.Equals("outputsfile", StringComparison.Ordinal)) | 257 | else if (parameter.Equals("outputsfile", StringComparison.Ordinal)) |
| 305 | { | 258 | { |
| 306 | this.OutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 259 | this.OutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 307 | |||
| 308 | if (String.IsNullOrEmpty(this.OutputsFile)) | ||
| 309 | { | ||
| 310 | break; | ||
| 311 | } | ||
| 312 | } | 260 | } |
| 313 | else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal)) | 261 | else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal)) |
| 314 | { | 262 | { |
| 315 | this.BuiltOutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 263 | this.BuiltOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 316 | |||
| 317 | if (String.IsNullOrEmpty(this.BuiltOutputsFile)) | ||
| 318 | { | ||
| 319 | break; | ||
| 320 | } | ||
| 321 | } | 264 | } |
| 322 | else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal)) | 265 | else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal)) |
| 323 | { | 266 | { |
| 324 | this.WixprojectFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 267 | this.WixprojectFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 325 | |||
| 326 | if (String.IsNullOrEmpty(this.WixprojectFile)) | ||
| 327 | { | ||
| 328 | break; | ||
| 329 | } | ||
| 330 | } | 268 | } |
| 331 | else if (parameter.Equals("pdbout", StringComparison.Ordinal)) | 269 | else if (parameter.Equals("pdbout", StringComparison.Ordinal)) |
| 332 | { | 270 | { |
| 333 | this.PdbFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i); | 271 | this.PdbFile = parser.GetNextArgumentAsFilePathOrError(arg); |
| 334 | |||
| 335 | if (String.IsNullOrEmpty(this.PdbFile)) | ||
| 336 | { | ||
| 337 | break; | ||
| 338 | } | ||
| 339 | } | 272 | } |
| 340 | else if (parameter.StartsWith("sice:", StringComparison.Ordinal)) | 273 | else if (parameter.StartsWith("sice:", StringComparison.Ordinal)) |
| 341 | { | 274 | { |
| @@ -410,45 +343,35 @@ namespace WixToolset.Tools | |||
| 410 | this.ShowHelp = true; | 343 | this.ShowHelp = true; |
| 411 | break; | 344 | break; |
| 412 | } | 345 | } |
| 413 | else | 346 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions)) |
| 414 | { | 347 | { |
| 415 | unprocessed.Add(arg); | 348 | unprocessed.Add(arg); |
| 416 | } | 349 | } |
| 417 | } | 350 | } |
| 418 | else if ('@' == arg[0]) | 351 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions)) |
| 419 | { | ||
| 420 | string[] parsedArgs = CommandLineResponseFile.Parse(arg.Substring(1)); | ||
| 421 | string[] unparsedArgs = this.Parse(parsedArgs); | ||
| 422 | unprocessed.AddRange(unparsedArgs); | ||
| 423 | } | ||
| 424 | else | ||
| 425 | { | 352 | { |
| 426 | unprocessed.Add(arg); | 353 | unprocessed.Add(arg); |
| 427 | } | 354 | } |
| 428 | } | 355 | } |
| 429 | 356 | ||
| 430 | return unprocessed.ToArray(); | 357 | return this.ParsePostExtensions(parser, unprocessed.ToArray()); |
| 431 | } | 358 | } |
| 432 | 359 | ||
| 433 | public string[] ParsePostExtensions(string[] remaining) | 360 | private string[] ParsePostExtensions(IParseCommandLine parser, string[] remaining) |
| 434 | { | 361 | { |
| 435 | List<string> unprocessed = new List<string>(); | 362 | var unprocessed = new List<string>(); |
| 436 | 363 | ||
| 437 | for (int i = 0; i < remaining.Length; ++i) | 364 | for (int i = 0; i < remaining.Length; ++i) |
| 438 | { | 365 | { |
| 439 | string arg = remaining[i]; | 366 | var arg = remaining[i]; |
| 440 | if (String.IsNullOrEmpty(arg)) // skip blank arguments | ||
| 441 | { | ||
| 442 | continue; | ||
| 443 | } | ||
| 444 | 367 | ||
| 445 | if (1 < arg.Length && ('-' == arg[0] || '/' == arg[0])) | 368 | if (parser.IsSwitch(arg)) |
| 446 | { | 369 | { |
| 447 | unprocessed.Add(arg); | 370 | unprocessed.Add(arg); |
| 448 | } | 371 | } |
| 449 | else | 372 | else |
| 450 | { | 373 | { |
| 451 | this.Files.AddRange(CommandLineHelper.GetFiles(arg, "Source")); | 374 | parser.GetArgumentAsFilePathOrError(arg, "source files", this.Files); |
| 452 | } | 375 | } |
| 453 | } | 376 | } |
| 454 | 377 | ||
| @@ -469,7 +392,7 @@ namespace WixToolset.Tools | |||
| 469 | // Add the directories of the input files as unnamed bind paths. | 392 | // Add the directories of the input files as unnamed bind paths. |
| 470 | foreach (string file in this.Files) | 393 | foreach (string file in this.Files) |
| 471 | { | 394 | { |
| 472 | BindPath bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file))); | 395 | var bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file))); |
| 473 | this.BindPaths.Add(bindPath); | 396 | this.BindPaths.Add(bindPath); |
| 474 | } | 397 | } |
| 475 | } | 398 | } |
| @@ -481,5 +404,18 @@ namespace WixToolset.Tools | |||
| 481 | 404 | ||
| 482 | return unprocessed.ToArray(); | 405 | return unprocessed.ToArray(); |
| 483 | } | 406 | } |
| 407 | |||
| 408 | private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parser, IEnumerable<IExtensionCommandLine> extensions) | ||
| 409 | { | ||
| 410 | foreach (var extension in extensions) | ||
| 411 | { | ||
| 412 | if (extension.TryParseArgument(parser, arg)) | ||
| 413 | { | ||
| 414 | return true; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | return false; | ||
| 419 | } | ||
| 484 | } | 420 | } |
| 485 | } | 421 | } |
diff --git a/src/light/light.cs b/src/light/light.cs index c0967caa..0f467bbb 100644 --- a/src/light/light.cs +++ b/src/light/light.cs | |||
| @@ -22,7 +22,6 @@ namespace WixToolset.Tools | |||
| 22 | public sealed class Light | 22 | public sealed class Light |
| 23 | { | 23 | { |
| 24 | LightCommandLine commandLine; | 24 | LightCommandLine commandLine; |
| 25 | private IEnumerable<IExtensionData> extensionData; | ||
| 26 | //private IEnumerable<IBinderExtension> binderExtensions; | 25 | //private IEnumerable<IBinderExtension> binderExtensions; |
| 27 | //private IEnumerable<IBinderFileManager> fileManagers; | 26 | //private IEnumerable<IBinderFileManager> fileManagers; |
| 28 | 27 | ||
| @@ -101,45 +100,20 @@ namespace WixToolset.Tools | |||
| 101 | /// <param name="args">Command line arguments to be parsed.</param> | 100 | /// <param name="args">Command line arguments to be parsed.</param> |
| 102 | private IEnumerable<string> ParseCommandLineAndLoadExtensions(IServiceProvider serviceProvider, IMessaging messaging, string[] args) | 101 | private IEnumerable<string> ParseCommandLineAndLoadExtensions(IServiceProvider serviceProvider, IMessaging messaging, string[] args) |
| 103 | { | 102 | { |
| 104 | this.commandLine = new LightCommandLine(messaging); | 103 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); |
| 104 | arguments.Populate(args); | ||
| 105 | 105 | ||
| 106 | string[] unprocessed = this.commandLine.Parse(args); | 106 | var extensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions); |
| 107 | if (messaging.EncounteredError) | ||
| 108 | { | ||
| 109 | return unprocessed; | ||
| 110 | } | ||
| 111 | 107 | ||
| 112 | // Load extensions. | ||
| 113 | var extensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider); | ||
| 114 | foreach (string extension in this.commandLine.Extensions) | ||
| 115 | { | ||
| 116 | extensionManager.Load(extension); | ||
| 117 | } | ||
| 118 | |||
| 119 | // Extension data command line processing. | ||
| 120 | var context = serviceProvider.GetService<ICommandLineContext>(); | 108 | var context = serviceProvider.GetService<ICommandLineContext>(); |
| 121 | context.Arguments = null; | ||
| 122 | context.ExtensionManager = extensionManager; | 109 | context.ExtensionManager = extensionManager; |
| 123 | context.Messaging = messaging; | 110 | context.Messaging = messaging; |
| 124 | context.ParsedArguments = args; | 111 | context.Arguments = arguments; |
| 125 | |||
| 126 | var commandLineExtensions = extensionManager.Create<IExtensionCommandLine>(); | ||
| 127 | foreach (var extension in commandLineExtensions) | ||
| 128 | { | ||
| 129 | extension.PreParse(context); | ||
| 130 | } | ||
| 131 | 112 | ||
| 132 | // Process unproccessed arguments. | 113 | this.commandLine = new LightCommandLine(messaging); |
| 133 | List<string> actuallyUnprocessed = new List<string>(); | 114 | var unprocessed = this.commandLine.Parse(context); |
| 134 | foreach (var arg in unprocessed) | ||
| 135 | { | ||
| 136 | if (!this.TryParseCommandLineArgumentWithExtension(arg, commandLineExtensions)) | ||
| 137 | { | ||
| 138 | actuallyUnprocessed.Add(arg); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | 115 | ||
| 142 | return this.commandLine.ParsePostExtensions(actuallyUnprocessed.ToArray()); | 116 | return unprocessed; |
| 143 | } | 117 | } |
| 144 | 118 | ||
| 145 | private void Bind(IServiceProvider serviceProvider, IMessaging messaging) | 119 | private void Bind(IServiceProvider serviceProvider, IMessaging messaging) |
| @@ -194,7 +168,7 @@ namespace WixToolset.Tools | |||
| 194 | binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation; | 168 | binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation; |
| 195 | binder.OutputPath = this.commandLine.OutputFile; | 169 | binder.OutputPath = this.commandLine.OutputFile; |
| 196 | binder.OutputPdbPath = Path.ChangeExtension(this.commandLine.OutputFile, ".wixpdb"); | 170 | binder.OutputPdbPath = Path.ChangeExtension(this.commandLine.OutputFile, ".wixpdb"); |
| 197 | binder.SuppressIces = this.commandLine.SuppressIces; | 171 | binder.SuppressIces = this.commandLine.SuppressIces; |
| 198 | binder.SuppressValidation = this.commandLine.SuppressValidation; | 172 | binder.SuppressValidation = this.commandLine.SuppressValidation; |
| 199 | 173 | ||
| 200 | bindResult = binder.Execute(); | 174 | bindResult = binder.Execute(); |
| @@ -526,7 +500,7 @@ namespace WixToolset.Tools | |||
| 526 | return Intermediate.Load(path); | 500 | return Intermediate.Load(path); |
| 527 | } | 501 | } |
| 528 | 502 | ||
| 529 | private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider) | 503 | private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, IEnumerable<string> extensions) |
| 530 | { | 504 | { |
| 531 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); | 505 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); |
| 532 | 506 | ||
| @@ -535,6 +509,11 @@ namespace WixToolset.Tools | |||
| 535 | extensionManager.Add(type.Assembly); | 509 | extensionManager.Add(type.Assembly); |
| 536 | } | 510 | } |
| 537 | 511 | ||
| 512 | foreach (var extension in extensions) | ||
| 513 | { | ||
| 514 | extensionManager.Load(extension); | ||
| 515 | } | ||
| 516 | |||
| 538 | return extensionManager; | 517 | return extensionManager; |
| 539 | } | 518 | } |
| 540 | 519 | ||
diff --git a/src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs b/src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs index 53394ea3..0d0771f3 100644 --- a/src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs +++ b/src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs | |||
| @@ -26,7 +26,7 @@ namespace Example.Extension | |||
| 26 | { | 26 | { |
| 27 | if (parseCommandLine.IsSwitch(arg) && arg.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase)) | 27 | if (parseCommandLine.IsSwitch(arg) && arg.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase)) |
| 28 | { | 28 | { |
| 29 | parseCommandLine.GetNextArgumentOrError(ref this.exampleValueFromCommandLine); | 29 | this.exampleValueFromCommandLine = parseCommandLine.GetNextArgumentOrError(arg); |
| 30 | return true; | 30 | return true; |
| 31 | } | 31 | } |
| 32 | 32 | ||
diff --git a/src/wix/Program.cs b/src/wix/Program.cs index 4cfc0138..aefc6be8 100644 --- a/src/wix/Program.cs +++ b/src/wix/Program.cs | |||
| @@ -42,17 +42,20 @@ namespace WixToolset.Core | |||
| 42 | var messaging = serviceProvider.GetService<IMessaging>(); | 42 | var messaging = serviceProvider.GetService<IMessaging>(); |
| 43 | messaging.SetListener(listener); | 43 | messaging.SetListener(listener); |
| 44 | 44 | ||
| 45 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); | ||
| 46 | arguments.Populate(args); | ||
| 47 | |||
| 45 | var context = serviceProvider.GetService<ICommandLineContext>(); | 48 | var context = serviceProvider.GetService<ICommandLineContext>(); |
| 46 | context.Messaging = messaging; | 49 | context.Messaging = messaging; |
| 47 | context.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider); | 50 | context.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions); |
| 48 | context.ParsedArguments = args; | 51 | context.Arguments = arguments; |
| 49 | 52 | ||
| 50 | var commandLine = serviceProvider.GetService<ICommandLine>(); | 53 | var commandLine = serviceProvider.GetService<ICommandLine>(); |
| 51 | var command = commandLine.ParseStandardCommandLine(context); | 54 | var command = commandLine.ParseStandardCommandLine(context); |
| 52 | return command?.Execute() ?? 1; | 55 | return command?.Execute() ?? 1; |
| 53 | } | 56 | } |
| 54 | 57 | ||
| 55 | private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider) | 58 | private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, string[] extensions) |
| 56 | { | 59 | { |
| 57 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); | 60 | var extensionManager = serviceProvider.GetService<IExtensionManager>(); |
| 58 | 61 | ||
| @@ -61,6 +64,11 @@ namespace WixToolset.Core | |||
| 61 | extensionManager.Add(type.Assembly); | 64 | extensionManager.Add(type.Assembly); |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 67 | foreach (var extension in extensions) | ||
| 68 | { | ||
| 69 | extensionManager.Load(extension); | ||
| 70 | } | ||
| 71 | |||
| 64 | return extensionManager; | 72 | return extensionManager; |
| 65 | } | 73 | } |
| 66 | 74 | ||
