Refactor command line parsing to enable extensions there in light.exe
Fixes wixtoolset/issues#5845
Rob Mensching committed
Jul 12, 2018 at 22:27 UTC
fc92b28f87599ac25d35399dc2df2f356a285960
13 files changed
+754
-822
WixToolset.Core.sln
+14
@@ -16,6 +16,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.BuildTasks",
16
EndProject
17
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.CoreIntegration", "src\test\WixToolsetTest.CoreIntegration\WixToolsetTest.CoreIntegration.csproj", "{E8A08E86-1780-4ED4-8F63-AB2B52C1C16B}"
18
EndProject
19
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "light", "src\light\light.csproj", "{821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}"
20
+EndProject
21
Global
22
GlobalSection(SolutionConfigurationPlatforms) = preSolution
23
Debug|Any CPU = Debug|Any CPU
@@ -110,6 +112,18 @@ Global
112
{E8A08E86-1780-4ED4-8F63-AB2B52C1C16B}.Release|x64.Build.0 = Release|Any CPU
113
{E8A08E86-1780-4ED4-8F63-AB2B52C1C16B}.Release|x86.ActiveCfg = Release|Any CPU
114
{E8A08E86-1780-4ED4-8F63-AB2B52C1C16B}.Release|x86.Build.0 = Release|Any CPU
115
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
116
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|Any CPU.Build.0 = Debug|Any CPU
117
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|x64.ActiveCfg = Debug|Any CPU
118
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|x64.Build.0 = Debug|Any CPU
119
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|x86.ActiveCfg = Debug|Any CPU
120
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Debug|x86.Build.0 = Debug|Any CPU
121
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|Any CPU.ActiveCfg = Release|Any CPU
122
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|Any CPU.Build.0 = Release|Any CPU
123
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|x64.ActiveCfg = Release|Any CPU
124
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|x64.Build.0 = Release|Any CPU
125
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|x86.ActiveCfg = Release|Any CPU
126
+ {821DCF25-4E4F-48B1-9DD6-80B01EBF9B16}.Release|x86.Build.0 = Release|Any CPU
127
EndGlobalSection
128
GlobalSection(SolutionProperties) = preSolution
129
HideSolutionNode = FALSE
src/WixToolset.BuildTasks/DoIt.cs
+12
-5
@@ -171,21 +171,23 @@ namespace WixToolset.BuildTasks
171
172
var serviceProvider = new WixToolsetServiceProvider();
173
174
- var context = serviceProvider.GetService<ICommandLineContext>();
175
-
174
var messaging = serviceProvider.GetService<IMessaging>();
175
messaging.SetListener(this.Listener);
176
177
+ var arguments = serviceProvider.GetService<ICommandLineArguments>();
178
+ arguments.Populate(commandLineString);
179
+
180
+ var context = serviceProvider.GetService<ICommandLineContext>();
181
context.Messaging = messaging;
180
- context.ExtensionManager = this.CreateExtensionManagerWithStandardBackends(serviceProvider);
181
- context.Arguments = commandLineString;
182
+ context.ExtensionManager = this.CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions);
183
+ context.Arguments = arguments;
184
185
var commandLine = serviceProvider.GetService<ICommandLine>();
186
var command = commandLine.ParseStandardCommandLine(context);
187
command?.Execute();
188
}
189
188
- private IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider)
190
+ private IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, string[] extensions)
191
{
192
var extensionManager = serviceProvider.GetService<IExtensionManager>();
193
@@ -194,6 +196,11 @@ namespace WixToolset.BuildTasks
196
extensionManager.Add(type.Assembly);
197
}
198
199
+ foreach (var extension in extensions)
200
+ {
201
+ extensionManager.Load(extension);
202
+ }
203
+
204
return extensionManager;
205
}
206
src/WixToolset.Core/CommandLine/CommandLineArguments.cs
new
+211
@@ -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
+}
src/WixToolset.Core/CommandLine/CommandLineContext.cs
+1
-3
@@ -18,8 +18,6 @@ namespace WixToolset.Core.CommandLine
18
19
public IExtensionManager ExtensionManager { get; set; }
20
21
- public string Arguments { get; set; }
22
-
23
- public string[] ParsedArguments { get; set; }
21
+ public ICommandLineArguments Arguments { get; set; }
22
}
23
}
src/WixToolset.Core/CommandLine/CommandLineHelper.cs
deleted
-216
@@ -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
-}
src/WixToolset.Core/CommandLine/CommandLineParser.cs
+165
-428
@@ -5,9 +5,6 @@ namespace WixToolset.Core.CommandLine
5
using System;
6
using System.Collections.Generic;
7
using System.IO;
8
- using System.Linq;
9
- using System.Text;
10
- using System.Text.RegularExpressions;
8
using WixToolset.Data;
9
using WixToolset.Extensibility;
10
using WixToolset.Extensibility.Services;
@@ -22,7 +19,7 @@ namespace WixToolset.Core.CommandLine
19
Bind,
20
}
21
25
- internal class CommandLineParser : ICommandLine, IParseCommandLine
22
+ internal class CommandLineParser : ICommandLine
23
{
24
private IServiceProvider ServiceProvider { get; set; }
25
@@ -32,14 +29,8 @@ namespace WixToolset.Core.CommandLine
29
30
public string ActiveCommand { get; private set; }
31
35
- public string[] OriginalArguments { get; private set; }
36
-
37
- public Queue<string> RemainingArguments { get; } = new Queue<string>();
38
-
32
public IExtensionManager ExtensionManager { get; private set; }
33
41
- public string ErrorArgument { get; set; }
42
-
34
public bool ShowHelp { get; set; }
35
36
public ICommandLineCommand ParseStandardCommandLine(ICommandLineContext context)
@@ -50,18 +41,6 @@ namespace WixToolset.Core.CommandLine
41
42
this.ExtensionManager = context.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>();
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
- {
44
var next = String.Empty;
45
46
var command = Commands.Unknown;
@@ -89,99 +68,99 @@ namespace WixToolset.Core.CommandLine
68
var outputsFile = String.Empty;
69
var builtOutputsFile = String.Empty;
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) =>
72
{
94
- if (cmdline.IsSwitch(arg))
73
+ if (parser.IsSwitch(arg))
74
{
75
var parameter = arg.Substring(1);
76
switch (parameter.ToLowerInvariant())
77
{
99
- case "?":
100
- case "h":
101
- case "help":
102
- cmdline.ShowHelp = true;
103
- return true;
104
-
105
- case "bindfiles":
106
- bindFiles = true;
107
- return true;
108
-
109
- case "bindpath":
110
- cmdline.GetNextArgumentOrError(bindPaths);
111
- return true;
112
-
113
- case "cc":
114
- cmdline.GetNextArgumentOrError(ref cabCachePath);
115
- return true;
116
-
117
- case "culture":
118
- cmdline.GetNextArgumentOrError(cultures);
119
- return true;
120
- case "contentsfile":
121
- cmdline.GetNextArgumentOrError(ref contentsFile);
122
- return true;
123
- case "outputsfile":
124
- cmdline.GetNextArgumentOrError(ref outputsFile);
125
- return true;
126
- case "builtoutputsfile":
127
- cmdline.GetNextArgumentOrError(ref builtOutputsFile);
128
- return true;
129
-
130
- case "d":
131
- case "define":
132
- cmdline.GetNextArgumentOrError(defines);
133
- return true;
134
-
135
- case "i":
136
- case "includepath":
137
- cmdline.GetNextArgumentOrError(includePaths);
138
- return true;
139
-
140
- case "intermediatefolder":
141
- cmdline.GetNextArgumentOrError(ref intermediateFolder);
142
- return true;
143
-
144
- case "loc":
145
- cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files");
146
- return true;
147
-
148
- case "lib":
149
- cmdline.GetNextArgumentAsFilePathOrError(libraryFiles, "library files");
150
- return true;
151
-
152
- case "o":
153
- case "out":
154
- cmdline.GetNextArgumentOrError(ref outputFile);
155
- return true;
156
-
157
- case "outputtype":
158
- cmdline.GetNextArgumentOrError(ref outputType);
159
- return true;
160
-
161
- case "nologo":
162
- showLogo = false;
163
- return true;
164
-
165
- case "v":
166
- case "verbose":
167
- verbose = true;
168
- return true;
169
-
170
- case "version":
171
- case "-version":
172
- showVersion = true;
173
- return true;
174
-
175
- case "sval":
176
- // todo: implement
177
- return true;
78
+ case "?":
79
+ case "h":
80
+ case "help":
81
+ cmdline.ShowHelp = true;
82
+ return true;
83
+
84
+ case "bindfiles":
85
+ bindFiles = true;
86
+ return true;
87
+
88
+ case "bindpath":
89
+ parser.GetNextArgumentOrError(arg, bindPaths);
90
+ return true;
91
+
92
+ case "cc":
93
+ cabCachePath = parser.GetNextArgumentOrError(arg);
94
+ return true;
95
+
96
+ case "culture":
97
+ parser.GetNextArgumentOrError(arg, cultures);
98
+ return true;
99
+ case "contentsfile":
100
+ contentsFile = parser.GetNextArgumentAsFilePathOrError(arg);
101
+ return true;
102
+ case "outputsfile":
103
+ outputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
104
+ return true;
105
+ case "builtoutputsfile":
106
+ builtOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
107
+ return true;
108
+
109
+ case "d":
110
+ case "define":
111
+ parser.GetNextArgumentOrError(arg, defines);
112
+ return true;
113
+
114
+ case "i":
115
+ case "includepath":
116
+ parser.GetNextArgumentOrError(arg, includePaths);
117
+ return true;
118
+
119
+ case "intermediatefolder":
120
+ intermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
121
+ return true;
122
+
123
+ case "loc":
124
+ parser.GetNextArgumentAsFilePathOrError(arg, "localization files", locFiles);
125
+ return true;
126
+
127
+ case "lib":
128
+ parser.GetNextArgumentAsFilePathOrError(arg, "library files", libraryFiles);
129
+ return true;
130
+
131
+ case "o":
132
+ case "out":
133
+ outputFile = parser.GetNextArgumentAsFilePathOrError(arg);
134
+ return true;
135
+
136
+ case "outputtype":
137
+ outputType= parser.GetNextArgumentOrError(arg);
138
+ return true;
139
+
140
+ case "nologo":
141
+ showLogo = false;
142
+ return true;
143
+
144
+ case "v":
145
+ case "verbose":
146
+ verbose = true;
147
+ return true;
148
+
149
+ case "version":
150
+ case "-version":
151
+ showVersion = true;
152
+ return true;
153
+
154
+ case "sval":
155
+ // todo: implement
156
+ return true;
157
}
158
159
return false;
160
}
161
else
162
{
184
- files.AddRange(CommandLineHelper.GetFiles(arg, "source code"));
163
+ parser.GetArgumentAsFilePathOrError(arg, "source code", files);
164
return true;
165
}
166
});
@@ -205,22 +184,22 @@ namespace WixToolset.Core.CommandLine
184
185
switch (command)
186
{
208
- case Commands.Build:
209
- {
210
- var sourceFiles = GatherSourceFiles(files, outputFolder);
211
- var variables = this.GatherPreprocessorVariables(defines);
212
- var bindPathList = this.GatherBindPaths(bindPaths);
213
- var filterCultures = CalculateFilterCultures(cultures);
214
- 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);
216
- }
187
+ case Commands.Build:
188
+ {
189
+ var sourceFiles = GatherSourceFiles(files, outputFolder);
190
+ var variables = this.GatherPreprocessorVariables(defines);
191
+ var bindPathList = this.GatherBindPaths(bindPaths);
192
+ var filterCultures = CalculateFilterCultures(cultures);
193
+ var type = CalculateOutputType(outputType, outputFile);
194
+ return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
195
+ }
196
218
- case Commands.Compile:
219
- {
220
- var sourceFiles = GatherSourceFiles(files, outputFolder);
221
- var variables = GatherPreprocessorVariables(defines);
222
- return new CompileCommand(this.ServiceProvider, sourceFiles, variables);
223
- }
197
+ case Commands.Compile:
198
+ {
199
+ var sourceFiles = GatherSourceFiles(files, outputFolder);
200
+ var variables = GatherPreprocessorVariables(defines);
201
+ return new CompileCommand(this.ServiceProvider, sourceFiles, variables);
202
+ }
203
}
204
205
return null;
@@ -262,63 +241,87 @@ namespace WixToolset.Core.CommandLine
241
242
switch (outputType.ToLowerInvariant())
243
{
265
- case "bundle":
266
- case ".exe":
267
- return OutputType.Bundle;
244
+ case "bundle":
245
+ case ".exe":
246
+ return OutputType.Bundle;
247
269
- case "library":
270
- case ".wixlib":
271
- return OutputType.Library;
248
+ case "library":
249
+ case ".wixlib":
250
+ return OutputType.Library;
251
273
- case "module":
274
- case ".msm":
275
- return OutputType.Module;
252
+ case "module":
253
+ case ".msm":
254
+ return OutputType.Module;
255
277
- case "patch":
278
- case ".msp":
279
- return OutputType.Patch;
256
+ case "patch":
257
+ case ".msp":
258
+ return OutputType.Patch;
259
281
- case ".pcp":
282
- return OutputType.PatchCreation;
260
+ case ".pcp":
261
+ return OutputType.PatchCreation;
262
284
- case "product":
285
- case "package":
286
- case ".msi":
287
- return OutputType.Product;
263
+ case "product":
264
+ case "package":
265
+ case ".msi":
266
+ return OutputType.Product;
267
289
- case "transform":
290
- case ".mst":
291
- return OutputType.Transform;
268
+ case "transform":
269
+ case ".mst":
270
+ return OutputType.Transform;
271
293
- case "intermediatepostlink":
294
- case ".wixipl":
295
- return OutputType.IntermediatePostLink;
272
+ case "intermediatepostlink":
273
+ case ".wixipl":
274
+ return OutputType.IntermediatePostLink;
275
}
276
277
return OutputType.Unknown;
278
}
279
301
-#if UNUSED
302
- private static CommandLine Parse(string commandLineString, Func<CommandLine, string, bool> parseArgument)
280
+ private ICommandLine Parse(ICommandLineContext context, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, IParseCommandLine, string, bool> parseArgument)
281
{
304
- var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray();
305
-
306
- return CommandLine.Parse(arguments, null, parseArgument);
307
- }
282
+ var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
283
309
- private static CommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseArgument)
310
- {
311
- return CommandLine.Parse(commandLineArguments, null, parseArgument);
312
- }
313
-#endif
284
+ foreach (var extension in extensions)
285
+ {
286
+ extension.PreParse(context);
287
+ }
288
315
- private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, string, bool> parseArgument)
316
- {
317
- this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments);
289
+ var parser = context.Arguments.Parse();
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
+ }
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
+ }
325
326
return this;
327
}
@@ -358,7 +361,7 @@ namespace WixToolset.Core.CommandLine
361
return variables;
362
}
363
361
- private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths)
364
+ private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths)
365
{
366
var result = new List<BindPath>();
367
@@ -379,172 +382,11 @@ namespace WixToolset.Core.CommandLine
382
return result;
383
}
384
382
- /// <summary>
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)
385
+ private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parse, IEnumerable<IExtensionCommandLine> extensions)
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
-
387
foreach (var extension in extensions)
388
{
504
- extension.PreParse(context);
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))
389
+ if (extension.TryParseArgument(parse, arg))
390
{
391
return true;
392
}
@@ -552,110 +394,5 @@ namespace WixToolset.Core.CommandLine
394
395
return false;
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
- }
397
}
398
}
src/WixToolset.Core/CommandLine/ParseCommandLine.cs
new
+257
@@ -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
+}
src/WixToolset.Core/Preprocessor.cs
+1
-1
@@ -644,7 +644,7 @@ namespace WixToolset.Core
644
645
if (null == includeFile)
646
{
647
- throw new WixException(ErrorMessages.FileNotFound(sourceLineNumbers, includePath, "include"));
647
+ throw new WixFileNotFoundException(sourceLineNumbers, includePath, "include");
648
}
649
650
using (XmlReader reader = XmlReader.Create(includeFile, DocumentXmlReaderSettings))
src/WixToolset.Core/WixToolsetServiceProvider.cs
+1
@@ -25,6 +25,7 @@ namespace WixToolset.Core
25
{ typeof(IWindowsInstallerBackendHelper), (provider, singletons) => AddSingleton(singletons, typeof(IWindowsInstallerBackendHelper), new WindowsInstallerBackendHelper(provider)) },
26
27
// Transients.
28
+ { typeof(ICommandLineArguments), (provider, singletons) => new CommandLineArguments(provider) },
29
{ typeof(ICommandLineContext), (provider, singletons) => new CommandLineContext(provider) },
30
{ typeof(ICommandLine), (provider, singletons) => new CommandLineParser() },
31
{ typeof(IPreprocessContext), (provider, singletons) => new PreprocessContext(provider) },
src/light/LightCommandLine.cs
+66
-130
@@ -6,8 +6,8 @@ namespace WixToolset.Tools
6
using System.Collections.Generic;
7
using System.Globalization;
8
using System.IO;
9
- using WixToolset.Core.CommandLine;
9
using WixToolset.Data;
10
+ using WixToolset.Extensibility;
11
using WixToolset.Extensibility.Services;
12
13
public class LightCommandLine
@@ -22,7 +22,6 @@ namespace WixToolset.Tools
22
this.SuppressIces = new List<string>();
23
this.Ices = new List<string>();
24
this.BindPaths = new List<BindPath>();
25
- this.Extensions = new List<string>();
25
this.Files = new List<string>();
26
this.LocalizationFiles = new List<string>();
27
this.Variables = new Dictionary<string, string>();
@@ -80,8 +79,6 @@ namespace WixToolset.Tools
79
80
public List<BindPath> BindPaths { get; private set; }
81
83
- public List<string> Extensions { get; private set; }
84
-
82
public List<string> Files { get; private set; }
83
84
public List<string> LocalizationFiles { get; private set; }
@@ -96,35 +93,40 @@ namespace WixToolset.Tools
93
/// Parse the commandline arguments.
94
/// </summary>
95
/// <param name="args">Commandline arguments.</param>
99
- public string[] Parse(string[] args)
96
+ public string[] Parse(ICommandLineContext context)
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();
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))
112
{
105
- string arg = args[i];
106
- if (String.IsNullOrEmpty(arg)) // skip blank arguments
113
+ if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments.
114
{
115
continue;
116
}
117
111
- if (1 == arg.Length) // treat '-' and '@' as filenames when by themselves.
118
+ if (parser.IsSwitch(arg))
119
{
113
- unprocessed.Add(arg);
114
- }
115
- else if ('-' == arg[0] || '/' == arg[0])
116
- {
117
- string parameter = arg.Substring(1);
120
+ var parameter = arg.Substring(1);
121
if (parameter.Equals("b", StringComparison.Ordinal))
122
{
120
- if (!CommandLineHelper.IsValidArg(args, ++i))
123
+ var result = parser.GetNextArgumentOrError(arg);
124
+ if (!String.IsNullOrEmpty(result))
125
{
122
- break;
123
- }
126
+ var bindPath = BindPath.Parse(result);
127
125
- var bindPath = BindPath.Parse(args[i]);
126
-
127
- this.BindPaths.Add(bindPath);
128
+ this.BindPaths.Add(bindPath);
129
+ }
130
}
131
else if (parameter.StartsWith("cultures:", StringComparison.Ordinal))
132
{
@@ -184,25 +186,9 @@ namespace WixToolset.Tools
186
this.Variables.Add(value[0], value[1]);
187
}
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
- }
189
else if (parameter.Equals("loc", StringComparison.Ordinal))
190
{
199
- string locFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
200
- if (String.IsNullOrEmpty(locFile))
201
- {
202
- break;
203
- }
204
-
205
- this.LocalizationFiles.Add(locFile);
191
+ parser.GetNextArgumentAsFilePathOrError(arg, "localization files", this.LocalizationFiles);
192
}
193
else if (parameter.Equals("nologo", StringComparison.Ordinal))
194
{
@@ -214,11 +200,7 @@ namespace WixToolset.Tools
200
}
201
else if ("o" == parameter || "out" == parameter)
202
{
217
- this.OutputFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
218
- if (String.IsNullOrEmpty(this.OutputFile))
219
- {
220
- break;
221
- }
203
+ this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg);
204
}
205
else if (parameter.Equals("pedantic", StringComparison.Ordinal))
206
{
@@ -230,12 +212,7 @@ namespace WixToolset.Tools
212
}
213
else if (parameter.Equals("usf", StringComparison.Ordinal))
214
{
233
- this.UnreferencedSymbolsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
234
-
235
- if (String.IsNullOrEmpty(this.UnreferencedSymbolsFile))
236
- {
237
- break;
238
- }
215
+ this.UnreferencedSymbolsFile = parser.GetNextArgumentAsDirectoryOrError(arg);
216
}
217
else if (parameter.Equals("xo", StringComparison.Ordinal))
218
{
@@ -243,41 +220,27 @@ namespace WixToolset.Tools
220
}
221
else if (parameter.Equals("cc", StringComparison.Ordinal))
222
{
246
- this.CabCachePath = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i);
247
-
248
- if (String.IsNullOrEmpty(this.CabCachePath))
249
- {
250
- break;
251
- }
223
+ this.CabCachePath = parser.GetNextArgumentAsDirectoryOrError(arg);
224
}
225
else if (parameter.Equals("ct", StringComparison.Ordinal))
226
{
255
- if (!CommandLineHelper.IsValidArg(args, ++i))
227
+ var result = parser.GetNextArgumentOrError(arg);
228
+ if (!String.IsNullOrEmpty(result))
229
{
257
- this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(String.Empty));
258
- break;
259
- }
230
+ if (!Int32.TryParse(result, out var ct) || 0 >= ct)
231
+ {
232
+ this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(result));
233
+ parser.ErrorArgument = arg;
234
+ break;
235
+ }
236
261
- int ct = 0;
262
- if (!Int32.TryParse(args[i], out ct) || 0 >= ct)
263
- {
264
- this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(args[i]));
265
- break;
237
+ this.CabbingThreadCount = ct;
238
+ this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString()));
239
}
267
-
268
- this.CabbingThreadCount = ct;
269
- this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString()));
240
}
241
else if (parameter.Equals("cub", StringComparison.Ordinal))
242
{
273
- string cubeFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
274
-
275
- if (String.IsNullOrEmpty(cubeFile))
276
- {
277
- break;
278
- }
279
-
280
- this.CubeFiles.Add(cubeFile);
243
+ parser.GetNextArgumentAsFilePathOrError(arg, "static validation files", this.CubeFiles);
244
}
245
else if (parameter.StartsWith("ice:", StringComparison.Ordinal))
246
{
@@ -285,57 +248,27 @@ namespace WixToolset.Tools
248
}
249
else if (parameter.Equals("intermediatefolder", StringComparison.OrdinalIgnoreCase))
250
{
288
- this.IntermediateFolder = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i);
289
-
290
- if (String.IsNullOrEmpty(this.IntermediateFolder))
291
- {
292
- break;
293
- }
251
+ this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
252
}
253
else if (parameter.Equals("contentsfile", StringComparison.Ordinal))
254
{
297
- this.ContentsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
298
-
299
- if (String.IsNullOrEmpty(this.ContentsFile))
300
- {
301
- break;
302
- }
255
+ this.ContentsFile = parser.GetNextArgumentAsFilePathOrError(arg);
256
}
257
else if (parameter.Equals("outputsfile", StringComparison.Ordinal))
258
{
306
- this.OutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
307
-
308
- if (String.IsNullOrEmpty(this.OutputsFile))
309
- {
310
- break;
311
- }
259
+ this.OutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
260
}
261
else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal))
262
{
315
- this.BuiltOutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
316
-
317
- if (String.IsNullOrEmpty(this.BuiltOutputsFile))
318
- {
319
- break;
320
- }
263
+ this.BuiltOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
264
}
265
else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal))
266
{
324
- this.WixprojectFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
325
-
326
- if (String.IsNullOrEmpty(this.WixprojectFile))
327
- {
328
- break;
329
- }
267
+ this.WixprojectFile = parser.GetNextArgumentAsFilePathOrError(arg);
268
}
269
else if (parameter.Equals("pdbout", StringComparison.Ordinal))
270
{
333
- this.PdbFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
334
-
335
- if (String.IsNullOrEmpty(this.PdbFile))
336
- {
337
- break;
338
- }
271
+ this.PdbFile = parser.GetNextArgumentAsFilePathOrError(arg);
272
}
273
else if (parameter.StartsWith("sice:", StringComparison.Ordinal))
274
{
@@ -410,45 +343,35 @@ namespace WixToolset.Tools
343
this.ShowHelp = true;
344
break;
345
}
413
- else
346
+ else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
347
{
348
unprocessed.Add(arg);
349
}
350
}
418
- else if ('@' == arg[0])
419
- {
420
- string[] parsedArgs = CommandLineResponseFile.Parse(arg.Substring(1));
421
- string[] unparsedArgs = this.Parse(parsedArgs);
422
- unprocessed.AddRange(unparsedArgs);
423
- }
424
- else
351
+ else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
352
{
353
unprocessed.Add(arg);
354
}
355
}
356
430
- return unprocessed.ToArray();
357
+ return this.ParsePostExtensions(parser, unprocessed.ToArray());
358
}
359
433
- public string[] ParsePostExtensions(string[] remaining)
360
+ private string[] ParsePostExtensions(IParseCommandLine parser, string[] remaining)
361
{
435
- List<string> unprocessed = new List<string>();
362
+ var unprocessed = new List<string>();
363
364
for (int i = 0; i < remaining.Length; ++i)
365
{
439
- string arg = remaining[i];
440
- if (String.IsNullOrEmpty(arg)) // skip blank arguments
441
- {
442
- continue;
443
- }
366
+ var arg = remaining[i];
367
445
- if (1 < arg.Length && ('-' == arg[0] || '/' == arg[0]))
368
+ if (parser.IsSwitch(arg))
369
{
370
unprocessed.Add(arg);
371
}
372
else
373
{
451
- this.Files.AddRange(CommandLineHelper.GetFiles(arg, "Source"));
374
+ parser.GetArgumentAsFilePathOrError(arg, "source files", this.Files);
375
}
376
}
377
@@ -469,7 +392,7 @@ namespace WixToolset.Tools
392
// Add the directories of the input files as unnamed bind paths.
393
foreach (string file in this.Files)
394
{
472
- BindPath bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file)));
395
+ var bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file)));
396
this.BindPaths.Add(bindPath);
397
}
398
}
@@ -481,5 +404,18 @@ namespace WixToolset.Tools
404
405
return unprocessed.ToArray();
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
+ }
420
}
421
}
src/light/light.cs
+14
-35
@@ -22,7 +22,6 @@ namespace WixToolset.Tools
22
public sealed class Light
23
{
24
LightCommandLine commandLine;
25
- private IEnumerable<IExtensionData> extensionData;
25
//private IEnumerable<IBinderExtension> binderExtensions;
26
//private IEnumerable<IBinderFileManager> fileManagers;
27
@@ -101,45 +100,20 @@ namespace WixToolset.Tools
100
/// <param name="args">Command line arguments to be parsed.</param>
101
private IEnumerable<string> ParseCommandLineAndLoadExtensions(IServiceProvider serviceProvider, IMessaging messaging, string[] args)
102
{
104
- this.commandLine = new LightCommandLine(messaging);
103
+ var arguments = serviceProvider.GetService<ICommandLineArguments>();
104
+ arguments.Populate(args);
105
106
- string[] unprocessed = this.commandLine.Parse(args);
107
- if (messaging.EncounteredError)
108
- {
109
- return unprocessed;
110
- }
106
+ var extensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions);
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.
108
var context = serviceProvider.GetService<ICommandLineContext>();
121
- context.Arguments = null;
109
context.ExtensionManager = extensionManager;
110
context.Messaging = messaging;
124
- context.ParsedArguments = args;
125
-
126
- var commandLineExtensions = extensionManager.Create<IExtensionCommandLine>();
127
- foreach (var extension in commandLineExtensions)
128
- {
129
- extension.PreParse(context);
130
- }
111
+ context.Arguments = arguments;
112
132
- // Process unproccessed arguments.
133
- List<string> actuallyUnprocessed = new List<string>();
134
- foreach (var arg in unprocessed)
135
- {
136
- if (!this.TryParseCommandLineArgumentWithExtension(arg, commandLineExtensions))
137
- {
138
- actuallyUnprocessed.Add(arg);
139
- }
140
- }
113
+ this.commandLine = new LightCommandLine(messaging);
114
+ var unprocessed = this.commandLine.Parse(context);
115
142
- return this.commandLine.ParsePostExtensions(actuallyUnprocessed.ToArray());
116
+ return unprocessed;
117
}
118
119
private void Bind(IServiceProvider serviceProvider, IMessaging messaging)
@@ -194,7 +168,7 @@ namespace WixToolset.Tools
168
binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
169
binder.OutputPath = this.commandLine.OutputFile;
170
binder.OutputPdbPath = Path.ChangeExtension(this.commandLine.OutputFile, ".wixpdb");
197
- binder.SuppressIces = this.commandLine.SuppressIces;
171
+ binder.SuppressIces = this.commandLine.SuppressIces;
172
binder.SuppressValidation = this.commandLine.SuppressValidation;
173
174
bindResult = binder.Execute();
@@ -526,7 +500,7 @@ namespace WixToolset.Tools
500
return Intermediate.Load(path);
501
}
502
529
- private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider)
503
+ private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, IEnumerable<string> extensions)
504
{
505
var extensionManager = serviceProvider.GetService<IExtensionManager>();
506
@@ -535,6 +509,11 @@ namespace WixToolset.Tools
509
extensionManager.Add(type.Assembly);
510
}
511
512
+ foreach (var extension in extensions)
513
+ {
514
+ extensionManager.Load(extension);
515
+ }
516
+
517
return extensionManager;
518
}
519
src/test/TestData/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
+1
-1
@@ -26,7 +26,7 @@ namespace Example.Extension
26
{
27
if (parseCommandLine.IsSwitch(arg) && arg.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase))
28
{
29
- parseCommandLine.GetNextArgumentOrError(ref this.exampleValueFromCommandLine);
29
+ this.exampleValueFromCommandLine = parseCommandLine.GetNextArgumentOrError(arg);
30
return true;
31
}
32
src/wix/Program.cs
+11
-3
@@ -42,17 +42,20 @@ namespace WixToolset.Core
42
var messaging = serviceProvider.GetService<IMessaging>();
43
messaging.SetListener(listener);
44
45
+ var arguments = serviceProvider.GetService<ICommandLineArguments>();
46
+ arguments.Populate(args);
47
+
48
var context = serviceProvider.GetService<ICommandLineContext>();
49
context.Messaging = messaging;
47
- context.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider);
48
- context.ParsedArguments = args;
50
+ context.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions);
51
+ context.Arguments = arguments;
52
53
var commandLine = serviceProvider.GetService<ICommandLine>();
54
var command = commandLine.ParseStandardCommandLine(context);
55
return command?.Execute() ?? 1;
56
}
57
55
- private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider)
58
+ private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider, string[] extensions)
59
{
60
var extensionManager = serviceProvider.GetService<IExtensionManager>();
61
@@ -61,6 +64,11 @@ namespace WixToolset.Core
64
extensionManager.Add(type.Assembly);
65
}
66
67
+ foreach (var extension in extensions)
68
+ {
69
+ extensionManager.Load(extension);
70
+ }
71
+
72
return extensionManager;
73
}
74