In ilasm/tests:
[mcs.git] / bmcs / driver.cs
blob2e2d2d3d69243aad575f0ee80f344106ab8f7c25
1 //
2 // driver.cs: The compiler command line driver.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
9 // (C) 2004 Novell, Inc
12 namespace Mono.CSharp
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
18 using System.Diagnostics;
19 using System.IO;
20 using System.Text;
21 using System.Globalization;
22 using System.Xml;
23 using Microsoft.VisualBasic;
25 public enum Target {
26 Library, Exe, Module, WinExe
29 /// <summary>
30 /// The compiler driver.
31 /// </summary>
32 public class Driver
36 // Assemblies references to be linked. Initialized with
37 // mscorlib.dll here.
38 static ArrayList references;
41 // If any of these fail, we ignore the problem. This is so
42 // that we can list all the assemblies in Windows and not fail
43 // if they are missing on Linux.
45 static ArrayList soft_references;
48 // Modules to be linked
50 static ArrayList modules;
52 // Lookup paths
53 static ArrayList link_paths;
55 // Whether we want to only run the tokenizer
56 static bool tokenize = false;
58 static string first_source;
60 static bool want_debugging_support = false;
62 static bool parse_only = false;
63 static bool timestamps = false;
64 static bool pause = false;
65 static bool show_counters = false;
68 // Whether to load the initial config file (what CSC.RSP has by default)
69 //
70 static bool load_default_config = true;
72 static Hashtable response_file_list;
75 // A list of resource files
77 static ArrayList resources;
78 static ArrayList embedded_resources;
79 static string win32ResourceFile;
80 static string win32IconFile;
83 // An array of the defines from the command line
85 static ArrayList defines;
88 // Output file
90 static string output_file = null;
93 // Last time we took the time
95 static DateTime last_time, first_time;
98 // Encoding: ISO-Latin1 is 28591
100 static Encoding encoding;
103 // Whether the user has specified a different encoder manually
105 static bool using_default_encoder = true;
107 public static void ShowTime (string msg)
109 if (!timestamps)
110 return;
112 DateTime now = DateTime.Now;
113 TimeSpan span = now - last_time;
114 last_time = now;
116 Console.WriteLine (
117 "[{0:00}:{1:000}] {2}",
118 (int) span.TotalSeconds, span.Milliseconds, msg);
121 public static void ShowTotalTime (string msg)
123 if (!timestamps)
124 return;
126 DateTime now = DateTime.Now;
127 TimeSpan span = now - first_time;
128 last_time = now;
130 Console.WriteLine (
131 "[{0:00}:{1:000}] {2}",
132 (int) span.TotalSeconds, span.Milliseconds, msg);
135 static void tokenize_file (SourceFile file)
137 Stream input;
139 try {
140 input = File.OpenRead (file.Name);
141 } catch {
142 Report.Error (2001, "Source file '" + file.Name + "' could not be opened");
143 return;
146 using (input){
147 SeekableStreamReader reader = new SeekableStreamReader (input, encoding, using_default_encoder);
148 Tokenizer lexer = new Tokenizer (reader, file, defines);
149 int token, tokens = 0, errors = 0;
151 while ((token = lexer.token ()) != Token.EOF){
152 tokens++;
153 if (token == Token.ERROR)
154 errors++;
156 Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
159 return;
162 // MonoTODO("Change error code for aborted compilation to something reasonable")]
163 static void parse (SourceFile file)
165 CSharpParser parser;
166 Stream input;
168 try {
169 input = File.OpenRead (file.Name);
170 } catch {
171 Report.Error (2001, "Source file '" + file.Name + "' could not be opened");
172 return;
175 SeekableStreamReader reader = new SeekableStreamReader (input, encoding, using_default_encoder);
177 parser = new CSharpParser (reader, file, defines);
178 try {
179 parser.parse ();
180 } catch (Exception ex) {
181 Report.Error(666, "Compilation aborted: " + ex);
182 } finally {
183 input.Close ();
187 static void OtherFlags ()
189 Console.WriteLine (
190 "Other flags in the compiler\n" +
191 " --fatal Makes errors fatal\n" +
192 " --parse Only parses the source file\n" +
193 " --stacktrace Shows stack trace at error location\n" +
194 " --timestamp Displays time stamps of various compiler events\n" +
195 " -2 Enables experimental C# features\n" +
196 " -v Verbose parsing (for debugging the parser)\n" +
197 " --mcs-debug X Sets MCS debugging level to X\n");
200 static void Usage ()
202 Console.WriteLine (
203 "Mono C# compiler, (C) 2001 - 2003 Ximian, Inc.\n" +
204 "mcs [options] source-files\n" +
205 " --about About the Mono C# compiler\n" +
206 " -addmodule:MODULE Adds the module to the generated assembly\n" +
207 " -checked[+|-] Set default context to checked\n" +
208 " -codepage:ID Sets code page to the one in ID\n" +
209 " (number, `utf8' or `reset')\n" +
210 " -clscheck[+|-] Disables CLS Compliance verifications" + Environment.NewLine +
211 " -define:S1[;S2] Defines one or more symbols (short: /d:)\n" +
212 " -debug[+|-] Generate debugging information\n" +
213 " -delaysign[+|-] Only insert the public key into the assembly (no signing)\n" +
214 " -doc:FILE XML Documentation file to generate\n" +
215 " -g Generate debugging information\n" +
216 " -keycontainer:NAME The key pair container used to strongname the assembly\n" +
217 " -keyfile:FILE The strongname key file used to strongname the assembly\n" +
218 " -langversion:TEXT Specifies language version modes: ISO-1 or Default" + Environment.NewLine +
219 " -lib:PATH1,PATH2 Adds the paths to the assembly link path\n" +
220 " -main:class Specified the class that contains the entry point\n" +
221 " -noconfig[+|-] Disables implicit references to assemblies\n" +
222 " -nostdlib[+|-] Does not load core libraries\n" +
223 " -nowarn:W1[,W2] Disables one or more warnings\n" +
224 " -out:FNAME Specifies output file\n" +
225 " -pkg:P1[,Pn] References packages P1..Pn\n" +
226 " --expect-error X Expect that error X will be encountered\n" +
227 " -recurse:SPEC Recursively compiles the files in SPEC ([dir]/file)\n" +
228 " -reference:ASS References the specified assembly (-r:ASS)\n" +
229 " -target:KIND Specifies the target (KIND is one of: exe, winexe,\n" +
230 " library, module), (short: /t:)\n" +
231 " -unsafe[+|-] Allows unsafe code\n" +
232 " -warnaserror[+|-] Treat warnings as errors\n" +
233 " -warn:LEVEL Sets warning level (the highest is 4, the default is 2)\n" +
234 " -help2 Show other help flags\n" +
237 // VB.NET specific compiler options
239 " -removeintchecks[+|-] Set default context to unchecked\n" +
240 " -optionstrict[+|-] Enables stricter type checking\n" +
241 " -optioncompare:[text|binary] Specifies the default mode for character comparisons\n" +
243 "\n" +
244 "Resources:\n" +
245 " -linkresource:FILE[,ID] Links FILE as a resource\n" +
246 " -resource:FILE[,ID] Embed FILE as a resource\n" +
247 " -win32res:FILE Specifies Win32 resource file (.res)\n" +
248 " -win32icon:FILE Use this icon for the output\n" +
249 " @file Read response file for more options\n\n" +
250 "Options can be of the form -option or /option");
253 static void TargetUsage ()
255 Report.Error (2019, "Valid options for -target: are exe, winexe, library or module");
258 static void About ()
260 Console.WriteLine (
261 "The Mono C# compiler is (C) 2001, 2002, 2003 Ximian, Inc.\n\n" +
262 "The compiler source code is released under the terms of the GNU GPL\n\n" +
264 "For more information on Mono, visit the project Web site\n" +
265 " http://www.go-mono.com\n\n" +
267 "The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig and Marek Safar");
268 Environment.Exit (0);
271 public static int counter1, counter2;
273 public static int Main (string[] args)
275 RootContext.Version = LanguageVersion.Default;
276 bool ok = MainDriver (args);
278 if (ok && Report.Errors == 0) {
279 if (Report.Warnings > 0) {
280 Console.WriteLine ("Compilation succeeded - {0} warning(s)", Report.Warnings);
282 if (show_counters){
283 Console.WriteLine ("Counter1: " + counter1);
284 Console.WriteLine ("Counter2: " + counter2);
286 if (pause)
287 Console.ReadLine ();
288 return 0;
289 } else {
290 Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
291 Report.Errors, Report.Warnings);
292 return 1;
296 static public void LoadAssembly (string assembly, bool soft)
298 Assembly a;
299 string total_log = "";
301 try {
302 char[] path_chars = { '/', '\\' };
304 if (assembly.IndexOfAny (path_chars) != -1) {
305 a = Assembly.LoadFrom (assembly);
306 } else {
307 string ass = assembly;
308 if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
309 ass = assembly.Substring (0, assembly.Length - 4);
310 a = Assembly.Load (ass);
312 TypeManager.AddAssembly (a);
314 } catch (FileNotFoundException){
315 foreach (string dir in link_paths){
316 string full_path = Path.Combine (dir, assembly);
317 if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
318 full_path += ".dll";
320 try {
321 a = Assembly.LoadFrom (full_path);
322 TypeManager.AddAssembly (a);
323 return;
324 } catch (FileNotFoundException ff) {
325 total_log += ff.FusionLog;
326 continue;
329 if (!soft) {
330 Report.Error (6, "Cannot find assembly `" + assembly + "'" );
331 Console.WriteLine ("Log: \n" + total_log);
333 } catch (BadImageFormatException f) {
334 Report.Error(6, "Cannot load assembly (bad file format)" + f.FusionLog);
335 } catch (FileLoadException f){
336 Report.Error(6, "Cannot load assembly " + f.FusionLog);
337 } catch (ArgumentNullException){
338 Report.Error(6, "Cannot load assembly (null argument)");
342 static public void LoadModule (MethodInfo adder_method, string module)
344 Module m;
345 string total_log = "";
347 try {
348 try {
349 m = (Module)adder_method.Invoke (CodeGen.Assembly.Builder, new object [] { module });
351 catch (TargetInvocationException ex) {
352 throw ex.InnerException;
354 TypeManager.AddModule (m);
357 catch (FileNotFoundException){
358 foreach (string dir in link_paths){
359 string full_path = Path.Combine (dir, module);
360 if (!module.EndsWith (".netmodule"))
361 full_path += ".netmodule";
363 try {
364 try {
365 m = (Module)adder_method.Invoke (CodeGen.Assembly.Builder, new object [] { full_path });
367 catch (TargetInvocationException ex) {
368 throw ex.InnerException;
370 TypeManager.AddModule (m);
371 return;
372 } catch (FileNotFoundException ff) {
373 total_log += ff.FusionLog;
374 continue;
377 Report.Error (6, "Cannot find module `" + module + "'" );
378 Console.WriteLine ("Log: \n" + total_log);
379 } catch (BadImageFormatException f) {
380 Report.Error(6, "Cannot load module (bad file format)" + f.FusionLog);
381 } catch (FileLoadException f){
382 Report.Error(6, "Cannot load module " + f.FusionLog);
383 } catch (ArgumentNullException){
384 Report.Error(6, "Cannot load module (null argument)");
388 /// <summary>
389 /// Loads all assemblies referenced on the command line
390 /// </summary>
391 static public void LoadReferences ()
393 foreach (string r in references)
394 LoadAssembly (r, false);
396 foreach (string r in soft_references)
397 LoadAssembly (r, true);
399 return;
402 static void SetupDefaultDefines ()
404 defines = new ArrayList ();
405 defines.Add ("__MonoCS__");
408 static string [] LoadArgs (string file)
410 StreamReader f;
411 ArrayList args = new ArrayList ();
412 string line;
413 try {
414 f = new StreamReader (file);
415 } catch {
416 return null;
419 StringBuilder sb = new StringBuilder ();
421 while ((line = f.ReadLine ()) != null){
422 int t = line.Length;
424 for (int i = 0; i < t; i++){
425 char c = line [i];
427 if (c == '"' || c == '\''){
428 char end = c;
430 for (i++; i < t; i++){
431 c = line [i];
433 if (c == end)
434 break;
435 sb.Append (c);
437 } else if (c == ' '){
438 if (sb.Length > 0){
439 args.Add (sb.ToString ());
440 sb.Length = 0;
442 } else
443 sb.Append (c);
445 if (sb.Length > 0){
446 args.Add (sb.ToString ());
447 sb.Length = 0;
451 string [] ret_value = new string [args.Count];
452 args.CopyTo (ret_value, 0);
454 return ret_value;
458 // Returns the directory where the system assemblies are installed
460 static string GetSystemDir ()
462 Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
464 foreach (Assembly a in assemblies){
465 string codebase = a.Location;
466 string fn = System.IO.Path.GetFileName (codebase);
467 if (fn == "corlib.dll" || fn == "mscorlib.dll"){
468 return codebase.Substring (0, codebase.LastIndexOf (System.IO.Path.DirectorySeparatorChar));
472 Report.Error (-15, "Can not compute my system path");
473 return "";
477 // Given a path specification, splits the path from the file/pattern
479 static void SplitPathAndPattern (string spec, out string path, out string pattern)
481 int p = spec.LastIndexOf ('/');
482 if (p != -1){
484 // Windows does not like /file.cs, switch that to:
485 // "\", "file.cs"
487 if (p == 0){
488 path = "\\";
489 pattern = spec.Substring (1);
490 } else {
491 path = spec.Substring (0, p);
492 pattern = spec.Substring (p + 1);
494 return;
497 p = spec.LastIndexOf ('\\');
498 if (p != -1){
499 path = spec.Substring (0, p);
500 pattern = spec.Substring (p + 1);
501 return;
504 path = ".";
505 pattern = spec;
508 static void ProcessFile (string f)
510 if (first_source == null)
511 first_source = f;
513 Location.AddFile (f);
516 static void ProcessFiles ()
518 Location.Initialize ();
520 foreach (SourceFile file in Location.SourceFiles) {
521 if (tokenize) {
522 tokenize_file (file);
523 } else {
524 parse (file);
529 static void CompileFiles (string spec, bool recurse)
531 string path, pattern;
533 SplitPathAndPattern (spec, out path, out pattern);
534 if (pattern.IndexOf ('*') == -1){
535 ProcessFile (spec);
536 return;
539 string [] files = null;
540 try {
541 files = Directory.GetFiles (path, pattern);
542 } catch (System.IO.DirectoryNotFoundException) {
543 Report.Error (2001, "Source file `" + spec + "' could not be found");
544 return;
545 } catch (System.IO.IOException){
546 Report.Error (2001, "Source file `" + spec + "' could not be found");
547 return;
549 foreach (string f in files) {
550 ProcessFile (f);
553 if (!recurse)
554 return;
556 string [] dirs = null;
558 try {
559 dirs = Directory.GetDirectories (path);
560 } catch {
563 foreach (string d in dirs) {
565 // Don't include path in this string, as each
566 // directory entry already does
567 CompileFiles (d + "/" + pattern, true);
571 static void DefineDefaultConfig ()
574 // For now the "default config" is harcoded into the compiler
575 // we can move this outside later
577 string [] default_config = {
578 "System",
579 "System.Xml",
580 "Microsoft.VisualBasic",
581 #if false
583 // Is it worth pre-loading all this stuff?
585 "Accessibility",
586 "System.Configuration.Install",
587 "System.Data",
588 "System.Design",
589 "System.DirectoryServices",
590 "System.Drawing.Design",
591 "System.Drawing",
592 "System.EnterpriseServices",
593 "System.Management",
594 "System.Messaging",
595 "System.Runtime.Remoting",
596 "System.Runtime.Serialization.Formatters.Soap",
597 "System.Security",
598 "System.ServiceProcess",
599 "System.Web",
600 "System.Web.RegularExpressions",
601 "System.Web.Services",
602 "System.Windows.Forms"
603 #endif
606 int p = 0;
607 foreach (string def in default_config)
608 soft_references.Insert (p++, def);
611 static void SetOutputFile (string name)
613 output_file = name;
616 static void SetWarningLevel (string s)
618 int level = 0;
620 try {
621 level = Int32.Parse (s);
622 } catch {
623 Report.Error (
624 1900,
625 "--wlevel requires a value from 0 to 4");
626 Environment.Exit (1);
628 if (level < 0 || level > 4){
629 Report.Error (1900, "Warning level must be 0 to 4");
630 Environment.Exit (1);
632 RootContext.WarningLevel = level;
633 TestWarningConflict ();
636 static void TestWarningConflict ()
638 if (RootContext.WarningLevel == 0 && Report.WarningsAreErrors) {
639 Report.Error (1901, "Conflicting options specified: Warning level 0; Treat warnings as errors");
640 Environment.Exit (1);
644 static void SetupV2 ()
646 RootContext.Version = LanguageVersion.Default;
647 defines.Add ("__V2__");
650 static void Version ()
652 string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
653 Console.WriteLine ("Mono C# compiler version {0}", version);
654 Environment.Exit (0);
658 // Currently handles the Unix-like command line options, but will be
659 // deprecated in favor of the CSCParseOption, which will also handle the
660 // options that start with a dash in the future.
662 static bool UnixParseOption (string arg, ref string [] args, ref int i)
664 switch (arg){
665 case "-v":
666 CSharpParser.yacc_verbose_flag++;
667 return true;
669 case "--version":
670 Version ();
671 return true;
673 case "--parse":
674 parse_only = true;
675 return true;
677 case "--main": case "-m":
678 if ((i + 1) >= args.Length){
679 Usage ();
680 Environment.Exit (1);
682 RootContext.MainClass = args [++i];
683 return true;
685 case "--unsafe":
686 RootContext.Unsafe = true;
687 return true;
689 case "/?": case "/h": case "/help":
690 case "--help":
691 Usage ();
692 Environment.Exit (0);
693 return true;
695 case "--define":
696 if ((i + 1) >= args.Length){
697 Usage ();
698 Environment.Exit (1);
700 defines.Add (args [++i]);
701 return true;
703 case "--show-counters":
704 show_counters = true;
705 return true;
707 case "--expect-error": {
708 int code = 0;
710 try {
711 code = Int32.Parse (
712 args [++i], NumberStyles.AllowLeadingSign);
713 Report.ExpectedError = code;
714 } catch {
715 Report.Error (-14, "Invalid number specified");
717 return true;
720 case "--tokenize":
721 tokenize = true;
722 return true;
724 case "-o":
725 case "--output":
726 if ((i + 1) >= args.Length){
727 Usage ();
728 Environment.Exit (1);
730 SetOutputFile (args [++i]);
731 return true;
733 case "--checked":
734 RootContext.Checked = true;
735 return true;
737 case "--stacktrace":
738 Report.Stacktrace = true;
739 return true;
741 case "--linkresource":
742 case "--linkres":
743 if ((i + 1) >= args.Length){
744 Usage ();
745 Report.Error (5, "Missing argument to --linkres");
746 Environment.Exit (1);
748 if (resources == null)
749 resources = new ArrayList ();
751 resources.Add (args [++i]);
752 return true;
754 case "--resource":
755 case "--res":
756 if ((i + 1) >= args.Length){
757 Usage ();
758 Report.Error (5, "Missing argument to --resource");
759 Environment.Exit (1);
761 if (embedded_resources == null)
762 embedded_resources = new ArrayList ();
764 embedded_resources.Add (args [++i]);
765 return true;
767 case "--target":
768 if ((i + 1) >= args.Length){
769 Environment.Exit (1);
770 return true;
773 string type = args [++i];
774 switch (type){
775 case "library":
776 RootContext.Target = Target.Library;
777 RootContext.TargetExt = ".dll";
778 break;
780 case "exe":
781 RootContext.Target = Target.Exe;
782 break;
784 case "winexe":
785 RootContext.Target = Target.WinExe;
786 break;
788 case "module":
789 RootContext.Target = Target.Module;
790 RootContext.TargetExt = ".dll";
791 break;
792 default:
793 TargetUsage ();
794 Environment.Exit (1);
795 break;
797 return true;
799 case "-r":
800 if ((i + 1) >= args.Length){
801 Usage ();
802 Environment.Exit (1);
805 references.Add (args [++i]);
806 return true;
808 case "-L":
809 if ((i + 1) >= args.Length){
810 Usage ();
811 Environment.Exit (1);
813 link_paths.Add (args [++i]);
814 return true;
816 case "--nostdlib":
817 RootContext.StdLib = false;
818 return true;
820 case "--fatal":
821 Report.Fatal = true;
822 return true;
824 case "--werror":
825 Report.WarningsAreErrors = true;
826 TestWarningConflict();
827 return true;
829 case "--nowarn":
830 if ((i + 1) >= args.Length){
831 Usage ();
832 Environment.Exit (1);
834 int warn = 0;
836 try {
837 warn = Int32.Parse (args [++i]);
838 } catch {
839 Usage ();
840 Environment.Exit (1);
842 Report.SetIgnoreWarning (warn);
843 return true;
845 case "--wlevel":
846 if ((i + 1) >= args.Length){
847 Report.Error (
848 1900,
849 "--wlevel requires a value from 0 to 4");
850 Environment.Exit (1);
853 SetWarningLevel (args [++i]);
854 return true;
856 case "--mcs-debug":
857 if ((i + 1) >= args.Length){
858 Report.Error (5, "--mcs-debug requires an argument");
859 Environment.Exit (1);
862 try {
863 Report.DebugFlags = Int32.Parse (args [++i]);
864 } catch {
865 Report.Error (5, "Invalid argument to --mcs-debug");
866 Environment.Exit (1);
868 return true;
870 case "--about":
871 About ();
872 return true;
874 case "--recurse":
875 if ((i + 1) >= args.Length){
876 Report.Error (5, "--recurse requires an argument");
877 Environment.Exit (1);
879 CompileFiles (args [++i], true);
880 return true;
882 case "--timestamp":
883 timestamps = true;
884 last_time = first_time = DateTime.Now;
885 return true;
887 case "--pause":
888 pause = true;
889 return true;
891 case "--debug": case "-g":
892 want_debugging_support = true;
893 return true;
895 case "--noconfig":
896 load_default_config = false;
897 return true;
900 // VB.NET specific compiler options
903 case "--removeintchecks":
904 RootContext.Checked = false;
905 return true;
907 case "--optionstrict":
908 RootContext.StricterTypeChecking= true;
909 return true;
911 case "--optioncompare":
912 if ((i + 1) >= args.Length){
913 Usage ();
914 Environment.Exit (1);
917 if (args [++i].Equals ("text"))
918 RootContext.StringComparisonMode = CompareMethod.Text;
919 else if (args[++i].Equals ("binary"))
920 RootContext.StringComparisonMode = CompareMethod.Binary;
921 else {
922 Report.Error (5, "Invalid argument to --optioncompare");
923 Environment.Exit (1);
926 return true;
930 return false;
934 // Currently it is very basic option parsing, but eventually, this will
935 // be the complete option parser
937 static bool CSCParseOption (string option, ref string [] args, ref int i)
939 int idx = option.IndexOf (':');
940 string arg, value;
942 if (idx == -1){
943 arg = option;
944 value = "";
945 } else {
946 arg = option.Substring (0, idx);
948 value = option.Substring (idx + 1);
951 switch (arg){
952 case "/nologo":
953 return true;
955 case "/t":
956 case "/target":
957 switch (value){
958 case "exe":
959 RootContext.Target = Target.Exe;
960 break;
962 case "winexe":
963 RootContext.Target = Target.WinExe;
964 break;
966 case "library":
967 RootContext.Target = Target.Library;
968 RootContext.TargetExt = ".dll";
969 break;
971 case "module":
972 RootContext.Target = Target.Module;
973 RootContext.TargetExt = ".netmodule";
974 break;
976 default:
977 TargetUsage ();
978 Environment.Exit (1);
979 break;
981 return true;
983 case "/out":
984 if (value == ""){
985 Usage ();
986 Environment.Exit (1);
988 SetOutputFile (value);
989 return true;
991 case "/optimize":
992 case "/optimize+":
993 case "/optimize-":
994 case "/incremental":
995 case "/incremental+":
996 case "/incremental-":
997 // nothing.
998 return true;
1000 case "/d":
1001 case "/define": {
1002 string [] defs;
1004 if (value == ""){
1005 Usage ();
1006 Environment.Exit (1);
1009 defs = value.Split (new Char [] {';', ','});
1010 foreach (string d in defs){
1011 defines.Add (d);
1013 return true;
1016 case "/linkres":
1017 case "/linkresource":
1018 if (value == ""){
1019 Report.Error (5, arg + " requires an argument");
1020 Environment.Exit (1);
1022 if (resources == null)
1023 resources = new ArrayList ();
1025 resources.Add (value);
1026 return true;
1028 case "/pkg": {
1029 string packages;
1031 if (value == ""){
1032 Usage ();
1033 Environment.Exit (1);
1035 packages = String.Join (" ", value.Split (new Char [] { ';', ',', '\n', '\r'}));
1037 ProcessStartInfo pi = new ProcessStartInfo ();
1038 pi.FileName = "pkg-config";
1039 pi.RedirectStandardOutput = true;
1040 pi.UseShellExecute = false;
1041 pi.Arguments = "--libs " + packages;
1042 Process p = null;
1043 try {
1044 p = Process.Start (pi);
1045 } catch (Exception e) {
1046 Report.Error (-27, "Couldn't run pkg-config: " + e.Message);
1047 Environment.Exit (1);
1050 if (p.StandardOutput == null){
1051 Report.Warning (-27, "Specified package did not return any information");
1052 return true;
1054 string pkgout = p.StandardOutput.ReadToEnd ();
1055 p.WaitForExit ();
1056 if (p.ExitCode != 0) {
1057 Report.Error (-27, "Error running pkg-config. Check the above output.");
1058 Environment.Exit (1);
1061 if (pkgout != null){
1062 string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
1063 Split (new Char [] { ' ', '\t'});
1064 args = AddArgs (args, xargs);
1067 p.Close ();
1068 return true;
1071 case "/res":
1072 case "/resource":
1073 if (value == ""){
1074 Report.Error (5, "-resource requires an argument");
1075 Environment.Exit (1);
1077 if (embedded_resources == null)
1078 embedded_resources = new ArrayList ();
1080 if (embedded_resources.Contains (value)) {
1081 Report.Error (1508, String.Format ("The resource identifier '{0}' has already been used in this assembly.", value));
1083 else if (value.IndexOf (',') != -1 && embedded_resources.Contains (value.Split (',')[1])) {
1084 Report.Error (1508, String.Format ("The resource identifier '{0}' has already been used in this assembly.", value));
1086 else {
1087 embedded_resources.Add (value);
1089 return true;
1091 case "/recurse":
1092 if (value == ""){
1093 Report.Error (5, "-recurse requires an argument");
1094 Environment.Exit (1);
1096 CompileFiles (value, true);
1097 return true;
1099 case "/r":
1100 case "/reference": {
1101 if (value == ""){
1102 Report.Error (5, "-reference requires an argument");
1103 Environment.Exit (1);
1106 string [] refs = value.Split (new char [] { ';', ',' });
1107 foreach (string r in refs){
1108 references.Add (r);
1110 return true;
1112 case "/addmodule": {
1113 if (value == ""){
1114 Report.Error (5, arg + " requires an argument");
1115 Environment.Exit (1);
1118 string [] refs = value.Split (new char [] { ';', ',' });
1119 foreach (string r in refs){
1120 modules.Add (r);
1122 return true;
1124 case "/win32res": {
1125 if (value == "") {
1126 Report.Error (5, arg + " requires an argument");
1127 Environment.Exit (1);
1130 win32ResourceFile = value;
1131 return true;
1133 case "/win32icon": {
1134 if (value == "") {
1135 Report.Error (5, arg + " requires an argument");
1136 Environment.Exit (1);
1139 win32IconFile = value;
1140 return true;
1142 case "/doc": {
1143 if (value == ""){
1144 Report.Error (2006, arg + " requires an argument");
1145 Environment.Exit (1);
1147 RootContext.Documentation = new Documentation (value);
1148 return true;
1150 case "/lib": {
1151 string [] libdirs;
1153 if (value == ""){
1154 Report.Error (5, "/lib requires an argument");
1155 Environment.Exit (1);
1158 libdirs = value.Split (new Char [] { ',' });
1159 foreach (string dir in libdirs)
1160 link_paths.Add (dir);
1161 return true;
1164 case "/debug-":
1165 want_debugging_support = false;
1166 return true;
1168 case "/debug":
1169 case "/debug+":
1170 want_debugging_support = true;
1171 return true;
1173 case "/checked":
1174 case "/checked+":
1175 RootContext.Checked = true;
1176 return true;
1178 case "/checked-":
1179 RootContext.Checked = false;
1180 return true;
1182 case "/clscheck":
1183 case "/clscheck+":
1184 return true;
1186 case "/clscheck-":
1187 RootContext.VerifyClsCompliance = false;
1188 return true;
1190 case "/unsafe":
1191 case "/unsafe+":
1192 RootContext.Unsafe = true;
1193 return true;
1195 case "/unsafe-":
1196 RootContext.Unsafe = false;
1197 return true;
1199 case "/warnaserror":
1200 case "/warnaserror+":
1201 Report.WarningsAreErrors = true;
1202 TestWarningConflict();
1203 return true;
1205 case "/warnaserror-":
1206 Report.WarningsAreErrors = false;
1207 return true;
1209 case "/warn":
1210 SetWarningLevel (value);
1211 return true;
1213 case "/nowarn": {
1214 string [] warns;
1216 if (value == ""){
1217 Report.Error (5, "/nowarn requires an argument");
1218 Environment.Exit (1);
1221 warns = value.Split (new Char [] {','});
1222 foreach (string wc in warns){
1223 try {
1224 int warn = Int32.Parse (wc);
1225 if (warn < 1) {
1226 throw new ArgumentOutOfRangeException("warn");
1228 Report.SetIgnoreWarning (warn);
1229 } catch {
1230 Report.Error (1904, String.Format("'{0}' is not a valid warning number", wc));
1231 Environment.Exit (1);
1234 return true;
1237 case "/noconfig-":
1238 load_default_config = true;
1239 return true;
1241 case "/noconfig":
1242 case "/noconfig+":
1243 load_default_config = false;
1244 return true;
1246 case "/help2":
1247 OtherFlags ();
1248 Environment.Exit(0);
1249 return true;
1251 case "/help":
1252 case "/?":
1253 Usage ();
1254 Environment.Exit (0);
1255 return true;
1257 case "/main":
1258 case "/m":
1259 if (value == ""){
1260 Report.Error (5, arg + " requires an argument");
1261 Environment.Exit (1);
1263 RootContext.MainClass = value;
1264 return true;
1266 case "/nostdlib":
1267 case "/nostdlib+":
1268 RootContext.StdLib = false;
1269 return true;
1271 case "/nostdlib-":
1272 RootContext.StdLib = true;
1273 return true;
1275 case "/fullpaths":
1276 return true;
1278 case "/keyfile":
1279 if (value == String.Empty) {
1280 Report.Error (5, arg + " requires an argument");
1281 Environment.Exit (1);
1283 RootContext.StrongNameKeyFile = value;
1284 return true;
1285 case "/keycontainer":
1286 if (value == String.Empty) {
1287 Report.Error (5, arg + " requires an argument");
1288 Environment.Exit (1);
1290 RootContext.StrongNameKeyContainer = value;
1291 return true;
1292 case "/delaysign+":
1293 RootContext.StrongNameDelaySign = true;
1294 return true;
1295 case "/delaysign-":
1296 RootContext.StrongNameDelaySign = false;
1297 return true;
1299 case "/v2":
1300 case "/2":
1301 Console.WriteLine ("The compiler option -2 is obsolete. Please use /langversion instead");
1302 SetupV2 ();
1303 return true;
1305 case "/langversion":
1306 switch (value.ToLower (CultureInfo.InvariantCulture)) {
1307 case "iso-1":
1308 RootContext.Version = LanguageVersion.ISO_1;
1309 return true;
1311 case "default":
1312 SetupV2 ();
1313 return true;
1315 Report.Error (1617, "Invalid option '{0}' for /langversion; must be ISO-1 or Default", value);
1316 Environment.Exit (1);
1317 return false;
1319 case "/codepage":
1320 int cp = -1;
1322 if (value == "utf8"){
1323 encoding = new UTF8Encoding();
1324 using_default_encoder = false;
1325 return true;
1327 if (value == "reset"){
1329 // 28591 is the code page for ISO-8859-1 encoding.
1331 cp = 28591;
1332 using_default_encoder = true;
1335 try {
1336 cp = Int32.Parse (value);
1337 encoding = Encoding.GetEncoding (cp);
1338 using_default_encoder = false;
1339 } catch {
1340 Report.Error (2016, String.Format("Code page '{0}' is invalid or not installed", cp));
1341 Environment.Exit (1);
1343 return true;
1346 // VB.NET specific compiler options
1349 case "/removeintchecks":
1350 case "/removeintchecks+":
1351 RootContext.Checked = false;
1352 return true;
1354 case "/removeintchecks-":
1355 RootContext.Checked = true;
1356 return true;
1358 case "/optionstrict":
1359 case "/optionstrict+":
1360 RootContext.StricterTypeChecking = true;
1361 return true;
1363 case "/optionstrict-":
1364 RootContext.StricterTypeChecking = false;
1365 return true;
1367 case "/optioncompare":
1368 if (value == ""){
1369 Report.Error (5, arg + " requires an argument");
1370 Environment.Exit (1);
1372 if (value.Equals ("text"))
1373 RootContext.StringComparisonMode = CompareMethod.Text;
1374 else if (value.Equals ("binary"))
1375 RootContext.StringComparisonMode = CompareMethod.Binary;
1376 else {
1377 Report.Error (5, "Invalid argument to /optioncompare");
1378 Environment.Exit (1);
1380 return true;
1383 //Report.Error (2007, String.Format ("Unrecognized command-line option: '{0}'", option));
1384 //Environment.Exit (1);
1385 return false;
1388 static string [] AddArgs (string [] args, string [] extra_args)
1390 string [] new_args;
1391 new_args = new string [extra_args.Length + args.Length];
1393 // if args contains '--' we have to take that into account
1394 // split args into first half and second half based on '--'
1395 // and add the extra_args before --
1396 int split_position = Array.IndexOf (args, "--");
1397 if (split_position != -1)
1399 Array.Copy (args, new_args, split_position);
1400 extra_args.CopyTo (new_args, split_position);
1401 Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
1403 else
1405 args.CopyTo (new_args, 0);
1406 extra_args.CopyTo (new_args, args.Length);
1409 return new_args;
1412 /// <summary>
1413 /// Parses the arguments, and drives the compilation
1414 /// process.
1415 /// </summary>
1417 /// <remarks>
1418 /// TODO: Mostly structured to debug the compiler
1419 /// now, needs to be turned into a real driver soon.
1420 /// </remarks>
1421 // [MonoTODO("Change error code for unknown argument to something reasonable")]
1422 internal static bool MainDriver (string [] args)
1424 int i;
1425 bool parsing_options = true;
1427 try {
1428 encoding = Encoding.GetEncoding (28591);
1429 } catch {
1430 Console.WriteLine ("Error: could not load encoding 28591, trying 1252");
1431 encoding = Encoding.GetEncoding (1252);
1434 references = new ArrayList ();
1435 soft_references = new ArrayList ();
1436 modules = new ArrayList ();
1437 link_paths = new ArrayList ();
1439 SetupDefaultDefines ();
1442 // Setup defaults
1444 // This is not required because Assembly.Load knows about this
1445 // path.
1448 for (i = 0; i < args.Length; i++){
1449 string arg = args [i];
1450 if (arg == "")
1451 continue;
1453 if (arg.StartsWith ("@")){
1454 string [] extra_args;
1455 string response_file = arg.Substring (1);
1457 if (response_file_list == null)
1458 response_file_list = new Hashtable ();
1460 if (response_file_list.Contains (response_file)){
1461 Report.Error (
1462 1515, "Response file `" + response_file +
1463 "' specified multiple times");
1464 Environment.Exit (1);
1467 response_file_list.Add (response_file, response_file);
1469 extra_args = LoadArgs (response_file);
1470 if (extra_args == null){
1471 Report.Error (2011, "Unable to open response file: " +
1472 response_file);
1473 return false;
1476 args = AddArgs (args, extra_args);
1477 continue;
1480 if (parsing_options){
1481 if (arg == "--"){
1482 parsing_options = false;
1483 continue;
1486 if (arg.StartsWith ("-")){
1487 if (UnixParseOption (arg, ref args, ref i))
1488 continue;
1490 // Try a -CSCOPTION
1491 string csc_opt = "/" + arg.Substring (1);
1492 if (CSCParseOption (csc_opt, ref args, ref i))
1493 continue;
1494 } else {
1495 if (arg.StartsWith ("/")){
1496 if (CSCParseOption (arg, ref args, ref i))
1497 continue;
1502 CompileFiles (arg, false);
1505 ProcessFiles ();
1507 if (tokenize)
1508 return true;
1511 // This will point to the NamespaceEntry of the last file that was parsed, and may
1512 // not be meaningful when resolving classes from other files. So, reset it to prevent
1513 // silent bugs.
1515 RootContext.Tree.Types.NamespaceEntry = null;
1518 // If we are an exe, require a source file for the entry point
1520 if (RootContext.Target == Target.Exe || RootContext.Target == Target.WinExe){
1521 if (first_source == null){
1522 Report.Error (2008, "No files to compile were specified");
1523 return false;
1529 // If there is nothing to put in the assembly, and we are not a library
1531 if (first_source == null && embedded_resources == null && resources == null){
1532 Report.Error (2008, "No files to compile were specified");
1533 return false;
1536 if (Report.Errors > 0)
1537 return false;
1539 if (parse_only)
1540 return true;
1542 Tokenizer.Cleanup ();
1545 // Load Core Library for default compilation
1547 if (RootContext.StdLib)
1548 references.Insert (0, "mscorlib");
1550 if (load_default_config)
1551 DefineDefaultConfig ();
1553 if (Report.Errors > 0){
1554 return false;
1558 // Load assemblies required
1560 if (timestamps)
1561 ShowTime ("Loading references");
1562 link_paths.Add (GetSystemDir ());
1563 link_paths.Add (Directory.GetCurrentDirectory ());
1564 LoadReferences ();
1566 if (timestamps)
1567 ShowTime (" References loaded");
1569 if (Report.Errors > 0){
1570 return false;
1574 // Quick hack
1576 if (output_file == null){
1577 int pos = first_source.LastIndexOf ('.');
1579 if (pos > 0)
1580 output_file = first_source.Substring (0, pos) + RootContext.TargetExt;
1581 else
1582 output_file = first_source + RootContext.TargetExt;
1585 CodeGen.Init (output_file, output_file, want_debugging_support);
1587 if (RootContext.Target == Target.Module) {
1588 PropertyInfo module_only = typeof (AssemblyBuilder).GetProperty ("IsModuleOnly", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
1589 if (module_only == null) {
1590 Report.Error (0, new Location (-1), "Cannot use /target:module on this runtime: try the Mono runtime instead.");
1591 Environment.Exit (1);
1594 MethodInfo set_method = module_only.GetSetMethod (true);
1595 set_method.Invoke (CodeGen.Assembly.Builder, BindingFlags.Default, null, new object[]{true}, null);
1598 TypeManager.AddModule (CodeGen.Module.Builder);
1600 if (modules.Count > 0) {
1601 MethodInfo adder_method = typeof (AssemblyBuilder).GetMethod ("AddModule", BindingFlags.Instance|BindingFlags.NonPublic);
1602 if (adder_method == null) {
1603 Report.Error (0, new Location (-1), "Cannot use /addmodule on this runtime: Try the Mono runtime instead.");
1604 Environment.Exit (1);
1607 foreach (string module in modules)
1608 LoadModule (adder_method, module);
1611 TypeManager.ComputeNamespaces ();
1614 // Before emitting, we need to get the core
1615 // types emitted from the user defined types
1616 // or from the system ones.
1618 if (timestamps)
1619 ShowTime ("Initializing Core Types");
1620 if (!RootContext.StdLib){
1621 RootContext.ResolveCore ();
1622 if (Report.Errors > 0)
1623 return false;
1626 TypeManager.InitCoreTypes ();
1627 if (timestamps)
1628 ShowTime (" Core Types done");
1631 // The second pass of the compiler
1633 if (timestamps)
1634 ShowTime ("Resolving tree");
1635 RootContext.ResolveTree ();
1637 if (Report.Errors > 0)
1638 return false;
1639 if (timestamps)
1640 ShowTime ("Populate tree");
1641 if (!RootContext.StdLib)
1642 RootContext.BootCorlib_PopulateCoreTypes ();
1643 RootContext.PopulateTypes ();
1644 RootContext.DefineTypes ();
1646 if (RootContext.Documentation != null &&
1647 !RootContext.Documentation.OutputDocComment (
1648 output_file))
1649 return false;
1651 TypeManager.InitCodeHelpers ();
1654 // Verify using aliases now
1656 Namespace.VerifyUsing ();
1658 if (Report.Errors > 0){
1659 return false;
1662 if (RootContext.VerifyClsCompliance) {
1663 CodeGen.Assembly.ResolveClsCompliance ();
1664 if (CodeGen.Assembly.IsClsCompliant) {
1665 AttributeTester.VerifyModulesClsCompliance ();
1666 TypeManager.LoadAllImportedTypes ();
1667 AttributeTester.VerifyTopLevelNameClsCompliance ();
1672 // The code generator
1674 if (timestamps)
1675 ShowTime ("Emitting code");
1676 ShowTotalTime ("Total so far");
1677 RootContext.EmitCode ();
1678 if (timestamps)
1679 ShowTime (" done");
1681 if (Report.Errors > 0){
1682 return false;
1685 if (timestamps)
1686 ShowTime ("Closing types");
1688 RootContext.CloseTypes ();
1690 PEFileKinds k = PEFileKinds.ConsoleApplication;
1692 switch (RootContext.Target) {
1693 case Target.Library:
1694 case Target.Module:
1695 k = PEFileKinds.Dll; break;
1696 case Target.Exe:
1697 k = PEFileKinds.ConsoleApplication; break;
1698 case Target.WinExe:
1699 k = PEFileKinds.WindowApplication; break;
1702 if (RootContext.NeedsEntryPoint) {
1703 MethodInfo ep = RootContext.EntryPoint;
1705 if (ep == null) {
1706 if (RootContext.MainClass != null) {
1707 DeclSpace main_cont = RootContext.Tree.Decls [RootContext.MainClass] as DeclSpace;
1708 if (main_cont == null) {
1709 Report.Error (1555, output_file, "Could not find '{0}' specified for Main method", RootContext.MainClass);
1710 return false;
1713 if (!(main_cont is ClassOrStruct)) {
1714 Report.Error (1556, output_file, "'{0}' specified for Main method must be a valid class or struct", RootContext.MainClass);
1715 return false;
1718 Report.Error (1558, main_cont.Location, "'{0}' does not have a suitable static Main method", main_cont.GetSignatureForError ());
1719 return false;
1722 if (Report.Errors == 0)
1723 Report.Error (5001, "Program " + output_file +
1724 " does not have an entry point defined");
1725 return false;
1728 CodeGen.Assembly.Builder.SetEntryPoint (ep, k);
1729 } else if (RootContext.MainClass != null) {
1730 Report.Error (2017, "Can not specify -main: when building module or library");
1734 // Add the resources
1736 if (resources != null){
1737 foreach (string spec in resources){
1738 string file, res;
1739 int cp;
1741 cp = spec.IndexOf (',');
1742 if (cp != -1){
1743 file = spec.Substring (0, cp);
1744 res = spec.Substring (cp + 1);
1745 } else
1746 file = res = spec;
1748 CodeGen.Assembly.Builder.AddResourceFile (res, file);
1752 if (embedded_resources != null){
1753 object[] margs = new object [2];
1754 Type[] argst = new Type [2];
1755 argst [0] = argst [1] = typeof (string);
1757 MethodInfo embed_res = typeof (AssemblyBuilder).GetMethod (
1758 "EmbedResourceFile", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic,
1759 null, CallingConventions.Any, argst, null);
1761 if (embed_res == null) {
1762 Report.Warning (0, new Location (-1),
1763 "Cannot embed resources on this runtime: try the Mono runtime instead.");
1764 } else {
1765 foreach (string spec in embedded_resources) {
1766 int cp;
1768 cp = spec.IndexOf (',');
1769 if (cp != -1){
1770 margs [0] = spec.Substring (cp + 1);
1771 margs [1] = spec.Substring (0, cp);
1772 } else {
1773 margs [1] = spec;
1774 margs [0] = Path.GetFileName (spec);
1777 if (File.Exists ((string) margs [1]))
1778 embed_res.Invoke (CodeGen.Assembly.Builder, margs);
1779 else {
1780 Report.Error (1566, "Can not find the resource " + margs [1]);
1787 // Add Win32 resources
1790 CodeGen.Assembly.Builder.DefineVersionInfoResource ();
1792 if (win32ResourceFile != null) {
1793 try {
1794 CodeGen.Assembly.Builder.DefineUnmanagedResource (win32ResourceFile);
1796 catch (ArgumentException) {
1797 Report.Warning (0, new Location (-1), "Cannot embed win32 resources on this runtime: try the Mono runtime instead.");
1801 if (win32IconFile != null) {
1802 MethodInfo define_icon = typeof (AssemblyBuilder).GetMethod ("DefineIconResource", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
1803 if (define_icon == null) {
1804 Report.Warning (0, new Location (-1), "Cannot embed icon resource on this runtime: try the Mono runtime instead.");
1806 define_icon.Invoke (CodeGen.Assembly.Builder, new object [] { win32IconFile });
1809 if (Report.Errors > 0)
1810 return false;
1812 CodeGen.Save (output_file);
1813 if (timestamps) {
1814 ShowTime ("Saved output");
1815 ShowTotalTime ("Total");
1818 Timer.ShowTimers ();
1820 if (Report.ExpectedError != 0){
1821 if (Report.Errors == 0) {
1822 Console.WriteLine ("Failed to report expected error " + Report.ExpectedError + ".\n" +
1823 "No other errors reported.");
1825 Environment.Exit (2);
1826 } else {
1827 Console.WriteLine ("Failed to report expected error " + Report.ExpectedError + ".\n" +
1828 "However, other errors were reported.");
1830 Environment.Exit (1);
1834 return false;
1837 #if DEBUGME
1838 Console.WriteLine ("Size of strings held: " + DeclSpace.length);
1839 Console.WriteLine ("Size of strings short: " + DeclSpace.small);
1840 #endif
1841 return (Report.Errors == 0);
1846 // This is the only public entry point
1848 public class CompilerCallableEntryPoint : MarshalByRefObject {
1849 static bool used = false;
1851 public bool InvokeCompiler (string [] args)
1853 if (used)
1854 Reset ();
1855 bool ok = Driver.MainDriver (args);
1856 return ok && Report.Errors == 0;
1859 public void Reset ()