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