2002-09-03 Rafael Teixeira <rafaelteixeirabr@hotmail.com>
[mono.git] / mcs / mbas / driver.cs
1 //
2 // driver.cs: The compiler command line driver.
3 //
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
5 // Based on mcs by : Miguel de Icaza (miguel@gnu.org)
6 //
7 // Licensed under the terms of the GNU GPL
8 //
9 // (C) 2002 Rafael Teixeira
10 //
11
12 namespace Mono.Languages
13 {
14         using System;
15         using System.Reflection;
16         using System.Reflection.Emit;
17         using System.Collections;
18         using System.IO;
19         using System.Globalization;
20         using Mono.CSharp;
21         using Mono.GetOptions;
22
23         enum Target 
24         {
25                 Library, Exe, Module, WinExe
26         };
27         
28         /// <summary>
29         ///    The compiler driver.
30         /// </summary>
31         public class Driver : Options
32         {
33                 
34                 [Option("Verbose parsing (for debugging the parser)",'v')] 
35                 public bool verbose     { set { GenericParser.yacc_verbose_flag = value; } }
36
37                 [Option("Specifies PARAM as main (starting) class", 'm')]
38                 public string main { set { RootContext.MainClass = value; } }
39
40                 [Option("About the MonoBASIC compiler", "about")]
41                 public override WhatToDoNext DoAbout()
42                 {
43                         return base.DoAbout();
44                 }
45
46                 [Option("Adds PARAM to the assembly link path", 'L')]
47                 public static string[] LinkPaths = null;
48
49                 [Option("Defines the symbol PARAM", "define")]
50                 public static string[] Defines = null;
51
52                 [Option("Only parses the source file (for debugging the tokenizer)", "parse")]
53                 public static bool parse_only = false;
54
55                 private static bool load_default_config = true;
56
57                 [Option("Disables implicit references to assemblies", "noconfig")]
58                 public bool NoConfig { set { load_default_config = !value; } }
59
60                 [Option("Allows unsafe code", "unsafe")]
61                 public bool AllowUnsafeCode { set { RootContext.Unsafe = value; } }
62
63                 private string output_file;
64
65                 [Option("Specifies output file", 'o', "output")]
66                 public WhatToDoNext SetOutputFile(string FileName)
67                 {
68                         output_file = FileName;
69                         string bname = CodeGen.Basename (output_file);
70                         if (bname.IndexOf (".") == -1)
71                                 output_file += ".exe";
72                         return WhatToDoNext.GoAhead;
73                 }
74
75
76                 [Option("Only tokenizes source files", "tokenize")]
77                 public static bool tokenize = true;
78
79                 [Option("Set default context to checked", "checked")]
80                 public bool Checked { set { RootContext.Checked = value; } }
81
82                 [Option("Shows stack trace at error location", "Stacktrace")]
83                 public bool Stacktrace { set { Report.Stacktrace = value; } }
84
85                 private static ArrayList references = new ArrayList();
86
87                 [Option("References an assembly", 'r')]
88                 public static string reference { set { references.Add(value); } }
89
90                 [Option("Adds PARAM as a resource", "resource")]
91                 public static string[] resources;
92
93                 [Option("Set default context to checked", "nostdlib")]
94                 public bool nostdlib { set { RootContext.StdLib = !value; } }
95
96                 [Option("Makes errors fatal", "fatal")]
97                 public bool Fatal { set { Report.Fatal = value; } }
98
99                 [Option("Treat warnings as errors", "werror")]
100                 public bool WarningsAreErrors { set { Report.WarningsAreErrors = value; } }
101
102                 [Option("Ignores warning number PARAM", "nowarn")]
103                 public WhatToDoNext SetIgnoreWarning(int warn)
104                 {
105                         Report.SetIgnoreWarning(warn);
106                         return WhatToDoNext.GoAhead;
107                 }
108
109                 [Option("Recursively compiles the files in PARAM ([dir]/file)", "recurse")]
110                 public WhatToDoNext recurse(string DirName)
111                 {
112                         AddFiles (DirName, true);
113                         return WhatToDoNext.GoAhead;
114                 }
115         
116
117                 [Option("Write symbolic debugging information to FILE-debug.s", 'g', "debug")]
118                 public static bool want_debugging_support = false;
119
120                 [Option("Debugger arguments", "debug-args")]
121                 public WhatToDoNext SetDebugArgs(string args)
122                 {
123                         char[] sep = { ',' };
124                         debug_arglist.AddRange (args.Split (sep));
125                         return WhatToDoNext.GoAhead;
126                 }
127
128                 [Option("Specifies the target (PARAM is one of: exe, winexe, library, module)", "target")]
129                 public WhatToDoNext SetTarget(string type)
130                 {
131                         switch (type)
132                         {
133                                 case "library":
134                                         target = Target.Library;
135                                         target_ext = ".dll";
136                                         break;
137                                                         
138                                 case "exe":
139                                         target = Target.Exe;
140                                         break;
141                                                         
142                                 case "winexe":
143                                         target = Target.WinExe;
144                                         break;
145                                                         
146                                 case "module":
147                                         target = Target.Module;
148                                         target_ext = ".dll";
149                                         break;
150                         }
151                         return WhatToDoNext.GoAhead;
152                 }
153
154                 [Option("Sets warning level (the highest is 4, the default)", "wlevel")]
155                 public int wlevel { set { RootContext.WarningLevel = value; } }
156
157                 [Option("Sets warning level (the highest is 4, the default)")]
158                 public bool timestamp
159                 {
160                         set
161                         {
162                                 timestamps = true;
163                                 last_time = DateTime.Now;
164                                 debug_arglist.Add("timestamp");
165                         }
166                 }
167
168         static void Usage (bool is_error)
169                 {
170                         Console.WriteLine (     @"
171 MonoBASIC Compiler, Copyright (C)2002 Rafael Teixeira.
172   --timestamp     Displays time stamps of various compiler events
173   @file           Read response file for more options
174 ");
175                 }
176                 
177                 //
178                 // If any of these fail, we ignore the problem.  This is so
179                 // that we can list all the assemblies in Windows and not fail
180                 // if they are missing on Linux.
181                 //
182                 static ArrayList soft_references;
183
184                 static int error_count = 0;
185
186                 static string first_source;
187
188                 static Target target = Target.Exe;
189                 static string target_ext = ".exe";
190
191                 static ArrayList debug_arglist = new ArrayList ();
192
193                 static bool timestamps = false;
194
195                 static Hashtable source_files = new Hashtable ();
196
197                 //
198                 // An array of the defines from the command line
199                 //
200                 static ArrayList defines;
201
202                 
203                 //
204                 // Last time we took the time
205                 //
206                 static DateTime last_time;
207                 static void ShowTime (string msg)
208                 {
209                         DateTime now = DateTime.Now;
210                         TimeSpan span = now - last_time;
211                         last_time = now;
212
213                         Console.WriteLine (
214                                 "[{0:00}:{1:000}] {2}",
215                                 (int) span.TotalSeconds, span.Milliseconds, msg);
216                 }
217                         
218                 static void error (string msg)
219                 {
220                         Console.WriteLine ("Error: " + msg);
221                 }
222
223                 static void notice (string msg)
224                 {
225                         Console.WriteLine (msg);
226                 }
227                 
228                 public static int Main (string[] args)
229                 {
230                         Driver Exec = new Driver();
231                         
232                         if (Exec.MainDriver(args) && Report.Errors == 0) 
233                         {
234                                 Console.Write("Compilation succeeded");
235                                 if (Report.Warnings > 0) 
236                                 {
237                                         Console.Write(" - {0} warning(s)", Report.Warnings);
238                                 } 
239                                 Console.WriteLine();
240                                 return 0;
241                         } 
242                         else 
243                         {
244                                 Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
245                                         Report.Errors, Report.Warnings);
246                                 return 1;
247                         }
248                 }
249
250                 static public int LoadAssembly (string assembly, bool soft)
251                 {
252                         Assembly a;
253                         string total_log = "";
254
255                         try {
256                                 char[] path_chars = { '/', '\\', '.' };
257
258                                 if (assembly.IndexOfAny (path_chars) != -1)
259                                         a = Assembly.LoadFrom (assembly);
260                                 else
261                                         a = Assembly.Load (assembly);
262                                 TypeManager.AddAssembly (a);
263                                 return 0;
264                         } catch (FileNotFoundException){
265                                 foreach (string dir in LinkPaths){
266                                         string full_path = dir + "/" + assembly + ".dll";
267
268                                         try {
269                                                 a = Assembly.LoadFrom (full_path);
270                                                 TypeManager.AddAssembly (a);
271                                                 return 0;
272                                         } catch (FileNotFoundException ff) {
273                                                 total_log += ff.FusionLog;
274                                                 continue;
275                                         }
276                                 }
277                                 if (soft)
278                                         return 0;
279                         } catch (BadImageFormatException f) {
280                                 error ("// Bad file format while loading assembly");
281                                 error ("Log: " + f.FusionLog);
282                                 return 1;
283                         } catch (FileLoadException f){
284                                 error ("File Load Exception: " + assembly);
285                                 error ("Log: " + f.FusionLog);
286                                 return 1;
287                         } catch (ArgumentNullException){
288                                 error ("// Argument Null exception ");
289                                 return 1;
290                         }
291                         
292                         Report.Error (6, "Can not find assembly `" + assembly + "'" );
293                         Console.WriteLine ("Log: \n" + total_log);
294
295                         return 0;
296                 }
297
298                 /// <summary>
299                 ///   Loads all assemblies referenced on the command line
300                 /// </summary>
301                 static public int LoadReferences ()
302                 {
303                         int errors = 0;
304
305                         foreach (string r in references)
306                                 errors += LoadAssembly (r, false);
307
308                         foreach (string r in soft_references)
309                                 errors += LoadAssembly (r, true);
310                         
311                         return errors;
312                 }
313
314                 static void SetupDefaultDefines ()
315                 {
316                         defines = new ArrayList ();
317                         defines.Add ("__MonoBASIC__");
318                 }
319
320
321                 //
322                 // Returns the directory where the system assemblies are installed
323                 //
324                 static string GetSystemDir ()
325                 {
326                         Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
327
328                         foreach (Assembly a in assemblies){
329                                 string codebase = a.CodeBase;
330                                 if (codebase.EndsWith ("corlib.dll")){
331                                         return codebase.Substring (0, codebase.LastIndexOf ("/"));
332                                 }
333                         }
334
335                         Report.Error (-15, "Can not compute my system path");
336                         return "";
337                 }
338
339                 //
340                 // Given a path specification, splits the path from the file/pattern
341                 //
342                 static void SplitPathAndPattern (string spec, out string path, out string pattern)
343                 {
344                         int p = spec.LastIndexOf ("/");
345                         if (p != -1){
346                                 //
347                                 // Windows does not like /file.cs, switch that to:
348                                 // "\", "file.cs"
349                                 //
350                                 if (p == 0){
351                                         path = "\\";
352                                         pattern = spec.Substring (1);
353                                 } else {
354                                         path = spec.Substring (0, p);
355                                         pattern = spec.Substring (p + 1);
356                                 }
357                                 return;
358                         }
359
360                         p = spec.LastIndexOf ("\\");
361                         if (p != -1){
362                                 path = spec.Substring (0, p);
363                                 pattern = spec.Substring (p + 1);
364                                 return;
365                         }
366
367                         path = ".";
368                         pattern = spec;
369                 }
370
371
372                 static int ProcessSourceFile(string filename)
373                 {
374                         if (tokenize)
375                                 GenericParser.Tokenize(filename);
376                         else
377                                 return GenericParser.Parse(filename);
378
379                         return 0;
380                 }
381
382                 static bool AddFiles (string spec, bool recurse)
383                 {
384                         string path, pattern;
385
386                         SplitPathAndPattern (spec, out path, out pattern);
387                         if (pattern.IndexOf ("*") == -1){
388                                 return AddFile (spec);
389                         }
390
391                         string [] files = null;
392                         try {
393                                 files = Directory.GetFiles (path, pattern);
394                         } catch (System.IO.DirectoryNotFoundException) {
395                                 Report.Error (2001, "Source file `" + spec + "' could not be found");
396                                 return false;
397                         } catch (System.IO.IOException){
398                                 Report.Error (2001, "Source file `" + spec + "' could not be found");
399                                 return false;
400                         }
401                         foreach (string f in files)
402                                 AddFile (f);
403
404                         if (!recurse)
405                                 return true;
406                         
407                         string [] dirs = null;
408
409                         try {
410                                 dirs = Directory.GetDirectories (path);
411                         } catch {
412                         }
413                         
414                         foreach (string d in dirs) {
415                                         
416                                 // Don't include path in this string, as each
417                                 // directory entry already does
418                                 AddFiles (d + "/" + pattern, true);
419                         }
420                         
421
422                         return true;
423                 }
424
425                 static void DefineDefaultConfig ()
426                 {
427                         //
428                         // For now the "default config" is harcoded into the compiler
429                         // we can move this outside later
430                         //
431                         string [] default_config = 
432                         {
433                                 "System",
434                                 "System.Data",
435                                 "System.Xml",
436                                 "Microsoft.VisualBasic", // just for now
437 #if false
438                                 //
439                                 // Is it worth pre-loading all this stuff?
440                                 //
441                                 "Accessibility",
442                                 "System.Configuration.Install",
443                                 "System.Design",
444                                 "System.DirectoryServices",
445                                 "System.Drawing.Design",
446                                 "System.Drawing",
447                                 "System.EnterpriseServices",
448                                 "System.Management",
449                                 "System.Messaging",
450                                 "System.Runtime.Remoting",
451                                 "System.Runtime.Serialization.Formatters.Soap",
452                                 "System.Security",
453                                 "System.ServiceProcess",
454                                 "System.Web",
455                                 "System.Web.RegularExpressions",
456                                 "System.Web.Services",
457                                 "System.Windows.Forms"
458 #endif
459                         };
460                         
461                         int p = 0;
462                         foreach (string def in default_config)
463                                 soft_references.Insert (p++, def);
464                 }
465
466                 private static bool AddFile(string fileName)
467                 {
468                         string f = fileName;
469                         if (first_source == null)
470                                 first_source = f;
471
472                         if (source_files.Contains(f))
473                         {
474                                 Report.Error (1516, "Source file `" + f + "' specified multiple times");
475                                 return false;
476                         } 
477                         else
478                                 source_files.Add(f, f);
479
480                         return true;
481                 }
482
483                 
484                 /// <summary>
485                 ///    Parses the arguments, and drives the compilation
486                 ///    process.
487                 /// </summary>
488                 ///
489                 /// <remarks>
490                 ///    TODO: Mostly structured to debug the compiler
491                 ///    now, needs to be turned into a real driver soon.
492                 /// </remarks>
493                 bool MainDriver(string [] args)
494                 {
495                         int errors = 0;//, i;
496                         string output_file = null;
497                         
498                         soft_references = new ArrayList ();
499                         SetupDefaultDefines ();
500                         
501                         this.ProcessArgs(args);
502
503                         foreach(string arg in this.RemainingArguments)
504                                 AddFile(arg); 
505
506                         foreach(string filename in source_files.Values)
507                                 errors += ProcessSourceFile(filename);
508
509                         if (first_source == null)
510                         {
511                                 Report.Error (2008, "No files to compile were specified");
512                                 return false;
513                         }
514
515                         if (tokenize)
516                                 return true;
517                         
518                         if (Report.Errors > 0)
519                                 return false;
520                         
521                         if (parse_only)
522                                 return true;
523                         
524                         //
525                         // Load Core Library for default compilation
526                         //
527                         if (RootContext.StdLib)
528                                 references.Insert (0, "mscorlib");
529
530                         if (load_default_config)
531                                 DefineDefaultConfig ();
532
533                         if (errors > 0)
534                         {
535                                 error ("Parsing failed");
536                                 return false;
537                         }
538
539                         //
540                         // Load assemblies required
541                         //
542                         if (timestamps)
543                                 ShowTime ("Loading references");
544                         errors += LoadReferences ();
545                         if (timestamps)
546                                 ShowTime ("   References loaded");
547                         
548                         if (errors > 0)
549                         {
550                                 error ("Could not load one or more assemblies");
551                                 return false;
552                         }
553
554                         error_count = errors;
555
556                         //
557                         // Quick hack
558                         //
559                         if (output_file == null)
560                         {
561                                 int pos = first_source.LastIndexOf (".");
562
563                                 if (pos > 0)
564                                         output_file = first_source.Substring (0, pos) + target_ext;
565                                 else
566                                         output_file = first_source + target_ext;
567                         }
568
569                         string[] debug_args = new string [debug_arglist.Count];
570                         debug_arglist.CopyTo(debug_args);
571                         CodeGen.Init (output_file, output_file, want_debugging_support, debug_args);
572
573                         TypeManager.AddModule (CodeGen.ModuleBuilder);
574
575                         //
576                         // Before emitting, we need to get the core
577                         // types emitted from the user defined types
578                         // or from the system ones.
579                         //
580                         if (timestamps)
581                                 ShowTime ("Initializing Core Types");
582                         if (!RootContext.StdLib)
583                         {
584                                 RootContext.ResolveCore ();
585                                 if (Report.Errors > 0)
586                                         return false;
587                         }
588                         
589                         TypeManager.InitCoreTypes ();
590                         if (timestamps)
591                                 ShowTime ("   Core Types done");
592                 
593                         //
594                         // The second pass of the compiler
595                         //
596                         if (timestamps)
597                                 ShowTime ("Resolving tree");
598                         RootContext.ResolveTree ();
599                         if (timestamps)
600                                 ShowTime ("Populate tree");
601
602                         if (Report.Errors > 0)
603                         {
604                                 error ("Compilation failed");
605                                 return false;
606                         }
607
608                         if (!RootContext.StdLib)
609                                 RootContext.BootCorlib_PopulateCoreTypes ();
610                         RootContext.PopulateTypes ();
611                         
612                         TypeManager.InitCodeHelpers ();
613                                 
614                         if (Report.Errors > 0)
615                         {
616                                 error ("Compilation failed");
617                                 return false;
618                         }
619                         
620                         //
621                         // The code generator
622                         //
623                         if (timestamps)
624                                 ShowTime ("Emitting code");
625                         RootContext.EmitCode ();
626                         if (timestamps)
627                                 ShowTime ("   done");
628
629                         if (Report.Errors > 0)
630                         {
631                                 error ("Compilation failed");
632                                 return false;
633                         }
634
635                         if (timestamps)
636                                 ShowTime ("Closing types");
637                         
638                         RootContext.CloseTypes ();
639
640                         //                      PEFileKinds k = PEFileKinds.ConsoleApplication;
641                         //                              
642                         //                      if (target == Target.Library || target == Target.Module)
643                         //                              k = PEFileKinds.Dll;
644                         //                      else if (target == Target.Exe)
645                         //                              k = PEFileKinds.ConsoleApplication;
646                         //                      else if (target == Target.WinExe)
647                         //                              k = PEFileKinds.WindowApplication;
648                         //
649                         //                      if (target == Target.Exe || target == Target.WinExe){
650                         //                              MethodInfo ep = RootContext.EntryPoint;
651                         //
652                         //                              if (ep == null){
653                         //                                      Report.Error (5001, "Program " + output_file +
654                         //                                                            " does not have an entry point defined");
655                         //                                      return;
656                         //                              }
657                         //                              
658                         //                              CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
659                         //                      }
660
661                         //
662                         // Add the resources
663                         //
664                         if (resources != null)
665                         {
666                                 foreach (string file in resources)
667                                         CodeGen.AssemblyBuilder.AddResourceFile (file, file);
668                         }
669                         
670                         CodeGen.Save (output_file);
671                         if (timestamps)
672                                 ShowTime ("Saved output");
673
674                         if (want_debugging_support) 
675                         {
676                                 CodeGen.SaveSymbols ();
677                                 if (timestamps)
678                                         ShowTime ("Saved symbols");
679                         }
680
681                         if (Report.ExpectedError != 0)
682                         {
683                                 Console.WriteLine("Failed to report expected error " + Report.ExpectedError);
684                                 Environment.Exit (1);
685                                 return false;
686                         }
687                         return (Report.Errors == 0);
688                 }
689
690         }
691 }