Click here to Skip to main content
15,897,273 members
Articles / Containers / Virtual Machine

Hacking the Mono C# Compiler.

Rate me:
Please Sign up or sign in to vote.
5.00/5 (12 votes)
9 Oct 2010CPOL7 min read 56.6K   1.5K   41  
Describes how to dump information from the C# parse tree
//
// driver.cs: The compiler command line driver.
//
// Authors:
//   Miguel de Icaza (miguel@gnu.org)
//   Marek Safar (marek.safar@gmail.com)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
//

namespace Mono.CSharp
{
	using System;
	using System.Reflection;
	using System.Reflection.Emit;
	using System.Collections;
	using System.Collections.Specialized;
	using System.IO;
	using System.Text;
	using System.Globalization;
	using System.Diagnostics;

	public enum Target {
		Library, Exe, Module, WinExe
	};

	public enum Platform {
		AnyCPU, X86, X64, IA64
	}
	
	/// <summary>
	///    The compiler driver.
	/// </summary>
	public class Driver
	{
		//
		// Assemblies references to be linked.   Initialized with
		// mscorlib.dll here.
		ArrayList references;

		//
		// If any of these fail, we ignore the problem.  This is so
		// that we can list all the assemblies in Windows and not fail
		// if they are missing on Linux.
		//
		ArrayList soft_references;

		// 
		// External aliases for assemblies.
		//
		Hashtable external_aliases;

		//
		// Modules to be linked
		//
		ArrayList modules;

		// Lookup paths
		static ArrayList link_paths;

		// Whether we want to only run the tokenizer
		bool tokenize;
		
		string first_source;

		bool want_debugging_support;
		bool parse_only;
		bool timestamps;
		
		//
		// Whether to load the initial config file (what CSC.RSP has by default)
		// 
		bool load_default_config = true;

		//
		// A list of resource files
		//
		Resources embedded_resources;
		string win32ResourceFile;
		string win32IconFile;

		//
		// Output file
		//
		static string output_file;

		//
		// Last time we took the time
		//
		DateTime last_time, first_time;

		//
		// Encoding.
		//
		Encoding encoding;

		readonly CompilerContext ctx;

		static readonly char[] argument_value_separator = new char [] { ';', ',' };

		static public void Reset ()
		{
			output_file = null;
		}

		private Driver (CompilerContext ctx)
		{
			this.ctx = ctx;
			encoding = Encoding.Default;
		}

		public static Driver Create (string[] args, bool require_files, ReportPrinter printer)
		{
			Driver d = new Driver (new CompilerContext (new Report (printer)));

			if (!d.ParseArguments (args, require_files))
				return null;

			return d;
		}

		Report Report {
			get { return ctx.Report; }
		}

		void ShowTime (string msg)
		{
			if (!timestamps)
				return;

			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);
		}

		void ShowTotalTime (string msg)
		{
			if (!timestamps)
				return;

			DateTime now = DateTime.Now;
			TimeSpan span = now - first_time;
			last_time = now;

			Console.WriteLine (
				"[{0:00}:{1:000}] {2}",
				(int) span.TotalSeconds, span.Milliseconds, msg);
		}	       
	       
		void tokenize_file (CompilationUnit file, CompilerContext ctx)
		{
			Stream input;

			try {
				input = File.OpenRead (file.Name);
			} catch {
				Report.Error (2001, "Source file `" + file.Name + "' could not be found");
				return;
			}

			using (input){
				SeekableStreamReader reader = new SeekableStreamReader (input, encoding);
				Tokenizer lexer = new Tokenizer (reader, file, ctx);
				int token, tokens = 0, errors = 0;

				while ((token = lexer.token ()) != Token.EOF){
					tokens++;
					if (token == Token.ERROR)
						errors++;
				}
				Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
			}
			
			return;
		}

        public static Func<string, string> FileReadHook = null;

		void Parse (CompilationUnit file)
		{
			Stream input;

			try {
                if (file.Name == ("FromHook")){
                    var text = FileReadHook(file.Name);
                    input = new MemoryStream(Encoding.Default.GetBytes(text));
                }
                else
				    input = File.OpenRead (file.Name);
			} catch {
				Report.Error (2001, "Source file `{0}' could not be found", file.Name);
				return;
			}

			SeekableStreamReader reader = new SeekableStreamReader (input, encoding);

			// Check 'MZ' header
			if (reader.Read () == 77 && reader.Read () == 90) {
				Report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
				input.Close ();
				return;
			}

			reader.Position = 0;
			Parse (reader, file);
			input.Close ();
		}	
		
		void Parse (SeekableStreamReader reader, CompilationUnit file)
		{
			CSharpParser parser = new CSharpParser (reader, file, ctx);
			try {
				parser.parse ();
			} catch (Exception ex) {
				Report.Error(589, parser.Lexer.Location,
					"Compilation aborted in file `{0}', {1}", file.Name, ex);
			}
		}

		static void OtherFlags ()
		{
			Console.WriteLine (
				"Other flags in the compiler\n" +
				"   --fatal            Makes errors fatal\n" +
				"   --lint             Enhanced warnings\n" +
				"   --parse            Only parses the source file\n" +
				"   --stacktrace       Shows stack trace at error location\n" +
				"   --timestamp        Displays time stamps of various compiler events\n" +
				"   -v                 Verbose parsing (for debugging the parser)\n" + 
				"   --mcs-debug X      Sets MCS debugging level to X\n");
		}
		
		static void Usage ()
		{
			Console.WriteLine (
				"Mono C# compiler, Copyright 2001 - 2008 Novell, Inc.\n" +
				"mcs [options] source-files\n" +
				"   --about              About the Mono C# compiler\n" +
				"   -addmodule:M1[,Mn]   Adds the module to the generated assembly\n" + 
				"   -checked[+|-]        Sets default aritmetic overflow context\n" +
				"   -codepage:ID         Sets code page to the one in ID (number, utf8, reset)\n" +
				"   -clscheck[+|-]       Disables CLS Compliance verifications\n" +
				"   -define:S1[;S2]      Defines one or more conditional symbols (short: -d)\n" +
				"   -debug[+|-], -g      Generate debugging information\n" + 
				"   -delaysign[+|-]      Only insert the public key into the assembly (no signing)\n" +
				"   -doc:FILE            Process documentation comments to XML file\n" + 
				"   -help                Lists all compiler options (short: -?)\n" + 
				"   -keycontainer:NAME   The key pair container used to sign the output assembly\n" +
				"   -keyfile:FILE        The key file used to strongname the ouput assembly\n" +
				"   -langversion:TEXT    Specifies language version: ISO-1, ISO-2, Default, or Future\n" + 
				"   -lib:PATH1[,PATHn]   Specifies the location of referenced assemblies\n" +
				"   -main:CLASS          Specifies the class with the Main method (short: -m)\n" +
				"   -noconfig            Disables implicitly referenced assemblies\n" +
				"   -nostdlib[+|-]       Does not reference mscorlib.dll library\n" +
				"   -nowarn:W1[,Wn]      Suppress one or more compiler warnings\n" + 
				"   -optimize[+|-]       Enables advanced compiler optimizations (short: -o)\n" + 
				"   -out:FILE            Specifies output assembly name\n" +
#if !SMCS_SOURCE
				"   -pkg:P1[,Pn]         References packages P1..Pn\n" + 
#endif
				"   -platform:ARCH       Specifies the target platform of the output assembly\n" +
				"                        ARCH can be one of: anycpu, x86, x64 or itanium\n" +
				"   -recurse:SPEC        Recursively compiles files according to SPEC pattern\n" + 
				"   -reference:A1[,An]   Imports metadata from the specified assembly (short: -r)\n" +
				"   -reference:ALIAS=A   Imports metadata using specified extern alias (short: -r)\n" +				
				"   -target:KIND         Specifies the format of the output assembly (short: -t)\n" +
				"                        KIND can be one of: exe, winexe, library, module\n" +
				"   -unsafe[+|-]         Allows to compile code which uses unsafe keyword\n" +
				"   -warnaserror[+|-]    Treats all warnings as errors\n" +
				"   -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors\n" +
				"   -warn:0-4            Sets warning level, the default is 4 (short -w:)\n" +
				"   -help2               Shows internal compiler options\n" + 
				"\n" +
				"Resources:\n" +
				"   -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)\n" +
				"   -resource:FILE[,ID]     Embed FILE as a resource (short: -res)\n" +
				"   -win32res:FILE          Specifies Win32 resource file (.res)\n" +
				"   -win32icon:FILE         Use this icon for the output\n" +
                                "   @file                   Read response file for more options\n\n" +
				"Options can be of the form -option or /option");
		}

		void TargetUsage ()
		{
			Report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'");
		}
		
		static void About ()
		{
			Console.WriteLine (
				"The Mono C# compiler is Copyright 2001-2008, Novell, Inc.\n\n" +
				"The compiler source code is released under the terms of the \n"+
				"MIT X11 or GNU GPL licenses\n\n" +

				"For more information on Mono, visit the project Web site\n" +
				"   http://www.mono-project.com\n\n" +

				"The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig, Marek Safar, Raja R Harinath, Atushi Enomoto");
			Environment.Exit (0);
		}

		public static int Main (string[] args)
		{
			Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";

			Driver d = Driver.Create (args, true, new ConsoleReportPrinter ());
			if (d == null)
				return 1;

			if (d.Compile () && d.Report.Errors == 0) {
				if (d.Report.Warnings > 0) {
					Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
				}
				Environment.Exit (0);
				return 0;
			}
			
			
			Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
				d.Report.Errors, d.Report.Warnings);
			Environment.Exit (1);
			return 1;
		}

		public void LoadAssembly (string assembly, bool soft)
		{
			LoadAssembly (assembly, null, soft);
		}

		void Error6 (string name, string log)
		{
			if (log != null && log.Length > 0)
				Report.ExtraInformation (Location.Null, "Log:\n" + log + "\n(log related to previous ");
			Report.Error (6, "cannot find metadata file `{0}'", name);
		}

		void Error9 (string type, string filename, string log)
		{
			if (log != null && log.Length > 0)
				Report.ExtraInformation (Location.Null, "Log:\n" + log + "\n(log related to previous ");
			Report.Error (9, "file `{0}' has invalid `{1}' metadata", filename, type);
		}

		void BadAssembly (string filename, string log)
		{
			MethodInfo adder_method = AssemblyClass.AddModule_Method;

			if (adder_method != null) {
				AssemblyName an = new AssemblyName ();
				an.Name = ".temp";
				AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly (an, AssemblyBuilderAccess.Run);
				try {
					object m = null;
					try {
						m = adder_method.Invoke (ab, new object [] { filename });
					} catch (TargetInvocationException ex) {
						throw ex.InnerException;
					}

					if (m != null) {
						Report.Error (1509, "Referenced file `{0}' is not an assembly. Consider using `-addmodule' option instead",
						              Path.GetFileName (filename));
						return;
					}
				} catch (FileNotFoundException) {
					// did the file get deleted during compilation? who cares? swallow the exception
				} catch (BadImageFormatException) {
					// swallow exception
				} catch (FileLoadException) {
					// swallow exception
				}
			}
			Error9 ("assembly", filename, log);
		}

		public void LoadAssembly (string assembly, string alias, bool soft)
		{
			Assembly a = null;
			string total_log = "";

			try {
				try {
					char[] path_chars = { '/', '\\' };

					if (assembly.IndexOfAny (path_chars) != -1) {
						a = Assembly.LoadFrom (assembly);
					} else {
						string ass = assembly;
						if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
							ass = assembly.Substring (0, assembly.Length - 4);
						a = Assembly.Load (ass);
					}
				} catch (FileNotFoundException) {
					bool err = !soft;
					foreach (string dir in link_paths) {
						string full_path = Path.Combine (dir, assembly);
						if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
							full_path += ".dll";

						try {
							a = Assembly.LoadFrom (full_path);
							err = false;
							break;
						} catch (FileNotFoundException ff) {
							if (soft)
								return;
							total_log += ff.FusionLog;
						}
					}
					if (err) {
						Error6 (assembly, total_log);
						return;
					}
				}

				// Extern aliased refs require special handling
				if (alias == null)
					GlobalRootNamespace.Instance.AddAssemblyReference (a);
				else
					GlobalRootNamespace.Instance.DefineRootNamespace (alias, a, ctx);

			} catch (BadImageFormatException f) {
				// .NET 2.0 throws this if we try to load a module without an assembly manifest ...
				BadAssembly (f.FileName, f.FusionLog);
			} catch (FileLoadException f) {
				// ... while .NET 1.1 throws this
				BadAssembly (f.FileName, f.FusionLog);
			}
		}

		public void LoadModule (string module)
		{
			Module m = null;
			string total_log = "";

			try {
				try {
					m = CodeGen.Assembly.AddModule (module);
				} catch (FileNotFoundException) {
					bool err = true;
					foreach (string dir in link_paths) {
						string full_path = Path.Combine (dir, module);
						if (!module.EndsWith (".netmodule"))
							full_path += ".netmodule";

						try {
							m = CodeGen.Assembly.AddModule (full_path);
							err = false;
							break;
						} catch (FileNotFoundException ff) {
							total_log += ff.FusionLog;
						}
					}
					if (err) {
						Error6 (module, total_log);
						return;
					}
				}

				GlobalRootNamespace.Instance.AddModuleReference (m);

			} catch (BadImageFormatException f) {
				Error9 ("module", f.FileName, f.FusionLog);
			} catch (FileLoadException f) {
				Error9 ("module", f.FileName, f.FusionLog);
			}
		}

		/// <summary>
		///   Loads all assemblies referenced on the command line
		/// </summary>
		public void LoadReferences ()
		{
			link_paths.Add (GetSystemDir ());
			link_paths.Add (Directory.GetCurrentDirectory ());

			//
			// Load Core Library for default compilation
			//
			if (RootContext.StdLib)
				LoadAssembly ("mscorlib", false);

			foreach (string r in soft_references)
				LoadAssembly (r, true);

			foreach (string r in references)
				LoadAssembly (r, false);

			foreach (DictionaryEntry entry in external_aliases)
				LoadAssembly ((string) entry.Value, (string) entry.Key, false);
				
			GlobalRootNamespace.Instance.ComputeNamespaces (ctx);
		}

		static string [] LoadArgs (string file)
		{
			StreamReader f;
			ArrayList args = new ArrayList ();
			string line;
			try {
				f = new StreamReader (file);
			} catch {
				return null;
			}

			StringBuilder sb = new StringBuilder ();
			
			while ((line = f.ReadLine ()) != null){
				int t = line.Length;

				for (int i = 0; i < t; i++){
					char c = line [i];
					
					if (c == '"' || c == '\''){
						char end = c;
						
						for (i++; i < t; i++){
							c = line [i];

							if (c == end)
								break;
							sb.Append (c);
						}
					} else if (c == ' '){
						if (sb.Length > 0){
							args.Add (sb.ToString ());
							sb.Length = 0;
						}
					} else
						sb.Append (c);
				}
				if (sb.Length > 0){
					args.Add (sb.ToString ());
					sb.Length = 0;
				}
			}

			string [] ret_value = new string [args.Count];
			args.CopyTo (ret_value, 0);

			return ret_value;
		}

		//
		// Returns the directory where the system assemblies are installed
		//
		static string GetSystemDir ()
		{
			return Path.GetDirectoryName (typeof (object).Assembly.Location);
		}

		//
		// Given a path specification, splits the path from the file/pattern
		//
		static 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;
		}

		public void AddSourceFile (string f)
		{
			if (first_source == null)
				first_source = f;

			Location.AddFile (Report, f);
		}

		bool ParseArguments (string[] args, bool require_files)
		{
			references = new ArrayList ();
			external_aliases = new Hashtable ();
			soft_references = new ArrayList ();
			modules = new ArrayList (2);
			link_paths = new ArrayList ();

			ArrayList response_file_list = null;
			bool parsing_options = true;

			for (int i = 0; i < args.Length; i++) {
				string arg = args [i];
				if (arg.Length == 0)
					continue;

				if (arg [0] == '@') {
					string [] extra_args;
					string response_file = arg.Substring (1);

					if (response_file_list == null)
						response_file_list = new ArrayList ();

					if (response_file_list.Contains (response_file)) {
						Report.Error (
							1515, "Response file `" + response_file +
							"' specified multiple times");
						return false;
					}

					response_file_list.Add (response_file);

					extra_args = LoadArgs (response_file);
					if (extra_args == null) {
						Report.Error (2011, "Unable to open response file: " +
								  response_file);
						return false;
					}

					args = AddArgs (args, extra_args);
					continue;
				}

				if (parsing_options) {
					if (arg == "--") {
						parsing_options = false;
						continue;
					}

					if (arg [0] == '-') {
						if (UnixParseOption (arg, ref args, ref i))
							continue;

						// Try a -CSCOPTION
						string csc_opt = "/" + arg.Substring (1);
						if (CSCParseOption (csc_opt, ref args))
							continue;

						Error_WrongOption (arg);
						return false;
					}
					if (arg [0] == '/') {
						if (CSCParseOption (arg, ref args))
							continue;

						// Need to skip `/home/test.cs' however /test.cs is considered as error
						if (arg.Length < 2 || arg.IndexOf ('/', 2) == -1) {
							Error_WrongOption (arg);
							return false;
						}
					}
				}

				ProcessSourceFiles (arg, false);
			}

			if (require_files == false)
				return true;
					
			//
			// If we are an exe, require a source file for the entry point
			//
			if (RootContext.Target == Target.Exe || RootContext.Target == Target.WinExe || RootContext.Target == Target.Module) {
				if (first_source == null) {
					Report.Error (2008, "No files to compile were specified");
					return false;
				}

			}

			//
			// If there is nothing to put in the assembly, and we are not a library
			//
			if (first_source == null && embedded_resources == null) {
				Report.Error (2008, "No files to compile were specified");
				return false;
			}

			return true;
		}

		public void Parse ()
		{
			Location.Initialize ();

			ArrayList cu = Location.SourceFiles;
			for (int i = 0; i < cu.Count; ++i) {
				if (tokenize) {
					tokenize_file ((CompilationUnit) cu [i], ctx);
				} else {
					Parse ((CompilationUnit) cu [i]);
				}
			}
		}

		void ProcessSourceFiles (string spec, bool recurse)
		{
			string path, pattern;
            if (spec == "FromHook")
            {
                AddSourceFile(spec);
                return;
            }

			SplitPathAndPattern (spec, out path, out pattern);
			if (pattern.IndexOf ('*') == -1){
				AddSourceFile (spec);
				return;
			}

			string [] files = null;
			try {
				files = Directory.GetFiles (path, pattern);
			} catch (System.IO.DirectoryNotFoundException) {
				Report.Error (2001, "Source file `" + spec + "' could not be found");
				return;
			} catch (System.IO.IOException){
				Report.Error (2001, "Source file `" + spec + "' could not be found");
				return;
			}
			foreach (string f in files) {
				AddSourceFile (f);
			}

			if (!recurse)
				return;
			
			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
				ProcessSourceFiles (d + "/" + pattern, true);
			}
		}

		public void ProcessDefaultConfig ()
		{
			if (!load_default_config)
				return;
	
			//
			// For now the "default config" is harcoded into the compiler
			// we can move this outside later
			//
			string [] default_config = {
				"System",
				"System.Xml",
#if NET_2_1
				"System.Net",
				"System.Windows",
				"System.Windows.Browser",
#endif
#if false
				//
				// Is it worth pre-loading all this stuff?
				//
				"Accessibility",
				"System.Configuration.Install",
				"System.Data",
				"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
			};

			soft_references.AddRange (default_config);

			if (RootContext.Version > LanguageVersion.ISO_2)
				soft_references.Add ("System.Core");
			if (RootContext.Version > LanguageVersion.V_3)
				soft_references.Add ("Microsoft.CSharp");
		}

		public static string OutputFile
		{
			set {
				output_file = value;
			}
			get {
				return Path.GetFileName (output_file);
			}
		}

		void SetWarningLevel (string s)
		{
			int level = -1;

			try {
				level = Int32.Parse (s);
			} catch {
			}
			if (level < 0 || level > 4){
				Report.Error (1900, "Warning level must be in the range 0-4");
				return;
			}
			Report.WarningLevel = level;
		}

		static void Version ()
		{
			string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
			Console.WriteLine ("Mono C# compiler version {0}", version);
			Environment.Exit (0);
		}
		
		//
		// Currently handles the Unix-like command line options, but will be
		// deprecated in favor of the CSCParseOption, which will also handle the
		// options that start with a dash in the future.
		//
		bool UnixParseOption (string arg, ref string [] args, ref int i)
		{
			switch (arg){
			case "-v":
				CSharpParser.yacc_verbose_flag++;
				return true;

			case "--version":
				Version ();
				return true;
				
			case "--parse":
				parse_only = true;
				return true;
				
			case "--main": case "-m":
				Report.Warning (-29, 1, "Compatibility: Use -main:CLASS instead of --main CLASS or -m CLASS");
				if ((i + 1) >= args.Length){
					Usage ();
					Environment.Exit (1);
				}
				RootContext.MainClass = args [++i];
				return true;
				
			case "--unsafe":
				Report.Warning (-29, 1, "Compatibility: Use -unsafe instead of --unsafe");
				RootContext.Unsafe = true;
				return true;
				
			case "/?": case "/h": case "/help":
			case "--help":
				Usage ();
				Environment.Exit (0);
				return true;

			case "--define":
				Report.Warning (-29, 1, "Compatibility: Use -d:SYMBOL instead of --define SYMBOL");
				if ((i + 1) >= args.Length){
					Usage ();
					Environment.Exit (1);
				}
				RootContext.AddConditional (args [++i]);
				return true;

			case "--tokenize": 
				tokenize = true;
				return true;
				
			case "-o": 
			case "--output":
				Report.Warning (-29, 1, "Compatibility: Use -out:FILE instead of --output FILE or -o FILE");
				if ((i + 1) >= args.Length){
					Usage ();
					Environment.Exit (1);
				}
				OutputFile = args [++i];
				return true;

			case "--checked":
				Report.Warning (-29, 1, "Compatibility: Use -checked instead of --checked");
				RootContext.Checked = true;
				return true;
				
			case "--stacktrace":
				Report.Printer.Stacktrace = true;
				return true;
				
			case "--linkresource":
			case "--linkres":
				Report.Warning (-29, 1, "Compatibility: Use -linkres:VALUE instead of --linkres VALUE");
				if ((i + 1) >= args.Length){
					Usage ();
					Report.Error (5, "Missing argument to --linkres"); 
					Environment.Exit (1);
				}
				if (embedded_resources == null)
					embedded_resources = new Resources (ctx);
				
				embedded_resources.Add (false, args [++i], args [i]);
				return true;
				
			case "--resource":
			case "--res":
				Report.Warning (-29, 1, "Compatibility: Use -res:VALUE instead of --res VALUE");
				if ((i + 1) >= args.Length){
					Usage ();
					Report.Error (5, "Missing argument to --resource"); 
					Environment.Exit (1);
				}
				if (embedded_resources == null)
					embedded_resources = new Resources (ctx);
				
				embedded_resources.Add (true, args [++i], args [i]);
				return true;
				
			case "--target":
				Report.Warning (-29, 1, "Compatibility: Use -target:KIND instead of --target KIND");
				if ((i + 1) >= args.Length){
					Environment.Exit (1);
					return true;
				}
				
				string type = args [++i];
				switch (type){
				case "library":
					RootContext.Target = Target.Library;
					RootContext.TargetExt = ".dll";
					break;
					
				case "exe":
					RootContext.Target = Target.Exe;
					break;
					
				case "winexe":
					RootContext.Target = Target.WinExe;
					break;
					
				case "module":
					RootContext.Target = Target.Module;
					RootContext.TargetExt = ".dll";
					break;
				default:
					TargetUsage ();
					break;
				}
				return true;
				
			case "-r":
				Report.Warning (-29, 1, "Compatibility: Use -r:LIBRARY instead of -r library");
				if ((i + 1) >= args.Length){
					Usage ();
					Environment.Exit (1);
				}
				
				string val = args [++i];
				int idx = val.IndexOf ('=');
				if (idx > -1) {
					string alias = val.Substring (0, idx);
					string assembly = val.Substring (idx + 1);
					AddExternAlias (alias, assembly);
					return true;
				}

				references.Add (val);
				return true;
				
			case "-L":
				Report.Warning (-29, 1, "Compatibility: Use -lib:ARG instead of --L arg");
				if ((i + 1) >= args.Length){
					Usage ();	
					Environment.Exit (1);
				}
				link_paths.Add (args [++i]);
				return true;

			case "--lint":
				RootContext.EnhancedWarnings = true;
				return true;
				
			case "--nostdlib":
				Report.Warning (-29, 1, "Compatibility: Use -nostdlib instead of --nostdlib");
				RootContext.StdLib = false;
				return true;
				
			case "--fatal":
				Report.Fatal = true;
				return true;
				
			case "--nowarn":
				Report.Warning (-29, 1, "Compatibility: Use -nowarn instead of --nowarn");
				if ((i + 1) >= args.Length){
					Usage ();
					Environment.Exit (1);
				}
				int warn = 0;
				
				try {
					warn = Int32.Parse (args [++i]);
				} catch {
					Usage ();
					Environment.Exit (1);
				}
				Report.SetIgnoreWarning (warn);
				return true;
				
			case "--wlevel":
				Report.Warning (-29, 1, "Compatibility: Use -warn:LEVEL instead of --wlevel LEVEL");
				if ((i + 1) >= args.Length){
					Report.Error (
						1900,
						"--wlevel requires a value from 0 to 4");
					Environment.Exit (1);
				}

				SetWarningLevel (args [++i]);
				return true;

			case "--mcs-debug":
				if ((i + 1) >= args.Length){
					Report.Error (5, "--mcs-debug requires an argument");
					Environment.Exit (1);
				}

				try {
					Report.DebugFlags = Int32.Parse (args [++i]);
				} catch {
					Report.Error (5, "Invalid argument to --mcs-debug");
					Environment.Exit (1);
				}
				return true;
				
			case "--about":
				About ();
				return true;
				
			case "--recurse":
				Report.Warning (-29, 1, "Compatibility: Use -recurse:PATTERN option instead --recurse PATTERN");
				if ((i + 1) >= args.Length){
					Report.Error (5, "--recurse requires an argument");
					Environment.Exit (1);
				}
				ProcessSourceFiles (args [++i], true); 
				return true;
				
			case "--timestamp":
				timestamps = true;
				last_time = first_time = DateTime.Now;
				return true;

			case "--debug": case "-g":
				Report.Warning (-29, 1, "Compatibility: Use -debug option instead of -g or --debug");
				want_debugging_support = true;
				return true;
				
			case "--noconfig":
				Report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig");
				load_default_config = false;
				return true;
			}

			return false;
		}

#if !SMCS_SOURCE
		public static string GetPackageFlags (string packages, bool fatal, Report report)
		{
			ProcessStartInfo pi = new ProcessStartInfo ();
			pi.FileName = "pkg-config";
			pi.RedirectStandardOutput = true;
			pi.UseShellExecute = false;
			pi.Arguments = "--libs " + packages;
			Process p = null;
			try {
				p = Process.Start (pi);
			} catch (Exception e) {
				report.Error (-27, "Couldn't run pkg-config: " + e.Message);
				if (fatal)
					Environment.Exit (1);
				p.Close ();
				return null;
			}
			
			if (p.StandardOutput == null){
				report.Warning (-27, 1, "Specified package did not return any information");
				p.Close ();
				return null;
			}
			string pkgout = p.StandardOutput.ReadToEnd ();
			p.WaitForExit ();
			if (p.ExitCode != 0) {
				report.Error (-27, "Error running pkg-config. Check the above output.");
				if (fatal)
					Environment.Exit (1);
				p.Close ();
				return null;
			}
			p.Close ();

			return pkgout;
		}
#endif

		//
		// This parses the -arg and /arg options to the compiler, even if the strings
		// in the following text use "/arg" on the strings.
		//
		bool CSCParseOption (string option, ref string [] args)
		{
			int idx = option.IndexOf (':');
			string arg, value;

			if (idx == -1){
				arg = option;
				value = "";
			} else {
				arg = option.Substring (0, idx);

				value = option.Substring (idx + 1);
			}

			switch (arg.ToLower (CultureInfo.InvariantCulture)){
			case "/nologo":
				return true;

			case "/t":
			case "/target":
				switch (value){
				case "exe":
					RootContext.Target = Target.Exe;
					break;

				case "winexe":
					RootContext.Target = Target.WinExe;
					break;

				case "library":
					RootContext.Target = Target.Library;
					RootContext.TargetExt = ".dll";
					break;

				case "module":
					RootContext.Target = Target.Module;
					RootContext.TargetExt = ".netmodule";
					break;

				default:
					TargetUsage ();
					break;
				}
				return true;

			case "/out":
				if (value.Length == 0){
					Usage ();
					Environment.Exit (1);
				}
				OutputFile = value;
				return true;

			case "/o":
			case "/o+":
			case "/optimize":
			case "/optimize+":
				RootContext.Optimize = true;
				return true;

			case "/o-":
			case "/optimize-":
				RootContext.Optimize = false;
				return true;

			// TODO: Not supported by csc 3.5+
			case "/incremental":
			case "/incremental+":
			case "/incremental-":
				// nothing.
				return true;

			case "/d":
			case "/define": {
				if (value.Length == 0){
					Usage ();
					Environment.Exit (1);
				}

				foreach (string d in value.Split (argument_value_separator)) {
					string conditional = d.Trim ();
					if (!Tokenizer.IsValidIdentifier (conditional)) {
						Report.Warning (2029, 1, "Invalid conditional define symbol `{0}'", conditional);
						continue;
					}
					RootContext.AddConditional (conditional);
				}
				return true;
			}

			case "/bugreport":
				//
				// We should collect data, runtime, etc and store in the file specified
				//
				Console.WriteLine ("To file bug reports, please visit: http://www.mono-project.com/Bugs");
				return true;
#if !SMCS_SOURCE
			case "/pkg": {
				string packages;

				if (value.Length == 0){
					Usage ();
					Environment.Exit (1);
				}
				packages = String.Join (" ", value.Split (new Char [] { ';', ',', '\n', '\r'}));
				string pkgout = GetPackageFlags (packages, true, Report);
				
				if (pkgout != null){
					string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
						Split (new Char [] { ' ', '\t'});
					args = AddArgs (args, xargs);
				}
				
				return true;
			}
#endif
			case "/linkres":
			case "/linkresource":
			case "/res":
			case "/resource":
				if (embedded_resources == null)
					embedded_resources = new Resources (ctx);

				bool embeded = arg [1] == 'r' || arg [1] == 'R';
				string[] s = value.Split (argument_value_separator);
				switch (s.Length) {
				case 1:
					if (s[0].Length == 0)
						goto default;
					embedded_resources.Add (embeded, s [0], Path.GetFileName (s[0]));
					break;
				case 2:
					embedded_resources.Add (embeded, s [0], s [1]);
					break;
				case 3:
					if (s [2] != "public" && s [2] != "private") {
						Report.Error (1906, "Invalid resource visibility option `{0}'. Use either `public' or `private' instead", s [2]);
						return true;
					}
					embedded_resources.Add (embeded, s [0], s [1], s [2] == "private");
					break;
				default:
					Report.Error (-2005, "Wrong number of arguments for option `{0}'", option);
					break;
				}

				return true;
				
			case "/recurse":
				if (value.Length == 0){
					Report.Error (5, "-recurse requires an argument");
					Environment.Exit (1);
				}
				ProcessSourceFiles (value, true); 
				return true;

			case "/r":
			case "/reference": {
				if (value.Length == 0){
					Report.Error (5, "-reference requires an argument");
					Environment.Exit (1);
				}

				string[] refs = value.Split (argument_value_separator);
				foreach (string r in refs){
					string val = r;
					int index = val.IndexOf ('=');
					if (index > -1) {
						string alias = r.Substring (0, index);
						string assembly = r.Substring (index + 1);
						AddExternAlias (alias, assembly);
						return true;
					}

					if (val.Length != 0)
						references.Add (val);
				}
				return true;
			}
			case "/addmodule": {
				if (value.Length == 0){
					Report.Error (5, arg + " requires an argument");
					Environment.Exit (1);
				}

				string[] refs = value.Split (argument_value_separator);
				foreach (string r in refs){
					modules.Add (r);
				}
				return true;
			}
			case "/win32res": {
				if (value.Length == 0) {
					Report.Error (5, arg + " requires an argument");
					Environment.Exit (1);
				}
				
				if (win32IconFile != null)
					Report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");

				win32ResourceFile = value;
				return true;
			}
			case "/win32icon": {
				if (value.Length == 0) {
					Report.Error (5, arg + " requires an argument");
					Environment.Exit (1);
				}

				if (win32ResourceFile != null)
					Report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");

				win32IconFile = value;
				return true;
			}
			case "/doc": {
				if (value.Length == 0){
					Report.Error (2006, arg + " requires an argument");
					Environment.Exit (1);
				}
				RootContext.Documentation = new Documentation (value);
				return true;
			}
			case "/lib": {
				string [] libdirs;
				
				if (value.Length == 0){
					Report.Error (5, "/lib requires an argument");
					Environment.Exit (1);
				}

				libdirs = value.Split (argument_value_separator);
				foreach (string dir in libdirs)
					link_paths.Add (dir);
				return true;
			}

			case "/debug-":
				want_debugging_support = false;
				return true;
				
			case "/debug":
				if (value == "full" || value == "")
					want_debugging_support = true;

				return true;
				
			case "/debug+":
				want_debugging_support = true;
				return true;

			case "/checked":
			case "/checked+":
				RootContext.Checked = true;
				return true;

			case "/checked-":
				RootContext.Checked = false;
				return true;

			case "/clscheck":
			case "/clscheck+":
				return true;

			case "/clscheck-":
				RootContext.VerifyClsCompliance = false;
				return true;

			case "/unsafe":
			case "/unsafe+":
				RootContext.Unsafe = true;
				return true;

			case "/unsafe-":
				RootContext.Unsafe = false;
				return true;

			case "/warnaserror":
			case "/warnaserror+":
				if (value.Length == 0) {
					Report.WarningsAreErrors = true;
				} else {
					foreach (string wid in value.Split (argument_value_separator))
						Report.AddWarningAsError (wid);
				}
				return true;

			case "/warnaserror-":
				if (value.Length == 0) {
					Report.WarningsAreErrors = false;
				} else {
					foreach (string wid in value.Split (argument_value_separator))
						Report.RemoveWarningAsError (wid);
				}
				return true;

			case "/warn":
				SetWarningLevel (value);
				return true;

			case "/nowarn": {
				string [] warns;

				if (value.Length == 0){
					Report.Error (5, "/nowarn requires an argument");
					Environment.Exit (1);
				}

				warns = value.Split (argument_value_separator);
				foreach (string wc in warns){
					try {
						if (wc.Trim ().Length == 0)
							continue;

						int warn = Int32.Parse (wc);
						if (warn < 1) {
							throw new ArgumentOutOfRangeException("warn");
						}
						Report.SetIgnoreWarning (warn);
					} catch {
						Report.Error (1904, String.Format("`{0}' is not a valid warning number", wc));
					}
				}
				return true;
			}

			case "/noconfig":
				load_default_config = false;
				return true;

			case "/platform":
#if GMCS_SOURCE
				switch (value.ToLower (CultureInfo.InvariantCulture)) {
				case "anycpu":
					RootContext.Platform = Platform.AnyCPU;
					break;
				case "x86":
					RootContext.Platform = Platform.X86;
					break;
				case "x64":
					RootContext.Platform = Platform.X64;
					break;
				case "itanium":
					RootContext.Platform = Platform.IA64;
					break;
				default:
					Report.Error (1672, "Invalid platform type for -platform. Valid options are `anycpu', `x86', `x64' or `itanium'");
					break;
				}
#endif
				return true;

				// We just ignore this.
			case "/errorreport":
			case "/filealign":
				return true;
				
			case "/help2":
				OtherFlags ();
				Environment.Exit(0);
				return true;
				
			case "/help":
			case "/?":
				Usage ();
				Environment.Exit (0);
				return true;

			case "/main":
			case "/m":
				if (value.Length == 0){
					Report.Error (5, arg + " requires an argument");					
					Environment.Exit (1);
				}
				RootContext.MainClass = value;
				return true;

			case "/nostdlib":
			case "/nostdlib+":
				RootContext.StdLib = false;
				return true;

			case "/nostdlib-":
				RootContext.StdLib = true;
				return true;

			case "/fullpaths":
				return true;

			case "/keyfile":
				if (value == String.Empty) {
					Report.Error (5, arg + " requires an argument");
					Environment.Exit (1);
				}
				RootContext.StrongNameKeyFile = value;
				return true;
			case "/keycontainer":
				if (value == String.Empty) {
					Report.Error (5, arg + " requires an argument");
					Environment.Exit (1);
				}
				RootContext.StrongNameKeyContainer = value;
				return true;
			case "/delaysign+":
				RootContext.StrongNameDelaySign = true;
				return true;
			case "/delaysign-":
				RootContext.StrongNameDelaySign = false;
				return true;

			case "/langversion":
				switch (value.ToLower (CultureInfo.InvariantCulture)) {
				case "iso-1":
					RootContext.Version = LanguageVersion.ISO_1;
					return true;
					
				case "default":
					RootContext.Version = LanguageVersion.Default;
#if GMCS_SOURCE					
					RootContext.AddConditional ("__V2__");
#endif
					return true;
				case "iso-2":
					RootContext.Version = LanguageVersion.ISO_2;
					return true;
				case "3":
					RootContext.Version = LanguageVersion.V_3;
					return true;
				case "future":
					RootContext.Version = LanguageVersion.Future;
					return true;
				}

				Report.Error (1617, "Invalid -langversion option `{0}'. It must be `ISO-1', `ISO-2', `3' or `Default'", value);
				return true;

			case "/codepage":
				switch (value) {
				case "utf8":
					encoding = new UTF8Encoding();
					break;
				case "reset":
					encoding = Encoding.Default;
					break;
				default:
					try {
						encoding = Encoding.GetEncoding (
						Int32.Parse (value));
					} catch {
						Report.Error (2016, "Code page `{0}' is invalid or not installed", value);
					}
					break;
				}
				return true;
			}

			return false;
		}

		void Error_WrongOption (string option)
		{
			Report.Error (2007, "Unrecognized command-line option: `{0}'", option);
		}

		static string [] AddArgs (string [] args, string [] extra_args)
		{
			string [] new_args;
			new_args = new string [extra_args.Length + args.Length];

			// if args contains '--' we have to take that into account
			// split args into first half and second half based on '--'
			// and add the extra_args before --
			int split_position = Array.IndexOf (args, "--");
			if (split_position != -1)
			{
				Array.Copy (args, new_args, split_position);
				extra_args.CopyTo (new_args, split_position);
				Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
			}
			else
			{
				args.CopyTo (new_args, 0);
				extra_args.CopyTo (new_args, args.Length);
			}

			return new_args;
		}

		void AddExternAlias (string identifier, string assembly)
		{
			if (assembly.Length == 0) {
				Report.Error (1680, "Invalid reference alias '" + identifier + "='. Missing filename");
				return;
			}

			if (!IsExternAliasValid (identifier)) {
				Report.Error (1679, "Invalid extern alias for /reference. Alias '" + identifier + "' is not a valid identifier");
				return;
			}
			
			// Could here hashtable throw an exception?
			external_aliases [identifier] = assembly;
		}
		
		static bool IsExternAliasValid (string identifier)
		{
			if (identifier.Length == 0)
				return false;
			if (identifier [0] != '_' && !Char.IsLetter (identifier [0]))
				return false;

			for (int i = 1; i < identifier.Length; i++) {
				char c = identifier [i];
				if (Char.IsLetter (c) || Char.IsDigit (c))
					continue;

				UnicodeCategory category = Char.GetUnicodeCategory (c);
				if (category != UnicodeCategory.Format || category != UnicodeCategory.NonSpacingMark ||
						category != UnicodeCategory.SpacingCombiningMark ||
						category != UnicodeCategory.ConnectorPunctuation)
					return false;
			}
			
			return true;
		}

		//
		// Main compilation method
		//
		public bool Compile ()
		{
			// TODO: Should be passed to parser as an argument
			RootContext.ToplevelTypes = new ModuleContainer (ctx, RootContext.Unsafe);

			Parse ();
			if (Report.Errors > 0)
				return false;

			if (tokenize || parse_only)
				return true;

			if (RootContext.ToplevelTypes.NamespaceEntry != null)
				throw new InternalErrorException ("who set it?");

			ProcessDefaultConfig ();

			//
			// Quick hack
			//
			if (output_file == null){
				if (first_source == null){
					Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
					return false;
				}
					
				int pos = first_source.LastIndexOf ('.');

				if (pos > 0)
					output_file = first_source.Substring (0, pos) + RootContext.TargetExt;
				else
					output_file = first_source + RootContext.TargetExt;
			}

			if (!CodeGen.Init (output_file, output_file, want_debugging_support, ctx))
				return false;

			if (RootContext.Target == Target.Module) {
				PropertyInfo module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
				if (module_only == null) {
					Report.RuntimeMissingSupport (Location.Null, "/target:module");
					Environment.Exit (1);
				}

				MethodInfo set_method = module_only.GetSetMethod (true);
				set_method.Invoke (CodeGen.Assembly.Builder, BindingFlags.Default, null, new object[]{true}, null);
			}

			GlobalRootNamespace.Instance.AddModuleReference (RootContext.ToplevelTypes.Builder);

			//
			// Load assemblies required
			//
			if (timestamps)
				ShowTime ("Loading references");

			LoadReferences ();
			
			if (modules.Count > 0) {
				foreach (string module in modules)
					LoadModule (module);
			}
			
			if (timestamps)
				ShowTime ("References loaded");
			
			if (!TypeManager.InitCoreTypes (ctx) || Report.Errors > 0)
				return false;

			TypeManager.InitOptionalCoreTypes (ctx);

			if (timestamps)
				ShowTime ("   Core Types done");

			//
			// The second pass of the compiler
			//
			if (timestamps)
				ShowTime ("Resolving tree");
			RootContext.ResolveTree ();

			if (Report.Errors > 0)
				return false;
			if (timestamps)
				ShowTime ("Populate tree");
			if (!RootContext.StdLib)
				RootContext.BootCorlib_PopulateCoreTypes ();
			RootContext.PopulateTypes ();

			if (Report.Errors == 0 &&
				RootContext.Documentation != null &&
				!RootContext.Documentation.OutputDocComment (
					output_file, Report))
				return false;

			//
			// Verify using aliases now
			//
			NamespaceEntry.VerifyAllUsing ();
			
			if (Report.Errors > 0){
				return false;
			}

			CodeGen.Assembly.Resolve ();
			
			if (RootContext.VerifyClsCompliance) {
				if (CodeGen.Assembly.IsClsCompliant) {
					AttributeTester.VerifyModulesClsCompliance (ctx);
					TypeManager.LoadAllImportedTypes ();
				}
			}
			if (Report.Errors > 0)
				return false;
			
			//
			// The code generator
			//
			if (timestamps)
				ShowTime ("Emitting code");
			ShowTotalTime ("Total so far");
			RootContext.EmitCode ();
			if (timestamps)
				ShowTime ("   done");

			if (Report.Errors > 0){
				return false;
			}

			if (timestamps)
				ShowTime ("Closing types");

			RootContext.CloseTypes ();

			PEFileKinds k = PEFileKinds.ConsoleApplication;

			switch (RootContext.Target) {
			case Target.Library:
			case Target.Module:
				k = PEFileKinds.Dll; break;
			case Target.Exe:
				k = PEFileKinds.ConsoleApplication; break;
			case Target.WinExe:
				k = PEFileKinds.WindowApplication; break;
			}

			if (RootContext.NeedsEntryPoint) {
				Method ep = RootContext.EntryPoint;

				if (ep == null) {
					if (RootContext.MainClass != null) {
						DeclSpace main_cont = RootContext.ToplevelTypes.GetDefinition (RootContext.MainClass) as DeclSpace;
						if (main_cont == null) {
							Report.Error (1555, "Could not find `{0}' specified for Main method", RootContext.MainClass); 
							return false;
						}

						if (!(main_cont is ClassOrStruct)) {
							Report.Error (1556, "`{0}' specified for Main method must be a valid class or struct", RootContext.MainClass);
							return false;
						}

						Report.Error (1558, main_cont.Location, "`{0}' does not have a suitable static Main method", main_cont.GetSignatureForError ());
						return false;
					}

					if (Report.Errors == 0)
						Report.Error (5001, "Program `{0}' does not contain a static `Main' method suitable for an entry point",
							output_file);
					return false;
				}

				CodeGen.Assembly.Builder.SetEntryPoint (ep.MethodBuilder, k);
			} else if (RootContext.MainClass != null) {
				Report.Error (2017, "Cannot specify -main if building a module or library");
			}

			if (embedded_resources != null){
				if (RootContext.Target == Target.Module) {
					Report.Error (1507, "Cannot link resource file when building a module");
					return false;
				}

				embedded_resources.Emit ();
			}

			//
			// Add Win32 resources
			//

			if (win32ResourceFile != null) {
				try {
					CodeGen.Assembly.Builder.DefineUnmanagedResource (win32ResourceFile);
				} catch (ArgumentException) {
					Report.RuntimeMissingSupport (Location.Null, "resource embedding ");
				}
			} else {
				CodeGen.Assembly.Builder.DefineVersionInfoResource ();
			}

			if (win32IconFile != null) {
				MethodInfo define_icon = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (define_icon == null) {
					Report.RuntimeMissingSupport (Location.Null, "resource embedding");
				} else {
					define_icon.Invoke (CodeGen.Assembly.Builder, new object [] { win32IconFile });
				}
			}

			if (Report.Errors > 0)
				return false;
			
			CodeGen.Save (output_file, want_debugging_support, Report);
			if (timestamps) {
				ShowTime ("Saved output");
				ShowTotalTime ("Total");
			}

			Timer.ShowTimers ();

#if DEBUGME
			Console.WriteLine ("Size of strings held: " + DeclSpace.length);
			Console.WriteLine ("Size of strings short: " + DeclSpace.small);
#endif
			return (Report.Errors == 0);
		}
	}

	class Resources
	{
		interface IResource
		{
			void Emit ();
			string FileName { get; }
		}

		class EmbededResource : IResource
		{
			static MethodInfo embed_res;

			static EmbededResource () {
				Type[] argst = new Type [] { 
											   typeof (string), typeof (string), typeof (ResourceAttributes)
										   };

				embed_res = typeof (AssemblyBuilder).GetMethod (
					"EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
					null, CallingConventions.Any, argst, null);
				
				if (embed_res == null) {
					RootContext.ToplevelTypes.Compiler.Report.RuntimeMissingSupport (Location.Null, "Resource embedding");
				}
			}

			readonly object[] args;

			public EmbededResource (string name, string file, bool isPrivate)
			{
				args = new object [3];
				args [0] = name;
				args [1] = file;
				args [2] = isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public;
			}

			public void Emit()
			{
				embed_res.Invoke (CodeGen.Assembly.Builder, args);
			}

			public string FileName {
				get {
					return (string)args [1];
				}
			}
		}

		class LinkedResource : IResource
		{
			readonly string file;
			readonly string name;
			readonly ResourceAttributes attribute;

			public LinkedResource (string name, string file, bool isPrivate)
			{
				this.name = name;
				this.file = file;
				this.attribute = isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public;
			}

			public void Emit ()
			{
				CodeGen.Assembly.Builder.AddResourceFile (name, Path.GetFileName(file), attribute);
			}

			public string FileName {
				get {
					return file;
				}
			}
		}


		IDictionary embedded_resources = new HybridDictionary ();
		readonly CompilerContext ctx;

		public Resources (CompilerContext ctx)
		{
			this.ctx = ctx;
		}

		public void Add (bool embeded, string file, string name)
		{
			Add (embeded, file, name, false);
		}

		public void Add (bool embeded, string file, string name, bool isPrivate)
		{
			if (embedded_resources.Contains (name)) {
				ctx.Report.Error (1508, "The resource identifier `{0}' has already been used in this assembly", name);
				return;
			}
			IResource r = embeded ? 
				(IResource) new EmbededResource (name, file, isPrivate) : 
				new LinkedResource (name, file, isPrivate);

			embedded_resources.Add (name, r);
		}

		public void Emit ()
		{
			foreach (IResource r in embedded_resources.Values) {
				if (!File.Exists (r.FileName)) {
					ctx.Report.Error (1566, "Error reading resource file `{0}'", r.FileName);
					continue;
				}
				
				r.Emit ();
			}
		}
	}

	//
	// This is the only public entry point
	//
	public class CompilerCallableEntryPoint : MarshalByRefObject {
		public static bool InvokeCompiler (string [] args, TextWriter error)
		{
			try {
				StreamReportPrinter srp = new StreamReportPrinter (error);
				Driver d = Driver.Create (args, true, srp);
				if (d == null)
					return false;

				return d.Compile () && srp.ErrorsCount == 0;
			} finally {
				Reset ();
			}
		}

		public static int[] AllWarningNumbers {
			get {
				return Report.AllWarnings;
			}
		}

		public static void Reset ()
		{
			Reset (true);
		}

		public static void PartialReset ()
		{
			Reset (false);
		}
		
		public static void Reset (bool full_flag)
		{
			Driver.Reset ();
			RootContext.Reset (full_flag);
			Location.Reset ();
			TypeManager.Reset ();
			PredefinedAttributes.Reset ();
			TypeHandle.Reset ();

			if (full_flag)
				GlobalRootNamespace.Reset ();
			
			NamespaceEntry.Reset ();
			CodeGen.Reset ();
			Attribute.Reset ();
			AttributeTester.Reset ();
			AnonymousTypeClass.Reset ();
			AnonymousMethodBody.Reset ();
			AnonymousMethodStorey.Reset ();
			SymbolWriter.Reset ();
			Switch.Reset ();
			Linq.QueryBlock.TransparentParameter.Reset ();
			Convert.Reset ();
			TypeInfo.Reset ();
			DynamicExpressionStatement.Reset ();
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions