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;
23 Library, Exe, Module, WinExe
27 /// The compiler driver.
33 // Assemblies references to be linked. Initialized with
34 // mscorlib.dll elsewhere.
35 static ArrayList references;
38 // If any of these fail, we ignore the problem. This is so
39 // that we can list all the assemblies in Windows and not fail
40 // if they are missing on Linux.
42 static ArrayList soft_references;
45 static ArrayList link_paths;
47 // Whether we want to only run the tokenizer
48 static bool tokenize = false;
50 static int error_count = 0;
52 static string first_source;
54 static Target target = Target.Exe;
55 static string target_ext = ".exe";
57 static bool want_debugging_support = false;
58 static ArrayList debug_arglist = new ArrayList ();
60 static bool parse_only = false;
61 static bool timestamps = false;
64 // Whether to load the initial config file (what CSC.RSP has by default)
66 static bool load_default_config = true;
68 static Hashtable response_file_list;
69 static Hashtable source_files = new Hashtable ();
72 // An array of the defines from the command line
74 static ArrayList defines;
77 // A list of resource files
79 static ArrayList resources = new ArrayList();
82 // Last time we took the time
84 static DateTime last_time;
85 static void ShowTime (string msg)
87 DateTime now = DateTime.Now;
88 TimeSpan span = now - last_time;
92 "[{0:00}:{1:000}] {2}",
93 (int) span.TotalSeconds, span.Milliseconds, msg);
97 static void Usage (bool is_error)
99 Console.WriteLine ( @"
100 MonoBASIC Compiler, Copyright (C)2002 Rafael Teixeira.
101 Usage: mbas [options] source-files
103 --about About the MonoBASIC compiler
104 --checked Set default context to checked
105 --define SYM Defines the symbol SYM
106 --fatal Makes errors fatal
107 -g, --debug Write symbolic debugging information to FILE-debug.s
108 -h, --help Prints this usage instructions
109 -L PATH Adds PATH to the assembly link path
111 --main CLASS Specifies CLASS as main (starting) class
112 --noconfig Disables implicit references to assemblies
113 --nostdlib Does not load core libraries
114 --nowarn XXX Ignores warning number XXX
116 --output FNAME Specifies output file
118 --parse Only parses the source file (for debugging the tokenizer)
119 --probe X Probes for the source to generate code X on line L
120 -r ASSEMBLY References an assembly
121 --recurse SPEC Recursively compiles the files in SPEC ([dir]/file)
122 --resource FILE Adds FILE as a resource
123 --stacktrace Shows stack trace at error location
124 --target KIND Specifies the target (KIND is one of: exe, winexe, library, module)
125 --tokenize Only tokenizes source files
126 --timestamp Displays time stamps of various compiler events
127 --unsafe Allows unsafe code
128 --werror Treat warnings as errors
129 -v Verbose parsing (for debugging the parser)
130 --wlevel LEVEL Sets warning level (the highest is 4, the default)
131 @file Read response file for more options
142 static void error (string msg)
144 Console.WriteLine ("Error: " + msg);
147 static void notice (string msg)
149 Console.WriteLine (msg);
152 private static Mono.GetOptions.OptionList Options;
154 private static bool SetVerboseParsing(object nothing)
156 GenericParser.yacc_verbose_flag = true;
160 public static int Main (string[] args)
162 Options = new Mono.GetOptions.OptionList("The compiler source code is released under the terms of the GNU GPL\n\n" +
163 "For more information on Mono, visit the project Web site\n" +
164 " http://www.go-mono.com" , "mbas [options] source-files");
165 Options.AddAbout(' ',"about", "About the MonoBASIC compiler");
166 Options.AddBooleanSwitch ('v',"verbose", "Verbose parsing (for debugging the parser)", false, new Mono.GetOptions.OptionFound(SetVerboseParsing) );
168 return (error_count + Report.Errors) != 0 ? 1 : 0;
171 static public int LoadAssembly (string assembly, bool soft)
174 string total_log = "";
177 char[] path_chars = { '/', '\\', '.' };
179 if (assembly.IndexOfAny (path_chars) != -1)
180 a = Assembly.LoadFrom (assembly);
182 a = Assembly.Load (assembly);
183 TypeManager.AddAssembly (a);
185 } catch (FileNotFoundException){
186 foreach (string dir in link_paths){
187 string full_path = dir + "/" + assembly + ".dll";
190 a = Assembly.LoadFrom (full_path);
191 TypeManager.AddAssembly (a);
193 } catch (FileNotFoundException ff) {
194 total_log += ff.FusionLog;
200 } catch (BadImageFormatException f) {
201 error ("// Bad file format while loading assembly");
202 error ("Log: " + f.FusionLog);
204 } catch (FileLoadException f){
205 error ("File Load Exception: " + assembly);
206 error ("Log: " + f.FusionLog);
208 } catch (ArgumentNullException){
209 error ("// Argument Null exception ");
213 Report.Error (6, "Can not find assembly `" + assembly + "'" );
214 Console.WriteLine ("Log: \n" + total_log);
220 /// Loads all assemblies referenced on the command line
222 static public int LoadReferences ()
226 foreach (string r in references)
227 errors += LoadAssembly (r, false);
229 foreach (string r in soft_references)
230 errors += LoadAssembly (r, true);
235 static void SetupDefaultDefines ()
237 defines = new ArrayList ();
238 defines.Add ("__MonoBASIC__");
241 static string [] LoadArgs (string file)
244 ArrayList args = new ArrayList ();
247 f = new StreamReader (file);
252 while ((line = f.ReadLine ()) != null){
253 string [] line_args = line.Split (new char [] { ' ' });
255 foreach (string arg in line_args)
259 string [] ret_value = new string [args.Count];
260 args.CopyTo (ret_value, 0);
266 // Returns the directory where the system assemblies are installed
268 static string GetSystemDir ()
270 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
272 foreach (Assembly a in assemblies){
273 string codebase = a.CodeBase;
274 if (codebase.EndsWith ("corlib.dll")){
275 return codebase.Substring (0, codebase.LastIndexOf ("/"));
279 Report.Error (-15, "Can not compute my system path");
284 // Given a path specification, splits the path from the file/pattern
286 static void SplitPathAndPattern (string spec, out string path, out string pattern)
288 int p = spec.LastIndexOf ("/");
291 // Windows does not like /file.cs, switch that to:
296 pattern = spec.Substring (1);
298 path = spec.Substring (0, p);
299 pattern = spec.Substring (p + 1);
304 p = spec.LastIndexOf ("\\");
306 path = spec.Substring (0, p);
307 pattern = spec.Substring (p + 1);
315 static int AddFile(string f)
317 if (first_source == null)
320 if (source_files.Contains (f)){
323 "Source file `" + f + "' specified multiple times");
326 source_files.Add(f, f);
331 static int ProcessSourceFile(string filename)
334 GenericParser.Tokenize(filename);
336 return GenericParser.Parse(filename);
341 static int AddFiles (string spec, bool recurse)
343 string path, pattern;
346 SplitPathAndPattern (spec, out path, out pattern);
347 if (pattern.IndexOf ("*") == -1){
348 return AddFile (spec);
351 string [] files = null;
353 files = Directory.GetFiles (path, pattern);
354 } catch (System.IO.DirectoryNotFoundException) {
355 Report.Error (2001, "Source file `" + spec + "' could not be found");
357 } catch (System.IO.IOException){
358 Report.Error (2001, "Source file `" + spec + "' could not be found");
361 foreach (string f in files)
362 errors += AddFile (f);
367 string [] dirs = null;
370 dirs = Directory.GetDirectories (path);
374 foreach (string d in dirs) {
376 // Don't include path in this string, as each
377 // directory entry already does
378 errors += AddFiles (d + "/" + pattern, true);
385 static void DefineDefaultConfig ()
388 // For now the "default config" is harcoded into the compiler
389 // we can move this outside later
391 string [] default_config =
396 "Microsoft.VisualBasic", // just for now
399 // Is it worth pre-loading all this stuff?
402 "System.Configuration.Install",
404 "System.DirectoryServices",
405 "System.Drawing.Design",
407 "System.EnterpriseServices",
410 "System.Runtime.Remoting",
411 "System.Runtime.Serialization.Formatters.Soap",
413 "System.ServiceProcess",
415 "System.Web.RegularExpressions",
416 "System.Web.Services",
417 "System.Windows.Forms"
422 foreach (string def in default_config)
423 soft_references.Insert (p++, def);
427 /// Parses the arguments, and drives the compilation
432 /// TODO: Mostly structured to debug the compiler
433 /// now, needs to be turned into a real driver soon.
435 static void MainDriver (string [] args)
438 string output_file = null;
439 bool parsing_options = true;
441 references = new ArrayList ();
442 soft_references = new ArrayList ();
443 link_paths = new ArrayList ();
444 SetupDefaultDefines ();
449 // This is not required because Assembly.Load knows about this
452 link_paths.Add (GetSystemDir ());
454 Options.ProcessArgs(args);
457 int argc = args.Length;
458 for (i = 0; i < argc; i++){
459 string arg = args [i];
461 if (arg.StartsWith ("@")){
462 string [] new_args, extra_args;
463 string response_file = arg.Substring (1);
465 if (response_file_list == null)
466 response_file_list = new Hashtable ();
468 if (response_file_list.Contains (response_file)){
470 1515, "Response file `" + response_file +
471 "' specified multiple times");
472 Environment.Exit (1);
475 response_file_list.Add (response_file, response_file);
477 extra_args = LoadArgs (response_file);
478 if (extra_args == null){
479 Report.Error (2011, "Unable to open response file: " +
484 new_args = new string [extra_args.Length + argc];
485 args.CopyTo (new_args, 0);
486 extra_args.CopyTo (new_args, argc);
488 argc = new_args.Length;
493 // Prepare to recurse
496 if (parsing_options && (arg.StartsWith ("-"))){
499 GenericParser.yacc_verbose_flag = true;
503 parsing_options = false;
510 case "--main": case "-m":
511 if ((i + 1) >= argc){
515 RootContext.MainClass = args [++i];
519 RootContext.Unsafe = true;
523 RootContext.Optimize = true;
526 case "/?": case "/h": case "/help":
532 if ((i + 1) >= argc){
536 defines.Add (args [++i]);
544 args [++i], NumberStyles.AllowLeadingSign);
545 Report.SetProbe (code);
547 Report.Error (-14, "Invalid number specified");
559 if ((i + 1) >= argc){
563 output_file = args [++i];
564 string bname = CodeGen.Basename (output_file);
565 if (bname.IndexOf (".") == -1)
566 output_file += ".exe";
570 RootContext.Checked = true;
574 Report.Stacktrace = true;
578 if ((i + 1) >= argc){
583 string type = args [++i];
586 target = Target.Library;
595 target = Target.WinExe;
599 target = Target.Module;
609 if ((i + 1) >= argc){
614 references.Add(args [++i]);
621 Console.WriteLine("Missing argument to --resource");
625 resources.Add(args [++i]);
630 if ((i + 1) >= argc){
634 link_paths.Add (args [++i]);
638 RootContext.StdLib = false;
646 Report.WarningsAreErrors = true;
650 if ((i + 1) >= argc){
657 warn = Int32.Parse (args [++i]);
662 Report.SetIgnoreWarning (warn);
666 if ((i + 1) >= argc){
669 "--wlevel requires an value from 0 to 4");
676 level = Int32.Parse (args [++i]);
680 "--wlevel requires an value from 0 to 4");
683 if (level < 0 || level > 4){
684 Report.Error (1900, "Warning level must be 0 to 4");
687 RootContext.WarningLevel = level;
695 if ((i + 1) >= argc){
696 Console.WriteLine ("--recurse requires an argument");
700 errors += AddFiles (args [++i], true);
705 last_time = DateTime.Now;
706 debug_arglist.Add("timestamp");
709 case "--debug": case "-g":
710 want_debugging_support = true;
714 if ((i + 1) >= argc){
715 Console.WriteLine ("--debug-args requires an argument");
719 char[] sep = { ',' };
720 debug_arglist.AddRange (args [++i].Split (sep));
724 load_default_config = false;
728 Console.WriteLine ("Unknown option: " + arg);
734 // Rafael: Does not compile them yet!!!
735 errors += AddFiles(arg, false);
738 //Rafael: Compile all source files!!!
739 foreach(string filename in source_files.Values)
740 errors += ProcessSourceFile(filename);
742 if (first_source == null)
744 Report.Error (2008, "No files to compile were specified");
751 if (Report.Errors > 0)
758 // Load Core Library for default compilation
760 if (RootContext.StdLib)
761 references.Insert (0, "mscorlib");
763 if (load_default_config)
764 DefineDefaultConfig ();
767 error ("Parsing failed");
772 // Load assemblies required
775 ShowTime ("Loading references");
776 errors += LoadReferences ();
778 ShowTime (" References loaded");
781 error ("Could not load one or more assemblies");
785 error_count = errors;
790 if (output_file == null){
791 int pos = first_source.LastIndexOf (".");
794 output_file = first_source.Substring (0, pos) + target_ext;
796 output_file = first_source + target_ext;
799 string[] debug_args = new string [debug_arglist.Count];
800 debug_arglist.CopyTo(debug_args);
801 CodeGen.Init (output_file, output_file, want_debugging_support, debug_args);
803 TypeManager.AddModule (CodeGen.ModuleBuilder);
806 // Before emitting, we need to get the core
807 // types emitted from the user defined types
808 // or from the system ones.
811 ShowTime ("Initializing Core Types");
812 if (!RootContext.StdLib){
813 RootContext.ResolveCore ();
814 if (Report.Errors > 0)
818 TypeManager.InitCoreTypes ();
820 ShowTime (" Core Types done");
823 // The second pass of the compiler
826 ShowTime ("Resolving tree");
827 RootContext.ResolveTree ();
829 ShowTime ("Populate tree");
831 if (Report.Errors > 0){
832 error ("Compilation failed");
836 if (!RootContext.StdLib)
837 RootContext.BootCorlib_PopulateCoreTypes ();
838 RootContext.PopulateTypes ();
840 TypeManager.InitCodeHelpers ();
842 if (Report.Errors > 0){
843 error ("Compilation failed");
848 // The code generator
851 ShowTime ("Emitting code");
852 RootContext.EmitCode ();
856 if (Report.Errors > 0){
857 error ("Compilation failed");
862 ShowTime ("Closing types");
864 RootContext.CloseTypes ();
866 // PEFileKinds k = PEFileKinds.ConsoleApplication;
868 // if (target == Target.Library || target == Target.Module)
869 // k = PEFileKinds.Dll;
870 // else if (target == Target.Exe)
871 // k = PEFileKinds.ConsoleApplication;
872 // else if (target == Target.WinExe)
873 // k = PEFileKinds.WindowApplication;
875 // if (target == Target.Exe || target == Target.WinExe){
876 // MethodInfo ep = RootContext.EntryPoint;
879 // Report.Error (5001, "Program " + output_file +
880 // " does not have an entry point defined");
884 // CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
890 if (resources != null){
891 foreach (string file in resources)
892 CodeGen.AssemblyBuilder.AddResourceFile (file, file);
895 CodeGen.Save (output_file);
897 ShowTime ("Saved output");
899 if (want_debugging_support) {
900 CodeGen.SaveSymbols ();
902 ShowTime ("Saved symbols");
905 if (Report.Errors > 0){
906 error ("Compilation failed");
908 } else if (Report.ProbeCode != 0){
909 error ("Failed to report code " + Report.ProbeCode);
910 Environment.Exit (124);