diff options
Diffstat (limited to 'src/WixToolset.Core/CommandLine')
-rw-r--r-- | src/WixToolset.Core/CommandLine/BuildCommand.cs | 100 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLine.cs | 592 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineHelper.cs | 255 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineOption.cs | 27 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs | 137 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CompileCommand.cs | 39 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/HelpCommand.cs | 30 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/ICommand.cs | 9 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/VersionCommand.cs | 17 |
9 files changed, 1206 insertions, 0 deletions
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs new file mode 100644 index 00000000..ffb48305 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs | |||
@@ -0,0 +1,100 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Data; | ||
10 | |||
11 | internal class BuildCommand : ICommand | ||
12 | { | ||
13 | public BuildCommand(IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, string outputPath, IEnumerable<string> cultures, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile) | ||
14 | { | ||
15 | this.LocFiles = locFiles; | ||
16 | this.PreprocessorVariables = preprocessorVariables; | ||
17 | this.SourceFiles = sources; | ||
18 | this.OutputPath = outputPath; | ||
19 | |||
20 | this.Cultures = cultures; | ||
21 | this.ContentsFile = contentsFile; | ||
22 | this.OutputsFile = outputsFile; | ||
23 | this.BuiltOutputsFile = builtOutputsFile; | ||
24 | this.WixProjectFile = wixProjectFile; | ||
25 | } | ||
26 | |||
27 | public IEnumerable<string> LocFiles { get; } | ||
28 | |||
29 | private IEnumerable<SourceFile> SourceFiles { get; } | ||
30 | |||
31 | private IDictionary<string, string> PreprocessorVariables { get; } | ||
32 | |||
33 | private string OutputPath { get; } | ||
34 | |||
35 | public IEnumerable<string> Cultures { get; } | ||
36 | |||
37 | public string ContentsFile { get; } | ||
38 | |||
39 | public string OutputsFile { get; } | ||
40 | |||
41 | public string BuiltOutputsFile { get; } | ||
42 | |||
43 | public string WixProjectFile { get; } | ||
44 | |||
45 | public int Execute() | ||
46 | { | ||
47 | var intermediates = CompilePhase(); | ||
48 | |||
49 | var sections = intermediates.SelectMany(i => i.Sections).ToList(); | ||
50 | |||
51 | var linker = new Linker(); | ||
52 | |||
53 | var output = linker.Link(sections, OutputType.Product); | ||
54 | |||
55 | var localizer = new Localizer(); | ||
56 | |||
57 | var binder = new Binder(); | ||
58 | binder.TempFilesLocation = Path.GetTempPath(); | ||
59 | binder.WixVariableResolver = new WixVariableResolver(); | ||
60 | binder.WixVariableResolver.Localizer = localizer; | ||
61 | binder.AddExtension(new BinderFileManager()); | ||
62 | binder.SuppressValidation = true; | ||
63 | |||
64 | binder.ContentsFile = this.ContentsFile; | ||
65 | binder.OutputsFile = this.OutputsFile; | ||
66 | binder.BuiltOutputsFile = this.BuiltOutputsFile; | ||
67 | binder.WixprojectFile = this.WixProjectFile; | ||
68 | |||
69 | foreach (var loc in this.LocFiles) | ||
70 | { | ||
71 | var localization = Localizer.ParseLocalizationFile(loc, linker.TableDefinitions); | ||
72 | binder.WixVariableResolver.Localizer.AddLocalization(localization); | ||
73 | } | ||
74 | |||
75 | binder.Bind(output, this.OutputPath); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | private IEnumerable<Intermediate> CompilePhase() | ||
81 | { | ||
82 | var intermediates = new List<Intermediate>(); | ||
83 | |||
84 | var preprocessor = new Preprocessor(); | ||
85 | |||
86 | var compiler = new Compiler(); | ||
87 | |||
88 | foreach (var sourceFile in this.SourceFiles) | ||
89 | { | ||
90 | var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables); | ||
91 | |||
92 | var intermediate = compiler.Compile(document); | ||
93 | |||
94 | intermediates.Add(intermediate); | ||
95 | } | ||
96 | |||
97 | return intermediates; | ||
98 | } | ||
99 | } | ||
100 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLine.cs b/src/WixToolset.Core/CommandLine/CommandLine.cs new file mode 100644 index 00000000..440ae9ef --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLine.cs | |||
@@ -0,0 +1,592 @@ | |||
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 | ||
4 | { | ||
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; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Extensibility; | ||
13 | |||
14 | internal enum Commands | ||
15 | { | ||
16 | Unknown, | ||
17 | Build, | ||
18 | Preprocess, | ||
19 | Compile, | ||
20 | Link, | ||
21 | Bind, | ||
22 | } | ||
23 | |||
24 | public class CommandLine | ||
25 | { | ||
26 | private CommandLine() | ||
27 | { | ||
28 | } | ||
29 | |||
30 | public static string ExpectedArgument { get; } = "expected argument"; | ||
31 | |||
32 | public string ActiveCommand { get; private set; } | ||
33 | |||
34 | public string[] OriginalArguments { get; private set; } | ||
35 | |||
36 | public Queue<string> RemainingArguments { get; } = new Queue<string>(); | ||
37 | |||
38 | public ExtensionManager ExtensionManager { get; } = new ExtensionManager(); | ||
39 | |||
40 | public string ErrorArgument { get; set; } | ||
41 | |||
42 | public bool ShowHelp { get; set; } | ||
43 | |||
44 | public static ICommand ParseStandardCommandLine(string commandLineString) | ||
45 | { | ||
46 | var args = CommandLine.ParseArgumentsToArray(commandLineString).ToArray(); | ||
47 | |||
48 | return ParseStandardCommandLine(args); | ||
49 | } | ||
50 | |||
51 | public static ICommand ParseStandardCommandLine(string[] args) | ||
52 | { | ||
53 | var next = String.Empty; | ||
54 | |||
55 | var command = Commands.Unknown; | ||
56 | var showLogo = true; | ||
57 | var showVersion = false; | ||
58 | var outputFolder = String.Empty; | ||
59 | var outputFile = String.Empty; | ||
60 | var sourceFile = String.Empty; | ||
61 | var verbose = false; | ||
62 | var files = new List<string>(); | ||
63 | var defines = new List<string>(); | ||
64 | var includePaths = new List<string>(); | ||
65 | var locFiles = new List<string>(); | ||
66 | var suppressedWarnings = new List<int>(); | ||
67 | |||
68 | var cultures = new List<string>(); | ||
69 | var contentsFile = String.Empty; | ||
70 | var outputsFile = String.Empty; | ||
71 | var builtOutputsFile = String.Empty; | ||
72 | var wixProjectFile = String.Empty; | ||
73 | |||
74 | var cli = CommandLine.Parse(args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) => | ||
75 | { | ||
76 | if (cmdline.IsSwitch(arg)) | ||
77 | { | ||
78 | var parameter = arg.TrimStart(new[] { '-', '/' }); | ||
79 | switch (parameter.ToLowerInvariant()) | ||
80 | { | ||
81 | case "?": | ||
82 | case "h": | ||
83 | case "help": | ||
84 | cmdline.ShowHelp = true; | ||
85 | return true; | ||
86 | |||
87 | case "cultures": | ||
88 | cmdline.GetNextArgumentOrError(cultures); | ||
89 | return true; | ||
90 | case "contentsfile": | ||
91 | cmdline.GetNextArgumentOrError(ref contentsFile); | ||
92 | return true; | ||
93 | case "outputsfile": | ||
94 | cmdline.GetNextArgumentOrError(ref outputsFile); | ||
95 | return true; | ||
96 | case "builtoutputsfile": | ||
97 | cmdline.GetNextArgumentOrError(ref builtOutputsFile); | ||
98 | return true; | ||
99 | case "wixprojectfile": | ||
100 | cmdline.GetNextArgumentOrError(ref wixProjectFile); | ||
101 | return true; | ||
102 | |||
103 | case "d": | ||
104 | case "define": | ||
105 | cmdline.GetNextArgumentOrError(defines); | ||
106 | return true; | ||
107 | |||
108 | case "i": | ||
109 | case "includepath": | ||
110 | cmdline.GetNextArgumentOrError(includePaths); | ||
111 | return true; | ||
112 | |||
113 | case "loc": | ||
114 | cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files"); | ||
115 | return true; | ||
116 | |||
117 | case "o": | ||
118 | case "out": | ||
119 | cmdline.GetNextArgumentOrError(ref outputFile); | ||
120 | return true; | ||
121 | |||
122 | case "nologo": | ||
123 | showLogo = false; | ||
124 | return true; | ||
125 | |||
126 | case "v": | ||
127 | case "verbose": | ||
128 | verbose = true; | ||
129 | return true; | ||
130 | |||
131 | case "version": | ||
132 | case "-version": | ||
133 | showVersion = true; | ||
134 | return true; | ||
135 | } | ||
136 | |||
137 | return false; | ||
138 | } | ||
139 | else | ||
140 | { | ||
141 | files.AddRange(cmdline.GetFiles(arg, "source code")); | ||
142 | return true; | ||
143 | } | ||
144 | }); | ||
145 | |||
146 | if (showVersion) | ||
147 | { | ||
148 | return new VersionCommand(); | ||
149 | } | ||
150 | |||
151 | if (showLogo) | ||
152 | { | ||
153 | AppCommon.DisplayToolHeader(); | ||
154 | } | ||
155 | |||
156 | if (cli.ShowHelp) | ||
157 | { | ||
158 | return new HelpCommand(command); | ||
159 | } | ||
160 | |||
161 | switch (command) | ||
162 | { | ||
163 | case Commands.Build: | ||
164 | { | ||
165 | var sourceFiles = GatherSourceFiles(files, outputFolder); | ||
166 | var variables = GatherPreprocessorVariables(defines); | ||
167 | var extensions = cli.ExtensionManager; | ||
168 | return new BuildCommand(sourceFiles, variables, locFiles, outputFile, cultures, contentsFile, outputsFile, builtOutputsFile, wixProjectFile); | ||
169 | } | ||
170 | |||
171 | case Commands.Compile: | ||
172 | { | ||
173 | var sourceFiles = GatherSourceFiles(files, outputFolder); | ||
174 | var variables = GatherPreprocessorVariables(defines); | ||
175 | return new CompileCommand(sourceFiles, variables); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | return null; | ||
180 | } | ||
181 | |||
182 | private static CommandLine Parse(string commandLineString, Func<CommandLine, string, bool> parseArgument) | ||
183 | { | ||
184 | var arguments = CommandLine.ParseArgumentsToArray(commandLineString).ToArray(); | ||
185 | |||
186 | return CommandLine.Parse(arguments, null, parseArgument); | ||
187 | } | ||
188 | |||
189 | private static CommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseArgument) | ||
190 | { | ||
191 | return CommandLine.Parse(commandLineArguments, null, parseArgument); | ||
192 | } | ||
193 | |||
194 | private static CommandLine Parse(string[] commandLineArguments, Func<CommandLine, string, bool> parseCommand, Func<CommandLine, string, bool> parseArgument) | ||
195 | { | ||
196 | var cmdline = new CommandLine(); | ||
197 | |||
198 | cmdline.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments); | ||
199 | |||
200 | cmdline.QueueArgumentsAndLoadExtensions(cmdline.OriginalArguments); | ||
201 | |||
202 | cmdline.ProcessRemainingArguments(parseArgument, parseCommand); | ||
203 | |||
204 | return cmdline; | ||
205 | } | ||
206 | |||
207 | private static IEnumerable<SourceFile> GatherSourceFiles(IEnumerable<string> sourceFiles, string intermediateDirectory) | ||
208 | { | ||
209 | var files = new List<SourceFile>(); | ||
210 | |||
211 | foreach (var item in sourceFiles) | ||
212 | { | ||
213 | var sourcePath = item; | ||
214 | var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir"); | ||
215 | |||
216 | files.Add(new SourceFile(sourcePath, outputPath)); | ||
217 | } | ||
218 | |||
219 | return files; | ||
220 | } | ||
221 | |||
222 | private static IDictionary<string, string> GatherPreprocessorVariables(IEnumerable<string> defineConstants) | ||
223 | { | ||
224 | var variables = new Dictionary<string, string>(); | ||
225 | |||
226 | foreach (var pair in defineConstants) | ||
227 | { | ||
228 | string[] value = pair.Split(new[] { '=' }, 2); | ||
229 | |||
230 | if (variables.ContainsKey(value[0])) | ||
231 | { | ||
232 | Messaging.Instance.OnMessage(WixErrors.DuplicateVariableDefinition(value[0], (1 == value.Length) ? String.Empty : value[1], variables[value[0]])); | ||
233 | continue; | ||
234 | } | ||
235 | |||
236 | variables.Add(value[0], (1 == value.Length) ? String.Empty : value[1]); | ||
237 | } | ||
238 | |||
239 | return variables; | ||
240 | } | ||
241 | |||
242 | |||
243 | /// <summary> | ||
244 | /// Get a set of files that possibly have a search pattern in the path (such as '*'). | ||
245 | /// </summary> | ||
246 | /// <param name="searchPath">Search path to find files in.</param> | ||
247 | /// <param name="fileType">Type of file; typically "Source".</param> | ||
248 | /// <returns>An array of files matching the search path.</returns> | ||
249 | /// <remarks> | ||
250 | /// This method is written in this verbose way because it needs to support ".." in the path. | ||
251 | /// It needs the directory path isolated from the file name in order to use Directory.GetFiles | ||
252 | /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since | ||
253 | /// Path.GetDirectoryName does not support ".." in the path. | ||
254 | /// </remarks> | ||
255 | /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception> | ||
256 | public string[] GetFiles(string searchPath, string fileType) | ||
257 | { | ||
258 | if (null == searchPath) | ||
259 | { | ||
260 | throw new ArgumentNullException(nameof(searchPath)); | ||
261 | } | ||
262 | |||
263 | // Convert alternate directory separators to the standard one. | ||
264 | string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
265 | int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
266 | string[] files = null; | ||
267 | |||
268 | try | ||
269 | { | ||
270 | if (0 > lastSeparator) | ||
271 | { | ||
272 | files = Directory.GetFiles(".", filePath); | ||
273 | } | ||
274 | else // found directory separator | ||
275 | { | ||
276 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1)); | ||
277 | } | ||
278 | } | ||
279 | catch (DirectoryNotFoundException) | ||
280 | { | ||
281 | // Don't let this function throw the DirectoryNotFoundException. This exception | ||
282 | // occurs for non-existant directories and invalid characters in the searchPattern. | ||
283 | } | ||
284 | catch (ArgumentException) | ||
285 | { | ||
286 | // Don't let this function throw the ArgumentException. This exception | ||
287 | // occurs in certain situations such as when passing a malformed UNC path. | ||
288 | } | ||
289 | catch (IOException) | ||
290 | { | ||
291 | throw new WixFileNotFoundException(searchPath, fileType); | ||
292 | } | ||
293 | |||
294 | if (null == files || 0 == files.Length) | ||
295 | { | ||
296 | throw new WixFileNotFoundException(searchPath, fileType); | ||
297 | } | ||
298 | |||
299 | return files; | ||
300 | } | ||
301 | |||
302 | /// <summary> | ||
303 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
304 | /// </summary> | ||
305 | /// <param name="args">The list of strings to check.</param> | ||
306 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
307 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
308 | public bool IsSwitch(string arg) | ||
309 | { | ||
310 | return arg != null && ('/' == arg[0] || '-' == arg[0]); | ||
311 | } | ||
312 | |||
313 | /// <summary> | ||
314 | /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity | ||
315 | /// </summary> | ||
316 | /// <param name="args">The list of strings to check.</param> | ||
317 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
318 | /// <returns>True if a valid switch exists there, false if not.</returns> | ||
319 | public bool IsSwitchAt(IEnumerable<string> args, int index) | ||
320 | { | ||
321 | var arg = args.ElementAtOrDefault(index); | ||
322 | return IsSwitch(arg); | ||
323 | } | ||
324 | |||
325 | public void GetNextArgumentOrError(ref string arg) | ||
326 | { | ||
327 | this.TryGetNextArgumentOrError(out arg); | ||
328 | } | ||
329 | |||
330 | public void GetNextArgumentOrError(IList<string> args) | ||
331 | { | ||
332 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
333 | { | ||
334 | args.Add(arg); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | public void GetNextArgumentAsFilePathOrError(IList<string> args, string fileType) | ||
339 | { | ||
340 | if (this.TryGetNextArgumentOrError(out var arg)) | ||
341 | { | ||
342 | foreach (var path in this.GetFiles(arg, fileType)) | ||
343 | { | ||
344 | args.Add(path); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | public bool TryGetNextArgumentOrError(out string arg) | ||
350 | { | ||
351 | //if (this.RemainingArguments.TryDequeue(out arg) && !this.IsSwitch(arg)) | ||
352 | if (TryDequeue(this.RemainingArguments, out arg) && !this.IsSwitch(arg)) | ||
353 | { | ||
354 | return true; | ||
355 | } | ||
356 | |||
357 | this.ErrorArgument = arg ?? CommandLine.ExpectedArgument; | ||
358 | |||
359 | return false; | ||
360 | } | ||
361 | |||
362 | private static bool TryDequeue(Queue<string> q, out string arg) | ||
363 | { | ||
364 | if (q.Count> 0) | ||
365 | { | ||
366 | arg = q.Dequeue(); | ||
367 | return true; | ||
368 | } | ||
369 | |||
370 | arg = null; | ||
371 | return false; | ||
372 | } | ||
373 | |||
374 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) | ||
375 | { | ||
376 | List<string> args = new List<string>(); | ||
377 | |||
378 | foreach (var arg in commandLineArguments) | ||
379 | { | ||
380 | if ('@' == arg[0]) | ||
381 | { | ||
382 | var responseFileArguments = CommandLine.ParseResponseFile(arg.Substring(1)); | ||
383 | args.AddRange(responseFileArguments); | ||
384 | } | ||
385 | else | ||
386 | { | ||
387 | args.Add(arg); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | this.OriginalArguments = args.ToArray(); | ||
392 | } | ||
393 | |||
394 | private void QueueArgumentsAndLoadExtensions(string[] args) | ||
395 | { | ||
396 | for (var i = 0; i < args.Length; ++i) | ||
397 | { | ||
398 | var arg = args[i]; | ||
399 | |||
400 | if ("-ext" == arg || "/ext" == arg) | ||
401 | { | ||
402 | if (!this.IsSwitchAt(args, ++i)) | ||
403 | { | ||
404 | this.ExtensionManager.Load(args[i]); | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | this.ErrorArgument = arg; | ||
409 | break; | ||
410 | } | ||
411 | } | ||
412 | else | ||
413 | { | ||
414 | this.RemainingArguments.Enqueue(arg); | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | |||
419 | private void ProcessRemainingArguments(Func<CommandLine, string, bool> parseArgument, Func<CommandLine, string, bool> parseCommand) | ||
420 | { | ||
421 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); | ||
422 | |||
423 | while (!this.ShowHelp && | ||
424 | String.IsNullOrEmpty(this.ErrorArgument) && | ||
425 | TryDequeue(this.RemainingArguments, out var arg)) | ||
426 | { | ||
427 | if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments. | ||
428 | { | ||
429 | continue; | ||
430 | } | ||
431 | |||
432 | if ('-' == arg[0] || '/' == arg[0]) | ||
433 | { | ||
434 | if (!parseArgument(this, arg) && | ||
435 | !this.TryParseCommandLineArgumentWithExtension(arg, extensions)) | ||
436 | { | ||
437 | this.ErrorArgument = arg; | ||
438 | } | ||
439 | } | ||
440 | else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported. | ||
441 | { | ||
442 | if (parseCommand(this, arg)) | ||
443 | { | ||
444 | this.ActiveCommand = arg; | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | this.ErrorArgument = arg; | ||
449 | } | ||
450 | } | ||
451 | else if (!this.TryParseCommandLineArgumentWithExtension(arg, extensions) && | ||
452 | !parseArgument(this, arg)) | ||
453 | { | ||
454 | this.ErrorArgument = arg; | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | |||
459 | private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable<IExtensionCommandLine> extensions) | ||
460 | { | ||
461 | foreach (var extension in extensions) | ||
462 | { | ||
463 | //if (extension.ParseArgument(this, arg)) | ||
464 | //{ | ||
465 | // return true; | ||
466 | //} | ||
467 | } | ||
468 | |||
469 | return false; | ||
470 | } | ||
471 | |||
472 | /// <summary> | ||
473 | /// Parses a response file. | ||
474 | /// </summary> | ||
475 | /// <param name="responseFile">The file to parse.</param> | ||
476 | /// <returns>The array of arguments.</returns> | ||
477 | private static List<string> ParseResponseFile(string responseFile) | ||
478 | { | ||
479 | string arguments; | ||
480 | |||
481 | using (StreamReader reader = new StreamReader(responseFile)) | ||
482 | { | ||
483 | arguments = reader.ReadToEnd(); | ||
484 | } | ||
485 | |||
486 | return CommandLine.ParseArgumentsToArray(arguments); | ||
487 | } | ||
488 | |||
489 | /// <summary> | ||
490 | /// Parses an argument string into an argument array based on whitespace and quoting. | ||
491 | /// </summary> | ||
492 | /// <param name="arguments">Argument string.</param> | ||
493 | /// <returns>Argument array.</returns> | ||
494 | private static List<string> ParseArgumentsToArray(string arguments) | ||
495 | { | ||
496 | // Scan and parse the arguments string, dividing up the arguments based on whitespace. | ||
497 | // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed. | ||
498 | // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments. | ||
499 | // Escaped quotes and escaped backslashes also need to be unescaped by this process. | ||
500 | |||
501 | // Collects the final list of arguments to be returned. | ||
502 | var argsList = new List<string>(); | ||
503 | |||
504 | // True if we are inside an unescaped quote, meaning whitespace should be ignored. | ||
505 | var insideQuote = false; | ||
506 | |||
507 | // Index of the start of the current argument substring; either the start of the argument | ||
508 | // or the start of a quoted or unquoted sequence within it. | ||
509 | var partStart = 0; | ||
510 | |||
511 | // The current argument string being built; when completed it will be added to the list. | ||
512 | var arg = new StringBuilder(); | ||
513 | |||
514 | for (int i = 0; i <= arguments.Length; i++) | ||
515 | { | ||
516 | if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote)) | ||
517 | { | ||
518 | // Reached a whitespace separator or the end of the string. | ||
519 | |||
520 | // Finish building the current argument. | ||
521 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
522 | |||
523 | // Skip over the whitespace character. | ||
524 | partStart = i + 1; | ||
525 | |||
526 | // Add the argument to the list if it's not empty. | ||
527 | if (arg.Length > 0) | ||
528 | { | ||
529 | argsList.Add(CommandLine.ExpandEnvVars(arg.ToString())); | ||
530 | arg.Length = 0; | ||
531 | } | ||
532 | } | ||
533 | else if (i > partStart && arguments[i - 1] == '\\') | ||
534 | { | ||
535 | // Check the character following an unprocessed backslash. | ||
536 | // Unescape quotes, and backslashes followed by a quote. | ||
537 | if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"')) | ||
538 | { | ||
539 | // Unescape the quote or backslash by skipping the preceeding backslash. | ||
540 | arg.Append(arguments.Substring(partStart, i - 1 - partStart)); | ||
541 | arg.Append(arguments[i]); | ||
542 | partStart = i + 1; | ||
543 | } | ||
544 | } | ||
545 | else if (arguments[i] == '"') | ||
546 | { | ||
547 | // Add the quoted or unquoted section to the argument string. | ||
548 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
549 | |||
550 | // And skip over the quote character. | ||
551 | partStart = i + 1; | ||
552 | |||
553 | insideQuote = !insideQuote; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | return argsList; | ||
558 | } | ||
559 | |||
560 | /// <summary> | ||
561 | /// Expand enxironment variables contained in the passed string | ||
562 | /// </summary> | ||
563 | /// <param name="arguments"></param> | ||
564 | /// <returns></returns> | ||
565 | private static string ExpandEnvVars(string arguments) | ||
566 | { | ||
567 | var id = Environment.GetEnvironmentVariables(); | ||
568 | |||
569 | var regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)"); | ||
570 | MatchCollection matches = regex.Matches(arguments); | ||
571 | |||
572 | string value = String.Empty; | ||
573 | for (int i = 0; i <= (matches.Count - 1); i++) | ||
574 | { | ||
575 | try | ||
576 | { | ||
577 | var key = matches[i].Value; | ||
578 | regex = new Regex(String.Concat("(?i)(?:\\%)(?:", key, ")(?:\\%)")); | ||
579 | value = id[key].ToString(); | ||
580 | arguments = regex.Replace(arguments, value); | ||
581 | } | ||
582 | catch (NullReferenceException) | ||
583 | { | ||
584 | // Collapse unresolved environment variables. | ||
585 | arguments = regex.Replace(arguments, value); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | return arguments; | ||
590 | } | ||
591 | } | ||
592 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineHelper.cs b/src/WixToolset.Core/CommandLine/CommandLineHelper.cs new file mode 100644 index 00000000..86724603 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLineHelper.cs | |||
@@ -0,0 +1,255 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Text; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Extensibility; | ||
11 | |||
12 | /// <summary> | ||
13 | /// Common utilities for Wix command-line processing. | ||
14 | /// </summary> | ||
15 | public static class CommandLineHelper | ||
16 | { | ||
17 | /// <summary> | ||
18 | /// Get a set of files that possibly have a search pattern in the path (such as '*'). | ||
19 | /// </summary> | ||
20 | /// <param name="searchPath">Search path to find files in.</param> | ||
21 | /// <param name="fileType">Type of file; typically "Source".</param> | ||
22 | /// <returns>An array of files matching the search path.</returns> | ||
23 | /// <remarks> | ||
24 | /// This method is written in this verbose way because it needs to support ".." in the path. | ||
25 | /// It needs the directory path isolated from the file name in order to use Directory.GetFiles | ||
26 | /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since | ||
27 | /// Path.GetDirectoryName does not support ".." in the path. | ||
28 | /// </remarks> | ||
29 | /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception> | ||
30 | public static string[] GetFiles(string searchPath, string fileType) | ||
31 | { | ||
32 | if (null == searchPath) | ||
33 | { | ||
34 | throw new ArgumentNullException("searchPath"); | ||
35 | } | ||
36 | |||
37 | // convert alternate directory separators to the standard one | ||
38 | string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||
39 | int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar); | ||
40 | string[] files = null; | ||
41 | |||
42 | try | ||
43 | { | ||
44 | if (0 > lastSeparator) | ||
45 | { | ||
46 | files = Directory.GetFiles(".", filePath); | ||
47 | } | ||
48 | else // found directory separator | ||
49 | { | ||
50 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1)); | ||
51 | } | ||
52 | } | ||
53 | catch (DirectoryNotFoundException) | ||
54 | { | ||
55 | // don't let this function throw the DirectoryNotFoundException. (this exception | ||
56 | // occurs for non-existant directories and invalid characters in the searchPattern) | ||
57 | } | ||
58 | catch (ArgumentException) | ||
59 | { | ||
60 | // don't let this function throw the ArgumentException. (this exception | ||
61 | // occurs in certain situations such as when passing a malformed UNC path) | ||
62 | } | ||
63 | catch (IOException) | ||
64 | { | ||
65 | throw new WixFileNotFoundException(searchPath, fileType); | ||
66 | } | ||
67 | |||
68 | // file could not be found or path is invalid in some way | ||
69 | if (null == files || 0 == files.Length) | ||
70 | { | ||
71 | throw new WixFileNotFoundException(searchPath, fileType); | ||
72 | } | ||
73 | |||
74 | return files; | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Validates that a valid string parameter (without "/" or "-"), and returns a bool indicating its validity | ||
79 | /// </summary> | ||
80 | /// <param name="args">The list of strings to check.</param> | ||
81 | /// <param name="index">The index (in args) of the commandline parameter to be validated.</param> | ||
82 | /// <returns>True if a valid string parameter exists there, false if not.</returns> | ||
83 | public static bool IsValidArg(string[] args, int index) | ||
84 | { | ||
85 | if (args.Length <= index || String.IsNullOrEmpty(args[index]) || '/' == args[index][0] || '-' == args[index][0]) | ||
86 | { | ||
87 | return false; | ||
88 | } | ||
89 | else | ||
90 | { | ||
91 | return true; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /// <summary> | ||
96 | /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not | ||
97 | /// </summary> | ||
98 | /// <param name="path">The path to test.</param> | ||
99 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
100 | public static string VerifyPath(string path) | ||
101 | { | ||
102 | return VerifyPath(path, false); | ||
103 | } | ||
104 | |||
105 | /// <summary> | ||
106 | /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not | ||
107 | /// </summary> | ||
108 | /// <param name="path">The path to test.</param> | ||
109 | /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param> | ||
110 | /// <returns>The full path if it is valid, null if it is invalid.</returns> | ||
111 | public static string VerifyPath(string path, bool allowPrefix) | ||
112 | { | ||
113 | string fullPath; | ||
114 | |||
115 | if (0 <= path.IndexOf('\"')) | ||
116 | { | ||
117 | Messaging.Instance.OnMessage(WixErrors.PathCannotContainQuote(path)); | ||
118 | return null; | ||
119 | } | ||
120 | |||
121 | try | ||
122 | { | ||
123 | string prefix = null; | ||
124 | if (allowPrefix) | ||
125 | { | ||
126 | int prefixLength = path.IndexOf('=') + 1; | ||
127 | if (0 != prefixLength) | ||
128 | { | ||
129 | prefix = path.Substring(0, prefixLength); | ||
130 | path = path.Substring(prefixLength); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | if (String.IsNullOrEmpty(prefix)) | ||
135 | { | ||
136 | fullPath = Path.GetFullPath(path); | ||
137 | } | ||
138 | else | ||
139 | { | ||
140 | fullPath = String.Concat(prefix, Path.GetFullPath(path)); | ||
141 | } | ||
142 | } | ||
143 | catch (Exception e) | ||
144 | { | ||
145 | Messaging.Instance.OnMessage(WixErrors.InvalidCommandLineFileName(path, e.Message)); | ||
146 | return null; | ||
147 | } | ||
148 | |||
149 | return fullPath; | ||
150 | } | ||
151 | |||
152 | /// <summary> | ||
153 | /// Validates that a string is a valid bind path, and throws appropriate warnings/errors if not | ||
154 | /// </summary> | ||
155 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
156 | /// <param name="args">The list of strings to check.</param> | ||
157 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
158 | /// <returns>The bind path if it is valid, null if it is invalid.</returns> | ||
159 | public static BindPath GetBindPath(string commandlineSwitch, string[] args, int index) | ||
160 | { | ||
161 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
162 | |||
163 | if (!IsValidArg(args, index)) | ||
164 | { | ||
165 | Messaging.Instance.OnMessage(WixErrors.DirectoryPathRequired(commandlineSwitch)); | ||
166 | return null; | ||
167 | } | ||
168 | |||
169 | BindPath bindPath = BindPath.Parse(args[index]); | ||
170 | |||
171 | if (File.Exists(bindPath.Path)) | ||
172 | { | ||
173 | Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile(commandlineSwitch, bindPath.Path)); | ||
174 | return null; | ||
175 | } | ||
176 | |||
177 | bindPath.Path = VerifyPath(bindPath.Path, true); | ||
178 | return String.IsNullOrEmpty(bindPath.Path) ? null : bindPath; | ||
179 | } | ||
180 | |||
181 | /// <summary> | ||
182 | /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not | ||
183 | /// </summary> | ||
184 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
185 | /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param> | ||
186 | /// <param name="args">The list of strings to check.</param> | ||
187 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
188 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
189 | public static string GetFileOrDirectory(string commandlineSwitch, string[] args, int index) | ||
190 | { | ||
191 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
192 | |||
193 | if (!IsValidArg(args, index)) | ||
194 | { | ||
195 | Messaging.Instance.OnMessage(WixErrors.FileOrDirectoryPathRequired(commandlineSwitch)); | ||
196 | return null; | ||
197 | } | ||
198 | |||
199 | return VerifyPath(args[index]); | ||
200 | } | ||
201 | |||
202 | /// <summary> | ||
203 | /// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not | ||
204 | /// </summary> | ||
205 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
206 | /// <param name="args">The list of strings to check.</param> | ||
207 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
208 | /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param> | ||
209 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
210 | public static string GetDirectory(string commandlineSwitch, string[] args, int index, bool allowPrefix = false) | ||
211 | { | ||
212 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
213 | |||
214 | if (!IsValidArg(args, index)) | ||
215 | { | ||
216 | Messaging.Instance.OnMessage(WixErrors.DirectoryPathRequired(commandlineSwitch)); | ||
217 | return null; | ||
218 | } | ||
219 | |||
220 | if (File.Exists(args[index])) | ||
221 | { | ||
222 | Messaging.Instance.OnMessage(WixErrors.ExpectedDirectoryGotFile(commandlineSwitch, args[index])); | ||
223 | return null; | ||
224 | } | ||
225 | |||
226 | return VerifyPath(args[index], allowPrefix); | ||
227 | } | ||
228 | |||
229 | /// <summary> | ||
230 | /// Validates that a string is a valid filename, and throws appropriate warnings/errors if not | ||
231 | /// </summary> | ||
232 | /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param> | ||
233 | /// <param name="args">The list of strings to check.</param> | ||
234 | /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param> | ||
235 | /// <returns>The string if it is valid, null if it is invalid.</returns> | ||
236 | public static string GetFile(string commandlineSwitch, string[] args, int index) | ||
237 | { | ||
238 | commandlineSwitch = String.Concat("-", commandlineSwitch); | ||
239 | |||
240 | if (!IsValidArg(args, index)) | ||
241 | { | ||
242 | Messaging.Instance.OnMessage(WixErrors.FilePathRequired(commandlineSwitch)); | ||
243 | return null; | ||
244 | } | ||
245 | |||
246 | if (Directory.Exists(args[index])) | ||
247 | { | ||
248 | Messaging.Instance.OnMessage(WixErrors.ExpectedFileGotDirectory(commandlineSwitch, args[index])); | ||
249 | return null; | ||
250 | } | ||
251 | |||
252 | return VerifyPath(args[index]); | ||
253 | } | ||
254 | } | ||
255 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineOption.cs b/src/WixToolset.Core/CommandLine/CommandLineOption.cs new file mode 100644 index 00000000..85a654bf --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLineOption.cs | |||
@@ -0,0 +1,27 @@ | |||
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 | ||
4 | { | ||
5 | /// <summary> | ||
6 | /// A command line option. | ||
7 | /// </summary> | ||
8 | public struct CommandLineOption | ||
9 | { | ||
10 | public string Option; | ||
11 | public string Description; | ||
12 | public int AdditionalArguments; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Instantiates a new BuilderCommandLineOption. | ||
16 | /// </summary> | ||
17 | /// <param name="option">The option name.</param> | ||
18 | /// <param name="description">The description of the option.</param> | ||
19 | /// <param name="additionalArguments">Count of additional arguments to require after this switch.</param> | ||
20 | public CommandLineOption(string option, string description, int additionalArguments) | ||
21 | { | ||
22 | this.Option = option; | ||
23 | this.Description = description; | ||
24 | this.AdditionalArguments = additionalArguments; | ||
25 | } | ||
26 | } | ||
27 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs b/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs new file mode 100644 index 00000000..f27296b7 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs | |||
@@ -0,0 +1,137 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Collections.Generic; | ||
8 | using System.IO; | ||
9 | using System.Text; | ||
10 | using System.Text.RegularExpressions; | ||
11 | |||
12 | /// <summary> | ||
13 | /// Common utilities for Wix command-line processing. | ||
14 | /// </summary> | ||
15 | public static class CommandLineResponseFile | ||
16 | { | ||
17 | /// <summary> | ||
18 | /// Parses a response file. | ||
19 | /// </summary> | ||
20 | /// <param name="responseFile">The file to parse.</param> | ||
21 | /// <returns>The array of arguments.</returns> | ||
22 | public static string[] Parse(string responseFile) | ||
23 | { | ||
24 | string arguments; | ||
25 | |||
26 | using (StreamReader reader = new StreamReader(responseFile)) | ||
27 | { | ||
28 | arguments = reader.ReadToEnd(); | ||
29 | } | ||
30 | |||
31 | return CommandLineResponseFile.ParseArgumentsToArray(arguments); | ||
32 | } | ||
33 | |||
34 | /// <summary> | ||
35 | /// Parses an argument string into an argument array based on whitespace and quoting. | ||
36 | /// </summary> | ||
37 | /// <param name="arguments">Argument string.</param> | ||
38 | /// <returns>Argument array.</returns> | ||
39 | public static string[] ParseArgumentsToArray(string arguments) | ||
40 | { | ||
41 | // Scan and parse the arguments string, dividing up the arguments based on whitespace. | ||
42 | // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed. | ||
43 | // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments. | ||
44 | // Escaped quotes and escaped backslashes also need to be unescaped by this process. | ||
45 | |||
46 | // Collects the final list of arguments to be returned. | ||
47 | List<string> argsList = new List<string>(); | ||
48 | |||
49 | // True if we are inside an unescaped quote, meaning whitespace should be ignored. | ||
50 | bool insideQuote = false; | ||
51 | |||
52 | // Index of the start of the current argument substring; either the start of the argument | ||
53 | // or the start of a quoted or unquoted sequence within it. | ||
54 | int partStart = 0; | ||
55 | |||
56 | // The current argument string being built; when completed it will be added to the list. | ||
57 | StringBuilder arg = new StringBuilder(); | ||
58 | |||
59 | for (int i = 0; i <= arguments.Length; i++) | ||
60 | { | ||
61 | if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote)) | ||
62 | { | ||
63 | // Reached a whitespace separator or the end of the string. | ||
64 | |||
65 | // Finish building the current argument. | ||
66 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
67 | |||
68 | // Skip over the whitespace character. | ||
69 | partStart = i + 1; | ||
70 | |||
71 | // Add the argument to the list if it's not empty. | ||
72 | if (arg.Length > 0) | ||
73 | { | ||
74 | argsList.Add(CommandLineResponseFile.ExpandEnvVars(arg.ToString())); | ||
75 | arg.Length = 0; | ||
76 | } | ||
77 | } | ||
78 | else if (i > partStart && arguments[i - 1] == '\\') | ||
79 | { | ||
80 | // Check the character following an unprocessed backslash. | ||
81 | // Unescape quotes, and backslashes followed by a quote. | ||
82 | if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"')) | ||
83 | { | ||
84 | // Unescape the quote or backslash by skipping the preceeding backslash. | ||
85 | arg.Append(arguments.Substring(partStart, i - 1 - partStart)); | ||
86 | arg.Append(arguments[i]); | ||
87 | partStart = i + 1; | ||
88 | } | ||
89 | } | ||
90 | else if (arguments[i] == '"') | ||
91 | { | ||
92 | // Add the quoted or unquoted section to the argument string. | ||
93 | arg.Append(arguments.Substring(partStart, i - partStart)); | ||
94 | |||
95 | // And skip over the quote character. | ||
96 | partStart = i + 1; | ||
97 | |||
98 | insideQuote = !insideQuote; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | return argsList.ToArray(); | ||
103 | } | ||
104 | |||
105 | /// <summary> | ||
106 | /// Expand enxironment variables contained in the passed string | ||
107 | /// </summary> | ||
108 | /// <param name="arguments"></param> | ||
109 | /// <returns></returns> | ||
110 | static private string ExpandEnvVars(string arguments) | ||
111 | { | ||
112 | IDictionary id = Environment.GetEnvironmentVariables(); | ||
113 | |||
114 | Regex regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)"); | ||
115 | MatchCollection matches = regex.Matches(arguments); | ||
116 | |||
117 | string value = String.Empty; | ||
118 | for (int i = 0; i <= (matches.Count - 1); i++) | ||
119 | { | ||
120 | try | ||
121 | { | ||
122 | string key = matches[i].Value; | ||
123 | regex = new Regex(String.Concat("(?i)(?:\\%)(?:" , key , ")(?:\\%)")); | ||
124 | value = id[key].ToString(); | ||
125 | arguments = regex.Replace(arguments, value); | ||
126 | } | ||
127 | catch (NullReferenceException) | ||
128 | { | ||
129 | // Collapse unresolved environment variables. | ||
130 | arguments = regex.Replace(arguments, value); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return arguments; | ||
135 | } | ||
136 | } | ||
137 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CompileCommand.cs b/src/WixToolset.Core/CommandLine/CompileCommand.cs new file mode 100644 index 00000000..17847b57 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CompileCommand.cs | |||
@@ -0,0 +1,39 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using WixToolset.Data; | ||
8 | |||
9 | internal class CompileCommand : ICommand | ||
10 | { | ||
11 | public CompileCommand(IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables) | ||
12 | { | ||
13 | this.PreprocessorVariables = preprocessorVariables; | ||
14 | this.SourceFiles = sources; | ||
15 | } | ||
16 | |||
17 | private IEnumerable<SourceFile> SourceFiles { get; } | ||
18 | |||
19 | private IDictionary<string, string> PreprocessorVariables { get; } | ||
20 | |||
21 | public int Execute() | ||
22 | { | ||
23 | var preprocessor = new Preprocessor(); | ||
24 | |||
25 | var compiler = new Compiler(); | ||
26 | |||
27 | foreach (var sourceFile in this.SourceFiles) | ||
28 | { | ||
29 | var document = preprocessor.Process(sourceFile.SourcePath, this.PreprocessorVariables); | ||
30 | |||
31 | var intermediate = compiler.Compile(document); | ||
32 | |||
33 | intermediate.Save(sourceFile.OutputPath); | ||
34 | } | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | } | ||
39 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/HelpCommand.cs b/src/WixToolset.Core/CommandLine/HelpCommand.cs new file mode 100644 index 00000000..1c101781 --- /dev/null +++ b/src/WixToolset.Core/CommandLine/HelpCommand.cs | |||
@@ -0,0 +1,30 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | |||
7 | internal class HelpCommand : ICommand | ||
8 | { | ||
9 | public HelpCommand(Commands command) | ||
10 | { | ||
11 | this.Command = command; | ||
12 | } | ||
13 | |||
14 | public Commands Command { get; } | ||
15 | |||
16 | public int Execute() | ||
17 | { | ||
18 | if (this.Command == Commands.Unknown) | ||
19 | { | ||
20 | Console.WriteLine(); | ||
21 | } | ||
22 | else | ||
23 | { | ||
24 | Console.WriteLine(); | ||
25 | } | ||
26 | |||
27 | return -1; | ||
28 | } | ||
29 | } | ||
30 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/ICommand.cs b/src/WixToolset.Core/CommandLine/ICommand.cs new file mode 100644 index 00000000..41abbbdc --- /dev/null +++ b/src/WixToolset.Core/CommandLine/ICommand.cs | |||
@@ -0,0 +1,9 @@ | |||
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 | ||
4 | { | ||
5 | public interface ICommand | ||
6 | { | ||
7 | int Execute(); | ||
8 | } | ||
9 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/VersionCommand.cs b/src/WixToolset.Core/CommandLine/VersionCommand.cs new file mode 100644 index 00000000..a1980a2b --- /dev/null +++ b/src/WixToolset.Core/CommandLine/VersionCommand.cs | |||
@@ -0,0 +1,17 @@ | |||
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 | using System; | ||
4 | |||
5 | namespace WixToolset.Core | ||
6 | { | ||
7 | internal class VersionCommand : ICommand | ||
8 | { | ||
9 | public int Execute() | ||
10 | { | ||
11 | Console.WriteLine("wix version {0}", ThisAssembly.AssemblyInformationalVersion); | ||
12 | Console.WriteLine(); | ||
13 | |||
14 | return 0; | ||
15 | } | ||
16 | } | ||
17 | } | ||