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