* driver.cs (InitializeRootContextAndOthersFromOptions):
[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, 2003, 2004 Rafael Teixeira
10 //
11
12 namespace Mono.Languages {
13
14         using System;
15         using System.Collections;
16         using System.IO;
17         using System.Text;
18         using System.Globalization;
19         using System.Reflection;
20         using System.Reflection.Emit;
21
22         using Mono.MonoBASIC;
23         using Mono.GetOptions;
24         using Mono.GetOptions.Useful;
25
26
27         enum OptionCompare {
28                 Binary, Text
29         };
30
31                 
32         /// <summary>
33         ///    The compiler driver.
34         /// </summary>
35         public class CompilerOptions : CommonCompilerOptions {
36
37                 // Temporary options
38                 //------------------------------------------------------------------
39                 [Option("[IGNORED] Only parses the source file (for debugging the tokenizer)", "parse", SecondLevelHelp = true)]
40                 public bool OnlyParse = false;
41
42                 [Option("[IGNORED] Only tokenizes source files", "tokenize", SecondLevelHelp = true)]
43                 public bool Tokenize = false;
44
45                 [Option("Shows stack trace at Error location", "stacktrace", SecondLevelHelp = true)]
46                 public bool Stacktrace = false;
47                 
48                 [Option("Makes errors fatal", "fatal", SecondLevelHelp = true)]
49                 public bool MakeErrorsFatal = false;
50                 
51                 [Option("Displays time stamps of various compiler events", "timestamp", SecondLevelHelp = true)]
52                 public virtual bool PrintTimeStamps {
53                         set
54                         {
55                                 printTimeStamps = true;
56                                 last_time = DateTime.Now;
57                                 DebugListOfArguments.Add("timestamp");
58                         }
59                 }
60
61                 // redefining some inherited options
62                 //------------------------------------------------------------------
63                 [Option("About the MonoBASIC compiler", "about")]
64                 public override WhatToDoNext DoAbout()
65                 {
66                         return base.DoAbout();
67                 }
68
69                 [KillOption]
70                 public override WhatToDoNext DoUsage() { return WhatToDoNext.GoAhead; }
71
72                 // language options
73                 //------------------------------------------------------------------
74
75                 [Option("Require explicit declaration of variables", "optionexplicit", VBCStyleBoolean = true)]
76                 public bool OptionExplicit = false;
77
78                 [Option("Enforce strict language semantics", "optionstrict", VBCStyleBoolean = true)]
79                 public bool OptionStrict = false;
80                 
81                 [Option("Specifies binary-style string comparisons. This is the default", "optioncompare:binary")]
82                 public bool OptionCompareBinary = true; 
83
84                 [Option("Specifies text-style string comparisons.", "optioncompare:text")]
85                 public bool OptionCompareText { set { OptionCompareBinary = false; } }
86
87                 protected override void InitializeOtherDefaults() 
88                 { 
89                         DefineSymbol = "__MonoBASIC__";
90                         ImportNamespaces = "Microsoft.VisualBasic";
91                 }
92                 
93                 private bool printTimeStamps = false;
94                 //
95                 // Last time we took the time
96                 //
97                 DateTime last_time;
98                 public void ShowTime (string msg)
99                 {
100                         DateTime now = DateTime.Now;
101                         TimeSpan span = now - last_time;
102                         last_time = now;
103
104                         Console.WriteLine (
105                                 "[{0:00}:{1:000}] {2}",
106                                 (int) span.TotalSeconds, span.Milliseconds, msg);
107                 }
108         }
109
110         public class Driver : CompilerOptions {
111                                 
112                 private void InitializeRootContextAndOthersFromOptions()
113                 {
114                         Report.Stacktrace = Stacktrace;
115                         Report.WarningsAreErrors = WarningsAreErrors;
116                         // TODO: change Report to receive the whole array
117                         for(int i = 0; i < WarningsToIgnore.Length; i++)
118                                 Report.SetIgnoreWarning(WarningsToIgnore[i]);
119                         Report.Fatal = MakeErrorsFatal;
120
121                         RootContext.WarningLevel = WarningLevel;
122                         RootContext.Checked = CheckedContext;
123                         RootContext.MainClass = MainClassName;
124                         RootContext.StdLib = !NoStandardLibraries;
125                         RootContext.Unsafe = AllowUnsafeCode;
126                         if (RootNamespace != null)
127                                 RootContext.RootNamespace = RootNamespace;
128                         
129                         // TODO: semantics are different and should be adjusted
130                         GenericParser.yacc_verbose_flag = Verbose ? 1 : 0;
131                         
132                         Mono.MonoBASIC.Parser.InitialOptionExplicit = OptionExplicit;
133                         Mono.MonoBASIC.Parser.InitialOptionStrict = OptionStrict;
134                     Mono.MonoBASIC.Parser.InitialOptionCompareBinary = OptionCompareBinary;
135                     Mono.MonoBASIC.Parser.ImportsList = Imports;
136                 }
137                 
138                 public ArrayList AssembliesToReferenceSoftly = new ArrayList();         
139
140                 public int LoadAssembly (string assembly, bool soft)
141                 {
142                         Assembly a;
143                         string total_log = "";
144
145                         try  {
146                                 char[] path_chars = { '/', '\\' };
147
148                                 if (assembly.IndexOfAny (path_chars) != -1)
149                                         a = Assembly.LoadFrom(assembly);
150                                 else {
151                                         string ass = assembly;
152                                         if (ass.EndsWith (".dll"))
153                                                 ass = assembly.Substring (0, assembly.Length - 4);
154                                         a = Assembly.Load (ass);
155                                 }
156                                 TypeManager.AddAssembly (a);
157                                 return 0;
158                         }
159                         catch (FileNotFoundException) {
160                                 if (PathsToSearchForLibraries != null) {
161                                         foreach (string dir in PathsToSearchForLibraries) {
162                                                 string full_path = dir + "/" + assembly + ".dll";
163
164                                                 try  {
165                                                         a = Assembly.LoadFrom (full_path);
166                                                         TypeManager.AddAssembly (a);
167                                                         return 0;
168                                                 } 
169                                                 catch (FileNotFoundException ff)  {
170                                                         total_log += ff.FusionLog;
171                                                         continue;
172                                                 }
173                                         }
174                                 }
175                                 if (soft)
176                                         return 0;
177                         }
178                         catch (BadImageFormatException f)  {
179                                 Error ("// Bad file format while loading assembly");
180                                 Error ("Log: " + f.FusionLog);
181                                 return 1;
182                         } catch (FileLoadException f){
183                                 Error ("File Load Exception: " + assembly);
184                                 Error ("Log: " + f.FusionLog);
185                                 return 1;
186                         } catch (ArgumentNullException){
187                                 Error ("// Argument Null exception ");
188                                 return 1;
189                         }
190                         
191                         Report.Error (6, "Can not find assembly `" + assembly + "'" );
192                         Console.WriteLine ("Log: \n" + total_log);
193
194                         return 0;
195                 }
196
197                 public void LoadModule (MethodInfo adder_method, string module)
198                 {
199                         System.Reflection.Module m;
200                         string total_log = "";
201
202                         try {
203                                 try {
204                                         m = (System.Reflection.Module)adder_method.Invoke (CodeGen.AssemblyBuilder, new object [] { module });
205                                 }
206                                 catch (TargetInvocationException ex) {
207                                         throw ex.InnerException;
208                                 }
209                                 TypeManager.AddModule (m);
210
211                         } 
212                         catch (FileNotFoundException) {
213                                 foreach (string dir in PathsToSearchForLibraries)       {
214                                         string full_path = Path.Combine (dir, module);
215                                         if (!module.EndsWith (".netmodule"))
216                                                 full_path += ".netmodule";
217
218                                         try {
219                                                 try {
220                                                         m = (System.Reflection.Module) adder_method.Invoke (CodeGen.AssemblyBuilder, new object [] { full_path });
221                                                 }
222                                                 catch (TargetInvocationException ex) {
223                                                         throw ex.InnerException;
224                                                 }
225                                                 TypeManager.AddModule (m);
226                                                 return;
227                                         }
228                                         catch (FileNotFoundException ff) {
229                                                 total_log += ff.FusionLog;
230                                                 continue;
231                                         }
232                                 }
233                                 Report.Error (6, "Cannot find module `" + module + "'" );
234                                 Console.WriteLine ("Log: \n" + total_log);
235                         }
236                         catch (BadImageFormatException f) {
237                                 Report.Error(6, "Cannot load module (bad file format)" + f.FusionLog);
238                         }
239                         catch (FileLoadException f)     {
240                                 Report.Error(6, "Cannot load module " + f.FusionLog);
241                         }
242                         catch (ArgumentNullException) {
243                                 Report.Error(6, "Cannot load module (null argument)");
244                         }
245                 }
246
247                 void Error(string message)
248                 {
249                         Console.WriteLine(message);
250                 }
251
252                 /// <summary>
253                 ///   Loads all assemblies referenced on the command line
254                 /// </summary>
255                 public int LoadReferences ()
256                 {
257                         int errors = 0;
258
259                         foreach (string r in AssembliesToReference)
260                                 errors += LoadAssembly (r, false);
261
262                         foreach (string r in AssembliesToReferenceSoftly)
263                                 errors += LoadAssembly (r, true);
264                         
265                         return errors;
266                 }
267
268                 //
269                 // Given a path specification, splits the path from the file/pattern
270                 //
271                 void SplitPathAndPattern (string spec, out string path, out string pattern)
272                 {
273                         int p = spec.LastIndexOf ("/");
274                         if (p != -1){
275                                 //
276                                 // Windows does not like /file.cs, switch that to:
277                                 // "\", "file.cs"
278                                 //
279                                 if (p == 0){
280                                         path = "\\";
281                                         pattern = spec.Substring (1);
282                                 } else {
283                                         path = spec.Substring (0, p);
284                                         pattern = spec.Substring (p + 1);
285                                 }
286                                 return;
287                         }
288
289                         p = spec.LastIndexOf ("\\");
290                         if (p != -1){
291                                 path = spec.Substring (0, p);
292                                 pattern = spec.Substring (p + 1);
293                                 return;
294                         }
295
296                         path = ".";
297                         pattern = spec;
298                 }
299
300                 bool AddFiles (string spec, bool recurse)
301                 {
302                         string path, pattern;
303
304                         SplitPathAndPattern(spec, out path, out pattern);
305                         if (pattern.IndexOf("*") == -1) {
306                                 DefaultArgumentProcessor(spec);
307                                 return true;
308                         }
309
310                         string [] files = null;
311                         try {
312                                 files = Directory.GetFiles(path, pattern);
313                         } catch (System.IO.DirectoryNotFoundException) {
314                                 Report.Error (2001, "Source file `" + spec + "' could not be found");
315                                 return false;
316                         } catch (System.IO.IOException){
317                                 Report.Error (2001, "Source file `" + spec + "' could not be found");
318                                 return false;
319                         }
320                         foreach (string f in files)
321                                 DefaultArgumentProcessor (f);
322
323                         if (!recurse)
324                                 return true;
325                         
326                         string [] dirs = null;
327
328                         try {
329                                 dirs = Directory.GetDirectories(path);
330                         } catch {
331                         }
332                         
333                         foreach (string d in dirs) {
334                                         
335                                 // Don't include path in this string, as each
336                                 // directory entry already does
337                                 AddFiles (d + "/" + pattern, true);
338                         }
339
340                         return true;
341                 }
342
343                 void DefineDefaultConfig ()
344                 {
345                         //
346                         // For now the "default config" is harcoded into the compiler
347                         // we can move this outside later
348                         //
349                         string [] default_config = 
350                         {
351                                 "System",
352                                 "System.Data",
353                                 "System.Xml",
354                                 "Microsoft.VisualBasic" , 
355 #if EXTRA_DEFAULT_REFS
356                                 //
357                                 // Is it worth pre-loading all this stuff?
358                                 //
359                                 "Accessibility",
360                                 "System.Configuration.Install",
361                                 "System.Design",
362                                 "System.DirectoryServices",
363                                 "System.Drawing.Design",
364                                 "System.Drawing",
365                                 "System.EnterpriseServices",
366                                 "System.Management",
367                                 "System.Messaging",
368                                 "System.Runtime.Remoting",
369                                 "System.Runtime.Serialization.Formatters.Soap",
370                                 "System.Security",
371                                 "System.ServiceProcess",
372                                 "System.Web",
373                                 "System.Web.RegularExpressions",
374                                 "System.Web.Services" ,
375                                 "System.Windows.Forms"
376 #endif
377                         };
378                         
379                         foreach (string def in default_config)
380                                 if (!(AssembliesToReference.Contains(def) || AssembliesToReference.Contains (def + ".dll")))
381                                         AssembliesToReferenceSoftly.Add(def);
382                 }
383
384                 void InitializeDebuggingSupport()
385                 {
386                         string[] debug_args = new string [DebugListOfArguments.Count];
387                         DebugListOfArguments.CopyTo(debug_args);
388                         CodeGen.Init(OutputFileName, OutputFileName, WantDebuggingSupport, debug_args);
389                         TypeManager.AddModule(CodeGen.ModuleBuilder);
390                 }
391
392                 public bool ResolveAllTypes() // Phase 2
393                 {
394                         // Load Core Library for default compilation
395                         if (RootContext.StdLib)
396                                 AssembliesToReference.Insert(0, "mscorlib");
397
398                         if (!NoConfig)
399                                 DefineDefaultConfig();
400
401                         ShowTime("Loading referenced assemblies");
402
403                         // Load assemblies required
404                         if (LoadReferences() > 0) {
405                                 Error ("Could not load one or more assemblies");
406                                 return false;
407                         }
408
409                                                 ShowTime("References loaded");
410
411                         InitializeDebuggingSupport();
412
413                         // TargetFileType is Module 
414                         if (TargetFileType == TargetType.Module) {
415                                 PropertyInfo module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
416                                 if (module_only == null) {
417                                         Report.Error (0, new Location (-1, -1), "Cannot use /TargetFileType:module on this runtime: try the Mono runtime instead.");
418                                         Environment.Exit (1);
419                                 }
420
421                                 MethodInfo set_method = module_only.GetSetMethod (true);
422                                 set_method.Invoke (CodeGen.AssemblyBuilder, BindingFlags.Default, null, new object[]{true}, null);
423
424                                 TypeManager.AddModule (CodeGen.ModuleBuilder);
425                         }
426
427                         if (NetModulesToAdd.Count > 0) {
428                                 MethodInfo adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic);
429                                 if (adder_method == null) {
430                                         Report.Error (0, new Location (-1, -1), "Cannot use /addmodule on this runtime: Try the Mono runtime instead.");
431                                         Environment.Exit (1);
432                                 }
433
434                                 foreach (string module in NetModulesToAdd)
435                                         LoadModule (adder_method, module);
436                         }
437
438
439                         //
440                         // Before emitting, we need to get the core
441                         // types emitted from the user defined types
442                         // or from the system ones.
443                         //
444                         ShowTime("Initializing Core Types");
445
446                         if (!RootContext.StdLib)
447                                 RootContext.ResolveCore ();
448                         if (Report.Errors > 0)
449                                 return false;
450                         
451                         TypeManager.InitCoreTypes();
452                         if (Report.Errors > 0)
453                                 return false;
454
455                         ShowTime("   Core Types done");
456
457                         ShowTime("Resolving tree");
458
459                         // The second pass of the compiler
460                         RootContext.ResolveTree ();
461                         if (Report.Errors > 0)
462                                 return false;
463                         
464                         ShowTime("Populate tree");
465
466                         if (!RootContext.StdLib)
467                                 RootContext.BootCorlib_PopulateCoreTypes();
468                         if (Report.Errors > 0)
469                                 return false;
470
471                         RootContext.PopulateTypes();
472                         if (Report.Errors > 0)
473                                 return false;
474                         
475                         TypeManager.InitCodeHelpers();
476                         if (Report.Errors > 0)
477                                 return false;
478
479                         return true;
480                 }
481                 
482                 bool IsSWFApp()
483                 {
484                         string mainclass = GetFQMainClass();
485                         
486                         if (mainclass != null) {
487                                 foreach (string r in AssembliesToReference) {
488                                         if (r.IndexOf ("System.Windows.Forms") >= 0) {
489                                                 Type t = TypeManager.LookupType(mainclass);
490                                                 if (t != null) 
491                                                         return t.IsSubclassOf (TypeManager.LookupType("System.Windows.Forms.Form"));
492                                                 break;  
493                                         }       
494                                 }
495                         }
496                         return false;
497                 }
498                 
499                 string GetFQMainClass()
500                 {       
501                         if (RootContext.RootNamespace != "")
502                                 return RootContext.RootNamespace + "." + RootContext.MainClass;
503                         else
504                                 return RootContext.MainClass;                   
505                 }
506                 
507                 void FixEntryPoint()
508                 {
509                         if (TargetFileType == TargetType.Exe || TargetFileType == TargetType.WinExe) {
510                                 MethodInfo ep = RootContext.EntryPoint;
511                         
512                                 if (ep == null) {
513                                         // If we don't have a valid entry point yet
514                                         // AND if System.Windows.Forms is included
515                                         // among the dependencies, we have to build
516                                         // a new entry point on-the-fly. Otherwise we
517                                         // won't be able to compile SWF code out of the box.
518
519                                         if (IsSWFApp())  {
520                                                 Type t = TypeManager.LookupType(GetFQMainClass());
521                                                 if (t != null) 
522                                                 {                                                       
523                                                         TypeBuilder tb = t as TypeBuilder;
524                                                         MethodBuilder mb = tb.DefineMethod ("Main", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, 
525                                                                 typeof(void), new Type[0]);
526
527                                                         Type SWFA = TypeManager.LookupType("System.Windows.Forms.Application");
528                                                         Type SWFF = TypeManager.LookupType("System.Windows.Forms.Form");
529                                                         Type[] args = new Type[1];
530                                                         args[0] = SWFF;
531                                                         MethodInfo mi = SWFA.GetMethod("Run", args);
532                                                         ILGenerator ig = mb.GetILGenerator();
533                                                         ConstructorInfo ci = TypeManager.GetConstructor (TypeManager.LookupType(t.FullName), new Type[0]);
534                                                         
535                                                         ig.Emit (OpCodes.Newobj, ci);
536                                                         ig.Emit (OpCodes.Call, mi);
537                                                         ig.Emit (OpCodes.Ret);
538
539                                                         RootContext.EntryPoint = mb as MethodInfo;
540                                                 }
541                                         }
542                                 }
543                         }
544                 }
545
546                 bool GenerateAssembly()
547                 {
548                         //
549                         // The code generator
550                         //
551                         ShowTime("Emitting code");
552                         
553                         RootContext.EmitCode();
554                         FixEntryPoint();
555                         if (Report.Errors > 0)
556                                 return false;
557
558                         ShowTime("   done");
559
560                         ShowTime("Closing types");
561
562                         RootContext.CloseTypes ();
563                         if (Report.Errors > 0)
564                                 return false;
565
566                         ShowTime("   done");
567
568                         PEFileKinds k = PEFileKinds.ConsoleApplication;
569                                                         
570                         if (TargetFileType == TargetType.Library || TargetFileType == TargetType.Module)
571                                 k = PEFileKinds.Dll;
572                         else if (TargetFileType == TargetType.Exe)
573                                 k = PEFileKinds.ConsoleApplication;
574                         else if (TargetFileType == TargetType.WinExe)
575                                 k = PEFileKinds.WindowApplication;
576                         
577                         if (TargetFileType == TargetType.Exe || TargetFileType == TargetType.WinExe) {
578                                 MethodInfo ep = RootContext.EntryPoint;
579                         
580                                 if (ep == null) {
581                                         Report.Error (30737, "Program " + OutputFileName +
582                                                 " does not have an entry point defined");
583                                         return false;
584                                 }
585                                                         
586                                 CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
587                         }
588
589                         // Add the resources
590                         if (EmbeddedResources != null)
591                                 foreach (string file in EmbeddedResources)
592                                                 CodeGen.AssemblyBuilder.AddResourceFile (file, file);
593                         
594                         CodeGen.Save(OutputFileName);
595
596                         ShowTime("Saved output");
597
598                         
599                         if (WantDebuggingSupport)  {
600                                 CodeGen.SaveSymbols ();
601                                 ShowTime ("Saved symbols");
602                         }
603
604                         return true;
605                 }
606
607                 public void CompileAll()
608                 {
609                         try {
610                                 InitializeRootContextAndOthersFromOptions();
611                                 
612                                 if (!ParseAll()) // Phase 1
613                                         return;
614
615                                 if (!ResolveAllTypes()) // Phase 2
616                                         return;
617
618                                 GenerateAssembly(); // Phase 3 
619                                 
620                         } catch (Exception ex) {
621                                 Error("Exception: " + ex.ToString());
622                         }
623                 }
624                 
625                 private bool quiet { get { return DontShowBanner || SuccintErrorDisplay; } } 
626                 
627                 private void Banner()
628                 {
629                         if (!quiet) {
630                                 ShowBanner();
631                                 // TODO: remove next lines when the compiler has matured enough
632                                 Console.WriteLine ("--------");
633                                 Console.WriteLine ("THIS IS AN ALPHA SOFTWARE.");
634                                 Console.WriteLine ("--------");
635                         }               
636                 }
637                 
638                 bool ParseAll() // Phase 1
639                 {
640                         foreach(FileToCompile file in SourceFilesToCompile)
641                                 GenericParser.Parse(file.Filename, file.Encoding);
642
643                         return (Report.Errors == 0);
644                 }
645
646                 /// <summary>
647                 ///    Parses the arguments, and calls the compilation process.
648                 /// </summary>
649                 int MainDriver(string [] args)
650                 {
651                         ProcessArgs(args);
652                         
653                         if (SourceFilesToCompile.Count == 0) {
654                                 if (!quiet) 
655                                         DoHelp();
656                                 return 2;
657                         }
658
659                         Banner();                       
660                         CompileAll();
661                         return Report.ProcessResults(quiet);
662                 }
663
664                 public static int Main (string[] args)
665                 {
666                         Driver Exec = new Driver();
667                         
668                         return Exec.MainDriver(args);
669                 }
670
671
672         }
673 }