BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / mcs / driver.cs
1 //
2 // driver.cs: The compiler command line driver.
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnu.org)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
12 // Copyright 2011 Xamarin Inc
13 //
14
15 using System;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Text;
21 using System.Globalization;
22 using System.Diagnostics;
23
24 namespace Mono.CSharp
25 {
26         /// <summary>
27         ///    The compiler driver.
28         /// </summary>
29         class Driver
30         {
31                 readonly CompilerContext ctx;
32
33                 public Driver (CompilerContext ctx)
34                 {
35                         this.ctx = ctx;
36                 }
37
38                 Report Report {
39                         get {
40                                 return ctx.Report;
41                         }
42                 }
43
44                 void tokenize_file (SourceFile sourceFile, ModuleContainer module)
45                 {
46                         Stream input;
47
48                         try {
49                                 input = File.OpenRead (sourceFile.Name);
50                         } catch {
51                                 Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found");
52                                 return;
53                         }
54
55                         using (input){
56                                 SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
57                                 var file = new CompilationSourceFile (module, sourceFile);
58
59                                 Tokenizer lexer = new Tokenizer (reader, file);
60                                 int token, tokens = 0, errors = 0;
61
62                                 while ((token = lexer.token ()) != Token.EOF){
63                                         tokens++;
64                                         if (token == Token.ERROR)
65                                                 errors++;
66                                 }
67                                 Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
68                         }
69                         
70                         return;
71                 }
72
73                 void Parse (ModuleContainer module)
74                 {
75                         bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
76                         var sources = module.Compiler.SourceFiles;
77
78                         Location.Initialize (sources);
79
80                         for (int i = 0; i < sources.Count; ++i) {
81                                 if (tokenize_only) {
82                                         tokenize_file (sources[i], module);
83                                 } else {
84                                         Parse (sources[i], module);
85                                 }
86                         }
87                 }
88
89                 public void Parse (SourceFile file, ModuleContainer module)
90                 {
91                         Stream input;
92
93                         try {
94                                 input = File.OpenRead (file.Name);
95                         } catch {
96                                 Report.Error (2001, "Source file `{0}' could not be found", file.Name);
97                                 return;
98                         }
99
100                         // Check 'MZ' header
101                         if (input.ReadByte () == 77 && input.ReadByte () == 90) {
102
103                                 Report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
104                                 input.Close ();
105                                 return;
106                         }
107
108                         input.Position = 0;
109                         SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
110
111                         Parse (reader, file, module);
112                         reader.Dispose ();
113                         input.Close ();
114                 }
115
116                 public static void Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module)
117                 {
118                         var file = new CompilationSourceFile (module, sourceFile);
119                         module.AddTypeContainer (file);
120
121                         CSharpParser parser = new CSharpParser (reader, file);
122                         parser.parse ();
123                 }
124                 
125                 public static int Main (string[] args)
126                 {
127                         Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
128
129                         CommandLineParser cmd = new CommandLineParser (Console.Out);
130                         var settings = cmd.ParseArguments (args);
131                         if (settings == null)
132                                 return 1;
133
134                         if (cmd.HasBeenStopped)
135                                 return 0;
136
137                         Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ()));
138
139                         if (d.Compile () && d.Report.Errors == 0) {
140                                 if (d.Report.Warnings > 0) {
141                                         Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
142                                 }
143                                 Environment.Exit (0);
144                                 return 0;
145                         }
146                         
147                         
148                         Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
149                                 d.Report.Errors, d.Report.Warnings);
150                         Environment.Exit (1);
151                         return 1;
152                 }
153
154                 public static string GetPackageFlags (string packages, Report report)
155                 {
156                         ProcessStartInfo pi = new ProcessStartInfo ();
157                         pi.FileName = "pkg-config";
158                         pi.RedirectStandardOutput = true;
159                         pi.UseShellExecute = false;
160                         pi.Arguments = "--libs " + packages;
161                         Process p = null;
162                         try {
163                                 p = Process.Start (pi);
164                         } catch (Exception e) {
165                                 if (report == null)
166                                         throw;
167
168                                 report.Error (-27, "Couldn't run pkg-config: " + e.Message);
169                                 return null;
170                         }
171                         
172                         if (p.StandardOutput == null) {
173                                 if (report == null)
174                                         throw new ApplicationException ("Specified package did not return any information");
175
176                                 report.Warning (-27, 1, "Specified package did not return any information");
177                                 p.Close ();
178                                 return null;
179                         }
180
181                         string pkgout = p.StandardOutput.ReadToEnd ();
182                         p.WaitForExit ();
183                         if (p.ExitCode != 0) {
184                                 if (report == null)
185                                         throw new ApplicationException (pkgout);
186
187                                 report.Error (-27, "Error running pkg-config. Check the above output.");
188                                 p.Close ();
189                                 return null;
190                         }
191
192                         p.Close ();
193                         return pkgout;
194                 }
195
196                 //
197                 // Main compilation method
198                 //
199                 public bool Compile ()
200                 {
201                         var settings = ctx.Settings;
202
203                         //
204                         // If we are an exe, require a source file for the entry point or
205                         // if there is nothing to put in the assembly, and we are not a library
206                         //
207                         if (settings.FirstSourceFile == null &&
208                                 ((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) ||
209                                 settings.Resources == null)) {
210                                 Report.Error (2008, "No files to compile were specified");
211                                 return false;
212                         }
213
214                         if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) {
215                                 Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables");
216                                 return false;
217                         }
218
219                         TimeReporter tr = new TimeReporter (settings.Timestamps);
220                         ctx.TimeReporter = tr;
221                         tr.StartTotal ();
222
223                         var module = new ModuleContainer (ctx);
224                         RootContext.ToplevelTypes = module;
225
226                         tr.Start (TimeReporter.TimerType.ParseTotal);
227                         Parse (module);
228                         tr.Stop (TimeReporter.TimerType.ParseTotal);
229
230                         if (Report.Errors > 0)
231                                 return false;
232
233                         if (settings.TokenizeOnly || settings.ParseOnly) {
234                                 tr.StopTotal ();
235                                 tr.ShowStats ();
236                                 return true;
237                         }
238
239                         var output_file = settings.OutputFile;
240                         string output_file_name;
241                         if (output_file == null) {
242                                 var source_file = settings.FirstSourceFile;
243
244                                 if (source_file == null) {
245                                         Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
246                                         return false;
247                                 }
248
249                                 output_file_name = source_file.Name;
250                                 int pos = output_file_name.LastIndexOf ('.');
251
252                                 if (pos > 0)
253                                         output_file_name = output_file_name.Substring (0, pos);
254                                 
255                                 output_file_name += settings.TargetExt;
256                                 output_file = output_file_name;
257                         } else {
258                                 output_file_name = Path.GetFileName (output_file);
259
260                                 if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) ||
261                                         output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) {
262                                         Report.Error (2021, "Output file name is not valid");
263                                         return false;
264                                 }
265                         }
266
267 #if STATIC
268                         var importer = new StaticImporter (module);
269                         var references_loader = new StaticLoader (importer, ctx);
270
271                         tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup);
272                         var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file);
273                         assembly.Create (references_loader.Domain);
274                         tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup);
275
276                         // Create compiler types first even before any referenced
277                         // assembly is loaded to allow forward referenced types from
278                         // loaded assembly into compiled builder to be resolved
279                         // correctly
280                         tr.Start (TimeReporter.TimerType.CreateTypeTotal);
281                         module.CreateContainer ();
282                         importer.AddCompiledAssembly (assembly);
283                         tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
284
285                         references_loader.LoadReferences (module);
286
287                         tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
288                         if (!ctx.BuiltinTypes.CheckDefinitions (module))
289                                 return false;
290
291                         tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
292
293                         references_loader.LoadModules (assembly, module.GlobalRootNamespace);
294 #else
295                         var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
296                         module.SetDeclaringAssembly (assembly);
297
298                         var importer = new ReflectionImporter (module, ctx.BuiltinTypes);
299                         assembly.Importer = importer;
300
301                         var loader = new DynamicLoader (importer, ctx);
302                         loader.LoadReferences (module);
303
304                         if (!ctx.BuiltinTypes.CheckDefinitions (module))
305                                 return false;
306
307                         if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
308                                 return false;
309
310                         module.CreateContainer ();
311
312                         loader.LoadModules (assembly, module.GlobalRootNamespace);
313 #endif
314                         module.InitializePredefinedTypes ();
315
316                         tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
317                         module.Define ();
318                         tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
319
320                         if (Report.Errors > 0)
321                                 return false;
322
323                         if (settings.DocumentationFile != null) {
324                                 var doc = new DocumentationBuilder (module);
325                                 doc.OutputDocComment (output_file, settings.DocumentationFile);
326                         }
327
328                         assembly.Resolve ();
329                         
330                         if (Report.Errors > 0)
331                                 return false;
332
333
334                         tr.Start (TimeReporter.TimerType.EmitTotal);
335                         assembly.Emit ();
336                         tr.Stop (TimeReporter.TimerType.EmitTotal);
337
338                         if (Report.Errors > 0){
339                                 return false;
340                         }
341
342                         tr.Start (TimeReporter.TimerType.CloseTypes);
343                         module.CloseContainer ();
344                         tr.Stop (TimeReporter.TimerType.CloseTypes);
345
346                         tr.Start (TimeReporter.TimerType.Resouces);
347                         assembly.EmbedResources ();
348                         tr.Stop (TimeReporter.TimerType.Resouces);
349
350                         if (Report.Errors > 0)
351                                 return false;
352
353                         assembly.Save ();
354
355 #if STATIC
356                         references_loader.Dispose ();
357 #endif
358                         tr.StopTotal ();
359                         tr.ShowStats ();
360
361                         return Report.Errors == 0;
362                 }
363         }
364
365         //
366         // This is the only public entry point
367         //
368         public class CompilerCallableEntryPoint : MarshalByRefObject {
369                 public static bool InvokeCompiler (string [] args, TextWriter error)
370                 {
371                         try {
372                                 CommandLineParser cmd = new CommandLineParser (error);
373                                 var setting = cmd.ParseArguments (args);
374                                 if (setting == null)
375                                         return false;
376
377                                 var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error)));
378                                 return d.Compile ();
379                         } finally {
380                                 Reset ();
381                         }
382                 }
383
384                 public static int[] AllWarningNumbers {
385                         get {
386                                 return Report.AllWarnings;
387                         }
388                 }
389
390                 public static void Reset ()
391                 {
392                         Reset (true);
393                 }
394
395                 public static void PartialReset ()
396                 {
397                         Reset (false);
398                 }
399                 
400                 public static void Reset (bool full_flag)
401                 {
402                         Location.Reset ();
403                         
404                         if (!full_flag)
405                                 return;
406
407                         Linq.QueryBlock.TransparentParameter.Reset ();
408                         TypeInfo.Reset ();
409                 }
410         }
411 }