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