2 // driver.cs: The compiler command line driver.
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
5 // Based on mcs by : Miguel de Icaza (miguel@gnu.org)
7 // Licensed under the terms of the GNU GPL
9 // (C) 2002 Rafael Teixeira
12 namespace Mono.Languages
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 using System.Globalization;
21 using Mono.GetOptions;
25 Library, Exe, Module, WinExe
34 /// The compiler driver.
36 public class Driver : Options
39 //------------------------------------------------------------------
40 [Option("[Mono] Only parses the source file (for debugging the tokenizer)", "parse")]
41 public bool parse_only = false;
43 [Option("[Mono] Only tokenizes source files")]
44 public bool tokenize = false;
46 [Option("[Mono] Shows stack trace at Error location")]
47 public bool stacktrace { set { Report.Stacktrace = value; } }
49 [Option("[Mono] Displays time stamps of various compiler events")]
55 last_time = DateTime.Now;
56 debug_arglist.Add("timestamp");
60 // Mono-specific options
61 //------------------------------------------------------------------
62 [Option("About the MonoBASIC compiler", "about")]
63 public override WhatToDoNext DoAbout()
65 return base.DoAbout();
68 [Option("[Mono] Don\'t assume the standard library", "nostdlib")]
69 public bool nostdlib { set { RootContext.StdLib = !value; } }
71 [Option("[Mono] Disables implicit references to assemblies", "noconfig")]
72 public bool NoConfig { set { load_default_config = !value; } }
74 [Option("[Mono] Allows unsafe code", "unsafe")]
75 public bool AllowUnsafeCode { set { RootContext.Unsafe = value; } }
77 [Option("[Mono] Set default context to checked", "checked")]
78 public bool Checked { set { RootContext.Checked = value; } }
80 [Option("[Mono] Debugger arguments", "debug-args")]
81 public WhatToDoNext SetDebugArgs(string args)
84 debug_arglist.AddRange (args.Split (sep));
85 return WhatToDoNext.GoAhead;
88 [Option("[Mono] Ignores warning number PARAM", "ignorewarn")]
89 public WhatToDoNext SetIgnoreWarning(int warn)
91 Report.SetIgnoreWarning(warn);
92 return WhatToDoNext.GoAhead;
95 [Option("[Mono] Sets warning level (the highest is 4, the default)", "wlevel")]
96 public int wlevel { set { RootContext.WarningLevel = value; } }
98 [Option("[Mono] Makes errors fatal", "fatal")]
99 public bool Fatal { set { Report.Fatal = value; } }
101 [Option("[Mono] Adds path to the assembly link path")]
102 public string[] linkpaths = null;
104 // Output file options
105 //------------------------------------------------------------------
106 [Option("Specifies the output file name", 'o', "out")]
107 public string output_file = null;
109 [Option("Specifies the target type for the output file (exe [default], winexe, library, module)", "target")]
110 public WhatToDoNext SetTarget(string type)
112 switch (type.ToLower())
115 target = Target.Library;
124 target = Target.WinExe;
128 target = Target.Module;
132 return WhatToDoNext.GoAhead;
135 // input file options
136 //------------------------------------------------------------------
137 [Option("[NOT IMPLEMENTED YET]Reference metadata from specified module", "addmodule")]
138 public string[] addedModules = null;
140 [Option("[NOT IMPLEMENTED YET]Include all files in the current directory and subdirectories according to the wildcard", "recurse")]
141 public WhatToDoNext recurse(string wildcard)
143 //AddFiles (DirName, true); // TODO wrong semantics
144 return WhatToDoNext.GoAhead;
147 [Option(-1, "References metadata from the specified assembly", 'r', "reference")]
148 public string reference { set { references.Add(value); } }
151 //------------------------------------------------------------------
152 public ArrayList EmbeddedResources = new ArrayList();
154 // TODO : accept a multi-letter short form: 'res'
155 [Option(-1, "Adds the specified file as an embedded assembly resource", "resource")]
156 public string resource { set { EmbeddedResources.Add(value); } }
158 public ArrayList LinkedResources = new ArrayList();
160 // TODO : accept a multi-letter short form: 'linkres'
161 [Option(-1, "[NOT IMPLEMENTED YET]Adds the specified file as an embedded assembly resource", "linkresource")]
162 public string linkresource { set { LinkedResources.Add(value); } }
164 public ArrayList Win32Resources = new ArrayList();
166 [Option(-1, "[NOT IMPLEMENTED YET]Specifies a Win32 resource file (.res)", "win32resource")]
167 public string win32resource { set { Win32Resources.Add(value); } }
169 public ArrayList Win32Icons = new ArrayList();
171 // TODO : accept a multi-letter short form: 'res'
172 [Option(-1, "[NOT IMPLEMENTED YET]Specifies a Win32 icon file (.ico) for the default Win32 resources", "win32icon")]
173 public string win32icon { set { Win32Icons.Add(value); } }
176 // code generation options
177 //------------------------------------------------------------------
178 [Option("[NOT IMPLEMENTED YET]Enable optimizations")]
179 public bool optimize = false;
181 [Option("[NOT IMPLEMENTED YET]Remove integer checks. Default off.")]
182 public bool removeintchecks = false;
184 // TODO: handle VB.NET [+|-] boolean syntax
185 [Option("Emit debugging information", "debug")]
186 public bool want_debugging_support = false;
188 [Option("Emit full debugging information (default)", "debug:full")]
189 public bool fullDebugging = false;
191 [Option("[IGNORED]Emit PDB file only", "debug:pdbonly")]
192 public bool pdbOnly = false;
194 // errors and warnings options
195 //------------------------------------------------------------------
196 [Option("Treat warnings as errors", "warnaserror")]
197 public bool WarningsAreErrors { set { Report.WarningsAreErrors = value; } }
199 [Option("Disable warnings")]
200 public bool nowarn { set { if (value) RootContext.WarningLevel = 0; } }
204 //------------------------------------------------------------------
205 public Hashtable Defines = new Hashtable();
207 // TODO: Symbol-List parsing
208 [Option(-1, "[NOT IMPLEMENTED YET]Declares global conditional compilation symbol(s). symbol list:name=value,...", 'd', "define")]
209 public string define {
211 foreach(string item in value.Split(',')) {
212 string[] dados = item.Split('=');
213 if (dados.Length > 1)
214 Defines.Add(dados[0], dados[1]);
216 Defines.Add(dados[0], string.Empty);
221 private string[] importsList = null;
223 [Option("[NOT IMPLEMENTED YET]Declare global Imports for namespaces in referenced metadata files. import list:namespace,...", "imports")]
224 public WhatToDoNext imports(string importslist)
226 importsList = importslist.Split(';');
227 return WhatToDoNext.GoAhead;
230 // TODO: handle VB.NET [+|-] boolean syntax
231 [Option("[NOT IMPLEMENTED YET]Require explicit declaration of variables")]
232 public bool optionexplicit { set { Mono.MonoBASIC.Parser.InitialOptionExplicit = value; } }
234 // TODO: handle VB.NET [+|-] boolean syntax
235 [Option("[NOT IMPLEMENTED YET]Enforce strict language semantics")]
236 public bool optionstrict { set { Mono.MonoBASIC.Parser.InitialOptionStrict = value; } }
238 [Option("[NOT IMPLEMENTED YET]Specifies binary-style string comparisons. This is the default", "optioncompare:binary")]
239 public bool optioncomparebinary { set { Mono.MonoBASIC.Parser.InitialOptionCompareBinary = true; } }
241 [Option("[NOT IMPLEMENTED YET]Specifies text-style string comparisons.", "optioncompare:text")]
242 public bool optioncomparetext { set { Mono.MonoBASIC.Parser.InitialOptionCompareBinary = false; } }
244 [Option("Specifies de root namespace for all type declarations")]
245 public string rootnamespace { set { RootContext.RootNamespace = value; } }
247 // Miscellaneous options
248 //------------------------------------------------------------------
250 [Option("[NOT IMPLEMENTED YET]Do not display compiler copyright banner")]
251 public bool nologo = false;
253 [Option("[NOT IMPLEMENTED YET]Quiet output mode")]
254 public bool quiet = false;
256 // TODO: semantics are different and should be adjusted
257 [Option("Display verbose messages", 'v')]
258 public bool verbose { set { GenericParser.yacc_verbose_flag = value; } }
261 //------------------------------------------------------------------
262 // TODO: force option to accept number in hex format
263 [Option("[NOT IMPLEMENTED YET]The base address for a library or module (hex)")]
264 public int baseaddress;
266 [Option("[NOT IMPLEMENTED YET]Create bug report file")]
267 public string bugreport;
269 // TODO: handle VB.NET [+|-] boolean syntax
270 [Option("[NOT IMPLEMENTED YET]Delay-sign the assembly using only the public portion of the strong name key")]
271 public bool delaysign;
273 [Option("[NOT IMPLEMENTED YET]Specifies a strong name key container")]
274 public string keycontainer;
276 [Option("[NOT IMPLEMENTED YET]Specifies a strong name key file")]
277 public string keyfile;
279 public string[] libpath = null;
281 [Option("[NOT IMPLEMENTED YET]List of directories to search for metada references (semi-colon delimited)", "libpath")]
282 public WhatToDoNext setlibpath(string pathlist)
284 libpath = pathlist.Split(';');
285 return WhatToDoNext.GoAhead;
288 [Option(@"Specifies the Class or Module that contains Sub Main.
289 It can also be a Class that inherits from System.Windows.Forms.Form.",
291 public string main { set { RootContext.MainClass = value; } }
293 // TODO: handle VB.NET [+|-] boolean syntax
294 [Option("[NOT IMPLEMENTED YET]Emit compiler output in UTF8 character encoding")]
295 public bool utf8output;
297 // TODO : response file support
299 ArrayList defines = new ArrayList();
300 ArrayList references = new ArrayList();
301 ArrayList soft_references = new ArrayList();
302 string first_source = null;
303 Target target = Target.Exe;
304 string target_ext = ".exe";
305 ArrayList debug_arglist = new ArrayList ();
306 bool timestamps = false;
307 Hashtable source_files = new Hashtable ();
308 bool load_default_config = true;
311 // Last time we took the time
314 void ShowTime (string msg)
316 DateTime now = DateTime.Now;
317 TimeSpan span = now - last_time;
321 "[{0:00}:{1:000}] {2}",
322 (int) span.TotalSeconds, span.Milliseconds, msg);
325 public static int Main (string[] args)
327 Driver Exec = new Driver();
329 return Exec.MainDriver(args);
332 public int LoadAssembly (string assembly, bool soft)
335 string total_log = "";
338 char[] path_chars = { '/', '\\' };
340 if (assembly.IndexOfAny (path_chars) != -1)
341 a = Assembly.LoadFrom(assembly);
343 a = Assembly.Load(assembly);
344 TypeManager.AddAssembly (a);
346 } catch (FileNotFoundException){
347 foreach (string dir in linkpaths){
348 string full_path = dir + "/" + assembly + ".dll";
351 a = Assembly.LoadFrom (full_path);
352 TypeManager.AddAssembly (a);
354 } catch (FileNotFoundException ff) {
355 total_log += ff.FusionLog;
361 } catch (BadImageFormatException f) {
362 Error ("// Bad file format while loading assembly");
363 Error ("Log: " + f.FusionLog);
365 } catch (FileLoadException f){
366 Error ("File Load Exception: " + assembly);
367 Error ("Log: " + f.FusionLog);
369 } catch (ArgumentNullException){
370 Error ("// Argument Null exception ");
374 Report.Error (6, "Can not find assembly `" + assembly + "'" );
375 Console.WriteLine ("Log: \n" + total_log);
380 void Error(string message)
382 Console.WriteLine(message);
386 /// Loads all assemblies referenced on the command line
388 public int LoadReferences ()
392 foreach (string r in references)
393 errors += LoadAssembly (r, false);
395 foreach (string r in soft_references)
396 errors += LoadAssembly (r, true);
401 void SetupDefaultDefines ()
403 defines = new ArrayList ();
404 defines.Add ("__MonoBASIC__");
409 // Returns the directory where the system assemblies are installed
411 string GetSystemDir ()
413 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
415 foreach (Assembly a in assemblies){
416 string codebase = a.CodeBase;
417 if (codebase.EndsWith ("corlib.dll")){
418 return codebase.Substring (0, codebase.LastIndexOf ("/"));
422 Report.Error (-15, "Can not compute my system path");
427 // Given a path specification, splits the path from the file/pattern
429 void SplitPathAndPattern (string spec, out string path, out string pattern)
431 int p = spec.LastIndexOf ("/");
434 // Windows does not like /file.cs, switch that to:
439 pattern = spec.Substring (1);
441 path = spec.Substring (0, p);
442 pattern = spec.Substring (p + 1);
447 p = spec.LastIndexOf ("\\");
449 path = spec.Substring (0, p);
450 pattern = spec.Substring (p + 1);
458 bool AddFiles (string spec, bool recurse)
460 string path, pattern;
462 SplitPathAndPattern(spec, out path, out pattern);
463 if (pattern.IndexOf("*") == -1)
469 string [] files = null;
471 files = Directory.GetFiles(path, pattern);
472 } catch (System.IO.DirectoryNotFoundException) {
473 Report.Error (2001, "Source file `" + spec + "' could not be found");
475 } catch (System.IO.IOException){
476 Report.Error (2001, "Source file `" + spec + "' could not be found");
479 foreach (string f in files)
485 string [] dirs = null;
488 dirs = Directory.GetDirectories(path);
492 foreach (string d in dirs) {
494 // Don't include path in this string, as each
495 // directory entry already does
496 AddFiles (d + "/" + pattern, true);
502 void DefineDefaultConfig ()
505 // For now the "default config" is harcoded into the compiler
506 // we can move this outside later
508 string [] default_config =
513 "Microsoft.VisualBasic" ,
514 #if EXTRA_DEFAULT_REFS
516 // Is it worth pre-loading all this stuff?
519 "System.Configuration.Install",
521 "System.DirectoryServices",
522 "System.Drawing.Design",
524 "System.EnterpriseServices",
527 "System.Runtime.Remoting",
528 "System.Runtime.Serialization.Formatters.Soap",
530 "System.ServiceProcess",
532 "System.Web.RegularExpressions",
533 "System.Web.Services" ,
534 "System.Windows.Forms"
538 foreach (string def in default_config)
539 soft_references.Add(def);
543 public void AddFile(string fileName)
546 if (first_source == null)
549 if (source_files.Contains(f))
550 Report.Error(1516, "Source file '" + f + "' specified multiple times");
552 source_files.Add(f, f);
555 void ProcessSourceFile(string filename)
558 GenericParser.Tokenize(filename);
560 GenericParser.Parse(filename);
563 string outputFile_Name = null;
565 string outputFileName
569 if (outputFile_Name == null)
571 if (output_file == null)
573 int pos = first_source.LastIndexOf(".");
576 output_file = first_source.Substring(0, pos);
578 output_file = first_source;
580 string bname = CodeGen.Basename(output_file);
581 if (bname.IndexOf(".") == -1)
582 output_file += target_ext;
583 outputFile_Name = output_file;
585 return outputFile_Name;
590 /// Parses the arguments, and calls the compilation process.
592 int MainDriver(string [] args)
595 if (first_source == null)
603 if (Report.Errors == 0)
605 Console.Write("Compilation succeeded");
606 if (Report.Warnings > 0)
608 Console.Write(" - {0} warning(s)", Report.Warnings);
613 Console.WriteLine("Compilation failed: {0} Error(s), {1} warnings",
614 Report.Errors, Report.Warnings);
620 SetupDefaultDefines();
623 bool ParseAll() // Phase 1
625 if (first_source == null)
627 Report.Error(2008, "No files to compile were specified");
631 foreach(string filename in source_files.Values)
632 ProcessSourceFile(filename);
634 if (tokenize || parse_only || (Report.Errors > 0))
637 return true; // everything went well go ahead
640 void InitializeDebuggingSupport()
642 string[] debug_args = new string [debug_arglist.Count];
643 debug_arglist.CopyTo(debug_args);
644 CodeGen.Init(outputFileName, outputFileName, want_debugging_support, debug_args);
645 TypeManager.AddModule(CodeGen.ModuleBuilder);
648 public bool ResolveAllTypes() // Phase 2
650 // Load Core Library for default compilation
651 if (RootContext.StdLib)
652 references.Insert(0, "mscorlib");
654 if (load_default_config)
655 DefineDefaultConfig();
658 ShowTime("Loading references");
660 // Load assemblies required
661 if (LoadReferences() > 0)
663 Error ("Could not load one or more assemblies");
668 ShowTime("References loaded");
670 InitializeDebuggingSupport();
673 // Before emitting, we need to get the core
674 // types emitted from the user defined types
675 // or from the system ones.
678 ShowTime ("Initializing Core Types");
680 if (!RootContext.StdLib)
681 RootContext.ResolveCore ();
682 if (Report.Errors > 0)
685 TypeManager.InitCoreTypes();
686 if (Report.Errors > 0)
690 ShowTime (" Core Types done");
693 ShowTime ("Resolving tree");
695 // The second pass of the compiler
696 RootContext.ResolveTree ();
697 if (Report.Errors > 0)
701 ShowTime ("Populate tree");
703 if (!RootContext.StdLib)
704 RootContext.BootCorlib_PopulateCoreTypes();
705 if (Report.Errors > 0)
708 RootContext.PopulateTypes();
709 if (Report.Errors > 0)
712 TypeManager.InitCodeHelpers();
713 if (Report.Errors > 0)
721 bool hasSWF = false, isForm = false;
722 string mainclass = GetFQMainClass();
724 foreach (string r in references) {
725 if (r.IndexOf ("System.Windows.Forms") >= 0) {
730 if (mainclass != ".") {
731 Type t = TypeManager.LookupType(mainclass);
733 isForm = t.IsSubclassOf (TypeManager.LookupType("System.Windows.Forms.Form"));
735 return (hasSWF && isForm);
738 string GetFQMainClass()
740 if (RootContext.RootNamespace != "")
741 return RootContext.RootNamespace + "." + RootContext.MainClass;
743 return RootContext.MainClass;
748 if (target == Target.Exe || target == Target.WinExe)
750 MethodInfo ep = RootContext.EntryPoint;
754 // If we don't have a valid entry point yet
755 // AND if System.Windows.Forms is included
756 // among the dependencies, we have to build
757 // a new entry point on-the-fly. Otherwise we
758 // won't be able to compile SWF code out of the box.
762 Type t = TypeManager.LookupType(GetFQMainClass());
765 TypeBuilder tb = t as TypeBuilder;
766 MethodBuilder mb = tb.DefineMethod ("Main", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard,
767 typeof(void), new Type[0]);
769 Type SWFA = TypeManager.LookupType("System.Windows.Forms.Application");
770 Type SWFF = TypeManager.LookupType("System.Windows.Forms.Form");
771 Type[] args = new Type[1];
773 MethodInfo mi = SWFA.GetMethod("Run", args);
774 ILGenerator ig = mb.GetILGenerator();
775 ConstructorInfo ci = TypeManager.GetConstructor (TypeManager.LookupType(t.FullName), new Type[0]);
777 ig.Emit (OpCodes.Newobj, ci);
778 ig.Emit (OpCodes.Call, mi);
779 ig.Emit (OpCodes.Ret);
781 RootContext.EntryPoint = mb as MethodInfo;
788 bool GenerateAssembly()
791 // The code generator
794 ShowTime ("Emitting code");
798 RootContext.EmitCode();
800 if (Report.Errors > 0)
808 ShowTime ("Closing types");
810 RootContext.CloseTypes ();
811 if (Report.Errors > 0)
817 PEFileKinds k = PEFileKinds.ConsoleApplication;
819 if (target == Target.Library || target == Target.Module)
821 else if (target == Target.Exe)
822 k = PEFileKinds.ConsoleApplication;
823 else if (target == Target.WinExe)
824 k = PEFileKinds.WindowApplication;
826 if (target == Target.Exe || target == Target.WinExe)
828 MethodInfo ep = RootContext.EntryPoint;
832 Report.Error (5001, "Program " + outputFileName +
833 " does not have an entry point defined");
837 CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
841 if (EmbeddedResources != null)
842 foreach (string file in EmbeddedResources)
843 CodeGen.AssemblyBuilder.AddResourceFile (file, file);
845 CodeGen.Save(outputFileName);
848 ShowTime ("Saved output");
851 if (want_debugging_support)
853 CodeGen.SaveSymbols ();
855 ShowTime ("Saved symbols");
861 public void CompileAll()
864 VB.NET expects the default namespace to be "" (empty string)
866 if (RootContext.RootNamespace == "")
868 RootContext.RootNamespace = System.IO.Path.GetFileNameWithoutExtension(outputFileName);
871 if (!ParseAll()) // Phase 1
874 if (!ResolveAllTypes()) // Phase 2
877 if (!GenerateAssembly()) // Phase 3
880 if (Report.ExpectedError != 0)
881 Error("Failed to report expected Error " + Report.ExpectedError);