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