Less static is good for my health
[mono.git] / mcs / mcs / rootcontext.cs
1 //
2 // rootcontext.cs: keeps track of our tree representation, and assemblies loaded.
3 //
4 // Author: Miguel de Icaza (miguel@ximian.com)
5 //            Ravi Pratap  (ravi@ximian.com)
6 //            Marek Safar  (marek.safar@gmail.com)
7 //
8 //
9 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 //
11 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
12 // Copyright 2004-2008 Novell, Inc
13
14 using System.Collections.Generic;
15 using System.IO;
16 using System.Text;
17 using System.Globalization;
18 using System;
19
20 namespace Mono.CSharp {
21
22         public enum LanguageVersion
23         {
24                 ISO_1 = 1,
25                 ISO_2 = 2,
26                 V_3 = 3,
27                 V_4 = 4,
28                 Future = 100,
29
30                 Default = LanguageVersion.V_4,
31         }
32
33         public enum RuntimeVersion
34         {
35                 v1,
36                 v2,
37                 v4
38         }
39
40         public enum Target
41         {
42                 Library, Exe, Module, WinExe
43         }
44
45         public enum Platform
46         {
47                 AnyCPU, X86, X64, IA64
48         }
49
50         public class CompilerSettings
51         {
52                 public Target Target;
53                 public Platform Platform;
54                 public string TargetExt;
55                 public bool VerifyClsCompliance;
56                 public bool Optimize;
57                 public LanguageVersion Version;
58                 public bool EnhancedWarnings;
59                 public bool LoadDefaultReferences;
60                 public string SdkVersion;
61
62                 public string StrongNameKeyFile;
63                 public string StrongNameKeyContainer;
64                 public bool StrongNameDelaySign;
65
66                 //
67                 // Assemblies references to be loaded
68                 //
69                 public List<string> AssemblyReferences;
70
71                 // 
72                 // External aliases for assemblies
73                 //
74                 public List<Tuple<string, string>> AssemblyReferencesAliases;
75
76                 //
77                 // Modules to be embedded
78                 //
79                 public List<string> Modules;
80
81                 //
82                 // Lookup paths for referenced assemblies
83                 //
84                 public List<string> ReferencesLookupPaths;
85
86                 //
87                 // Encoding.
88                 //
89                 public Encoding Encoding;
90
91                 //
92                 // If set, enable XML documentation generation
93                 //
94                 public Documentation Documentation;
95
96                 public string MainClass;
97
98                 //
99                 // Output file
100                 //
101                 public string OutputFile;
102
103                 // 
104                 // The default compiler checked state
105                 //
106                 public bool Checked;
107
108                 //
109                 // If true, the compiler is operating in statement mode,
110                 // this currently turns local variable declaration into
111                 // static variables of a class
112                 //
113                 public bool StatementMode;      // TODO: SUPER UGLY
114                 
115                 //
116                 // Whether to allow Unsafe code
117                 //
118                 public bool Unsafe;
119
120                 public string Win32ResourceFile;
121                 public string Win32IconFile;
122
123                 //
124                 // A list of resource files for embedding
125                 //
126                 public List<AssemblyResource> Resources;
127
128                 public bool GenerateDebugInfo;
129
130                 // Compiler debug flags only
131                 public bool ParseOnly, TokenizeOnly, Timestamps;
132
133                 //
134                 // Whether we are being linked against the standard libraries.
135                 // This is only used to tell whether `System.Object' should
136                 // have a base class or not.
137                 //
138                 public bool StdLib;
139
140                 public RuntimeVersion StdLibRuntimeVersion;
141
142                 public CompilerSettings ()
143                 {
144                         StdLib = true;
145                         Target = Target.Exe;
146                         TargetExt = ".exe";
147                         Platform = Platform.AnyCPU;
148                         Version = LanguageVersion.Default;
149                         VerifyClsCompliance = true;
150                         Optimize = true;
151                         Encoding = Encoding.Default;
152                         LoadDefaultReferences = true;
153                         StdLibRuntimeVersion = RuntimeVersion.v4;
154
155                         AssemblyReferences = new List<string> ();
156                         AssemblyReferencesAliases = new List<Tuple<string, string>> ();
157                         Modules = new List<string> ();
158                         ReferencesLookupPaths = new List<string> ();
159                 }
160
161                 public bool HasKeyFileOrContainer {
162                         get {
163                                 return StrongNameKeyFile != null || StrongNameKeyContainer != null;
164                         }
165                 }
166
167                 public bool NeedsEntryPoint {
168                         get {
169                                 return Target == Target.Exe || Target == Target.WinExe;
170                         }
171                 }
172         }
173
174         class CommandLineParser
175         {
176                 enum ParseResult
177                 {
178                         Success,
179                         Error,
180                         Stop,
181                         UnknownOption
182                 }
183
184                 static readonly char[] argument_value_separator = new char[] { ';', ',' };
185
186                 readonly Report report;
187                 readonly TextWriter output;
188
189                 public event Func<string[], int, int> UnknownOptionHandler;
190
191                 public CommandLineParser (Report report)
192                         : this (report, Console.Out)
193                 {
194                 }
195
196                 public CommandLineParser (Report report, TextWriter messagesOutput)
197                 {
198                         this.report = report;
199                         this.output = messagesOutput;
200                 }
201
202                 void About ()
203                 {
204                         output.WriteLine (
205                                 "The Mono C# compiler is Copyright 2001-2011, Novell, Inc.\n\n" +
206                                 "The compiler source code is released under the terms of the \n" +
207                                 "MIT X11 or GNU GPL licenses\n\n" +
208
209                                 "For more information on Mono, visit the project Web site\n" +
210                                 "   http://www.mono-project.com\n\n" +
211
212                                 "The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig, Marek Safar, Raja R Harinath, Atushi Enomoto");
213                 }
214
215                 public CompilerSettings ParseArguments (string[] args)
216                 {
217                         CompilerSettings settings = new CompilerSettings ();
218                         List<string> response_file_list = null;
219                         bool parsing_options = true;
220
221                         for (int i = 0; i < args.Length; i++) {
222                                 string arg = args[i];
223                                 if (arg.Length == 0)
224                                         continue;
225
226                                 if (arg[0] == '@') {
227                                         string[] extra_args;
228                                         string response_file = arg.Substring (1);
229
230                                         if (response_file_list == null)
231                                                 response_file_list = new List<string> ();
232
233                                         if (response_file_list.Contains (response_file)) {
234                                                 report.Error (1515, "Response file `{0}' specified multiple times", response_file);
235                                                 return null;
236                                         }
237
238                                         response_file_list.Add (response_file);
239
240                                         extra_args = LoadArgs (response_file);
241                                         if (extra_args == null) {
242                                                 report.Error (2011, "Unable to open response file: " + response_file);
243                                                 return null;
244                                         }
245
246                                         args = AddArgs (args, extra_args);
247                                         continue;
248                                 }
249
250                                 if (parsing_options) {
251                                         if (arg == "--") {
252                                                 parsing_options = false;
253                                                 continue;
254                                         }
255
256                                         bool dash_opt = arg[0] == '-';
257                                         bool slash_opt = arg[0] == '/';
258                                         if (dash_opt) {
259                                                 switch (ParseOptionUnix (arg, ref args, ref i, settings)) {
260                                                 case ParseResult.Error:
261                                                 case ParseResult.Success:
262                                                         continue;
263                                                 case ParseResult.Stop:
264                                                         return null;
265                                                 }
266                                         }
267
268                                         if (dash_opt || slash_opt) {
269                                                 // Try a -CSCOPTION
270                                                 string csc_opt = dash_opt ? "/" + arg.Substring (1) : arg;
271                                                 switch (ParseOption (csc_opt, ref args, settings)) {
272                                                 case ParseResult.Error:
273                                                 case ParseResult.Success:
274                                                         continue;
275                                                 case ParseResult.UnknownOption:
276                                                         if (UnknownOptionHandler != null) {
277                                                                 var ret = UnknownOptionHandler (args, i);
278                                                                 if (ret != -1) {
279                                                                         i = ret;
280                                                                         continue;
281                                                                 }
282                                                         }
283
284                                                         Error_WrongOption (arg);
285                                                         return null;
286
287                                                 case ParseResult.Stop:
288                                                         return null;
289                                                 }
290                                         }
291                                         
292                                         if (slash_opt) {
293                                                 // Need to skip `/home/test.cs' however /test.cs is considered as error
294                                                 if (arg.Length < 2 || arg.IndexOf ('/', 2) == -1) {
295                                                         Error_WrongOption (arg);
296                                                         return null;
297                                                 }
298                                         }
299                                 }
300
301                                 ProcessSourceFiles (arg, false);
302                         }
303
304                         return settings;
305                 }
306
307                 void ProcessSourceFiles (string spec, bool recurse)
308                 {
309                         string path, pattern;
310
311                         SplitPathAndPattern (spec, out path, out pattern);
312                         if (pattern.IndexOf ('*') == -1) {
313                                 AddSourceFile (spec);
314                                 return;
315                         }
316
317                         string[] files = null;
318                         try {
319                                 files = Directory.GetFiles (path, pattern);
320                         } catch (System.IO.DirectoryNotFoundException) {
321                                 report.Error (2001, "Source file `" + spec + "' could not be found");
322                                 return;
323                         } catch (System.IO.IOException) {
324                                 report.Error (2001, "Source file `" + spec + "' could not be found");
325                                 return;
326                         }
327                         foreach (string f in files) {
328                                 AddSourceFile (f);
329                         }
330
331                         if (!recurse)
332                                 return;
333
334                         string[] dirs = null;
335
336                         try {
337                                 dirs = Directory.GetDirectories (path);
338                         } catch {
339                         }
340
341                         foreach (string d in dirs) {
342
343                                 // Don't include path in this string, as each
344                                 // directory entry already does
345                                 ProcessSourceFiles (d + "/" + pattern, true);
346                         }
347                 }
348
349                 static string[] AddArgs (string[] args, string[] extra_args)
350                 {
351                         string[] new_args;
352                         new_args = new string[extra_args.Length + args.Length];
353
354                         // if args contains '--' we have to take that into account
355                         // split args into first half and second half based on '--'
356                         // and add the extra_args before --
357                         int split_position = Array.IndexOf (args, "--");
358                         if (split_position != -1) {
359                                 Array.Copy (args, new_args, split_position);
360                                 extra_args.CopyTo (new_args, split_position);
361                                 Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
362                         } else {
363                                 args.CopyTo (new_args, 0);
364                                 extra_args.CopyTo (new_args, args.Length);
365                         }
366
367                         return new_args;
368                 }
369
370                 void AddAssemblyReference (string alias, string assembly, CompilerSettings settings)
371                 {
372                         if (assembly.Length == 0) {
373                                 report.Error (1680, "Invalid reference alias `{0}='. Missing filename", alias);
374                                 return;
375                         }
376
377                         if (!IsExternAliasValid (alias)) {
378                                 report.Error (1679, "Invalid extern alias for -reference. Alias `{0}' is not a valid identifier", alias);
379                                 return;
380                         }
381
382                         settings.AssemblyReferencesAliases.Add (Tuple.Create (alias, assembly));
383                 }
384
385                 void AddResource (AssemblyResource res, CompilerSettings settings)
386                 {
387                         if (settings.Resources == null) {
388                                 settings.Resources = new List<AssemblyResource> ();
389                                 settings.Resources.Add (res);
390                                 return;
391                         }
392
393                         if (settings.Resources.Contains (res)) {
394                                 report.Error (1508, "The resource identifier `{0}' has already been used in this assembly", res.Name);
395                                 return;
396                         }
397
398                         settings.Resources.Add (res);
399                 }
400
401                 void AddSourceFile (string f)
402                 {
403                         Location.AddFile (report, f);
404                 }
405
406
407                 void Error_RequiresArgument (string option)
408                 {
409                         report.Error (2006, "Missing argument for `{0}' option", option);
410                 }
411
412                 void Error_RequiresFileName (string option)
413                 {
414                         report.Error (2005, "Missing file specification for `{0}' option", option);
415                 }
416
417                 void Error_WrongOption (string option)
418                 {
419                         report.Error (2007, "Unrecognized command-line option: `{0}'", option);
420                 }
421
422                 static bool IsExternAliasValid (string identifier)
423                 {
424                         if (identifier.Length == 0)
425                                 return false;
426                         if (identifier[0] != '_' && !char.IsLetter (identifier[0]))
427                                 return false;
428
429                         for (int i = 1; i < identifier.Length; i++) {
430                                 char c = identifier[i];
431                                 if (char.IsLetter (c) || char.IsDigit (c))
432                                         continue;
433
434                                 UnicodeCategory category = char.GetUnicodeCategory (c);
435                                 if (category != UnicodeCategory.Format || category != UnicodeCategory.NonSpacingMark ||
436                                                 category != UnicodeCategory.SpacingCombiningMark ||
437                                                 category != UnicodeCategory.ConnectorPunctuation)
438                                         return false;
439                         }
440
441                         return true;
442                 }
443
444                 static string[] LoadArgs (string file)
445                 {
446                         StreamReader f;
447                         var args = new List<string> ();
448                         string line;
449                         try {
450                                 f = new StreamReader (file);
451                         } catch {
452                                 return null;
453                         }
454
455                         StringBuilder sb = new StringBuilder ();
456
457                         while ((line = f.ReadLine ()) != null) {
458                                 int t = line.Length;
459
460                                 for (int i = 0; i < t; i++) {
461                                         char c = line[i];
462
463                                         if (c == '"' || c == '\'') {
464                                                 char end = c;
465
466                                                 for (i++; i < t; i++) {
467                                                         c = line[i];
468
469                                                         if (c == end)
470                                                                 break;
471                                                         sb.Append (c);
472                                                 }
473                                         } else if (c == ' ') {
474                                                 if (sb.Length > 0) {
475                                                         args.Add (sb.ToString ());
476                                                         sb.Length = 0;
477                                                 }
478                                         } else
479                                                 sb.Append (c);
480                                 }
481                                 if (sb.Length > 0) {
482                                         args.Add (sb.ToString ());
483                                         sb.Length = 0;
484                                 }
485                         }
486
487                         return args.ToArray ();
488                 }
489
490                 void OtherFlags ()
491                 {
492                         output.WriteLine (
493                                 "Other flags in the compiler\n" +
494                                 "   --fatal[=COUNT]    Makes errors after COUNT fatal\n" +
495                                 "   --lint             Enhanced warnings\n" +
496                                 "   --parse            Only parses the source file\n" +
497                                 "   --runtime:VERSION  Sets mscorlib.dll metadata version: v1, v2, v4\n" +
498                                 "   --stacktrace       Shows stack trace at error location\n" +
499                                 "   --timestamp        Displays time stamps of various compiler events\n" +
500                                 "   -v                 Verbose parsing (for debugging the parser)\n" +
501                                 "   --mcs-debug X      Sets MCS debugging level to X\n");
502                 }
503
504                 //
505                 // This parses the -arg and /arg options to the compiler, even if the strings
506                 // in the following text use "/arg" on the strings.
507                 //
508                 ParseResult ParseOption (string option, ref string[] args, CompilerSettings settings)
509                 {
510                         int idx = option.IndexOf (':');
511                         string arg, value;
512
513                         if (idx == -1) {
514                                 arg = option;
515                                 value = "";
516                         } else {
517                                 arg = option.Substring (0, idx);
518
519                                 value = option.Substring (idx + 1);
520                         }
521
522                         switch (arg.ToLowerInvariant ()) {
523                         case "/nologo":
524                                 return ParseResult.Success;
525
526                         case "/t":
527                         case "/target":
528                                 switch (value) {
529                                 case "exe":
530                                         settings.Target = Target.Exe;
531                                         break;
532
533                                 case "winexe":
534                                         settings.Target = Target.WinExe;
535                                         break;
536
537                                 case "library":
538                                         settings.Target = Target.Library;
539                                         settings.TargetExt = ".dll";
540                                         break;
541
542                                 case "module":
543                                         settings.Target = Target.Module;
544                                         settings.TargetExt = ".netmodule";
545                                         break;
546
547                                 default:
548                                         report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'");
549                                         return ParseResult.Error;
550                                 }
551                                 return ParseResult.Success;
552
553                         case "/out":
554                                 if (value.Length == 0) {
555                                         Error_RequiresFileName (option);
556                                         return ParseResult.Error;
557                                 }
558                                 settings.OutputFile = value;
559                                 return ParseResult.Success;
560
561                         case "/o":
562                         case "/o+":
563                         case "/optimize":
564                         case "/optimize+":
565                                 settings.Optimize = true;
566                                 return ParseResult.Success;
567
568                         case "/o-":
569                         case "/optimize-":
570                                 settings.Optimize = false;
571                                 return ParseResult.Success;
572
573                         // TODO: Not supported by csc 3.5+
574                         case "/incremental":
575                         case "/incremental+":
576                         case "/incremental-":
577                                 // nothing.
578                                 return ParseResult.Success;
579
580                         case "/d":
581                         case "/define": {
582                                         if (value.Length == 0) {
583                                                 Error_RequiresArgument (option);
584                                                 return ParseResult.Error;
585                                         }
586
587                                         foreach (string d in value.Split (argument_value_separator)) {
588                                                 string conditional = d.Trim ();
589                                                 if (!Tokenizer.IsValidIdentifier (conditional)) {
590                                                         report.Warning (2029, 1, "Invalid conditional define symbol `{0}'", conditional);
591                                                         continue;
592                                                 }
593                                                 RootContext.AddConditional (conditional);
594                                         }
595                                         return ParseResult.Success;
596                                 }
597
598                         case "/bugreport":
599                                 //
600                                 // We should collect data, runtime, etc and store in the file specified
601                                 //
602                                 output.WriteLine ("To file bug reports, please visit: http://www.mono-project.com/Bugs");
603                                 return ParseResult.Success;
604
605                         case "/pkg": {
606                                         string packages;
607
608                                         if (value.Length == 0) {
609                                                 Error_RequiresArgument (option);
610                                                 return ParseResult.Error;
611                                         }
612                                         packages = String.Join (" ", value.Split (new Char[] { ';', ',', '\n', '\r' }));
613                                         string pkgout = Driver.GetPackageFlags (packages, true, report);
614
615                                         if (pkgout != null) {
616                                                 string[] xargs = pkgout.Trim (new Char[] { ' ', '\n', '\r', '\t' }).
617                                                         Split (new Char[] { ' ', '\t' });
618                                                 args = AddArgs (args, xargs);
619                                         }
620
621                                         return ParseResult.Success;
622                                 }
623
624                         case "/linkres":
625                         case "/linkresource":
626                         case "/res":
627                         case "/resource":
628                                 AssemblyResource res = null;
629                                 string[] s = value.Split (argument_value_separator, StringSplitOptions.RemoveEmptyEntries);
630                                 switch (s.Length) {
631                                 case 1:
632                                         if (s[0].Length == 0)
633                                                 goto default;
634                                         res = new AssemblyResource (s[0], Path.GetFileName (s[0]));
635                                         break;
636                                 case 2:
637                                         res = new AssemblyResource (s[0], s[1]);
638                                         break;
639                                 case 3:
640                                         if (s[2] != "public" && s[2] != "private") {
641                                                 report.Error (1906, "Invalid resource visibility option `{0}'. Use either `public' or `private' instead", s[2]);
642                                                 return ParseResult.Error;
643                                         }
644                                         res = new AssemblyResource (s[0], s[1], s[2] == "private");
645                                         break;
646                                 default:
647                                         report.Error (-2005, "Wrong number of arguments for option `{0}'", option);
648                                         return ParseResult.Error;
649                                 }
650
651                                 if (res != null) {
652                                         res.IsEmbeded = arg[1] == 'r' || arg[1] == 'R';
653                                         AddResource (res, settings);
654                                 }
655
656                                 return ParseResult.Success;
657
658                         case "/recurse":
659                                 if (value.Length == 0) {
660                                         Error_RequiresFileName (option);
661                                         return ParseResult.Error;
662                                 }
663                                 ProcessSourceFiles (value, true);
664                                 return ParseResult.Success;
665
666                         case "/r":
667                         case "/reference": {
668                                         if (value.Length == 0) {
669                                                 Error_RequiresFileName (option);
670                                                 return ParseResult.Error;
671                                         }
672
673                                         string[] refs = value.Split (argument_value_separator);
674                                         foreach (string r in refs) {
675                                                 if (r.Length == 0)
676                                                         continue;
677
678                                                 string val = r;
679                                                 int index = val.IndexOf ('=');
680                                                 if (index > -1) {
681                                                         string alias = r.Substring (0, index);
682                                                         string assembly = r.Substring (index + 1);
683                                                         AddAssemblyReference (alias, assembly, settings);
684                                                         if (refs.Length != 1) {
685                                                                 report.Error (2034, "Cannot specify multiple aliases using single /reference option");
686                                                                 return ParseResult.Error;
687                                                         }
688                                                 } else {
689                                                         settings.AssemblyReferences.Add (val);
690                                                 }
691                                         }
692                                         return ParseResult.Success;
693                                 }
694                         case "/addmodule": {
695                                         if (value.Length == 0) {
696                                                 Error_RequiresFileName (option);
697                                                 return ParseResult.Error;
698                                         }
699
700                                         string[] refs = value.Split (argument_value_separator);
701                                         foreach (string r in refs) {
702                                                 settings.Modules.Add (r);
703                                         }
704                                         return ParseResult.Success;
705                                 }
706                         case "/win32res": {
707                                         if (value.Length == 0) {
708                                                 Error_RequiresFileName (option);
709                                                 return ParseResult.Error;
710                                         }
711
712                                         if (settings.Win32IconFile != null)
713                                                 report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");
714
715                                         settings.Win32ResourceFile = value;
716                                         return ParseResult.Success;
717                                 }
718                         case "/win32icon": {
719                                         if (value.Length == 0) {
720                                                 Error_RequiresFileName (option);
721                                                 return ParseResult.Error;
722                                         }
723
724                                         if (settings.Win32ResourceFile != null)
725                                                 report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");
726
727                                         settings.Win32IconFile = value;
728                                         return ParseResult.Success;
729                                 }
730                         case "/doc": {
731                                         if (value.Length == 0) {
732                                                 Error_RequiresFileName (option);
733                                                 return ParseResult.Error;
734                                         }
735
736                                         settings.Documentation = new Documentation (value);
737                                         return ParseResult.Success;
738                                 }
739                         case "/lib": {
740                                         string[] libdirs;
741
742                                         if (value.Length == 0) {
743                                                 return ParseResult.Error;
744                                         }
745
746                                         libdirs = value.Split (argument_value_separator);
747                                         foreach (string dir in libdirs)
748                                                 settings.ReferencesLookupPaths.Add (dir);
749                                         return ParseResult.Success;
750                                 }
751
752                         case "/debug-":
753                                 settings.GenerateDebugInfo = false;
754                                 return ParseResult.Success;
755
756                         case "/debug":
757                                 if (value == "full" || value == "")
758                                         settings.GenerateDebugInfo = true;
759
760                                 return ParseResult.Success;
761
762                         case "/debug+":
763                                 settings.GenerateDebugInfo = true;
764                                 return ParseResult.Success;
765
766                         case "/checked":
767                         case "/checked+":
768                                 settings.Checked = true;
769                                 return ParseResult.Success;
770
771                         case "/checked-":
772                                 settings.Checked = false;
773                                 return ParseResult.Success;
774
775                         case "/clscheck":
776                         case "/clscheck+":
777                                 settings.VerifyClsCompliance = true;
778                                 return ParseResult.Success;
779
780                         case "/clscheck-":
781                                 settings.VerifyClsCompliance = false;
782                                 return ParseResult.Success;
783
784                         case "/unsafe":
785                         case "/unsafe+":
786                                 settings.Unsafe = true;
787                                 return ParseResult.Success;
788
789                         case "/unsafe-":
790                                 settings.Unsafe = false;
791                                 return ParseResult.Success;
792
793                         case "/warnaserror":
794                         case "/warnaserror+":
795                                 if (value.Length == 0) {
796                                         report.WarningsAreErrors = true;
797                                 } else {
798                                         foreach (string wid in value.Split (argument_value_separator))
799                                                 report.AddWarningAsError (wid);
800                                 }
801                                 return ParseResult.Success;
802
803                         case "/warnaserror-":
804                                 if (value.Length == 0) {
805                                         report.WarningsAreErrors = false;
806                                 } else {
807                                         foreach (string wid in value.Split (argument_value_separator))
808                                                 report.RemoveWarningAsError (wid);
809                                 }
810                                 return ParseResult.Success;
811
812                         case "/warn":
813                                 if (value.Length == 0) {
814                                         Error_RequiresArgument (option);
815                                         return ParseResult.Error;
816                                 }
817
818                                 SetWarningLevel (value);
819                                 return ParseResult.Success;
820
821                         case "/nowarn":
822                                         if (value.Length == 0) {
823                                                 Error_RequiresArgument (option);
824                                                 return ParseResult.Error;
825                                         }
826
827                                         var warns = value.Split (argument_value_separator);
828                                         foreach (string wc in warns) {
829                                                 try {
830                                                         if (wc.Trim ().Length == 0)
831                                                                 continue;
832
833                                                         int warn = Int32.Parse (wc);
834                                                         if (warn < 1) {
835                                                                 throw new ArgumentOutOfRangeException ("warn");
836                                                         }
837                                                         report.SetIgnoreWarning (warn);
838                                                 } catch {
839                                                         report.Error (1904, "`{0}' is not a valid warning number", wc);
840                                                         return ParseResult.Error;
841                                                 }
842                                         }
843                                         return ParseResult.Success;
844
845                         case "/noconfig":
846                                 settings.LoadDefaultReferences = false;
847                                 return ParseResult.Success;
848
849                         case "/platform":
850                                 if (value.Length == 0) {
851                                         Error_RequiresArgument (option);
852                                         return ParseResult.Error;
853                                 }
854
855                                 switch (value.ToLower (CultureInfo.InvariantCulture)) {
856                                 case "anycpu":
857                                         settings.Platform = Platform.AnyCPU;
858                                         break;
859                                 case "x86":
860                                         settings.Platform = Platform.X86;
861                                         break;
862                                 case "x64":
863                                         settings.Platform = Platform.X64;
864                                         break;
865                                 case "itanium":
866                                         settings.Platform = Platform.IA64;
867                                         break;
868                                 default:
869                                         report.Error (1672, "Invalid platform type for -platform. Valid options are `anycpu', `x86', `x64' or `itanium'");
870                                         return ParseResult.Error;
871                                 }
872
873                                 return ParseResult.Success;
874
875                         case "/sdk":
876                                 if (value.Length == 0) {
877                                         Error_RequiresArgument (option);
878                                         return ParseResult.Error;
879                                 }
880
881                                 settings.SdkVersion = value;
882                                 return ParseResult.Success;
883
884                         // We just ignore this.
885                         case "/errorreport":
886                         case "/filealign":
887                                 if (value.Length == 0) {
888                                         Error_RequiresArgument (option);
889                                         return ParseResult.Error;
890                                 }
891
892                                 return ParseResult.Success;
893
894                         case "/helpinternal":
895                                 OtherFlags ();
896                                 return ParseResult.Stop;
897
898                         case "/help":
899                         case "/?":
900                                 Usage ();
901                                 return ParseResult.Stop;
902
903                         case "/main":
904                         case "/m":
905                                 if (value.Length == 0) {
906                                         Error_RequiresArgument (option);
907                                         return ParseResult.Error;
908                                 }
909                                 settings.MainClass = value;
910                                 return ParseResult.Success;
911
912                         case "/nostdlib":
913                         case "/nostdlib+":
914                                 settings.StdLib = false;
915                                 return ParseResult.Success;
916
917                         case "/nostdlib-":
918                                 settings.StdLib = true;
919                                 return ParseResult.Success;
920
921                         case "/fullpaths":
922                                 report.Printer.ShowFullPaths = true;
923                                 return ParseResult.Success;
924
925                         case "/keyfile":
926                                 if (value.Length == 0) {
927                                         Error_RequiresFileName (option);
928                                         return ParseResult.Error;
929                                 }
930
931                                 settings.StrongNameKeyFile = value;
932                                 return ParseResult.Success;
933
934                         case "/keycontainer":
935                                 if (value.Length == 0) {
936                                         Error_RequiresArgument (option);
937                                         return ParseResult.Error;
938                                 }
939
940                                 settings.StrongNameKeyContainer = value;
941                                 return ParseResult.Success;
942
943                         case "/delaysign+":
944                         case "/delaysign":
945                                 settings.StrongNameDelaySign = true;
946                                 return ParseResult.Success;
947
948                         case "/delaysign-":
949                                 settings.StrongNameDelaySign = false;
950                                 return ParseResult.Success;
951
952                         case "/langversion":
953                                 if (value.Length == 0) {
954                                         Error_RequiresArgument (option);
955                                         return ParseResult.Error;
956                                 }
957
958                                 switch (value.ToLowerInvariant ()) {
959                                 case "iso-1":
960                                         settings.Version = LanguageVersion.ISO_1;
961                                         return ParseResult.Success;
962                                 case "default":
963                                         settings.Version = LanguageVersion.Default;
964                                         RootContext.AddConditional ("__V2__");
965                                         return ParseResult.Success;
966                                 case "iso-2":
967                                         settings.Version = LanguageVersion.ISO_2;
968                                         return ParseResult.Success;
969                                 case "3":
970                                         settings.Version = LanguageVersion.V_3;
971                                         return ParseResult.Success;
972                                 case "future":
973                                         settings.Version = LanguageVersion.Future;
974                                         return ParseResult.Success;
975                                 }
976
977                                 report.Error (1617, "Invalid -langversion option `{0}'. It must be `ISO-1', `ISO-2', `3' or `Default'", value);
978                                 return ParseResult.Error;
979
980                         case "/codepage":
981                                 if (value.Length == 0) {
982                                         Error_RequiresArgument (option);
983                                         return ParseResult.Error;
984                                 }
985
986                                 switch (value) {
987                                 case "utf8":
988                                         settings.Encoding = new UTF8Encoding ();
989                                         break;
990                                 case "reset":
991                                         settings.Encoding = Encoding.Default;
992                                         break;
993                                 default:
994                                         try {
995                                                 settings.Encoding = Encoding.GetEncoding (int.Parse (value));
996                                         } catch {
997                                                 report.Error (2016, "Code page `{0}' is invalid or not installed", value);
998                                         }
999                                         return ParseResult.Error;
1000                                 }
1001                                 return ParseResult.Success;
1002
1003                         default:
1004                                 return ParseResult.UnknownOption;
1005                         }
1006                 }
1007
1008                 //
1009                 // Currently handles the Unix-like command line options, but will be
1010                 // deprecated in favor of the CSCParseOption, which will also handle the
1011                 // options that start with a dash in the future.
1012                 //
1013                 ParseResult ParseOptionUnix (string arg, ref string[] args, ref int i, CompilerSettings settings)
1014                 {
1015                         switch (arg){
1016                         case "-v":
1017                                 CSharpParser.yacc_verbose_flag++;
1018                                 return ParseResult.Success;
1019
1020                         case "--version":
1021                                 Version ();
1022                                 return ParseResult.Stop;
1023                                 
1024                         case "--parse":
1025                                 settings.ParseOnly = true;
1026                                 return ParseResult.Success;
1027                                 
1028                         case "--main": case "-m":
1029                                 report.Warning (-29, 1, "Compatibility: Use -main:CLASS instead of --main CLASS or -m CLASS");
1030                                 if ((i + 1) >= args.Length){
1031                                         Error_RequiresArgument (arg);
1032                                         return ParseResult.Error;
1033                                 }
1034                                 settings.MainClass = args[++i];
1035                                 return ParseResult.Success;
1036                                 
1037                         case "--unsafe":
1038                                 report.Warning (-29, 1, "Compatibility: Use -unsafe instead of --unsafe");
1039                                 settings.Unsafe = true;
1040                                 return ParseResult.Success;
1041                                 
1042                         case "/?": case "/h": case "/help":
1043                         case "--help":
1044                                 Usage ();
1045                                 return ParseResult.Stop;
1046
1047                         case "--define":
1048                                 report.Warning (-29, 1, "Compatibility: Use -d:SYMBOL instead of --define SYMBOL");
1049                                 if ((i + 1) >= args.Length){
1050                                         Error_RequiresArgument (arg);
1051                                         return ParseResult.Error;
1052                                 }
1053                                 RootContext.AddConditional (args [++i]);
1054                                 return ParseResult.Success;
1055
1056                         case "--tokenize":
1057                                 settings.TokenizeOnly = true;
1058                                 return ParseResult.Success;
1059                                 
1060                         case "-o": 
1061                         case "--output":
1062                                 report.Warning (-29, 1, "Compatibility: Use -out:FILE instead of --output FILE or -o FILE");
1063                                 if ((i + 1) >= args.Length){
1064                                         Error_RequiresArgument (arg);
1065                                         return ParseResult.Error;
1066                                 }
1067                                 settings.OutputFile = args[++i];
1068                                 return ParseResult.Success;
1069
1070                         case "--checked":
1071                                 report.Warning (-29, 1, "Compatibility: Use -checked instead of --checked");
1072                                 settings.Checked = true;
1073                                 return ParseResult.Success;
1074                                 
1075                         case "--stacktrace":
1076                                 report.Printer.Stacktrace = true;
1077                                 return ParseResult.Success;
1078                                 
1079                         case "--linkresource":
1080                         case "--linkres":
1081                                 report.Warning (-29, 1, "Compatibility: Use -linkres:VALUE instead of --linkres VALUE");
1082                                 if ((i + 1) >= args.Length){
1083                                         Error_RequiresArgument (arg);
1084                                         return ParseResult.Error;
1085                                 }
1086
1087                                 AddResource (new AssemblyResource (args[++i], args[i]), settings);
1088                                 return ParseResult.Success;
1089                                 
1090                         case "--resource":
1091                         case "--res":
1092                                 report.Warning (-29, 1, "Compatibility: Use -res:VALUE instead of --res VALUE");
1093                                 if ((i + 1) >= args.Length){
1094                                         Error_RequiresArgument (arg);
1095                                         return ParseResult.Error;
1096                                 }
1097
1098                                 AddResource (new AssemblyResource (args[++i], args[i], true), settings);
1099                                 return ParseResult.Success;
1100                                 
1101                         case "--target":
1102                                 report.Warning (-29, 1, "Compatibility: Use -target:KIND instead of --target KIND");
1103                                 if ((i + 1) >= args.Length){
1104                                         Error_RequiresArgument (arg);
1105                                         return ParseResult.Error;
1106                                 }
1107                                 
1108                                 string type = args [++i];
1109                                 switch (type){
1110                                 case "library":
1111                                         settings.Target = Target.Library;
1112                                         settings.TargetExt = ".dll";
1113                                         break;
1114                                         
1115                                 case "exe":
1116                                         settings.Target = Target.Exe;
1117                                         break;
1118                                         
1119                                 case "winexe":
1120                                         settings.Target = Target.WinExe;
1121                                         break;
1122                                         
1123                                 case "module":
1124                                         settings.Target = Target.Module;
1125                                         settings.TargetExt = ".dll";
1126                                         break;
1127                                 default:
1128                                         report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'");
1129                                         break;
1130                                 }
1131                                 return ParseResult.Success;
1132                                 
1133                         case "-r":
1134                                 report.Warning (-29, 1, "Compatibility: Use -r:LIBRARY instead of -r library");
1135                                 if ((i + 1) >= args.Length){
1136                                         Error_RequiresArgument (arg);
1137                                         return ParseResult.Error;
1138                                 }
1139                                 
1140                                 string val = args [++i];
1141                                 int idx = val.IndexOf ('=');
1142                                 if (idx > -1) {
1143                                         string alias = val.Substring (0, idx);
1144                                         string assembly = val.Substring (idx + 1);
1145                                         AddAssemblyReference (alias, assembly, settings);
1146                                         return ParseResult.Success;
1147                                 }
1148
1149                                 settings.AssemblyReferences.Add (val);
1150                                 return ParseResult.Success;
1151                                 
1152                         case "-L":
1153                                 report.Warning (-29, 1, "Compatibility: Use -lib:ARG instead of --L arg");
1154                                 if ((i + 1) >= args.Length){
1155                                         Error_RequiresArgument (arg);
1156                                         return ParseResult.Error;
1157                                 }
1158                                 settings.ReferencesLookupPaths.Add (args [++i]);
1159                                 return ParseResult.Success;
1160
1161                         case "--lint":
1162                                 settings.EnhancedWarnings = true;
1163                                 return ParseResult.Success;
1164                                 
1165                         case "--nostdlib":
1166                                 report.Warning (-29, 1, "Compatibility: Use -nostdlib instead of --nostdlib");
1167                                 settings.StdLib = false;
1168                                 return ParseResult.Success;
1169                                 
1170                         case "--nowarn":
1171                                 report.Warning (-29, 1, "Compatibility: Use -nowarn instead of --nowarn");
1172                                 if ((i + 1) >= args.Length){
1173                                         Error_RequiresArgument (arg);
1174                                         return ParseResult.Error;
1175                                 }
1176                                 int warn = 0;
1177                                 
1178                                 try {
1179                                         warn = int.Parse (args [++i]);
1180                                 } catch {
1181                                         Usage ();
1182                                         Environment.Exit (1);
1183                                 }
1184                                 report.SetIgnoreWarning (warn);
1185                                 return ParseResult.Success;
1186
1187                         case "--wlevel":
1188                                 report.Warning (-29, 1, "Compatibility: Use -warn:LEVEL instead of --wlevel LEVEL");
1189                                 if ((i + 1) >= args.Length){
1190                                         Error_RequiresArgument (arg);
1191                                         return ParseResult.Error;
1192                                 }
1193
1194                                 SetWarningLevel (args [++i]);
1195                                 return ParseResult.Success;
1196
1197                         case "--mcs-debug":
1198                                 if ((i + 1) >= args.Length){
1199                                         Error_RequiresArgument (arg);
1200                                         return ParseResult.Error;
1201                                 }
1202
1203                                 try {
1204                                         Report.DebugFlags = int.Parse (args [++i]);
1205                                 } catch {
1206                                         Error_RequiresArgument (arg);
1207                                         return ParseResult.Error;
1208                                 }
1209
1210                                 return ParseResult.Success;
1211                                 
1212                         case "--about":
1213                                 About ();
1214                                 return ParseResult.Stop;
1215                                 
1216                         case "--recurse":
1217                                 report.Warning (-29, 1, "Compatibility: Use -recurse:PATTERN option instead --recurse PATTERN");
1218                                 if ((i + 1) >= args.Length){
1219                                         Error_RequiresArgument (arg);
1220                                         return ParseResult.Error;
1221                                 }
1222                                 ProcessSourceFiles (args [++i], true); 
1223                                 return ParseResult.Success;
1224                                 
1225                         case "--timestamp":
1226                                 settings.Timestamps = true;
1227                                 return ParseResult.Success;
1228
1229                         case "--debug": case "-g":
1230                                 report.Warning (-29, 1, "Compatibility: Use -debug option instead of -g or --debug");
1231                                 settings.GenerateDebugInfo = true;
1232                                 return ParseResult.Success;
1233                                 
1234                         case "--noconfig":
1235                                 report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig");
1236                                 settings.LoadDefaultReferences = false;
1237                                 return ParseResult.Success;
1238
1239                         default:
1240                                 if (arg.StartsWith ("--fatal")){
1241                                         int fatal = 1;
1242                                         if (arg.StartsWith ("--fatal="))
1243                                                 int.TryParse (arg.Substring (8), out fatal);
1244
1245                                         report.Printer.FatalCounter = fatal;
1246                                         return ParseResult.Success;
1247                                 }
1248                                 if (arg.StartsWith ("--runtime:", StringComparison.Ordinal)) {
1249                                         string version = arg.Substring (10);
1250
1251                                         switch (version) {
1252                                         case "v1":
1253                                         case "V1":
1254                                                 settings.StdLibRuntimeVersion = RuntimeVersion.v1;
1255                                                 break;
1256                                         case "v2":
1257                                         case "V2":
1258                                                 settings.StdLibRuntimeVersion = RuntimeVersion.v2;
1259                                                 break;
1260                                         case "v4":
1261                                         case "V4":
1262                                                 settings.StdLibRuntimeVersion = RuntimeVersion.v4;
1263                                                 break;
1264                                         }
1265                                         return ParseResult.Success;
1266                                 }
1267
1268                                 return ParseResult.UnknownOption;
1269                         }
1270                 }
1271
1272                 void SetWarningLevel (string s)
1273                 {
1274                         int level = -1;
1275
1276                         try {
1277                                 level = int.Parse (s);
1278                         } catch {
1279                         }
1280                         if (level < 0 || level > 4) {
1281                                 report.Error (1900, "Warning level must be in the range 0-4");
1282                                 return;
1283                         }
1284                         report.WarningLevel = level;
1285                 }
1286
1287                 //
1288                 // Given a path specification, splits the path from the file/pattern
1289                 //
1290                 static void SplitPathAndPattern (string spec, out string path, out string pattern)
1291                 {
1292                         int p = spec.LastIndexOf ('/');
1293                         if (p != -1) {
1294                                 //
1295                                 // Windows does not like /file.cs, switch that to:
1296                                 // "\", "file.cs"
1297                                 //
1298                                 if (p == 0) {
1299                                         path = "\\";
1300                                         pattern = spec.Substring (1);
1301                                 } else {
1302                                         path = spec.Substring (0, p);
1303                                         pattern = spec.Substring (p + 1);
1304                                 }
1305                                 return;
1306                         }
1307
1308                         p = spec.LastIndexOf ('\\');
1309                         if (p != -1) {
1310                                 path = spec.Substring (0, p);
1311                                 pattern = spec.Substring (p + 1);
1312                                 return;
1313                         }
1314
1315                         path = ".";
1316                         pattern = spec;
1317                 }
1318
1319                 void Usage ()
1320                 {
1321                         output.WriteLine (
1322                                 "Mono C# compiler, Copyright 2001 - 2011 Novell, Inc.\n" +
1323                                 "mcs [options] source-files\n" +
1324                                 "   --about              About the Mono C# compiler\n" +
1325                                 "   -addmodule:M1[,Mn]   Adds the module to the generated assembly\n" +
1326                                 "   -checked[+|-]        Sets default aritmetic overflow context\n" +
1327                                 "   -clscheck[+|-]       Disables CLS Compliance verifications\n" +
1328                                 "   -codepage:ID         Sets code page to the one in ID (number, utf8, reset)\n" +
1329                                 "   -define:S1[;S2]      Defines one or more conditional symbols (short: -d)\n" +
1330                                 "   -debug[+|-], -g      Generate debugging information\n" +
1331                                 "   -delaysign[+|-]      Only insert the public key into the assembly (no signing)\n" +
1332                                 "   -doc:FILE            Process documentation comments to XML file\n" +
1333                                 "   -fullpaths           Any issued error or warning uses absolute file path\n" +
1334                                 "   -help                Lists all compiler options (short: -?)\n" +
1335                                 "   -keycontainer:NAME   The key pair container used to sign the output assembly\n" +
1336                                 "   -keyfile:FILE        The key file used to strongname the ouput assembly\n" +
1337                                 "   -langversion:TEXT    Specifies language version: ISO-1, ISO-2, 3, Default or Future\n" +
1338                                 "   -lib:PATH1[,PATHn]   Specifies the location of referenced assemblies\n" +
1339                                 "   -main:CLASS          Specifies the class with the Main method (short: -m)\n" +
1340                                 "   -noconfig            Disables implicitly referenced assemblies\n" +
1341                                 "   -nostdlib[+|-]       Does not reference mscorlib.dll library\n" +
1342                                 "   -nowarn:W1[,Wn]      Suppress one or more compiler warnings\n" +
1343                                 "   -optimize[+|-]       Enables advanced compiler optimizations (short: -o)\n" +
1344                                 "   -out:FILE            Specifies output assembly name\n" +
1345                                 "   -pkg:P1[,Pn]         References packages P1..Pn\n" +
1346                                 "   -platform:ARCH       Specifies the target platform of the output assembly\n" +
1347                                 "                        ARCH can be one of: anycpu, x86, x64 or itanium\n" +
1348                                 "   -recurse:SPEC        Recursively compiles files according to SPEC pattern\n" +
1349                                 "   -reference:A1[,An]   Imports metadata from the specified assembly (short: -r)\n" +
1350                                 "   -reference:ALIAS=A   Imports metadata using specified extern alias (short: -r)\n" +
1351                                 "   -sdk:VERSION         Specifies SDK version of referenced assemblies\n" +
1352                                 "                        VERSION can be one of: 2, 4 (default) or custom value\n" +
1353                                 "   -target:KIND         Specifies the format of the output assembly (short: -t)\n" +
1354                                 "                        KIND can be one of: exe, winexe, library, module\n" +
1355                                 "   -unsafe[+|-]         Allows to compile code which uses unsafe keyword\n" +
1356                                 "   -warnaserror[+|-]    Treats all warnings as errors\n" +
1357                                 "   -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors\n" +
1358                                 "   -warn:0-4            Sets warning level, the default is 4 (short -w:)\n" +
1359                                 "   -helpinternal        Shows internal and advanced compiler options\n" +
1360                                 "\n" +
1361                                 "Resources:\n" +
1362                                 "   -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)\n" +
1363                                 "   -resource:FILE[,ID]     Embed FILE as a resource (short: -res)\n" +
1364                                 "   -win32res:FILE          Specifies Win32 resource file (.res)\n" +
1365                                 "   -win32icon:FILE         Use this icon for the output\n" +
1366                                                                 "   @file                   Read response file for more options\n\n" +
1367                                 "Options can be of the form -option or /option");
1368                 }
1369
1370                 void Version ()
1371                 {
1372                         string version = System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType.Assembly.GetName ().Version.ToString ();
1373                         output.WriteLine ("Mono C# compiler version {0}", version);
1374                 }
1375         }
1376
1377         public class RootContext
1378         {
1379                 //
1380                 // Contains the parsed tree
1381                 //
1382                 static ModuleContainer root;
1383
1384                 //
1385                 // This hashtable contains all of the #definitions across the source code
1386                 // it is used by the ConditionalAttribute handler.
1387                 //
1388                 static List<string> AllDefines;
1389
1390                 //
1391                 // Constructor
1392                 //
1393                 static RootContext ()
1394                 {
1395                         Reset (true);
1396                 }
1397
1398                 public static void PartialReset ()
1399                 {
1400                         Reset (false);
1401                 }
1402                 
1403                 public static void Reset (bool full)
1404                 {
1405                         if (!full)
1406                                 return;
1407                         
1408                         //
1409                         // Setup default defines
1410                         //
1411                         AllDefines = new List<string> ();
1412                         AddConditional ("__MonoCS__");
1413                 }
1414
1415                 public static void AddConditional (string p)
1416                 {
1417                         if (AllDefines.Contains (p))
1418                                 return;
1419                         AllDefines.Add (p);
1420                 }
1421
1422                 public static bool IsConditionalDefined (string value)
1423                 {
1424                         return AllDefines.Contains (value);
1425                 }
1426
1427                 static public ModuleContainer ToplevelTypes {
1428                         get { return root; }
1429                         set { root = value; }
1430                 }
1431         }
1432 }