2001-12-20 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / driver.cs
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 Ximian, Inc (http://www.ximian.com)
9 //
10
11 namespace Mono.CSharp
12 {
13         using System;
14         using System.Reflection;
15         using System.Reflection.Emit;
16         using System.Collections;
17         using System.IO;
18         using Mono.Languages;
19
20         /// <summary>
21         ///    The compiler driver.
22         /// </summary>
23         public class Driver
24         {
25                 enum Target {
26                         Library, Exe, Module, WinExe
27                 };
28                 
29                 //
30                 // Assemblies references to be linked.   Initialized with
31                 // mscorlib.dll here.
32                 static ArrayList references;
33
34                 // Lookup paths
35                 static ArrayList link_paths;
36
37                 static bool yacc_verbose = false;
38
39                 static int error_count = 0;
40
41                 static string first_source;
42
43                 static Target target = Target.Exe;
44                 static string target_ext = ".exe";
45
46                 static bool parse_only = false;
47
48                 static ArrayList defines;
49                 
50                 static int parse (string input_file)
51                 {
52                         CSharpParser parser;
53                         Stream input;
54                         int errors;
55
56                         try {
57                                 input = File.OpenRead (input_file);
58                         } catch {
59                                 Report.Error (2001, "Source file '" + input_file + "' could not be opened");
60                                 return 1;
61                         }
62
63                         parser = new CSharpParser (input_file, input, defines);
64                         parser.yacc_verbose = yacc_verbose;
65                         try {
66                                 errors = parser.parse ();\r
67                         } catch (Exception ex) {
68                                 Console.WriteLine (ex);
69                                 Console.WriteLine ("Compilation aborted");
70                                 return 1;
71                         }
72                         
73                         return errors;
74                 }
75                 
76                 static void Usage (bool is_error)
77                 {
78                         Console.WriteLine (
79                                 "Mono C# compiler, (C) 2001 Ximian, Inc.\n" +
80                                 "mcs [options] source-files\n" +
81                                 "   --about         About the Mono C# compiler\n" +
82                                 "   --checked       Set default context to checked\n" +
83                                 "   --define SYM    Defines the symbol SYM\n" + 
84                                 "   --fatal         Makes errors fatal\n" +
85                                 "   --stacktrace    Shows stack trace at error location\n" +
86                                 "   -L PATH         Adds PATH to the assembly link path\n" +
87                                 "   --nostdlib      Does not load core libraries\n" +
88                                 "   --nowarn XXX    Ignores warning number XXX\n" +
89                                 "   -o FNAME        Specifies output file\n" +
90                                 "   --optimize      Optimizes\n" +
91                                 "   --parse         Only parses the source file\n" +
92                                 "   --probe X L     Probes for the source to generate code X on line L\n" +
93                                 "   --target KIND   Specifies the target (KIND is one of: exe, winexe, " +
94                                                     "library, module)\n" +
95                                 "   --unsafe        Allows unsafe code\n" +
96                                 "   --werror        Treat warnings as errors\n" +
97                                 "   --wlevel LEVEL  Sets warning level (the highest is 4, the default)\n" +
98                                 "   -r              References an assembly\n" +
99                                 "   -v              Verbose parsing (for debugging the parser)\n" +
100                                 "   @file           Read response file for more options");
101                         if (is_error)
102                                 error_count++;
103                 }
104
105                 static void About ()
106                 {
107                         Console.WriteLine (
108                                 "The Mono C# compiler is (C) 2001 Ximian, Inc.\n\n" +
109                                 "The compiler source code is released under the terms of the GNU GPL\n\n" +
110
111                                 "For more information on Mono, visit the project Web site\n" +
112                                 "   http://www.go-mono.com\n\n" +
113
114                                 "The compiler was written by Miguel de Icaza and Ravi Pratap");
115                 }
116                 
117                 static void error (string msg)
118                 {
119                         Console.WriteLine ("Error: " + msg);
120                 }
121
122                 static void notice (string msg)
123                 {
124                         Console.WriteLine (msg);
125                 }
126                 
127                 public static int Main (string[] args)
128                 {
129                         MainDriver (args);
130                         
131                         return error_count;
132                 }
133
134                 static public int LoadAssembly (string assembly)
135                 {
136                         Assembly a;
137                         string total_log = "";
138
139                         try {
140                                 a = Assembly.Load (assembly);
141                                 RootContext.TypeManager.AddAssembly (a);
142                                 return 0;
143                         } catch (FileNotFoundException){
144                                 foreach (string dir in link_paths){
145                                         string full_path = dir + "/" + assembly + ".dll";
146
147                                         try {
148                                                 a = Assembly.LoadFrom (full_path);
149                                                 RootContext.TypeManager.AddAssembly (a);
150                                                 return 0;
151                                         } catch (FileNotFoundException ff) {
152                                                 total_log += ff.FusionLog;
153                                                 continue;
154                                         }
155                                 }
156                         } catch (BadImageFormatException f) {
157                                 error ("// Bad file format while loading assembly");
158                                 error ("Log: " + f.FusionLog);
159                                 return 1;
160                         } catch (FileLoadException f){
161                                 error ("// File Load Exception: ");
162                                 error ("Log: " + f.FusionLog);
163                                 return 1;
164                         } catch (ArgumentNullException){
165                                 error ("// Argument Null exception ");
166                                 return 1;
167                         }
168                         
169                         Report.Error (6, "Can not find assembly `" + assembly + "'" );
170                         Console.WriteLine ("Log: \n" + total_log);
171
172                         return 0;
173                 }
174
175                 /// <summary>
176                 ///   Loads all assemblies referenced on the command line
177                 /// </summary>
178                 static public int LoadReferences ()
179                 {
180                         int errors = 0;
181
182                         foreach (string r in references)
183                                 errors += LoadAssembly (r);
184
185                         return errors;
186                 }
187
188                 static void SetupDefaultDefines ()
189                 {
190                         defines = new ArrayList ();
191                         defines.Add ("__MonoCS__");
192                 }
193
194                 static string [] LoadArgs (string file)
195                 {
196                         StreamReader f;
197                         ArrayList args = new ArrayList ();
198                         string line;
199                         try {
200                                 f = new StreamReader (file);
201                         } catch {
202                                 return null;
203                         }
204
205                         while ((line = f.ReadLine ()) != null){
206                                 string [] line_args = line.Split (new char [] { ' ' });
207
208                                 foreach (string arg in line_args)
209                                         args.Add (arg);
210                         }
211
212                         string [] ret_value = new string [args.Count];
213                         args.CopyTo (ret_value, 0);
214
215                         return ret_value;
216                 }
217                 
218                 /// <summary>
219                 ///    Parses the arguments, and drives the compilation
220                 ///    process.
221                 /// </summary>
222                 ///
223                 /// <remarks>
224                 ///    TODO: Mostly structured to debug the compiler
225                 ///    now, needs to be turned into a real driver soon.
226                 /// </remarks>
227                 static void MainDriver (string [] args)
228                 {
229                         int errors = 0, i;
230                         string output_file = null;
231
232                         references = new ArrayList ();
233                         link_paths = new ArrayList ();
234
235                         SetupDefaultDefines ();
236                         
237                         //
238                         // Setup defaults
239                         //
240                         // This is not required because Assembly.Load knows about this
241                         // path.
242                         //
243                         link_paths.Add ("file:///C:/WINNT/Microsoft.NET/Framework/v1.0.2914");
244
245                         int argc = args.Length;
246                         for (i = 0; i < argc; i++){
247                                 string arg = args [i];
248
249                                 if (arg.StartsWith ("@")){
250                                         string [] new_args, extra_args;
251                                         string response_file = arg.Substring (1);
252                                         
253                                         extra_args = LoadArgs (response_file);
254                                         if (extra_args == null){
255                                                 Report.Error (2011, "Unable to open response file: " +
256                                                               response_file);
257                                                 return;
258                                         }
259
260                                         new_args = new string [extra_args.Length + argc];
261                                         args.CopyTo (new_args, 0);
262                                         extra_args.CopyTo (new_args, argc);
263                                         args = new_args;
264                                         argc = new_args.Length;
265                                         continue;
266                                 }
267                                 
268                                 if (arg.StartsWith ("-")){
269                                         switch (arg){
270                                         case "-v":
271                                                 yacc_verbose = true;
272                                                 continue;
273
274                                         case "--parse":
275                                                 parse_only = true;
276                                                 continue;
277
278                                         case "--main": case "-m":
279                                                 if ((i + 1) >= argc){
280                                                         Usage (true);
281                                                         return;
282                                                 }
283                                                 RootContext.MainClass = args [++i];
284                                                 continue;
285
286                                         case "--unsafe":
287                                                 RootContext.Unsafe = true;
288                                                 break;
289                                                 
290                                         case "--optimize":
291                                                 RootContext.Optimize = true;
292                                                 continue;
293
294                                         case "--help":
295                                                 Usage (false);
296                                                 return;
297
298                                         case "--define":
299                                                 if ((i + 1) >= argc){
300                                                         Usage (true);
301                                                         return;
302                                                 }
303                                                 defines.Add (args [++i]);
304                                                 continue;
305                                                 
306                                         case "--probe": {
307                                                 int code, line;
308                                                 
309                                                 code = Int32.Parse (args [++i], 0);
310                                                 line = Int32.Parse (args [++i], 0);
311                                                 Report.SetProbe (code, line);
312                                                 continue;
313                                         }
314
315                                         case "-o": case "--output":
316                                                 if ((i + 1) >= argc){
317                                                         Usage (true);
318                                                         return;
319                                                 }
320                                                 output_file = args [++i];
321                                                 continue;
322                                                 
323                                         case "--checked":
324                                                 RootContext.Checked = true;
325                                                 continue;
326
327                                         case "--stacktrace":
328                                                 Report.Stacktrace = true;
329                                                 continue;
330                                                 
331                                         case "--target":
332                                                 if ((i + 1) >= argc){
333                                                         Usage (true);
334                                                         return;
335                                                 }
336
337                                                 string type = args [++i];
338                                                 switch (type){
339                                                 case "library":
340                                                         target = Target.Library;
341                                                         target_ext = ".dll";
342                                                         break;
343                                                         
344                                                 case "exe":
345                                                         target = Target.Exe;
346                                                         break;
347                                                         
348                                                 case "winexe":
349                                                         target = Target.WinExe;
350                                                         break;
351                                                         
352                                                 case "module":
353                                                         target = Target.Module;
354                                                         target_ext = ".dll";
355                                                         break;
356                                                 }
357                                                 continue;
358
359                                         case "-r":
360                                                 if ((i + 1) >= argc){
361                                                         Usage (true);
362                                                         return;
363                                                 }
364                                                 
365                                                 references.Add (args [++i]);
366                                                 continue;
367                                                 
368                                         case "-L":
369                                                 if ((i + 1) >= argc){
370                                                         Usage (true);
371                                                         return;
372                                                 }
373                                                 link_paths.Add (args [++i]);
374                                                 continue;
375                                                 
376                                         case "--nostdlib":
377                                                 RootContext.StdLib = false;
378                                                 continue;
379                                                 
380                                         case "--fatal":
381                                                 Report.Fatal = true;
382                                                 continue;
383
384                                         case "--werror":
385                                                 Report.WarningsAreErrors = true;
386                                                 continue;
387
388                                         case "--nowarn":
389                                                 if ((i + 1) >= argc){
390                                                         Usage (true);
391                                                         return;
392                                                 }
393                                                 int warn;
394                                                 
395                                                 try {
396                                                         warn = Int32.Parse (args [++i]);
397                                                 } catch {
398                                                         Usage (true);
399                                                         return;
400                                                 }
401                                                 Report.SetIgnoreWarning (warn);
402                                                 continue;
403
404                                         case "--wlevel":
405                                                 if ((i + 1) >= argc){
406                                                         Usage (true);
407                                                         error_count++;
408                                                         return;
409                                                 }
410                                                 int level;
411                                                 
412                                                 try {
413                                                         level = Int32.Parse (args [++i]);
414                                                 } catch {
415                                                         Usage (true);
416                                                         return;
417                                                 }
418                                                 if (level < 0 || level > 4){
419                                                         Report.Error (1900, "Warning level must be 0 to 4");
420                                                         return;
421                                                 } else
422                                                         RootContext.WarningLevel = level;
423                                                 continue;
424                                                 
425                                         case "--about":
426                                                 About ();
427                                                 return;
428                                                 
429                                         default:
430                                                 Usage (true);
431                                                 return;
432                                         }
433                                 }
434
435                                 if (first_source == null)
436                                         first_source = arg;
437
438                                 string [] files = Directory.GetFiles (".", arg);
439                                 foreach (string f in files){
440                                         if (!f.ToLower ().EndsWith (".cs")){
441                                                 error ("Do not know how to compile " + arg);
442                                                 errors++;
443                                                 continue;
444                                         }
445                                         errors += parse (f);
446                                 }
447                         }
448
449                         if (first_source == null){
450                                 Report.Error (2008, "No files to compile were specified");
451                                 return;
452                         }
453
454                         if (Report.Errors > 0)
455                                 return;
456                         
457                         if (parse_only)
458                                 return;
459                         
460                         //
461                         // Load Core Library for default compilation
462                         //
463                         if (RootContext.StdLib){
464                                 references.Insert (0, "mscorlib");
465                                 references.Insert (1, "System");
466                         }
467
468                         if (errors > 0){
469                                 error ("Parsing failed");
470                                 return;
471                         }
472
473                         //
474                         // Load assemblies required
475                         //
476                         errors += LoadReferences ();
477
478                         if (errors > 0){
479                                 error ("Could not load one or more assemblies");
480                                 return;
481                         }
482
483                         error_count = errors;
484
485                         //
486                         // Quick hack
487                         //
488                         if (output_file == null){
489                                 int pos = first_source.LastIndexOf (".");
490
491                                 output_file = first_source.Substring (0, pos) + target_ext;
492                         }
493
494                         RootContext.CodeGen = new CodeGen (output_file, output_file);
495
496                         //
497                         // Before emitting, we need to get the core
498                         // types emitted from the user defined types
499                         // or from the system ones.
500                         //
501                         RootContext.TypeManager.InitCoreTypes ();
502
503                         RootContext.TypeManager.AddModule (RootContext.CodeGen.ModuleBuilder);
504                         
505                         //
506                         // The second pass of the compiler
507                         //
508                         RootContext.ResolveTree ();
509                         RootContext.PopulateTypes ();
510                         
511                         if (Report.Errors > 0){
512                                 error ("Compilation failed");
513                                 return;
514                         }
515                         
516                         //
517                         // The code generator
518                         //
519                         RootContext.EmitCode ();
520                         
521                         if (Report.Errors > 0){
522                                 error ("Compilation failed");
523                                 return;
524                         }
525                         
526                         RootContext.CloseTypes ();
527
528                         PEFileKinds k = PEFileKinds.ConsoleApplication;
529                                 
530                         if (target == Target.Library || target == Target.Module)
531                                 k = PEFileKinds.Dll;
532                         else if (target == Target.Exe)
533                                 k = PEFileKinds.ConsoleApplication;
534                         else if (target == Target.WinExe)
535                                 k = PEFileKinds.WindowApplication;
536
537                         if (target == Target.Exe || target == Target.WinExe){
538                                 MethodInfo ep = RootContext.EntryPoint;
539
540                                 if (ep == null){
541                                         Report.Error (5001, "Program " + output_file +
542                                                               " does not have an entry point defined");
543                                         return;
544                                 }
545                                 
546                                 RootContext.CodeGen.AssemblyBuilder.SetEntryPoint (ep, k);
547                         }
548                         
549                         RootContext.CodeGen.Save (output_file);
550
551                         if (Report.Errors > 0){
552                                 error ("Compilation failed");
553                                 return;
554                         } else if (Report.ProbeCode != 0){
555                                 error ("Failed to report code " + Report.ProbeCode);
556                                 Environment.Exit (124);
557                         }
558                 }
559
560         }
561 }