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;
24 Library, Exe, Module, WinExe
28 /// The compiler driver.
34 // Assemblies references to be linked. Initialized with
35 // mscorlib.dll elsewhere.
36 static ArrayList references;
39 // If any of these fail, we ignore the problem. This is so
40 // that we can list all the assemblies in Windows and not fail
41 // if they are missing on Linux.
43 static ArrayList soft_references;
46 static ArrayList link_paths;
48 // Whether we want to only run the tokenizer
49 static bool tokenize = false;
51 static int error_count = 0;
53 static string first_source;
55 static Target target = Target.Exe;
56 static string target_ext = ".exe";
58 static bool want_debugging_support = false;
59 static ArrayList debug_arglist = new ArrayList ();
61 static bool parse_only = false;
62 static bool timestamps = false;
65 // Whether to load the initial config file (what CSC.RSP has by default)
67 static bool load_default_config = true;
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
117 --parse Only parses the source file (for debugging the tokenizer)
118 --probe X Probes for the source to generate code X on line L
119 -r ASSEMBLY References an assembly
120 --recurse SPEC Recursively compiles the files in SPEC ([dir]/file)
121 --resource FILE Adds FILE as a resource
122 --stacktrace Shows stack trace at error location
123 --target KIND Specifies the target (KIND is one of: exe, winexe, library, module)
124 --tokenize Only tokenizes source files
125 --timestamp Displays time stamps of various compiler events
126 --unsafe Allows unsafe code
127 --werror Treat warnings as errors
128 -v Verbose parsing (for debugging the parser)
129 --wlevel LEVEL Sets warning level (the highest is 4, the default)
130 @file Read response file for more options
138 // Options.ShowAbout();
141 static void error (string msg)
143 Console.WriteLine ("Error: " + msg);
146 static void notice (string msg)
148 Console.WriteLine (msg);
151 private static Mono.GetOptions.OptionList Options;
153 private static bool SetVerboseParsing(object nothing)
155 GenericParser.yacc_verbose_flag = true;
159 private static bool SetMainClass(object className)
161 RootContext.MainClass = (string)className;
165 private static bool AddFile(object fileName)
167 string f = (string)fileName;
168 if (first_source == null)
171 if (source_files.Contains(f))
173 Report.Error (1516, "Source file `" + f + "' specified multiple times");
177 source_files.Add(f, f);
182 public static int Main (string[] args)
184 Options = new OptionList();
186 Options.AddParameterReader(new OptionFound(AddFile));
187 Options.AddAbout(' ',"about", "About the MonoBASIC compiler");
188 Options.AddBooleanSwitch('v',"verbose", "Verbose parsing (for debugging the parser)", new OptionFound(SetVerboseParsing) );
189 Options.AddSymbolAdder('m',"main", "Specifies CLASS as main (starting) class", "CLASS", new OptionFound(SetMainClass) );
191 bool ok = MainDriver (args);
193 if (ok && Report.Errors == 0)
195 Console.Write("Compilation succeeded");
196 if (Report.Warnings > 0)
198 Console.Write(" - {0} warning(s)", Report.Warnings);
205 Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
206 Report.Errors, Report.Warnings);
211 static public int LoadAssembly (string assembly, bool soft)
214 string total_log = "";
217 char[] path_chars = { '/', '\\', '.' };
219 if (assembly.IndexOfAny (path_chars) != -1)
220 a = Assembly.LoadFrom (assembly);
222 a = Assembly.Load (assembly);
223 TypeManager.AddAssembly (a);
225 } catch (FileNotFoundException){
226 foreach (string dir in link_paths){
227 string full_path = dir + "/" + assembly + ".dll";
230 a = Assembly.LoadFrom (full_path);
231 TypeManager.AddAssembly (a);
233 } catch (FileNotFoundException ff) {
234 total_log += ff.FusionLog;
240 } catch (BadImageFormatException f) {
241 error ("// Bad file format while loading assembly");
242 error ("Log: " + f.FusionLog);
244 } catch (FileLoadException f){
245 error ("File Load Exception: " + assembly);
246 error ("Log: " + f.FusionLog);
248 } catch (ArgumentNullException){
249 error ("// Argument Null exception ");
253 Report.Error (6, "Can not find assembly `" + assembly + "'" );
254 Console.WriteLine ("Log: \n" + total_log);
260 /// Loads all assemblies referenced on the command line
262 static public int LoadReferences ()
266 foreach (string r in references)
267 errors += LoadAssembly (r, false);
269 foreach (string r in soft_references)
270 errors += LoadAssembly (r, true);
275 static void SetupDefaultDefines ()
277 defines = new ArrayList ();
278 defines.Add ("__MonoBASIC__");
283 // Returns the directory where the system assemblies are installed
285 static string GetSystemDir ()
287 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
289 foreach (Assembly a in assemblies){
290 string codebase = a.CodeBase;
291 if (codebase.EndsWith ("corlib.dll")){
292 return codebase.Substring (0, codebase.LastIndexOf ("/"));
296 Report.Error (-15, "Can not compute my system path");
301 // Given a path specification, splits the path from the file/pattern
303 static void SplitPathAndPattern (string spec, out string path, out string pattern)
305 int p = spec.LastIndexOf ("/");
308 // Windows does not like /file.cs, switch that to:
313 pattern = spec.Substring (1);
315 path = spec.Substring (0, p);
316 pattern = spec.Substring (p + 1);
321 p = spec.LastIndexOf ("\\");
323 path = spec.Substring (0, p);
324 pattern = spec.Substring (p + 1);
333 static int ProcessSourceFile(string filename)
336 GenericParser.Tokenize(filename);
338 return GenericParser.Parse(filename);
343 static bool AddFiles (string spec, bool recurse)
345 string path, pattern;
347 SplitPathAndPattern (spec, out path, out pattern);
348 if (pattern.IndexOf ("*") == -1){
349 return AddFile (spec);
352 string [] files = null;
354 files = Directory.GetFiles (path, pattern);
355 } catch (System.IO.DirectoryNotFoundException) {
356 Report.Error (2001, "Source file `" + spec + "' could not be found");
358 } catch (System.IO.IOException){
359 Report.Error (2001, "Source file `" + spec + "' could not be found");
362 foreach (string f in files)
368 string [] dirs = null;
371 dirs = Directory.GetDirectories (path);
375 foreach (string d in dirs) {
377 // Don't include path in this string, as each
378 // directory entry already does
379 AddFiles (d + "/" + pattern, true);
386 static void DefineDefaultConfig ()
389 // For now the "default config" is harcoded into the compiler
390 // we can move this outside later
392 string [] default_config =
397 "Microsoft.VisualBasic", // just for now
400 // Is it worth pre-loading all this stuff?
403 "System.Configuration.Install",
405 "System.DirectoryServices",
406 "System.Drawing.Design",
408 "System.EnterpriseServices",
411 "System.Runtime.Remoting",
412 "System.Runtime.Serialization.Formatters.Soap",
414 "System.ServiceProcess",
416 "System.Web.RegularExpressions",
417 "System.Web.Services",
418 "System.Windows.Forms"
423 foreach (string def in default_config)
424 soft_references.Insert (p++, def);
428 /// Parses the arguments, and drives the compilation
433 /// TODO: Mostly structured to debug the compiler
434 /// now, needs to be turned into a real driver soon.
436 static bool MainDriver (string [] args)
438 int errors = 0;//, i;
439 string output_file = null;
440 //bool parsing_options = true;
442 references = new ArrayList ();
443 soft_references = new ArrayList ();
444 link_paths = new ArrayList ();
445 SetupDefaultDefines ();
450 // This is not required because Assembly.Load knows about this
453 link_paths.Add (GetSystemDir ());
455 if (!Options.ProcessArgs(args))
458 /* int argc = args.Length;
459 for (i = 0; i < argc; i++){
460 string arg = args [i];
462 // Prepare to recurse
465 if (parsing_options && (arg.StartsWith ("-"))){
469 parsing_options = false;
477 RootContext.Unsafe = true;
480 case "/?": case "/h": case "/help":
486 if ((i + 1) >= argc){
490 defines.Add (args [++i]);
498 args [++i], NumberStyles.AllowLeadingSign);
499 Report.SetProbe (code);
501 Report.Error (-14, "Invalid number specified");
513 if ((i + 1) >= argc){
517 output_file = args [++i];
518 string bname = CodeGen.Basename (output_file);
519 if (bname.IndexOf (".") == -1)
520 output_file += ".exe";
524 RootContext.Checked = true;
528 Report.Stacktrace = true;
532 if ((i + 1) >= argc){
537 string type = args [++i];
540 target = Target.Library;
549 target = Target.WinExe;
553 target = Target.Module;
563 if ((i + 1) >= argc){
568 references.Add(args [++i]);
575 Console.WriteLine("Missing argument to --resource");
579 resources.Add(args [++i]);
584 if ((i + 1) >= argc){
588 link_paths.Add (args [++i]);
592 RootContext.StdLib = false;
600 Report.WarningsAreErrors = true;
604 if ((i + 1) >= argc){
611 warn = Int32.Parse (args [++i]);
616 Report.SetIgnoreWarning (warn);
620 if ((i + 1) >= argc){
623 "--wlevel requires an value from 0 to 4");
630 level = Int32.Parse (args [++i]);
634 "--wlevel requires an value from 0 to 4");
637 if (level < 0 || level > 4){
638 Report.Error (1900, "Warning level must be 0 to 4");
641 RootContext.WarningLevel = level;
649 if ((i + 1) >= argc){
650 Console.WriteLine ("--recurse requires an argument");
654 AddFiles (args [++i], true);
659 last_time = DateTime.Now;
660 debug_arglist.Add("timestamp");
663 case "--debug": case "-g":
664 want_debugging_support = true;
668 if ((i + 1) >= argc){
669 Console.WriteLine ("--debug-args requires an argument");
673 char[] sep = { ',' };
674 debug_arglist.AddRange (args [++i].Split (sep));
678 load_default_config = false;
682 Console.WriteLine ("Unknown option: " + arg);
688 // Rafael: Does not compile them yet!!!
689 errors += AddFiles(arg, false);
692 //Rafael: Compile all source files!!!
693 foreach(string filename in source_files.Values)
694 errors += ProcessSourceFile(filename);
696 if (first_source == null)
698 Report.Error (2008, "No files to compile were specified");
705 if (Report.Errors > 0)
712 // Load Core Library for default compilation
714 if (RootContext.StdLib)
715 references.Insert (0, "mscorlib");
717 if (load_default_config)
718 DefineDefaultConfig ();
722 error ("Parsing failed");
727 // Load assemblies required
730 ShowTime ("Loading references");
731 errors += LoadReferences ();
733 ShowTime (" References loaded");
737 error ("Could not load one or more assemblies");
741 error_count = errors;
746 if (output_file == null)
748 int pos = first_source.LastIndexOf (".");
751 output_file = first_source.Substring (0, pos) + target_ext;
753 output_file = first_source + target_ext;
756 string[] debug_args = new string [debug_arglist.Count];
757 debug_arglist.CopyTo(debug_args);
758 CodeGen.Init (output_file, output_file, want_debugging_support, debug_args);
760 TypeManager.AddModule (CodeGen.ModuleBuilder);
763 // Before emitting, we need to get the core
764 // types emitted from the user defined types
765 // or from the system ones.
768 ShowTime ("Initializing Core Types");
769 if (!RootContext.StdLib)
771 RootContext.ResolveCore ();
772 if (Report.Errors > 0)
776 TypeManager.InitCoreTypes ();
778 ShowTime (" Core Types done");
781 // The second pass of the compiler
784 ShowTime ("Resolving tree");
785 RootContext.ResolveTree ();
787 ShowTime ("Populate tree");
789 if (Report.Errors > 0)
791 error ("Compilation failed");
795 if (!RootContext.StdLib)
796 RootContext.BootCorlib_PopulateCoreTypes ();
797 RootContext.PopulateTypes ();
799 TypeManager.InitCodeHelpers ();
801 if (Report.Errors > 0)
803 error ("Compilation failed");
808 // The code generator
811 ShowTime ("Emitting code");
812 RootContext.EmitCode ();
816 if (Report.Errors > 0)
818 error ("Compilation failed");
823 ShowTime ("Closing types");
825 RootContext.CloseTypes ();
827 // PEFileKinds k = PEFileKinds.ConsoleApplication;
829 // if (target == Target.Library || target == Target.Module)
830 // k = PEFileKinds.Dll;
831 // else if (target == Target.Exe)
832 // k = PEFileKinds.ConsoleApplication;
833 // else if (target == Target.WinExe)
834 // k = PEFileKinds.WindowApplication;
836 // if (target == Target.Exe || target == Target.WinExe){
837 // MethodInfo ep = RootContext.EntryPoint;
840 // Report.Error (5001, "Program " + output_file +
841 // " does not have an entry point defined");
845 // CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
851 if (resources != null)
853 foreach (string file in resources)
854 CodeGen.AssemblyBuilder.AddResourceFile (file, file);
857 CodeGen.Save (output_file);
859 ShowTime ("Saved output");
861 if (want_debugging_support)
863 CodeGen.SaveSymbols ();
865 ShowTime ("Saved symbols");
868 if (Report.ExpectedError != 0)
870 Console.WriteLine("Failed to report expected error " + Report.ExpectedError);
871 Environment.Exit (1);
874 return (Report.Errors == 0);