aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/WixToolset.BuildTasks/Properties/AssemblyInfo.cs (renamed from src/WixToolset.BuildTasks/AssemblyInfo.cs)0
-rw-r--r--src/WixToolset.Core/CommandLine/CommandLineHelper.cs255
-rw-r--r--src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs5
-rw-r--r--src/WixToolset.Core/Properties/AssemblyInfo.cs (renamed from src/WixToolset.Core/AssemblyInfo.cs)0
-rw-r--r--src/wix/Program.cs482
-rw-r--r--src/wix/X_CommandLine.cs394
6 files changed, 10 insertions, 1126 deletions
diff --git a/src/WixToolset.BuildTasks/AssemblyInfo.cs b/src/WixToolset.BuildTasks/Properties/AssemblyInfo.cs
index ae52fce8..ae52fce8 100644
--- a/src/WixToolset.BuildTasks/AssemblyInfo.cs
+++ b/src/WixToolset.BuildTasks/Properties/AssemblyInfo.cs
diff --git a/src/WixToolset.Core/CommandLine/CommandLineHelper.cs b/src/WixToolset.Core/CommandLine/CommandLineHelper.cs
deleted file mode 100644
index 86724603..00000000
--- a/src/WixToolset.Core/CommandLine/CommandLineHelper.cs
+++ /dev/null
@@ -1,255 +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
3namespace 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/CommandLineResponseFile.cs b/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs
index f27296b7..578c3b22 100644
--- a/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs
+++ b/src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs
@@ -102,11 +102,6 @@ namespace WixToolset
102 return argsList.ToArray(); 102 return argsList.ToArray();
103 } 103 }
104 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) 105 static private string ExpandEnvVars(string arguments)
111 { 106 {
112 IDictionary id = Environment.GetEnvironmentVariables(); 107 IDictionary id = Environment.GetEnvironmentVariables();
diff --git a/src/WixToolset.Core/AssemblyInfo.cs b/src/WixToolset.Core/Properties/AssemblyInfo.cs
index b3740b2a..b3740b2a 100644
--- a/src/WixToolset.Core/AssemblyInfo.cs
+++ b/src/WixToolset.Core/Properties/AssemblyInfo.cs
diff --git a/src/wix/Program.cs b/src/wix/Program.cs
index 277f99fe..c60831d0 100644
--- a/src/wix/Program.cs
+++ b/src/wix/Program.cs
@@ -6,16 +6,12 @@ namespace WixToolset.Core
6 using WixToolset.Data; 6 using WixToolset.Data;
7 7
8 /// <summary> 8 /// <summary>
9 /// The main entry point for candle. 9 /// Wix Toolset Command-Line Interface.
10 /// </summary> 10 /// </summary>
11 public sealed class Program 11 public sealed class Program
12 { 12 {
13 //private IEnumerable<IPreprocessorExtension> preprocessorExtensions;
14 //private IEnumerable<ICompilerExtension> compilerExtensions;
15 //private IEnumerable<IExtensionData> extensionData;
16
17 /// <summary> 13 /// <summary>
18 /// The main entry point for candle. 14 /// The main entry point for wix command-line interface.
19 /// </summary> 15 /// </summary>
20 /// <param name="args">Commandline arguments for the application.</param> 16 /// <param name="args">Commandline arguments for the application.</param>
21 /// <returns>Returns the application error code.</returns> 17 /// <returns>Returns the application error code.</returns>
@@ -33,474 +29,16 @@ namespace WixToolset.Core
33 29
34 private static void DisplayMessage(object sender, DisplayEventArgs e) 30 private static void DisplayMessage(object sender, DisplayEventArgs e)
35 { 31 {
36 Console.WriteLine(e.Message); 32 switch (e.Level)
37 }
38
39#if false
40 private static ICommand ParseCommandLine(string[] args)
41 {
42 var next = String.Empty;
43
44 var command = Commands.Unknown;
45 var showLogo = true;
46 var showVersion = false;
47 var outputFolder = String.Empty;
48 var outputFile = String.Empty;
49 var sourceFile = String.Empty;
50 var verbose = false;
51 var files = new List<string>();
52 var defines = new List<string>();
53 var includePaths = new List<string>();
54 var locFiles = new List<string>();
55 var suppressedWarnings = new List<int>();
56
57 var cli = CommandLine.Parse(args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) =>
58 {
59 if (cmdline.IsSwitch(arg))
60 {
61 var parameter = arg.TrimStart(new[] { '-', '/' });
62 switch (parameter.ToLowerInvariant())
63 {
64 case "?":
65 case "h":
66 case "help":
67 cmdline.ShowHelp = true;
68 return true;
69
70 case "d":
71 case "define":
72 cmdline.GetNextArgumentOrError(defines);
73 return true;
74
75 case "i":
76 case "includepath":
77 cmdline.GetNextArgumentOrError(includePaths);
78 return true;
79
80 case "loc":
81 cmdline.GetNextArgumentAsFilePathOrError(locFiles, "localization files");
82 return true;
83
84 case "o":
85 case "out":
86 cmdline.GetNextArgumentOrError(ref outputFile);
87 return true;
88
89 case "nologo":
90 showLogo = false;
91 return true;
92
93 case "v":
94 case "verbose":
95 verbose = true;
96 return true;
97
98 case "version":
99 case "-version":
100 showVersion = true;
101 return true;
102 }
103
104 return false;
105 }
106 else
107 {
108 files.AddRange(cmdline.GetFiles(arg, "source code"));
109 return true;
110 }
111 });
112
113 if (showVersion)
114 {
115 return new VersionCommand();
116 }
117
118 if (showLogo)
119 {
120 AppCommon.DisplayToolHeader();
121 }
122
123 if (cli.ShowHelp)
124 {
125 return new HelpCommand(command);
126 }
127
128 switch (command)
129 {
130 case Commands.Build:
131 {
132 var sourceFiles = GatherSourceFiles(files, outputFolder);
133 var variables = GatherPreprocessorVariables(defines);
134 var extensions = cli.ExtensionManager;
135 return new BuildCommand(sourceFiles, variables, locFiles, outputFile);
136 }
137
138 case Commands.Compile:
139 {
140 var sourceFiles = GatherSourceFiles(files, outputFolder);
141 var variables = GatherPreprocessorVariables(defines);
142 return new CompileCommand(sourceFiles, variables);
143 }
144 }
145
146 return null;
147 }
148
149 private static IEnumerable<SourceFile> GatherSourceFiles(IEnumerable<string> sourceFiles, string intermediateDirectory)
150 {
151 var files = new List<SourceFile>();
152
153 foreach (var item in sourceFiles)
154 {
155 var sourcePath = item;
156 var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir");
157
158 files.Add(new SourceFile(sourcePath, outputPath));
159 }
160
161 return files;
162 }
163
164 private static IDictionary<string, string> GatherPreprocessorVariables(IEnumerable<string> defineConstants)
165 {
166 var variables = new Dictionary<string, string>();
167
168 foreach (var pair in defineConstants)
169 {
170 string[] value = pair.Split(new[] { '=' }, 2);
171
172 if (variables.ContainsKey(value[0]))
173 {
174 Messaging.Instance.OnMessage(WixErrors.DuplicateVariableDefinition(value[0], (1 == value.Length) ? String.Empty : value[1], variables[value[0]]));
175 continue;
176 }
177
178 variables.Add(value[0], (1 == value.Length) ? String.Empty : value[1]);
179 }
180
181 return variables;
182 }
183#endif
184
185#if false
186 private static ICommand ParseCommandLine2(string[] args)
187 {
188 var command = Commands.Unknown;
189
190 var nologo = false;
191 var outputFolder = String.Empty;
192 var outputFile = String.Empty;
193 var sourceFile = String.Empty;
194 var verbose = false;
195 IReadOnlyList<string> files = Array.Empty<string>();
196 IReadOnlyList<string> defines = Array.Empty<string>();
197 IReadOnlyList<string> includePaths = Array.Empty<string>();
198 IReadOnlyList<int> suppressedWarnings = Array.Empty<int>();
199 IReadOnlyList<string> locFiles = Array.Empty<string>();
200
201 ArgumentSyntax parsed = null;
202 try
203 {
204 parsed = ArgumentSyntax.Parse(args, syntax =>
205 {
206 syntax.HandleErrors = false;
207 //syntax.HandleHelp = false;
208 syntax.ErrorOnUnexpectedArguments = false;
209
210 syntax.DefineCommand("build", ref command, Commands.Build, "Build to final output");
211 syntax.DefineOptionList("d|D|define", ref defines, "Preprocessor name value pairs");
212 syntax.DefineOptionList("I|includePath", ref includePaths, "Include search paths");
213 syntax.DefineOption("nologo", ref nologo, false, "Do not display logo");
214 syntax.DefineOption("o|out", ref outputFile, "Output file");
215 syntax.DefineOptionList("sw", ref suppressedWarnings, false, "Do not display logo");
216 syntax.DefineOption("v|verbose", ref verbose, false, "Display verbose messages");
217 syntax.DefineOptionList("l|loc", ref locFiles, "Localization files to load (.wxl)");
218 syntax.DefineParameterList("files", ref files, "Source files to compile (.wxs)");
219
220 syntax.DefineCommand("preprocess", ref command, Commands.Preprocess, "Preprocess a source files");
221 syntax.DefineOptionList("d|D|define", ref defines, "Preprocessor name value pairs");
222 syntax.DefineOptionList("I|includePath", ref includePaths, "Include search paths");
223 syntax.DefineOption("nologo", ref nologo, false, "Do not display logo");
224 syntax.DefineOption("o|out", ref outputFile, "Output file");
225 syntax.DefineParameter("file", ref sourceFile, "File to process");
226
227 syntax.DefineCommand("compile", ref command, Commands.Compile, "Compile source files");
228 syntax.DefineOptionList("I|includePath", ref includePaths, "Include search paths");
229 syntax.DefineOption("nologo", ref nologo, false, "Do not display logo");
230 syntax.DefineOption("o|out", ref outputFolder, "Output folder");
231 syntax.DefineOptionList("sw", ref suppressedWarnings, false, "Do not display logo");
232 syntax.DefineOption("v|verbose", ref verbose, false, "Display verbose messages");
233 syntax.DefineParameterList("files", ref files, "Source files to compile (.wxs)");
234
235 syntax.DefineCommand("link", ref command, Commands.Link, "Link intermediate files");
236 syntax.DefineOption("nologo", ref nologo, "Do not display logo");
237 syntax.DefineOption("o|out", ref outputFile, "Output intermediate file (.wir)");
238 syntax.DefineParameterList("files", ref files, "Intermediate files to link (.wir)");
239
240 syntax.DefineCommand("bind", ref command, Commands.Bind, "Bind to final output");
241 syntax.DefineOption("nologo", ref nologo, false, "Do not display logo");
242 syntax.DefineOption("o|out", ref outputFile, "Output file");
243 syntax.DefineParameterList("files", ref files, "Intermediate files to bind (.wir)");
244
245 syntax.DefineCommand("version", ref command, Commands.Version, "Display version information");
246 });
247
248 if (IsHelpRequested(parsed))
249 {
250 var width = Console.WindowWidth - 2;
251 var text = parsed.GetHelpText(width < 20 ? 72 : width);
252 Console.Error.WriteLine(text);
253
254 return null;
255 }
256
257 //var u = result.GetArguments();
258
259 //var p = result.GetActiveParameters();
260
261 //var o = result.GetActiveOptions();
262
263 //var a = result.GetActiveArguments();
264
265 //var h = result.GetHelpText();
266
267 //foreach (var x in p)
268 //{
269 // Console.WriteLine("{0}", x.Name);
270 //}
271
272 switch (command)
273 {
274 case Commands.Build:
275 {
276 var sourceFiles = GatherSourceFiles(files, outputFolder);
277 var variables = GatherPreprocessorVariables(defines);
278 return new BuildCommand(sourceFiles, variables, locFiles, outputFile);
279 }
280
281 case Commands.Compile:
282 {
283 var sourceFiles = GatherSourceFiles(files, outputFolder);
284 var variables = GatherPreprocessorVariables(defines);
285 return new CompileCommand(sourceFiles, variables);
286 }
287
288 case Commands.Version:
289 return new VersionCommand();
290 }
291
292 //var preprocessorVariables = this.GatherPreprocessorVariables();
293
294 //foreach (var sourceFile in sourceFiles)
295 //{
296 // var document = preprocessor.Process(sourceFile.SourcePath, preprocessorVariables);
297
298 // var intermediate = compiler.Compile(document);
299
300 // intermediate.Save(sourceFile.OutputPath);
301 //}
302 }
303 //catch (ArgumentSyntaxException e)
304 //{
305 // if (IsHelpRequested(parsed))
306 // {
307 // var width = Console.WindowWidth - 2;
308 // var text = parsed.GetHelpText(width < 20 ? 72 : width);
309 // Console.Error.WriteLine(text);
310 // }
311 // else
312 // {
313 // Console.Error.WriteLine(e.Message);
314 // }
315 //}
316
317 return null;
318 }
319
320 //private static bool IsHelpRequested(ArgumentSyntax syntax)
321 //{
322 // return syntax?.RemainingArguments
323 // .Any(a => String.Equals(a, @"-?", StringComparison.Ordinal) ||
324 // String.Equals(a, @"-h", StringComparison.Ordinal) ||
325 // String.Equals(a, @"--help", StringComparison.Ordinal)) ?? false;
326 //}
327#endif
328
329#if false
330 private int Execute(string[] args)
331 {
332 try
333 {
334 string[] unparsed = this.ParseCommandLineAndLoadExtensions(args);
335
336 if (!Messaging.Instance.EncounteredError)
337 {
338 if (this.commandLine.ShowLogo)
339 {
340 AppCommon.DisplayToolHeader();
341 }
342
343 if (this.commandLine.ShowHelp)
344 {
345 Console.WriteLine(CandleStrings.HelpMessage);
346 AppCommon.DisplayToolFooter();
347 }
348 else
349 {
350 foreach (string arg in unparsed)
351 {
352 Messaging.Instance.OnMessage(WixWarnings.UnsupportedCommandLineArgument(arg));
353 }
354
355 this.Run();
356 }
357 }
358 }
359 catch (WixException we)
360 {
361 Messaging.Instance.OnMessage(we.Error);
362 }
363 catch (Exception e)
364 {
365 Messaging.Instance.OnMessage(WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace));
366 if (e is NullReferenceException || e is SEHException)
367 {
368 throw;
369 }
370 }
371
372 return Messaging.Instance.LastErrorNumber;
373 }
374
375 private string[] ParseCommandLineAndLoadExtensions(string[] args)
376 {
377 this.commandLine = new CandleCommandLine();
378 string[] unprocessed = commandLine.Parse(args);
379 if (Messaging.Instance.EncounteredError)
380 {
381 return unprocessed;
382 }
383
384 // Load extensions.
385 ExtensionManager extensionManager = new ExtensionManager();
386 foreach (string extension in this.commandLine.Extensions)
387 {
388 extensionManager.Load(extension);
389 }
390
391 // Preprocessor extension command line processing.
392 this.preprocessorExtensions = extensionManager.Create<IPreprocessorExtension>();
393 foreach (IExtensionCommandLine pce in this.preprocessorExtensions.Where(e => e is IExtensionCommandLine).Cast<IExtensionCommandLine>())
394 {
395 pce.MessageHandler = Messaging.Instance;
396 unprocessed = pce.ParseCommandLine(unprocessed);
397 }
398
399 // Compiler extension command line processing.
400 this.compilerExtensions = extensionManager.Create<ICompilerExtension>();
401 foreach (IExtensionCommandLine cce in this.compilerExtensions.Where(e => e is IExtensionCommandLine).Cast<IExtensionCommandLine>())
402 {
403 cce.MessageHandler = Messaging.Instance;
404 unprocessed = cce.ParseCommandLine(unprocessed);
405 }
406
407 // Extension data command line processing.
408 this.extensionData = extensionManager.Create<IExtensionData>();
409 foreach (IExtensionCommandLine dce in this.extensionData.Where(e => e is IExtensionCommandLine).Cast<IExtensionCommandLine>())
410 { 33 {
411 dce.MessageHandler = Messaging.Instance; 34 case MessageLevel.Warning:
412 unprocessed = dce.ParseCommandLine(unprocessed); 35 case MessageLevel.Error:
36 Console.Error.WriteLine(e.Message);
37 break;
38 default:
39 Console.WriteLine(e.Message);
40 break;
413 } 41 }
414
415 return commandLine.ParsePostExtensions(unprocessed);
416 }
417
418 private void Run()
419 {
420 // Create the preprocessor and compiler
421 Preprocessor preprocessor = new Preprocessor();
422 preprocessor.CurrentPlatform = this.commandLine.Platform;
423
424 foreach (string includePath in this.commandLine.IncludeSearchPaths)
425 {
426 preprocessor.IncludeSearchPaths.Add(includePath);
427 }
428
429 foreach (IPreprocessorExtension pe in this.preprocessorExtensions)
430 {
431 preprocessor.AddExtension(pe);
432 }
433
434 Compiler compiler = new Compiler();
435 compiler.ShowPedanticMessages = this.commandLine.ShowPedanticMessages;
436 compiler.CurrentPlatform = this.commandLine.Platform;
437
438 foreach (IExtensionData ed in this.extensionData)
439 {
440 compiler.AddExtensionData(ed);
441 }
442
443 foreach (ICompilerExtension ce in this.compilerExtensions)
444 {
445 compiler.AddExtension(ce);
446 }
447
448 // Preprocess then compile each source file.
449 foreach (CompileFile file in this.commandLine.Files)
450 {
451 // print friendly message saying what file is being compiled
452 Console.WriteLine(file.SourcePath);
453
454 // preprocess the source
455 XDocument sourceDocument;
456 try
457 {
458 if (!String.IsNullOrEmpty(this.commandLine.PreprocessFile))
459 {
460 preprocessor.PreprocessOut = this.commandLine.PreprocessFile.Equals("con:", StringComparison.OrdinalIgnoreCase) ? Console.Out : new StreamWriter(this.commandLine.PreprocessFile);
461 }
462
463 sourceDocument = preprocessor.Process(file.SourcePath, this.commandLine.PreprocessorVariables);
464 }
465 finally
466 {
467 if (null != preprocessor.PreprocessOut && Console.Out != preprocessor.PreprocessOut)
468 {
469 preprocessor.PreprocessOut.Close();
470 }
471 }
472
473 // If we're not actually going to compile anything, move on to the next file.
474 if (null == sourceDocument || !String.IsNullOrEmpty(this.commandLine.PreprocessFile))
475 {
476 continue;
477 }
478
479 // and now we do what we came here to do...
480 Intermediate intermediate = compiler.Compile(sourceDocument);
481
482 // save the intermediate to disk if no errors were found for this source file
483 if (null != intermediate)
484 {
485 intermediate.Save(file.OutputPath);
486 }
487 }
488 }
489
490 public interface IOptions
491 {
492 IEnumerable<SourceFile> SourceFiles { get; }
493 }
494
495 public class CompilerOptions : IOptions
496 {
497 public CompilerOptions(IEnumerable<SourceFile> sources)
498 {
499 this.SourceFiles = sources;
500 }
501
502 public IEnumerable<SourceFile> SourceFiles { get; private set; }
503 } 42 }
504#endif
505 } 43 }
506} 44}
diff --git a/src/wix/X_CommandLine.cs b/src/wix/X_CommandLine.cs
deleted file mode 100644
index 2c7ceb83..00000000
--- a/src/wix/X_CommandLine.cs
+++ /dev/null
@@ -1,394 +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
3namespace 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.Extensibility;
12
13 internal class X_CommandLine
14 {
15 private X_CommandLine()
16 {
17 }
18
19 public static string ExpectedArgument { get; } = "expected argument";
20
21 public string ActiveCommand { get; private set; }
22
23 public string[] OriginalArguments { get; private set; }
24
25 public Queue<string> RemainingArguments { get; } = new Queue<string>();
26
27 public ExtensionManager ExtensionManager { get; } = new ExtensionManager();
28
29 public string ErrorArgument { get; set; }
30
31 public bool ShowHelp { get; set; }
32
33 public static X_CommandLine Parse(string commandLineString, Func<X_CommandLine, string, bool> parseArgument)
34 {
35 var arguments = X_CommandLine.ParseArgumentsToArray(commandLineString).ToArray();
36
37 return X_CommandLine.Parse(arguments, null, parseArgument);
38 }
39
40 public static X_CommandLine Parse(string[] commandLineArguments, Func<X_CommandLine, string, bool> parseArgument)
41 {
42 return X_CommandLine.Parse(commandLineArguments, null, parseArgument);
43 }
44
45 public static X_CommandLine Parse(string[] commandLineArguments, Func<X_CommandLine, string, bool> parseCommand, Func<X_CommandLine, string, bool> parseArgument)
46 {
47 var cmdline = new X_CommandLine();
48
49 cmdline.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments);
50
51 cmdline.QueueArgumentsAndLoadExtensions(cmdline.OriginalArguments);
52
53 cmdline.ProcessRemainingArguments(parseArgument, parseCommand);
54
55 return cmdline;
56 }
57
58 /// <summary>
59 /// Get a set of files that possibly have a search pattern in the path (such as '*').
60 /// </summary>
61 /// <param name="searchPath">Search path to find files in.</param>
62 /// <param name="fileType">Type of file; typically "Source".</param>
63 /// <returns>An array of files matching the search path.</returns>
64 /// <remarks>
65 /// This method is written in this verbose way because it needs to support ".." in the path.
66 /// It needs the directory path isolated from the file name in order to use Directory.GetFiles
67 /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
68 /// Path.GetDirectoryName does not support ".." in the path.
69 /// </remarks>
70 /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception>
71 public string[] GetFiles(string searchPath, string fileType)
72 {
73 if (null == searchPath)
74 {
75 throw new ArgumentNullException(nameof(searchPath));
76 }
77
78 // Convert alternate directory separators to the standard one.
79 string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
80 int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
81 string[] files = null;
82
83 try
84 {
85 if (0 > lastSeparator)
86 {
87 files = Directory.GetFiles(".", filePath);
88 }
89 else // found directory separator
90 {
91 files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
92 }
93 }
94 catch (DirectoryNotFoundException)
95 {
96 // Don't let this function throw the DirectoryNotFoundException. This exception
97 // occurs for non-existant directories and invalid characters in the searchPattern.
98 }
99 catch (ArgumentException)
100 {
101 // Don't let this function throw the ArgumentException. This exception
102 // occurs in certain situations such as when passing a malformed UNC path.
103 }
104 catch (IOException)
105 {
106 throw new WixFileNotFoundException(searchPath, fileType);
107 }
108
109 if (null == files || 0 == files.Length)
110 {
111 throw new WixFileNotFoundException(searchPath, fileType);
112 }
113
114 return files;
115 }
116
117 /// <summary>
118 /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity
119 /// </summary>
120 /// <param name="args">The list of strings to check.</param>
121 /// <param name="index">The index (in args) of the commandline parameter to be validated.</param>
122 /// <returns>True if a valid switch exists there, false if not.</returns>
123 public bool IsSwitch(string arg)
124 {
125 return arg != null && ('/' == arg[0] || '-' == arg[0]);
126 }
127
128 /// <summary>
129 /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity
130 /// </summary>
131 /// <param name="args">The list of strings to check.</param>
132 /// <param name="index">The index (in args) of the commandline parameter to be validated.</param>
133 /// <returns>True if a valid switch exists there, false if not.</returns>
134 public bool IsSwitchAt(IEnumerable<string> args, int index)
135 {
136 var arg = args.ElementAtOrDefault(index);
137 return IsSwitch(arg);
138 }
139
140 public void GetNextArgumentOrError(ref string arg)
141 {
142 this.TryGetNextArgumentOrError(out arg);
143 }
144
145 public void GetNextArgumentOrError(IList<string> args)
146 {
147 if (this.TryGetNextArgumentOrError(out var arg))
148 {
149 args.Add(arg);
150 }
151 }
152
153 public void GetNextArgumentAsFilePathOrError(IList<string> args, string fileType)
154 {
155 if (this.TryGetNextArgumentOrError(out var arg))
156 {
157 foreach (var path in this.GetFiles(arg, fileType))
158 {
159 args.Add(path);
160 }
161 }
162 }
163
164 public bool TryGetNextArgumentOrError(out string arg)
165 {
166 if (this.RemainingArguments.TryDequeue(out arg) && !this.IsSwitch(arg))
167 {
168 return true;
169 }
170
171 this.ErrorArgument = arg ?? X_CommandLine.ExpectedArgument;
172
173 return false;
174 }
175
176 private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments)
177 {
178 List<string> args = new List<string>();
179
180 foreach (var arg in commandLineArguments)
181 {
182 if ('@' == arg[0])
183 {
184 var responseFileArguments = X_CommandLine.ParseResponseFile(arg.Substring(1));
185 args.AddRange(responseFileArguments);
186 }
187 else
188 {
189 args.Add(arg);
190 }
191 }
192
193 this.OriginalArguments = args.ToArray();
194 }
195
196 private void QueueArgumentsAndLoadExtensions(string[] args)
197 {
198 for (var i = 0; i < args.Length; ++i)
199 {
200 var arg = args[i];
201
202 if ("-ext" == arg || "/ext" == arg)
203 {
204 if (!this.IsSwitchAt(args, ++i))
205 {
206 this.ExtensionManager.Load(args[i]);
207 }
208 else
209 {
210 this.ErrorArgument = arg;
211 break;
212 }
213 }
214 else
215 {
216 this.RemainingArguments.Enqueue(arg);
217 }
218 }
219 }
220
221 private void ProcessRemainingArguments(Func<X_CommandLine, string, bool> parseArgument, Func<X_CommandLine, string, bool> parseCommand)
222 {
223 var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
224
225 while (!this.ShowHelp &&
226 String.IsNullOrEmpty(this.ErrorArgument) &&
227 this.RemainingArguments.TryDequeue(out var arg))
228 {
229 if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments.
230 {
231 continue;
232 }
233
234 if ('-' == arg[0] || '/' == arg[0])
235 {
236 if (!parseArgument(this, arg) &&
237 !this.TryParseCommandLineArgumentWithExtension(arg, extensions))
238 {
239 this.ErrorArgument = arg;
240 }
241 }
242 else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported.
243 {
244 if (parseCommand(this, arg))
245 {
246 this.ActiveCommand = arg;
247 }
248 else
249 {
250 this.ErrorArgument = arg;
251 }
252 }
253 else if (!this.TryParseCommandLineArgumentWithExtension(arg, extensions) &&
254 !parseArgument(this, arg))
255 {
256 this.ErrorArgument = arg;
257 }
258 }
259 }
260
261 private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable<IExtensionCommandLine> extensions)
262 {
263 foreach (var extension in extensions)
264 {
265 //if (extension.ParseArgument(this, arg))
266 //{
267 // return true;
268 //}
269 }
270
271 return false;
272 }
273
274 /// <summary>
275 /// Parses a response file.
276 /// </summary>
277 /// <param name="responseFile">The file to parse.</param>
278 /// <returns>The array of arguments.</returns>
279 private static List<string> ParseResponseFile(string responseFile)
280 {
281 string arguments;
282
283 using (StreamReader reader = new StreamReader(responseFile))
284 {
285 arguments = reader.ReadToEnd();
286 }
287
288 return X_CommandLine.ParseArgumentsToArray(arguments);
289 }
290
291 /// <summary>
292 /// Parses an argument string into an argument array based on whitespace and quoting.
293 /// </summary>
294 /// <param name="arguments">Argument string.</param>
295 /// <returns>Argument array.</returns>
296 private static List<string> ParseArgumentsToArray(string arguments)
297 {
298 // Scan and parse the arguments string, dividing up the arguments based on whitespace.
299 // Unescaped quotes cause whitespace to be ignored, while the quotes themselves are removed.
300 // Quotes may begin and end inside arguments; they don't necessarily just surround whole arguments.
301 // Escaped quotes and escaped backslashes also need to be unescaped by this process.
302
303 // Collects the final list of arguments to be returned.
304 var argsList = new List<string>();
305
306 // True if we are inside an unescaped quote, meaning whitespace should be ignored.
307 var insideQuote = false;
308
309 // Index of the start of the current argument substring; either the start of the argument
310 // or the start of a quoted or unquoted sequence within it.
311 var partStart = 0;
312
313 // The current argument string being built; when completed it will be added to the list.
314 var arg = new StringBuilder();
315
316 for (int i = 0; i <= arguments.Length; i++)
317 {
318 if (i == arguments.Length || (Char.IsWhiteSpace(arguments[i]) && !insideQuote))
319 {
320 // Reached a whitespace separator or the end of the string.
321
322 // Finish building the current argument.
323 arg.Append(arguments.Substring(partStart, i - partStart));
324
325 // Skip over the whitespace character.
326 partStart = i + 1;
327
328 // Add the argument to the list if it's not empty.
329 if (arg.Length > 0)
330 {
331 argsList.Add(X_CommandLine.ExpandEnvVars(arg.ToString()));
332 arg.Length = 0;
333 }
334 }
335 else if (i > partStart && arguments[i - 1] == '\\')
336 {
337 // Check the character following an unprocessed backslash.
338 // Unescape quotes, and backslashes followed by a quote.
339 if (arguments[i] == '"' || (arguments[i] == '\\' && arguments.Length > i + 1 && arguments[i + 1] == '"'))
340 {
341 // Unescape the quote or backslash by skipping the preceeding backslash.
342 arg.Append(arguments.Substring(partStart, i - 1 - partStart));
343 arg.Append(arguments[i]);
344 partStart = i + 1;
345 }
346 }
347 else if (arguments[i] == '"')
348 {
349 // Add the quoted or unquoted section to the argument string.
350 arg.Append(arguments.Substring(partStart, i - partStart));
351
352 // And skip over the quote character.
353 partStart = i + 1;
354
355 insideQuote = !insideQuote;
356 }
357 }
358
359 return argsList;
360 }
361
362 /// <summary>
363 /// Expand enxironment variables contained in the passed string
364 /// </summary>
365 /// <param name="arguments"></param>
366 /// <returns></returns>
367 private static string ExpandEnvVars(string arguments)
368 {
369 var id = Environment.GetEnvironmentVariables();
370
371 var regex = new Regex("(?<=\\%)(?:[\\w\\.]+)(?=\\%)");
372 MatchCollection matches = regex.Matches(arguments);
373
374 string value = String.Empty;
375 for (int i = 0; i <= (matches.Count - 1); i++)
376 {
377 try
378 {
379 var key = matches[i].Value;
380 regex = new Regex(String.Concat("(?i)(?:\\%)(?:", key, ")(?:\\%)"));
381 value = id[key].ToString();
382 arguments = regex.Replace(arguments, value);
383 }
384 catch (NullReferenceException)
385 {
386 // Collapse unresolved environment variables.
387 arguments = regex.Replace(arguments, value);
388 }
389 }
390
391 return arguments;
392 }
393 }
394}