// // driver.cs: The compiler command line driver. // // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com) // Based on mcs by : Miguel de Icaza (miguel@gnu.org) // // Licensed under the terms of the GNU GPL // // (C) 2002, 2003, 2004 Rafael Teixeira // namespace Mono.Languages { using System; using System.Collections; using System.IO; using System.Text; using System.Globalization; using System.Reflection; using System.Reflection.Emit; using Mono.MonoBASIC; using Mono.GetOptions; using Mono.GetOptions.Useful; enum OptionCompare { Binary, Text }; /// /// The compiler driver. /// public class CompilerOptions : CommonCompilerOptions { // Temporary options //------------------------------------------------------------------ [Option("[IGNORED] Only parses the source file (for debugging the tokenizer)", "parse", SecondLevelHelp = true)] public bool OnlyParse = false; [Option("[IGNORED] Only tokenizes source files", "tokenize", SecondLevelHelp = true)] public bool Tokenize = false; [Option("Shows stack trace at Error location", "stacktrace", SecondLevelHelp = true)] public bool Stacktrace = false; [Option("Makes errors fatal", "fatal", SecondLevelHelp = true)] public bool MakeErrorsFatal = false; [Option("Displays time stamps of various compiler events", "timestamp", SecondLevelHelp = true)] public virtual bool PrintTimeStamps { set { printTimeStamps = true; last_time = DateTime.Now; DebugListOfArguments.Add("timestamp"); } } // redefining some inherited options //------------------------------------------------------------------ [Option("About the MonoBASIC compiler", "about")] public override WhatToDoNext DoAbout() { return base.DoAbout(); } [KillOption] public override WhatToDoNext DoUsage() { return WhatToDoNext.GoAhead; } // language options //------------------------------------------------------------------ [Option("Require explicit declaration of variables", "optionexplicit", VBCStyleBoolean = true)] public bool OptionExplicit = false; [Option("Enforce strict language semantics", "optionstrict", VBCStyleBoolean = true)] public bool OptionStrict = false; [Option("Specifies binary-style string comparisons. This is the default", "optioncompare:binary")] public bool OptionCompareBinary = true; [Option("Specifies text-style string comparisons.", "optioncompare:text")] public bool OptionCompareText { set { OptionCompareBinary = false; } } protected override void InitializeOtherDefaults() { DefineSymbol = "__MonoBASIC__"; ImportNamespaces = "Microsoft.VisualBasic"; } private bool printTimeStamps = false; // // Last time we took the time // DateTime last_time; public void ShowTime (string msg) { DateTime now = DateTime.Now; TimeSpan span = now - last_time; last_time = now; Console.WriteLine ( "[{0:00}:{1:000}] {2}", (int) span.TotalSeconds, span.Milliseconds, msg); } } public class Driver : CompilerOptions { private void InitializeRootContextAndOthersFromOptions() { Report.Stacktrace = Stacktrace; Report.WarningsAreErrors = WarningsAreErrors; // TODO: change Report to receive the whole array for(int i = 0; i < WarningsToIgnore.Length; i++) Report.SetIgnoreWarning(WarningsToIgnore[i]); Report.Fatal = MakeErrorsFatal; RootContext.WarningLevel = WarningLevel; RootContext.Checked = CheckedContext; RootContext.MainClass = MainClassName; RootContext.StdLib = !NoStandardLibraries; RootContext.Unsafe = AllowUnsafeCode; if (RootNamespace != null) RootContext.RootNamespace = RootNamespace; // TODO: semantics are different and should be adjusted GenericParser.yacc_verbose_flag = Verbose ? 1 : 0; Mono.MonoBASIC.Parser.InitialOptionExplicit = OptionExplicit; Mono.MonoBASIC.Parser.InitialOptionStrict = OptionStrict; Mono.MonoBASIC.Parser.InitialOptionCompareBinary = OptionCompareBinary; Mono.MonoBASIC.Parser.ImportsList = Imports; } public ArrayList AssembliesToReferenceSoftly = new ArrayList(); public int LoadAssembly (string assembly, bool soft) { Assembly a; string total_log = ""; try { char[] path_chars = { '/', '\\' }; if (assembly.IndexOfAny (path_chars) != -1) a = Assembly.LoadFrom(assembly); else { string ass = assembly; if (ass.EndsWith (".dll")) ass = assembly.Substring (0, assembly.Length - 4); a = Assembly.Load (ass); } TypeManager.AddAssembly (a); return 0; } catch (FileNotFoundException) { if (PathsToSearchForLibraries != null) { foreach (string dir in PathsToSearchForLibraries) { string full_path = dir + "/" + assembly + ".dll"; try { a = Assembly.LoadFrom (full_path); TypeManager.AddAssembly (a); return 0; } catch (FileNotFoundException ff) { total_log += ff.FusionLog; continue; } } } if (soft) return 0; } catch (BadImageFormatException f) { Error ("// Bad file format while loading assembly"); Error ("Log: " + f.FusionLog); return 1; } catch (FileLoadException f){ Error ("File Load Exception: " + assembly); Error ("Log: " + f.FusionLog); return 1; } catch (ArgumentNullException){ Error ("// Argument Null exception "); return 1; } Report.Error (6, "Can not find assembly `" + assembly + "'" ); Console.WriteLine ("Log: \n" + total_log); return 0; } public void LoadModule (MethodInfo adder_method, string module) { System.Reflection.Module m; string total_log = ""; try { try { m = (System.Reflection.Module)adder_method.Invoke (CodeGen.AssemblyBuilder, new object [] { module }); } catch (TargetInvocationException ex) { throw ex.InnerException; } TypeManager.AddModule (m); } catch (FileNotFoundException) { foreach (string dir in PathsToSearchForLibraries) { string full_path = Path.Combine (dir, module); if (!module.EndsWith (".netmodule")) full_path += ".netmodule"; try { try { m = (System.Reflection.Module) adder_method.Invoke (CodeGen.AssemblyBuilder, new object [] { full_path }); } catch (TargetInvocationException ex) { throw ex.InnerException; } TypeManager.AddModule (m); return; } catch (FileNotFoundException ff) { total_log += ff.FusionLog; continue; } } Report.Error (6, "Cannot find module `" + module + "'" ); Console.WriteLine ("Log: \n" + total_log); } catch (BadImageFormatException f) { Report.Error(6, "Cannot load module (bad file format)" + f.FusionLog); } catch (FileLoadException f) { Report.Error(6, "Cannot load module " + f.FusionLog); } catch (ArgumentNullException) { Report.Error(6, "Cannot load module (null argument)"); } } void Error(string message) { Console.WriteLine(message); } /// /// Loads all assemblies referenced on the command line /// public int LoadReferences () { int errors = 0; foreach (string r in AssembliesToReference) errors += LoadAssembly (r, false); foreach (string r in AssembliesToReferenceSoftly) errors += LoadAssembly (r, true); return errors; } // // Given a path specification, splits the path from the file/pattern // void SplitPathAndPattern (string spec, out string path, out string pattern) { int p = spec.LastIndexOf ("/"); if (p != -1){ // // Windows does not like /file.cs, switch that to: // "\", "file.cs" // if (p == 0){ path = "\\"; pattern = spec.Substring (1); } else { path = spec.Substring (0, p); pattern = spec.Substring (p + 1); } return; } p = spec.LastIndexOf ("\\"); if (p != -1){ path = spec.Substring (0, p); pattern = spec.Substring (p + 1); return; } path = "."; pattern = spec; } bool AddFiles (string spec, bool recurse) { string path, pattern; SplitPathAndPattern(spec, out path, out pattern); if (pattern.IndexOf("*") == -1) { DefaultArgumentProcessor(spec); return true; } string [] files = null; try { files = Directory.GetFiles(path, pattern); } catch (System.IO.DirectoryNotFoundException) { Report.Error (2001, "Source file `" + spec + "' could not be found"); return false; } catch (System.IO.IOException){ Report.Error (2001, "Source file `" + spec + "' could not be found"); return false; } foreach (string f in files) DefaultArgumentProcessor (f); if (!recurse) return true; string [] dirs = null; try { dirs = Directory.GetDirectories(path); } catch { } foreach (string d in dirs) { // Don't include path in this string, as each // directory entry already does AddFiles (d + "/" + pattern, true); } return true; } void DefineDefaultConfig () { // // For now the "default config" is harcoded into the compiler // we can move this outside later // string [] default_config = { "System", "System.Data", "System.Xml", "Microsoft.VisualBasic" , #if EXTRA_DEFAULT_REFS // // Is it worth pre-loading all this stuff? // "Accessibility", "System.Configuration.Install", "System.Design", "System.DirectoryServices", "System.Drawing.Design", "System.Drawing", "System.EnterpriseServices", "System.Management", "System.Messaging", "System.Runtime.Remoting", "System.Runtime.Serialization.Formatters.Soap", "System.Security", "System.ServiceProcess", "System.Web", "System.Web.RegularExpressions", "System.Web.Services" , "System.Windows.Forms" #endif }; foreach (string def in default_config) if (!(AssembliesToReference.Contains(def) || AssembliesToReference.Contains (def + ".dll"))) AssembliesToReferenceSoftly.Add(def); } void InitializeDebuggingSupport() { string[] debug_args = new string [DebugListOfArguments.Count]; DebugListOfArguments.CopyTo(debug_args); CodeGen.Init(OutputFileName, OutputFileName, WantDebuggingSupport, debug_args); TypeManager.AddModule(CodeGen.ModuleBuilder); } public bool ResolveAllTypes() // Phase 2 { // Load Core Library for default compilation if (RootContext.StdLib) AssembliesToReference.Insert(0, "mscorlib"); if (!NoConfig) DefineDefaultConfig(); ShowTime("Loading referenced assemblies"); // Load assemblies required if (LoadReferences() > 0) { Error ("Could not load one or more assemblies"); return false; } ShowTime("References loaded"); InitializeDebuggingSupport(); // TargetFileType is Module if (TargetFileType == TargetType.Module) { PropertyInfo module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic); if (module_only == null) { Report.Error (0, new Location (-1, -1), "Cannot use /TargetFileType:module on this runtime: try the Mono runtime instead."); Environment.Exit (1); } MethodInfo set_method = module_only.GetSetMethod (true); set_method.Invoke (CodeGen.AssemblyBuilder, BindingFlags.Default, null, new object[]{true}, null); TypeManager.AddModule (CodeGen.ModuleBuilder); } if (NetModulesToAdd.Count > 0) { MethodInfo adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic); if (adder_method == null) { Report.Error (0, new Location (-1, -1), "Cannot use /addmodule on this runtime: Try the Mono runtime instead."); Environment.Exit (1); } foreach (string module in NetModulesToAdd) LoadModule (adder_method, module); } // // Before emitting, we need to get the core // types emitted from the user defined types // or from the system ones. // ShowTime("Initializing Core Types"); if (!RootContext.StdLib) RootContext.ResolveCore (); if (Report.Errors > 0) return false; TypeManager.InitCoreTypes(); if (Report.Errors > 0) return false; ShowTime(" Core Types done"); ShowTime("Resolving tree"); // The second pass of the compiler RootContext.ResolveTree (); if (Report.Errors > 0) return false; ShowTime("Populate tree"); if (!RootContext.StdLib) RootContext.BootCorlib_PopulateCoreTypes(); if (Report.Errors > 0) return false; RootContext.PopulateTypes(); if (Report.Errors > 0) return false; TypeManager.InitCodeHelpers(); if (Report.Errors > 0) return false; return true; } bool IsSWFApp() { string mainclass = GetFQMainClass(); if (mainclass != null) { foreach (string r in AssembliesToReference) { if (r.IndexOf ("System.Windows.Forms") >= 0) { Type t = TypeManager.LookupType(mainclass); if (t != null) return t.IsSubclassOf (TypeManager.LookupType("System.Windows.Forms.Form")); break; } } } return false; } string GetFQMainClass() { if (RootContext.RootNamespace != "") return RootContext.RootNamespace + "." + RootContext.MainClass; else return RootContext.MainClass; } void FixEntryPoint() { if (TargetFileType == TargetType.Exe || TargetFileType == TargetType.WinExe) { MethodInfo ep = RootContext.EntryPoint; if (ep == null) { // If we don't have a valid entry point yet // AND if System.Windows.Forms is included // among the dependencies, we have to build // a new entry point on-the-fly. Otherwise we // won't be able to compile SWF code out of the box. if (IsSWFApp()) { Type t = TypeManager.LookupType(GetFQMainClass()); if (t != null) { TypeBuilder tb = t as TypeBuilder; MethodBuilder mb = tb.DefineMethod ("Main", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new Type[0]); Type SWFA = TypeManager.LookupType("System.Windows.Forms.Application"); Type SWFF = TypeManager.LookupType("System.Windows.Forms.Form"); Type[] args = new Type[1]; args[0] = SWFF; MethodInfo mi = SWFA.GetMethod("Run", args); ILGenerator ig = mb.GetILGenerator(); ConstructorInfo ci = TypeManager.GetConstructor (TypeManager.LookupType(t.FullName), new Type[0]); ig.Emit (OpCodes.Newobj, ci); ig.Emit (OpCodes.Call, mi); ig.Emit (OpCodes.Ret); RootContext.EntryPoint = mb as MethodInfo; } } } } } bool GenerateAssembly() { // // The code generator // ShowTime("Emitting code"); RootContext.EmitCode(); FixEntryPoint(); if (Report.Errors > 0) return false; ShowTime(" done"); ShowTime("Closing types"); RootContext.CloseTypes (); if (Report.Errors > 0) return false; ShowTime(" done"); PEFileKinds k = PEFileKinds.ConsoleApplication; if (TargetFileType == TargetType.Library || TargetFileType == TargetType.Module) k = PEFileKinds.Dll; else if (TargetFileType == TargetType.Exe) k = PEFileKinds.ConsoleApplication; else if (TargetFileType == TargetType.WinExe) k = PEFileKinds.WindowApplication; if (TargetFileType == TargetType.Exe || TargetFileType == TargetType.WinExe) { MethodInfo ep = RootContext.EntryPoint; if (ep == null) { Report.Error (30737, "Program " + OutputFileName + " does not have an entry point defined"); return false; } CodeGen.AssemblyBuilder.SetEntryPoint (ep, k); } // Add the resources if (EmbeddedResources != null) foreach (string file in EmbeddedResources) CodeGen.AssemblyBuilder.AddResourceFile (file, file); CodeGen.Save(OutputFileName); ShowTime("Saved output"); if (WantDebuggingSupport) { CodeGen.SaveSymbols (); ShowTime ("Saved symbols"); } return true; } public void CompileAll() { try { InitializeRootContextAndOthersFromOptions(); if (!ParseAll()) // Phase 1 return; if (!ResolveAllTypes()) // Phase 2 return; GenerateAssembly(); // Phase 3 } catch (Exception ex) { Error("Exception: " + ex.ToString()); } } private bool quiet { get { return DontShowBanner || SuccintErrorDisplay; } } private void Banner() { if (!quiet) { ShowBanner(); // TODO: remove next lines when the compiler has matured enough Console.WriteLine ("--------"); Console.WriteLine ("THIS IS AN ALPHA SOFTWARE."); Console.WriteLine ("--------"); } } bool ParseAll() // Phase 1 { foreach(FileToCompile file in SourceFilesToCompile) GenericParser.Parse(file.Filename, file.Encoding); return (Report.Errors == 0); } /// /// Parses the arguments, and calls the compilation process. /// int MainDriver(string [] args) { ProcessArgs(args); if (SourceFilesToCompile.Count == 0) { if (!quiet) DoHelp(); return 2; } Banner(); CompileAll(); return Report.ProcessResults(quiet); } public static int Main (string[] args) { Driver Exec = new Driver(); return Exec.MainDriver(args); } } }