2002-03-15 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / driver.cs
1 //
2 // driver.cs: The compiler command line driver.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10
11 namespace Mono.CSharp
12 {
13         using System;
14         using System.Reflection;
15         using System.Reflection.Emit;
16         using System.Collections;
17         using System.IO;
18         using System.Globalization;
19         using Mono.Languages;
20
21         enum Target {
22                 Library, Exe, Module, WinExe
23         };
24         
25         /// <summary>
26         ///    The compiler driver.
27         /// </summary>
28         public class Driver
29         {
30                 
31                 //
32                 // Assemblies references to be linked.   Initialized with
33                 // mscorlib.dll here.
34                 static ArrayList references;
35
36                 // Lookup paths
37                 static ArrayList link_paths;
38
39                 // Whether we want Yacc to output its progress
40                 static bool yacc_verbose = false;
41
42                 // Whether we want to only run the tokenizer
43                 static bool tokenize = false;
44                 
45                 static int error_count = 0;
46
47                 static string first_source;
48
49                 static Target target = Target.Exe;
50                 static string target_ext = ".exe";
51
52                 static bool parse_only = false;
53                 static bool timestamps = false;
54
55                 static Hashtable response_file_list;
56                 static Hashtable source_files = new Hashtable ();
57
58                 //
59                 // A list of resource files
60                 //
61                 static ArrayList resources;
62                 
63                 //
64                 // An array of the defines from the command line
65                 //
66                 static ArrayList defines;
67
68                 //
69                 // Last time we took the time
70                 //
71                 static DateTime last_time;
72                 static void ShowTime (string msg)
73                 {
74                         DateTime now = DateTime.Now;
75                         TimeSpan span = now - last_time;
76                         last_time = now;
77                         
78                         Console.WriteLine (
79                                 "[{0:00}:{1:000}] {2}",
80                                 span.Seconds, span.Milliseconds, msg);
81                 }
82                 
83                 static int tokenize_file (string input_file)
84                 {
85                         Stream input;
86
87                         try {
88                                 input = File.OpenRead (input_file);
89
90                         } catch {
91                                 Report.Error (2001, "Source file '" + input_file + "' could not be opened");
92                                 return 1;
93                         }
94
95                         using (input){
96                                 Tokenizer lexer = new Tokenizer (input, input_file, defines);
97                                 int token, tokens = 0, errors = 0;
98
99                                 while ((token = lexer.token ()) != Token.EOF){
100                                         Location l = lexer.Location;
101                                         tokens++;
102                                         if (token == Token.ERROR)
103                                                 errors++;
104                                 }
105                                 Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
106                         }
107                         
108                         return 0;
109                 }
110                 
111                 static int parse (string input_file)
112                 {
113                         CSharpParser parser;
114                         Stream input;
115                         int errors;
116
117                         try {
118                                 input = File.OpenRead (input_file);
119                         } catch {
120                                 Report.Error (2001, "Source file '" + input_file + "' could not be opened");
121                                 return 1;
122                         }
123
124                         parser = new CSharpParser (input_file, input, defines);
125                         parser.yacc_verbose = yacc_verbose;
126                         try {
127                                 errors = parser.parse ();
128                         } catch (Exception ex) {
129                                 Console.WriteLine (ex);
130                                 Console.WriteLine ("Compilation aborted");
131                                 return 1;
132                         } finally {
133                                 input.Close ();
134                         }
135                         
136                         return errors;
137                 }
138                 
139                 static void Usage (bool is_error)
140                 {
141                         Console.WriteLine (
142                                 "Mono C# compiler, (C) 2001 Ximian, Inc.\n" +
143                                 "mcs [options] source-files\n" +
144                                 "   --about         About the Mono C# compiler\n" +
145                                 "   --checked       Set default context to checked\n" +
146                                 "   --define SYM    Defines the symbol SYM\n" + 
147                                 "   --fatal         Makes errors fatal\n" +
148                                 "   -L PATH         Adds PATH to the assembly link path\n" +
149                                 "   --nostdlib      Does not load core libraries\n" +
150                                 "   --nowarn XXX    Ignores warning number XXX\n" +
151                                 "   -o FNAME        Specifies output file\n" +
152                                 "   --optimize      Optimizes\n" +
153                                 "   --parse         Only parses the source file\n" +
154                                 "   --probe X       Probes for the source to generate code X on line L\n" +
155                                 "   --resource FILE Addds FILE as a resource\n" + 
156                                 "   --stacktrace    Shows stack trace at error location\n" +
157                                 "   --target KIND   Specifies the target (KIND is one of: exe, winexe, " +
158                                                     "library, module)\n" +
159                                 "   --timestamp     Displays time stamps of various compiler events\n" +
160                                 "   --unsafe        Allows unsafe code\n" +
161                                 "   --werror        Treat warnings as errors\n" +
162                                 "   --wlevel LEVEL  Sets warning level (the highest is 4, the default)\n" +
163                                 "   -r              References an assembly\n" +
164                                 "   -v              Verbose parsing (for debugging the parser)\n" +
165                                 "   @file           Read response file for more options");
166                         if (is_error)
167                                 error_count++;
168                 }
169
170                 static void About ()
171                 {
172                         Console.WriteLine (
173                                 "The Mono C# compiler is (C) 2001 Ximian, Inc.\n\n" +
174                                 "The compiler source code is released under the terms of the GNU GPL\n\n" +
175
176                                 "For more information on Mono, visit the project Web site\n" +
177                                 "   http://www.go-mono.com\n\n" +
178
179                                 "The compiler was written by Miguel de Icaza and Ravi Pratap");
180                 }
181                 
182                 static void error (string msg)
183                 {
184                         Console.WriteLine ("Error: " + msg);
185                 }
186
187                 static void notice (string msg)
188                 {
189                         Console.WriteLine (msg);
190                 }
191                 
192                 public static int Main (string[] args)
193                 {
194                         MainDriver (args);
195                         
196                         return (error_count + Report.Errors) != 0 ? 1 : 0;
197                 }
198
199                 static public int LoadAssembly (string assembly)
200                 {
201                         Assembly a;
202                         string total_log = "";
203
204                         try {
205                                 a = Assembly.Load (assembly);
206                                 RootContext.TypeManager.AddAssembly (a);
207                                 return 0;
208                         } catch (FileNotFoundException){
209                                 foreach (string dir in link_paths){
210                                         string full_path = dir + "/" + assembly + ".dll";
211
212                                         try {
213                                                 a = Assembly.LoadFrom (full_path);
214                                                 RootContext.TypeManager.AddAssembly (a);
215                                                 return 0;
216                                         } catch (FileNotFoundException ff) {
217                                                 total_log += ff.FusionLog;
218                                                 continue;
219                                         }
220                                 }
221                         } catch (BadImageFormatException f) {
222                                 error ("// Bad file format while loading assembly");
223                                 error ("Log: " + f.FusionLog);
224                                 return 1;
225                         } catch (FileLoadException f){
226                                 error ("// File Load Exception: ");
227                                 error ("Log: " + f.FusionLog);
228                                 return 1;
229                         } catch (ArgumentNullException){
230                                 error ("// Argument Null exception ");
231                                 return 1;
232                         }
233                         
234                         Report.Error (6, "Can not find assembly `" + assembly + "'" );
235                         Console.WriteLine ("Log: \n" + total_log);
236
237                         return 0;
238                 }
239
240                 /// <summary>
241                 ///   Loads all assemblies referenced on the command line
242                 /// </summary>
243                 static public int LoadReferences ()
244                 {
245                         int errors = 0;
246
247                         foreach (string r in references)
248                                 errors += LoadAssembly (r);
249
250                         return errors;
251                 }
252
253                 static void SetupDefaultDefines ()
254                 {
255                         defines = new ArrayList ();
256                         defines.Add ("__MonoCS__");
257                 }
258
259                 static string [] LoadArgs (string file)
260                 {
261                         StreamReader f;
262                         ArrayList args = new ArrayList ();
263                         string line;
264                         try {
265                                 f = new StreamReader (file);
266                         } catch {
267                                 return null;
268                         }
269
270                         while ((line = f.ReadLine ()) != null){
271                                 string [] line_args = line.Split (new char [] { ' ' });
272
273                                 foreach (string arg in line_args)
274                                         args.Add (arg);
275                         }
276
277                         string [] ret_value = new string [args.Count];
278                         args.CopyTo (ret_value, 0);
279
280                         return ret_value;
281                 }
282
283                 //
284                 // Returns the directory where the system assemblies are installed
285                 //
286                 static string GetSystemDir ()
287                 {
288                         Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
289
290                         foreach (Assembly a in assemblies){
291                                 string codebase = a.CodeBase;
292                                 if (codebase.EndsWith ("corlib.dll")){
293                                         return codebase.Substring (0, codebase.LastIndexOf ("/"));
294                                 }
295                         }
296
297                         Report.Error (-15, "Can not compute my system path");
298                         return "";
299                 }
300
301                 //
302                 // Given a path specification, splits the path from the file/pattern
303                 //
304                 static void SplitPathAndPattern (string spec, out string path, out string pattern)
305                 {
306                         int p = spec.LastIndexOf ("/");
307                         if (p != -1){
308                                 //
309                                 // Windows does not like /file.cs, switch that to:
310                                 // "\", "file.cs"
311                                 //
312                                 if (p == 0){
313                                         path = "\\";
314                                         pattern = spec.Substring (1);
315                                 } else {
316                                         path = spec.Substring (0, p);
317                                         pattern = spec.Substring (p + 1);
318                                 }
319                                 return;
320                         }
321
322                         p = spec.LastIndexOf ("\\");
323                         if (p != -1){
324                                 path = spec.Substring (0, p - 1);
325                                 pattern = spec.Substring (p);
326                                 return;
327                         }
328
329                         path = ".";
330                         pattern = spec;
331                 }
332
333                 static int ProcessFile (string f)
334                 {
335                         if (source_files.Contains (f)){
336                                 Report.Error (
337                                         1516,
338                                         "Source file `" + f + "' specified multiple times");
339                                 Environment.Exit (1);
340                         } else
341                                 source_files.Add (f, f);
342                                         
343                         if (tokenize)
344                                 tokenize_file (f);
345                         else
346                                 return parse (f);
347                         return 0;
348                 }
349
350                 static void RecurseOn (string pattern)
351                 {
352                         // FIXME: implement.
353                 }
354                 
355                 /// <summary>
356                 ///    Parses the arguments, and drives the compilation
357                 ///    process.
358                 /// </summary>
359                 ///
360                 /// <remarks>
361                 ///    TODO: Mostly structured to debug the compiler
362                 ///    now, needs to be turned into a real driver soon.
363                 /// </remarks>
364                 static void MainDriver (string [] args)
365                 {
366                         int errors = 0, i;
367                         string output_file = null;
368                         bool parsing_options = true;
369                         
370                         references = new ArrayList ();
371                         link_paths = new ArrayList ();
372
373                         SetupDefaultDefines ();
374                         
375                         //
376                         // Setup defaults
377                         //
378                         // This is not required because Assembly.Load knows about this
379                         // path.
380                         //
381                         link_paths.Add (GetSystemDir ());
382
383                         int argc = args.Length;
384                         for (i = 0; i < argc; i++){
385                                 string arg = args [i];
386
387                                 if (arg.StartsWith ("@")){
388                                         string [] new_args, extra_args;
389                                         string response_file = arg.Substring (1);
390
391                                         if (response_file_list == null)
392                                                 response_file_list = new Hashtable ();
393                                         
394                                         if (response_file_list.Contains (response_file)){
395                                                 Report.Error (
396                                                         1515, "Response file `" + response_file +
397                                                         "' specified multiple times");
398                                                 Environment.Exit (1);
399                                         }
400                                         
401                                         response_file_list.Add (response_file, response_file);
402                                                     
403                                         extra_args = LoadArgs (response_file);
404                                         if (extra_args == null){
405                                                 Report.Error (2011, "Unable to open response file: " +
406                                                               response_file);
407                                                 return;
408                                         }
409
410                                         new_args = new string [extra_args.Length + argc];
411                                         args.CopyTo (new_args, 0);
412                                         extra_args.CopyTo (new_args, argc);
413                                         args = new_args;
414                                         argc = new_args.Length;
415                                         continue;
416                                 }
417
418                                 //
419                                 // Prepare to recurse
420                                 //
421                                 
422                                 if (parsing_options && (arg.StartsWith ("-") || arg.StartsWith ("/"))){
423                                         switch (arg){
424                                         case "-v":
425                                                 yacc_verbose = true;
426                                                 continue;
427
428                                         case "--":
429                                                 parsing_options = false;
430                                                 continue;
431
432                                         case "--parse":
433                                                 parse_only = true;
434                                                 continue;
435
436                                         case "--main": case "-m":
437                                                 if ((i + 1) >= argc){
438                                                         Usage (true);
439                                                         return;
440                                                 }
441                                                 RootContext.MainClass = args [++i];
442                                                 continue;
443
444                                         case "--unsafe":
445                                                 RootContext.Unsafe = true;
446                                                 continue;
447                                                 
448                                         case "--optimize":
449                                                 RootContext.Optimize = true;
450                                                 continue;
451
452                                         case "/?": case "/h": case "/help":
453                                         case "--help":
454                                                 Usage (false);
455                                                 return;
456
457                                         case "--define":
458                                                 if ((i + 1) >= argc){
459                                                         Usage (true);
460                                                         return;
461                                                 }
462                                                 defines.Add (args [++i]);
463                                                 continue;
464                                                 
465                                         case "--probe": {
466                                                 int code = 0;
467
468                                                 try {
469                                                         code = Int32.Parse (
470                                                                 args [++i], NumberStyles.AllowLeadingSign);
471                                                         Report.SetProbe (code);
472                                                 } catch {
473                                                         Report.Error (-14, "Invalid number specified");
474                                                 } 
475                                                 continue;
476                                         }
477
478                                         case "--tokenize": {
479                                                 tokenize = true;
480                                                 continue;
481                                         }
482                                         
483                                         case "-o": 
484                                         case "--output":
485                                                 if ((i + 1) >= argc){
486                                                         Usage (true);
487                                                         return;
488                                                 }
489                                                 output_file = args [++i];
490                                                 string bname = CodeGen.Basename (output_file);
491                                                 if (bname.IndexOf (".") == -1)
492                                                         output_file += ".exe";
493                                                 continue;
494
495                                         case "--checked":
496                                                 RootContext.Checked = true;
497                                                 continue;
498
499                                         case "--stacktrace":
500                                                 Report.Stacktrace = true;
501                                                 continue;
502
503                                         case "--resource":
504                                                 if ((i + 1) >= argc){
505                                                         Usage (true);
506                                                         Console.WriteLine("Missing argument to --resource"); 
507                                                         return;
508                                                 }
509                                                 if (resources == null)
510                                                         resources = new ArrayList ();
511                                                 
512                                                 resources.Add (args [++i]);
513                                                 continue;
514                                                         
515                                         case "--target":
516                                                 if ((i + 1) >= argc){
517                                                         Usage (true);
518                                                         return;
519                                                 }
520
521                                                 string type = args [++i];
522                                                 switch (type){
523                                                 case "library":
524                                                         target = Target.Library;
525                                                         target_ext = ".dll";
526                                                         break;
527                                                         
528                                                 case "exe":
529                                                         target = Target.Exe;
530                                                         break;
531                                                         
532                                                 case "winexe":
533                                                         target = Target.WinExe;
534                                                         break;
535                                                         
536                                                 case "module":
537                                                         target = Target.Module;
538                                                         target_ext = ".dll";
539                                                         break;
540                                                 }
541                                                 continue;
542
543                                         case "-r":
544                                                 if ((i + 1) >= argc){
545                                                         Usage (true);
546                                                         return;
547                                                 }
548                                                 
549                                                 references.Add (args [++i]);
550                                                 continue;
551                                                 
552                                         case "-L":
553                                                 if ((i + 1) >= argc){
554                                                         Usage (true);
555                                                         return;
556                                                 }
557                                                 link_paths.Add (args [++i]);
558                                                 continue;
559                                                 
560                                         case "--nostdlib":
561                                                 RootContext.StdLib = false;
562                                                 continue;
563                                                 
564                                         case "--fatal":
565                                                 Report.Fatal = true;
566                                                 continue;
567
568                                         case "--werror":
569                                                 Report.WarningsAreErrors = true;
570                                                 continue;
571
572                                         case "--nowarn":
573                                                 if ((i + 1) >= argc){
574                                                         Usage (true);
575                                                         return;
576                                                 }
577                                                 int warn;
578                                                 
579                                                 try {
580                                                         warn = Int32.Parse (args [++i]);
581                                                 } catch {
582                                                         Usage (true);
583                                                         return;
584                                                 }
585                                                 Report.SetIgnoreWarning (warn);
586                                                 continue;
587
588                                         case "--wlevel":
589                                                 if ((i + 1) >= argc){
590                                                         Usage (true);
591                                                         error_count++;
592                                                         return;
593                                                 }
594                                                 int level;
595                                                 
596                                                 try {
597                                                         level = Int32.Parse (args [++i]);
598                                                 } catch {
599                                                         Usage (true);
600                                                         return;
601                                                 }
602                                                 if (level < 0 || level > 4){
603                                                         Report.Error (1900, "Warning level must be 0 to 4");
604                                                         return;
605                                                 } else
606                                                         RootContext.WarningLevel = level;
607                                                 continue;
608                                                 
609                                         case "--about":
610                                                 About ();
611                                                 return;
612
613                                         case "--recurse":
614                                                 if ((i + 1) >= argc){
615                                                         Usage (true);
616                                                         error_count++;
617                                                         return;
618                                                 }
619                                                 RecurseOn (args [++i]);
620                                                 continue;
621                                                 
622                                         case "--timestamp":
623                                                 timestamps = true;
624                                                 last_time = DateTime.Now;
625                                                 continue;
626                                         }
627                                 }
628
629                                 if (first_source == null)
630                                         first_source = arg;
631
632                                 string path, pattern;
633                                 SplitPathAndPattern (arg, out path, out pattern);
634                                 string [] files = null;
635                                 try {
636                                         files = Directory.GetFiles (path, pattern);
637                                 } catch (System.IO.DirectoryNotFoundException) {
638                                         Report.Error (2001, "Source file `" + arg + "' could not be found");
639                                         continue;
640                                 }
641                                 
642                                 foreach (string f in files)
643                                         errors += ProcessFile (f);
644                         }
645
646                         if (tokenize)
647                                 return;
648                         
649                         if (first_source == null){
650                                 Report.Error (2008, "No files to compile were specified");
651                                 return;
652                         }
653
654                         if (Report.Errors > 0)
655                                 return;
656                         
657                         if (parse_only)
658                                 return;
659                         
660                         //
661                         // Load Core Library for default compilation
662                         //
663                         if (RootContext.StdLib){
664                                 references.Insert (0, "mscorlib");
665                                 references.Insert (1, "System");
666                         }
667
668                         if (errors > 0){
669                                 error ("Parsing failed");
670                                 return;
671                         }
672
673                         //
674                         // Load assemblies required
675                         //
676                         if (timestamps)
677                                 ShowTime ("Loading references");
678                         errors += LoadReferences ();
679                         if (timestamps)
680                                 ShowTime ("   References loaded");
681                         
682                         if (errors > 0){
683                                 error ("Could not load one or more assemblies");
684                                 return;
685                         }
686
687                         error_count = errors;
688
689                         //
690                         // Quick hack
691                         //
692                         if (output_file == null){
693                                 int pos = first_source.LastIndexOf (".");
694
695                                 if (pos > 0)
696                                         output_file = first_source.Substring (0, pos) + target_ext;
697                                 else
698                                         output_file = first_source + target_ext;
699                         }
700
701                         RootContext.CodeGen = new CodeGen (output_file, output_file);
702
703                         //
704                         // Before emitting, we need to get the core
705                         // types emitted from the user defined types
706                         // or from the system ones.
707                         //
708                         if (timestamps)
709                                 ShowTime ("Initializing Core Types");
710                         RootContext.TypeManager.InitCoreTypes ();
711                         if (timestamps)
712                                 ShowTime ("   Core Types done");
713
714                         RootContext.TypeManager.AddModule (RootContext.CodeGen.ModuleBuilder);
715                         
716                         //
717                         // The second pass of the compiler
718                         //
719                         if (timestamps)
720                                 ShowTime ("Resolving tree");
721                         RootContext.ResolveTree ();
722                         if (timestamps)
723                                 ShowTime ("Populate tree");
724                         RootContext.PopulateTypes ();
725                         
726                         if (Report.Errors > 0){
727                                 error ("Compilation failed");
728                                 return;
729                         }
730                         
731                         //
732                         // The code generator
733                         //
734                         if (timestamps)
735                                 ShowTime ("Emitting code");
736                         RootContext.EmitCode ();
737                         if (timestamps)
738                                 ShowTime ("   done");
739
740                         if (Report.Errors > 0){
741                                 error ("Compilation failed");
742                                 return;
743                         }
744
745                         if (timestamps)
746                                 ShowTime ("Closing types");
747                         
748                         RootContext.CloseTypes ();
749
750                         PEFileKinds k = PEFileKinds.ConsoleApplication;
751                                 
752                         if (target == Target.Library || target == Target.Module)
753                                 k = PEFileKinds.Dll;
754                         else if (target == Target.Exe)
755                                 k = PEFileKinds.ConsoleApplication;
756                         else if (target == Target.WinExe)
757                                 k = PEFileKinds.WindowApplication;
758
759                         if (target == Target.Exe || target == Target.WinExe){
760                                 MethodInfo ep = RootContext.EntryPoint;
761
762                                 if (ep == null){
763                                         Report.Error (5001, "Program " + output_file +
764                                                               " does not have an entry point defined");
765                                         return;
766                                 }
767                                 
768                                 RootContext.CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
769                         }
770
771                         //
772                         // Add the resources
773                         //
774                         if (resources != null){
775                                 foreach (string file in resources)
776                                         RootContext.CodeGen.AssemblyBuilder.AddResourceFile (file, file);
777                         }
778                         
779                         RootContext.CodeGen.Save (output_file);
780                         if (timestamps)
781                                 ShowTime ("Saved output");
782
783                         if (Report.Errors > 0){
784                                 error ("Compilation failed");
785                                 return;
786                         } else if (Report.ProbeCode != 0){
787                                 error ("Failed to report code " + Report.ProbeCode);
788                                 Environment.Exit (124);
789                         }
790                 }
791
792         }
793 }