Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / tools / msbuild / Parameters.cs
1 //
2 // Parameters.cs: Class that contains information about command line parameters
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 using Microsoft.Build.Logging;
28
29 #if NET_2_0
30
31 using System;
32 using System.IO;
33 using System.Collections;
34 using System.Collections.Generic;
35 using System.Linq;
36 using System.Text;
37 using System.Reflection;
38 using Microsoft.Build.BuildEngine;
39 using Microsoft.Build.Framework;
40 using Microsoft.Build.Utilities;
41
42 namespace Mono.XBuild.CommandLine {
43         public class Parameters {
44         
45                 string                  consoleLoggerParameters;
46                 bool                    displayHelp;
47                 IList                   flatArguments;
48                 IList<LoggerInfo>       loggerInfos;
49                 LoggerVerbosity         loggerVerbosity;
50                 bool                    noConsoleLogger;
51                 bool                    noLogo;
52                 string                  projectFile;
53                 Dictionary<string,string>       properties;
54                 IList                   remainingArguments;
55                 Hashtable               responseFiles;
56                 string[]                targets;
57                 bool                    validate;
58                 string                  validationSchema;
59                 string                  toolsVersion;
60                 
61                 string                  responseFile;
62         
63                 public Parameters ()
64                 {
65                         consoleLoggerParameters = "";
66                         displayHelp = false;
67                         loggerInfos = new List<LoggerInfo> ();
68                         loggerVerbosity = LoggerVerbosity.Normal;
69                         noConsoleLogger = false;
70                         noLogo = false;
71                         properties = new Dictionary<string,string> ();
72                         targets = new string [0];
73                         
74                         responseFile = Path.Combine (
75                                         Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location),
76                                         "msbuild.rsp");
77                 }
78                 
79                 public void ParseArguments (string[] args)
80                 {
81                         bool autoResponse = true;
82                         flatArguments = new ArrayList ();
83                         remainingArguments = new ArrayList ();
84                         responseFiles = new Hashtable ();
85                         FileLoggerParameters = new string[10];
86                         foreach (string s in args) {
87                                 if (s.StartsWith ("/noautoresponse") || s.StartsWith ("/noautorsp")) {
88                                         autoResponse = false;
89                                         continue;
90                                 }
91                                 if (s [0] != '@') {
92                                         flatArguments.Add (s);
93                                         continue;
94                                 }
95                                 string responseFilename = Path.GetFullPath (UnquoteIfNeeded (s.Substring (1)));
96                                 if (responseFiles.ContainsKey (responseFilename))
97                                         ReportError (1, String.Format ("We already have {0} file.", responseFilename));
98                                 responseFiles [responseFilename] = responseFilename;
99                                 LoadResponseFile (responseFilename);
100                         }
101                         if (autoResponse == true) {
102                                 // FIXME: we do not allow nested auto response file
103                                 LoadResponseFile (responseFile);
104                         }
105                         foreach (string s in flatArguments) {
106                                 if (s [0] != '/' || !ParseFlatArgument (s))
107                                         remainingArguments.Add (s);
108                         }
109                         if (remainingArguments.Count == 0) {
110                                 string[] sln_files = Directory.GetFiles (Directory.GetCurrentDirectory (), "*.sln");
111                                 string[] proj_files = Directory.GetFiles (Directory.GetCurrentDirectory (), "*proj");
112
113                                 if (sln_files.Length == 0 && proj_files.Length == 0)
114                                         ReportError (3, "Please specify the project or solution file " +
115                                                         "to build, as none was found in the current directory.");
116
117                                 if (sln_files.Length == 1 && proj_files.Length > 0) {
118                                         var projects_table = new Dictionary<string, string> ();
119                                         foreach (string pfile in SolutionParser.GetAllProjectFileNames (sln_files [0])) {
120                                                 string full_path = Path.GetFullPath (pfile);
121                                                 projects_table [full_path] = full_path;
122                                         }
123
124                                         if (!proj_files.Any (p => !projects_table.ContainsKey (Path.GetFullPath (p))))
125                                                 // if all the project files in the cur dir, are referenced
126                                                 // from the single .sln in the cur dir, then pick the sln
127                                                 proj_files = new string [0];
128                                 }
129
130                                 if (sln_files.Length + proj_files.Length > 1)
131                                         ReportError (5, "Please specify the project or solution file " +
132                                                         "to build, as more than one solution or project file was found " +
133                                                         "in the current directory");
134
135                                 if (sln_files.Length == 1)
136                                         projectFile = sln_files [0];
137                                 else
138                                         projectFile = proj_files [0];
139                         } else if (remainingArguments.Count == 1) {
140                                 projectFile = (string) remainingArguments [0];
141                         } else {
142                                 ReportError (4, "Too many project files specified");
143                         }
144                 }
145
146                 private string UnquoteIfNeeded(string arg)
147                 {
148                         if (arg.StartsWith("\""))
149                                 return arg.Substring(1, arg.Length - 2);
150                         return arg;
151                 }
152
153                 void LoadResponseFile (string filename)
154                 {
155                         StreamReader sr = null;
156                         string line;
157                         try {
158                                 sr = new StreamReader (filename);
159                                 StringBuilder sb = new StringBuilder ();
160
161                                 while ((line = sr.ReadLine ()) != null) {
162                                         int t = line.Length;
163
164                                         for (int i = 0; i < t; i++) {
165                                                 char c = line [i];
166
167                                                 if (c == '#')
168                                                         // comment, ignore rest of the line
169                                                         break;
170
171                                                 if (c == '"' || c == '\'') {
172                                                         char end = c;
173
174                                                         for (i++; i < t; i++) {
175                                                                 c = line [i];
176
177                                                                 if (c == end)
178                                                                         break;
179                                                                 sb.Append (c);
180                                                         }
181                                                 } else if (c == ' ') {
182                                                         if (sb.Length > 0) {
183                                                                 flatArguments.Add (sb.ToString ());
184                                                                 sb.Length = 0;
185                                                         }
186                                                 } else
187                                                         sb.Append (c);
188                                         }
189                                         if (sb.Length > 0){
190                                                 flatArguments.Add (sb.ToString ());
191                                                 sb.Length = 0;
192                                         }
193                                 }
194                         } catch (IOException x) {
195                                 ErrorUtilities.ReportWarning (2, String.Format (
196                                                         "Error loading response file. (Exception: {0}). Ignoring.",
197                                                         x.Message));
198                         } finally {
199                                 if (sr != null)
200                                         sr.Close ();
201                         }
202                 }
203                 
204                 private bool ParseFlatArgument (string s)
205                 {
206                         switch (s) {
207                         case "/help":
208                         case "/h":
209                         case "/?":
210                                 ErrorUtilities.ShowUsage ();
211                                 break;
212                         case "/nologo":
213                                 noLogo = true;
214                                 break;
215                         case "/version":
216                         case "/ver":
217                                 ErrorUtilities.ShowVersion (true);
218                                 break;
219                         case "/noconsolelogger":
220                         case "/noconlog":
221                                 noConsoleLogger = true;
222                                 break;
223                         case "/validate":
224                         case "/val":
225                                 validate = true;
226                                 break;
227                         case "/fl":
228                         case "/filelogger":
229                                 if (FileLoggerParameters [0] == null)
230                                         FileLoggerParameters [0] = String.Empty;
231                                 break;
232                         default:
233                                 if (s.StartsWith ("/fl") && s.Length == 4 && Char.IsDigit (s[3])) {
234                                         int index = Int32.Parse (s[3].ToString ());
235                                         if (FileLoggerParameters [index] == null)
236                                                 FileLoggerParameters [index] = String.Empty;
237                                 } else if (s.StartsWith ("/fileloggerparameters") || s.StartsWith ("/flp")) {
238                                         ProcessFileLoggerParameters (s);
239                                 } else if (s.StartsWith ("/target:") || s.StartsWith ("/t:")) {
240                                         ProcessTarget (s);
241                                 } else if (s.StartsWith ("/property:") || s.StartsWith ("/p:")) {
242                                         if (!ProcessProperty (s))
243                                                 return false;
244                                 } else  if (s.StartsWith ("/logger:") || s.StartsWith ("/l:")) {
245                                         ProcessLogger (s);
246                                 } else if (s.StartsWith ("/verbosity:") || s.StartsWith ("/v:")) {
247                                         ProcessVerbosity (s);
248                                 } else if (s.StartsWith ("/consoleloggerparameters:") || s.StartsWith ("/clp:")) {
249                                         ProcessConsoleLoggerParameters (s);
250                                 } else if (s.StartsWith ("/validate:") || s.StartsWith ("/val:")) {
251                                         ProcessValidate (s);
252                                 } else if (s.StartsWith ("/toolsversion:") || s.StartsWith ("/tv:")) {
253                                         ToolsVersion = s.Split (':') [1];
254                                 } else
255                                         return false;
256                                 break;
257                         }
258
259                         return true;
260                 }
261                 
262                 internal void ProcessTarget (string s)
263                 {
264                         TryProcessMultiOption (s, "Target names must be specified as /t:Target1;Target2",
265                                                 out targets);
266                 }
267                 
268                 internal bool ProcessProperty (string s)
269                 {
270                         string[] splitProperties;
271                         if (!TryProcessMultiOption (s, "Property name and value expected as /p:<prop name>=<prop value>",
272                                                 out splitProperties))
273                                 return false;
274
275                         foreach (string st in splitProperties) {
276                                 if (st.IndexOf ('=') < 0) {
277                                         ReportError (5,
278                                                         "Invalid syntax. Property name and value expected as " +
279                                                         "<prop name>=[<prop value>]");
280                                         return false;
281                                 }
282                                 string [] property = st.Split ('=');
283                                 properties.Add (property [0], property.Length == 2 ? property [1] : "");
284                         }
285
286                         return true;
287                 }
288
289                 bool TryProcessMultiOption (string s, string error_message, out string[] values)
290                 {
291                         values = null;
292                         int colon = s.IndexOf (':');
293                         if (colon + 1 == s.Length) {
294                                 ReportError (5, error_message);
295                                 return false;
296                         }
297
298                         values = s.Substring (colon + 1).Split (';');
299                         return true;
300                 }
301
302                 private void ReportError (int errorCode, string message)
303                 {
304                         throw new CommandLineException (message, errorCode);
305                 }
306
307                 private void ReportError (int errorCode, string message, Exception cause)
308                 {
309                         throw new CommandLineException (message, cause, errorCode);
310                 }
311
312                 internal void ProcessLogger (string s)
313                 {
314                         loggerInfos.Add (new LoggerInfo (s));
315                 }
316                 
317                 internal void ProcessVerbosity (string s)
318                 {
319                         string[] temp = s.Split (':');
320                         switch (temp [1]) {
321                         case "q":
322                         case "quiet":
323                                 loggerVerbosity = LoggerVerbosity.Quiet;
324                                 break;
325                         case "m":
326                         case "minimal":
327                                 loggerVerbosity = LoggerVerbosity.Minimal;
328                                 break;
329                         case "n":
330                         case "normal":
331                                 loggerVerbosity = LoggerVerbosity.Normal;
332                                 break;
333                         case "d":
334                         case "detailed":
335                                 loggerVerbosity = LoggerVerbosity.Detailed;
336                                 break;
337                         case "diag":
338                         case "diagnostic":
339                                 loggerVerbosity = LoggerVerbosity.Diagnostic;
340                                 break;
341                         }
342                 }
343
344                 void ProcessFileLoggerParameters (string s)
345                 {
346                         int colon = s.IndexOf (':');
347                         if (colon + 1 == s.Length)
348                                 ReportError (5, "Invalid syntax, specify parameters as /fileloggerparameters[n]:parameters");
349
350                         int index = 0;
351                         string key = s.Substring (0, colon);
352                         if (Char.IsDigit (key [key.Length - 1]))
353                         //if (key.Length == 22 && Char.IsDigit (key [21]))
354                                 index = Int32.Parse (key [key.Length - 1].ToString ());
355
356                         FileLoggerParameters [index] = s.Substring (colon + 1);
357                 }
358
359                 internal void ProcessConsoleLoggerParameters (string s)
360                 {
361                         int colon = s.IndexOf (':');
362                         if (colon + 1 == s.Length)
363                                 ReportError (5, "Invalid syntax, specify parameters as /clp:parameters");
364
365                         consoleLoggerParameters = s.Substring (colon + 1);
366                 }
367                 
368                 internal void ProcessValidate (string s)
369                 {
370                         string[] temp;
371                         validate = true;
372                         temp = s.Split (':');
373                         validationSchema = temp [1];
374                 }
375                 public bool DisplayHelp {
376                         get { return displayHelp; }
377                 }
378                 
379                 public bool NoLogo {
380                         get { return noLogo; }
381                 }
382                 
383                 public string ProjectFile {
384                         get { return projectFile; }
385                 }
386                 
387                 public string[] Targets {
388                         get { return targets; }
389                 }
390                 
391                 public Dictionary<string,string> Properties {
392                         get { return properties; }
393                 }
394                 
395                 IList<ILogger> loggers;
396                 public IList<ILogger> Loggers {
397                         get {
398                                 if (loggers == null)
399                                         loggers = loggerInfos.Select (li => new LoggerDescription (li.ClassName, li.AssemblyNameString, li.Filename, li.Parameters, LoggerVerbosity).CreateLogger ()).ToArray ();
400                                 return loggers; 
401                         }
402                 }
403                 
404                 public LoggerVerbosity LoggerVerbosity {
405                         get { return loggerVerbosity; }
406                 }
407                 
408                 public string ConsoleLoggerParameters {
409                         get { return consoleLoggerParameters; }
410                 }
411                 
412                 public bool NoConsoleLogger {
413                         get { return noConsoleLogger; }
414                 }
415
416                 public string[] FileLoggerParameters { get; set; }
417                 
418                 public bool Validate {
419                         get { return validate; }
420                 }
421                 
422                 public string ValidationSchema {
423                         get { return validationSchema; }
424                 }
425
426                 public string ToolsVersion {
427                         get { return toolsVersion; }
428                         private set { toolsVersion = value; }
429                 }
430                 
431         }
432 }
433
434 #endif