Merge pull request #5010 from Unity-Technologies/boehm-gc-alloc-fixed-sre
[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 using System.Threading;
24
25 namespace Mono.CSharp
26 {
27         /// <summary>
28         ///    The compiler driver.
29         /// </summary>
30         class Driver
31         {
32                 readonly CompilerContext ctx;
33
34                 public Driver (CompilerContext ctx)
35                 {
36                         this.ctx = ctx;
37                 }
38
39                 Report Report {
40                         get {
41                                 return ctx.Report;
42                         }
43                 }
44
45                 void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session)
46                 {
47                         Stream input = null;
48                         SeekableStreamReader reader = null;
49
50                         try {
51                                 if (sourceFile.GetInputStream != null) {
52                                         reader = sourceFile.GetInputStream (sourceFile);
53                                         if (reader == null) {
54                                                 throw new FileNotFoundException ("Delegate returned null", sourceFile.Name);
55                                         }
56                                 } else {
57                                         input = File.OpenRead (sourceFile.Name);
58                                 }
59                         } catch {
60                                 Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found");
61                                 return;
62                         }
63
64                         if (reader == null) {
65                                 using (input) {
66                                         reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
67                                         DoTokenize (sourceFile, module, session, reader);
68                                 }
69                         } else {
70                                 DoTokenize (sourceFile, module, session, reader);
71                         }
72                 }
73
74                 void DoTokenize (SourceFile sourceFile, ModuleContainer module, ParserSession session, SeekableStreamReader reader) {
75                         var file = new CompilationSourceFile (module, sourceFile);
76
77                         Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report);
78                         int token, tokens = 0, errors = 0;
79
80                         while ((token = lexer.token ()) != Token.EOF) {
81                                 tokens++;
82                                 if (token == Token.ERROR)
83                                         errors++;
84                         }
85                         Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
86                 }
87
88                 void Parse (ModuleContainer module)
89                 {
90                         bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
91                         var sources = module.Compiler.SourceFiles;
92
93                         Location.Initialize (sources);
94
95                         var session = new ParserSession {
96                                 UseJayGlobalArrays = true,
97                                 LocatedTokens = new LocatedToken[15000]
98                         };
99
100                         for (int i = 0; i < sources.Count; ++i) {
101                                 if (tokenize_only) {
102                                         tokenize_file (sources[i], module, session);
103                                 } else {
104                                         Parse (sources[i], module, session, Report);
105                                 }
106                         }
107                 }
108
109 #if false
110                 void ParseParallel (ModuleContainer module)
111                 {
112                         var sources = module.Compiler.SourceFiles;
113
114                         Location.Initialize (sources);
115
116                         var pcount = Environment.ProcessorCount;
117                         var threads = new Thread[System.Math.Max (2, pcount - 1)];
118
119                         for (int i = 0; i < threads.Length; ++i) {
120                                 var t = new Thread (l => {
121                                         var session = new ParserSession () {
122                                                 //UseJayGlobalArrays = true,
123                                         };
124
125                                         var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer
126
127                                         for (int ii = (int) l; ii < sources.Count; ii += threads.Length) {
128                                                 Parse (sources[ii], module, session, report);
129                                         }
130
131                                         // TODO: Merge warning regions
132                                 });
133
134                                 t.Start (i);
135                                 threads[i] = t;
136                         }
137
138                         for (int t = 0; t < threads.Length; ++t) {
139                                 threads[t].Join ();
140                         }
141                 }
142 #endif
143
144                 public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report)
145                 {
146                         Stream input = null;
147                         SeekableStreamReader reader = null;
148
149                         try {
150                                 if (file.GetInputStream != null) {
151                                         reader = file.GetInputStream (file);
152                                         if (reader == null) {
153                                                 throw new FileNotFoundException ("Delegate returned null", file.Name);
154                                         }
155                                 } else {
156                                         input = File.OpenRead (file.Name);
157                                 }
158                         } catch {
159                                 report.Error (2001, "Source file `{0}' could not be found", file.Name);
160                                 return;
161                         }
162
163                         if (reader == null) {
164                                 using (input) {
165                                         // Check 'MZ' header
166                                         if (input.ReadByte () == 77 && input.ReadByte () == 90) {
167
168                                                 report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
169                                                 return;
170                                         }
171
172                                         input.Position = 0;
173                                         reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer);
174
175                                         DoParse (file, module, session, report, reader);
176                                 }
177                         } else {
178                                 DoParse (file, module, session, report, reader);
179                         }
180                 }
181
182                 void DoParse (SourceFile file, ModuleContainer module, ParserSession session, Report report, SeekableStreamReader reader) {
183                         Parse (reader, file, module, session, report);
184
185                         if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum) {
186                                 reader.Stream.Position = 0;
187                                 var checksum = session.GetChecksumAlgorithm ();
188                                 file.SetChecksum (checksum.ComputeHash (reader.Stream));
189                         }
190                 }
191
192                 public static void Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report)
193                 {
194                         var file = new CompilationSourceFile (module, sourceFile);
195                         module.AddTypeContainer (file);
196
197                         CSharpParser parser = new CSharpParser (reader, file, report, session);
198                         parser.parse ();
199                 }
200                 
201                 public static int Main (string[] args)
202                 {
203                         Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
204
205                         CommandLineParser cmd = new CommandLineParser (Console.Out);
206                         var settings = cmd.ParseArguments (args);
207                         if (settings == null)
208                                 return 1;
209
210                         if (cmd.HasBeenStopped)
211                                 return 0;
212
213                         Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ()));
214
215                         if (d.Compile () && d.Report.Errors == 0) {
216                                 if (d.Report.Warnings > 0) {
217                                         Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
218                                 }
219                                 Environment.Exit (0);
220                                 return 0;
221                         }
222                         
223                         
224                         Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
225                                 d.Report.Errors, d.Report.Warnings);
226                         Environment.Exit (1);
227                         return 1;
228                 }
229
230                 public static string GetPackageFlags (string packages, Report report)
231                 {
232 #if MONO_FEATURE_PROCESS_START
233                         ProcessStartInfo pi = new ProcessStartInfo ();
234                         pi.FileName = "pkg-config";
235                         pi.RedirectStandardOutput = true;
236                         pi.UseShellExecute = false;
237                         pi.Arguments = "--libs " + packages;
238                         Process p = null;
239                         try {
240                                 p = Process.Start (pi);
241                         } catch (Exception e) {
242                                 if (report == null)
243                                         throw;
244
245                                 report.Error (-27, "Couldn't run pkg-config: " + e.Message);
246                                 return null;
247                         }
248                         
249                         if (p.StandardOutput == null) {
250                                 if (report == null)
251                                         throw new ApplicationException ("Specified package did not return any information");
252
253                                 report.Warning (-27, 1, "Specified package did not return any information");
254                                 p.Close ();
255                                 return null;
256                         }
257
258                         string pkgout = p.StandardOutput.ReadToEnd ();
259                         p.WaitForExit ();
260                         if (p.ExitCode != 0) {
261                                 if (report == null)
262                                         throw new ApplicationException (pkgout);
263
264                                 report.Error (-27, "Error running pkg-config. Check the above output.");
265                                 p.Close ();
266                                 return null;
267                         }
268
269                         p.Close ();
270                         return pkgout;
271 #else
272                         throw new NotSupportedException ("Process.Start is not supported on this platform.");
273 #endif // MONO_FEATURE_PROCESS_START
274                 }
275
276                 //
277                 // Main compilation method
278                 //
279                 public bool Compile ()
280                 {
281                         var settings = ctx.Settings;
282
283                         //
284                         // If we are an exe, require a source file for the entry point or
285                         // if there is nothing to put in the assembly, and we are not a library
286                         //
287                         if (settings.FirstSourceFile == null &&
288                                 ((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) ||
289                                 settings.Resources == null)) {
290                                 Report.Error (2008, "No files to compile were specified");
291                                 return false;
292                         }
293
294                         if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) {
295                                 Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables");
296                                 return false;
297                         }
298
299                         TimeReporter tr = new TimeReporter (settings.Timestamps);
300                         ctx.TimeReporter = tr;
301                         tr.StartTotal ();
302
303                         var module = new ModuleContainer (ctx);
304                         RootContext.ToplevelTypes = module;
305
306                         tr.Start (TimeReporter.TimerType.ParseTotal);
307                         Parse (module);
308                         tr.Stop (TimeReporter.TimerType.ParseTotal);
309
310                         if (Report.Errors > 0)
311                                 return false;
312
313                         if (settings.TokenizeOnly || settings.ParseOnly) {
314                                 tr.StopTotal ();
315                                 tr.ShowStats ();
316                                 return true;
317                         }
318
319                         var output_file = settings.OutputFile;
320                         string output_file_name;
321                         if (output_file == null) {
322                                 var source_file = settings.FirstSourceFile;
323
324                                 if (source_file == null) {
325                                         Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
326                                         return false;
327                                 }
328
329                                 output_file_name = source_file.Name;
330                                 int pos = output_file_name.LastIndexOf ('.');
331
332                                 if (pos > 0)
333                                         output_file_name = output_file_name.Substring (0, pos);
334                                 
335                                 output_file_name += settings.TargetExt;
336                                 output_file = output_file_name;
337                         } else {
338                                 output_file_name = Path.GetFileName (output_file);
339
340                                 if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) ||
341                                         output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) {
342                                         Report.Error (2021, "Output file name is not valid");
343                                         return false;
344                                 }
345                         }
346
347 #if STATIC
348                         var importer = new StaticImporter (module);
349                         var references_loader = new StaticLoader (importer, ctx);
350
351                         tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup);
352                         var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file);
353                         assembly.Create (references_loader.Domain);
354                         tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup);
355
356                         // Create compiler types first even before any referenced
357                         // assembly is loaded to allow forward referenced types from
358                         // loaded assembly into compiled builder to be resolved
359                         // correctly
360                         tr.Start (TimeReporter.TimerType.CreateTypeTotal);
361                         module.CreateContainer ();
362                         importer.AddCompiledAssembly (assembly);
363                         references_loader.CompiledAssembly = assembly;
364                         tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
365
366                         references_loader.LoadReferences (module);
367
368                         tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
369                         if (!ctx.BuiltinTypes.CheckDefinitions (module))
370                                 return false;
371
372                         tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
373
374                         references_loader.LoadModules (assembly, module.GlobalRootNamespace);
375 #else
376                         var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
377                         module.SetDeclaringAssembly (assembly);
378
379                         var importer = new ReflectionImporter (module, ctx.BuiltinTypes);
380                         assembly.Importer = importer;
381
382                         var loader = new DynamicLoader (importer, ctx);
383                         loader.LoadReferences (module);
384
385                         if (!ctx.BuiltinTypes.CheckDefinitions (module))
386                                 return false;
387
388                         if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
389                                 return false;
390
391                         module.CreateContainer ();
392
393                         loader.LoadModules (assembly, module.GlobalRootNamespace);
394 #endif
395                         module.InitializePredefinedTypes ();
396
397                         if (settings.GetResourceStrings != null)
398                                 module.LoadGetResourceStrings (settings.GetResourceStrings);
399
400                         tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
401                         module.Define ();
402                         tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
403
404                         if (Report.Errors > 0)
405                                 return false;
406
407                         if (settings.DocumentationFile != null) {
408                                 var doc = new DocumentationBuilder (module);
409                                 doc.OutputDocComment (output_file, settings.DocumentationFile);
410                         }
411
412                         assembly.Resolve ();
413                         
414                         if (Report.Errors > 0)
415                                 return false;
416
417
418                         tr.Start (TimeReporter.TimerType.EmitTotal);
419                         assembly.Emit ();
420                         tr.Stop (TimeReporter.TimerType.EmitTotal);
421
422                         if (Report.Errors > 0){
423                                 return false;
424                         }
425
426                         tr.Start (TimeReporter.TimerType.CloseTypes);
427                         module.CloseContainer ();
428                         tr.Stop (TimeReporter.TimerType.CloseTypes);
429
430                         tr.Start (TimeReporter.TimerType.Resouces);
431                         if (!settings.WriteMetadataOnly)
432                                 assembly.EmbedResources ();
433                         tr.Stop (TimeReporter.TimerType.Resouces);
434
435                         if (Report.Errors > 0)
436                                 return false;
437
438                         assembly.Save ();
439
440 #if STATIC
441                         references_loader.Dispose ();
442 #endif
443                         tr.StopTotal ();
444                         tr.ShowStats ();
445
446                         return Report.Errors == 0;
447                 }
448         }
449
450         //
451         // This is the only public entry point
452         //
453         public class CompilerCallableEntryPoint : MarshalByRefObject {
454                 public static bool InvokeCompiler (string [] args, TextWriter error)
455                 {
456                         try {
457                                 CommandLineParser cmd = new CommandLineParser (error);
458                                 var setting = cmd.ParseArguments (args);
459                                 if (setting == null)
460                                         return false;
461
462                                 var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error)));
463                                 return d.Compile ();
464                         } finally {
465                                 Reset ();
466                         }
467                 }
468
469                 public static int[] AllWarningNumbers {
470                         get {
471                                 return Report.AllWarnings;
472                         }
473                 }
474
475                 public static void Reset ()
476                 {
477                         Reset (true);
478                 }
479
480                 public static void PartialReset ()
481                 {
482                         Reset (false);
483                 }
484                 
485                 public static void Reset (bool full_flag)
486                 {
487                         Location.Reset ();
488                         
489                         if (!full_flag)
490                                 return;
491
492                         Linq.QueryBlock.TransparentParameter.Reset ();
493                         TypeInfo.Reset ();
494                 }
495         }
496 }