main
cs 276 lines 9.58 KB
Raw
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 WixToolset.Data;
8 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services;
11
12 internal class CommandLine : ICommandLine
13 {
14 public CommandLine(IServiceProvider serviceProvider)
15 {
16 this.ServiceProvider = serviceProvider;
17 this.Messaging = serviceProvider.GetService<IMessaging>();
18 }
19
20 private IServiceProvider ServiceProvider { get; }
21
22 private IMessaging Messaging { get; }
23
24 private bool ShowHelp { get; set; }
25
26 private bool SuppressLogo { get; set; }
27
28 public ICommandLineCommand CreateCommand(string[] args)
29 {
30 var arguments = this.ServiceProvider.GetService<ICommandLineArguments>();
31 arguments.Populate(args);
32
33 this.LoadExtensions(arguments.Extensions);
34
35 return this.ParseStandardCommandLine(arguments);
36 }
37
38 public ICommandLineCommand CreateCommand(string commandLine)
39 {
40 var arguments = this.ServiceProvider.GetService<ICommandLineArguments>();
41 arguments.Populate(commandLine);
42
43 this.LoadExtensions(arguments.Extensions);
44
45 return this.ParseStandardCommandLine(arguments);
46 }
47
48 public ICommandLineCommand ParseStandardCommandLine(ICommandLineArguments arguments)
49 {
50 var context = this.ServiceProvider.GetService<ICommandLineContext>();
51 context.ExtensionManager = this.ServiceProvider.GetService<IExtensionManager>();
52 context.Arguments = arguments;
53
54 var command = this.Parse(context);
55
56 if (!this.SuppressLogo && command?.ShowLogo == true)
57 {
58 var branding = this.ServiceProvider.GetService<IWixBranding>();
59 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyProduct] version [ProductVersion]"));
60 Console.WriteLine(branding.ReplacePlaceholders("[AssemblyCopyright]"));
61 Console.WriteLine();
62 }
63
64 return command;
65 }
66
67 private void LoadExtensions(string[] extensions)
68 {
69 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
70
71 foreach (var extension in extensions)
72 {
73 extensionManager.Load(extension);
74 }
75 }
76
77 private ICommandLineCommand Parse(ICommandLineContext context)
78 {
79 var extensions = context.ExtensionManager.GetServices<IExtensionCommandLine>();
80
81 foreach (var extension in extensions)
82 {
83 extension.PreParse(context);
84 }
85
86 ICommandLineCommand command = null;
87 var parser = context.Arguments.Parse();
88
89 while (command?.StopParsing != true &&
90 String.IsNullOrEmpty(parser.ErrorArgument) &&
91 parser.TryGetNextSwitchOrArgument(out var arg))
92 {
93 // If we don't have a command yet, try to parse for a command or a global switch.
94 if (command == null)
95 {
96 if (this.TryParseCommand(arg, parser, extensions, out command))
97 {
98 // Found our command, all good.
99 }
100 else if (!parser.IsSwitch(arg) || !TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
101 {
102 // Not a global switch handled by an extension, so failure.
103 parser.ReportErrorArgument(arg);
104 }
105 }
106 else if (parser.IsSwitch(arg))
107 {
108 // Commands get first crack at parsing switches then extensions then the standard.
109 if (!command.TryParseArgument(parser, arg) &&
110 !TryParseCommandLineArgumentWithExtension(arg, parser, extensions) &&
111 !this.TryParseStandardCommandLineSwitch(parser, arg))
112 {
113 parser.ReportErrorArgument(arg);
114 }
115 }
116 else if (!TryParseCommandLineArgumentWithExtension(arg, parser, extensions) &&
117 !command.TryParseArgument(parser, arg))
118 {
119 parser.ReportErrorArgument(arg);
120 }
121 }
122
123 foreach (var extension in extensions)
124 {
125 extension.PostParse();
126 }
127
128 // If we hit an error, do not return a command.
129 if (!String.IsNullOrEmpty(parser.ErrorArgument))
130 {
131 command = null;
132 }
133 else if (this.ShowHelp || command == null)
134 {
135 var branding = context.ServiceProvider.GetService<IWixBranding>();
136 command = new HelpCommand(extensions, branding, command);
137 }
138
139 return command;
140 }
141
142 private bool TryParseCommand(string arg, ICommandLineParser parser, IEnumerable<IExtensionCommandLine> extensions, out ICommandLineCommand command)
143 {
144 command = null;
145
146 if (parser.IsSwitch(arg))
147 {
148 var parameter = arg.Substring(1);
149 switch (parameter.ToLowerInvariant())
150 {
151 case "?":
152 case "h":
153 case "help":
154 case "-help":
155 var branding = this.ServiceProvider.GetService<IWixBranding>();
156 command = new HelpCommand(extensions, branding, null);
157 break;
158
159 case "version":
160 case "-version":
161 command = new VersionCommand();
162 break;
163 }
164 }
165 else
166 {
167 if ("build".Equals(arg, StringComparison.OrdinalIgnoreCase))
168 {
169 command = new BuildCommand(this.ServiceProvider);
170 }
171 else
172 {
173 foreach (var extension in extensions)
174 {
175 if (extension.TryParseCommand(parser, arg, out command))
176 {
177 break;
178 }
179 }
180 }
181 }
182
183 return command != null;
184 }
185
186 private static bool TryParseCommandLineArgumentWithExtension(string arg, ICommandLineParser parse, IEnumerable<IExtensionCommandLine> extensions)
187 {
188 foreach (var extension in extensions)
189 {
190 if (extension.TryParseArgument(parse, arg))
191 {
192 return true;
193 }
194 }
195
196 return false;
197 }
198
199 private bool TryParseStandardCommandLineSwitch(ICommandLineParser parser, string arg)
200 {
201 var parameter = arg.Substring(1).ToLowerInvariant();
202
203 switch (parameter)
204 {
205 case "?":
206 case "h":
207 case "help":
208 case "-help":
209 this.ShowHelp = true;
210 return true;
211
212 case "nologo":
213 case "-nologo":
214 this.SuppressLogo = true;
215 return true;
216
217 case "v":
218 case "verbose":
219 this.Messaging.ShowVerboseMessages = true;
220 return true;
221 }
222
223 if (parameter.StartsWith("sw"))
224 {
225 this.ParseSuppressWarning(parameter, "sw".Length, parser);
226 return true;
227 }
228 else if (parameter.StartsWith("suppresswarning"))
229 {
230 this.ParseSuppressWarning(parameter, "suppresswarning".Length, parser);
231 return true;
232 }
233 else if (parameter.StartsWith("wx"))
234 {
235 this.ParseWarningAsError(parameter, "wx".Length, parser);
236 return true;
237 }
238
239 return false;
240 }
241
242 private void ParseSuppressWarning(string parameter, int offset, ICommandLineParser parser)
243 {
244 var paramArg = parameter.Substring(offset);
245 if (paramArg.Length == 0)
246 {
247 this.Messaging.SuppressAllWarnings = true;
248 }
249 else if (Int32.TryParse(paramArg, out var suppressWarning) && suppressWarning > 0)
250 {
251 this.Messaging.SuppressWarningMessage(suppressWarning);
252 }
253 else
254 {
255 parser.ReportErrorArgument(parameter, ErrorMessages.IllegalSuppressWarningId(paramArg));
256 }
257 }
258
259 private void ParseWarningAsError(string parameter, int offset, ICommandLineParser parser)
260 {
261 var paramArg = parameter.Substring(offset);
262 if (paramArg.Length == 0)
263 {
264 this.Messaging.WarningsAsError = true;
265 }
266 else if (Int32.TryParse(paramArg, out var elevateWarning) && elevateWarning > 0)
267 {
268 this.Messaging.ElevateWarningMessage(elevateWarning);
269 }
270 else
271 {
272 parser.ReportErrorArgument(parameter, ErrorMessages.IllegalWarningIdAsError(paramArg));
273 }
274 }
275 }
276 }