[runtime] Avoid holding the init lock while calling the cctor in mono_runtime_class_i...
[mono-project.git] / mcs / mcs / driver.cs
blob0a11914328ae79629b356b78bd879de1942f3b69
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
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;
25 namespace Mono.CSharp
27 /// <summary>
28 /// The compiler driver.
29 /// </summary>
30 class Driver
32 readonly CompilerContext ctx;
34 public Driver (CompilerContext ctx)
36 this.ctx = ctx;
39 Report Report {
40 get {
41 return ctx.Report;
45 void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session)
47 Stream input;
49 try {
50 input = File.OpenRead (sourceFile.Name);
51 } catch {
52 Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found");
53 return;
56 using (input){
57 SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
58 var file = new CompilationSourceFile (module, sourceFile);
60 Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report);
61 int token, tokens = 0, errors = 0;
63 while ((token = lexer.token ()) != Token.EOF){
64 tokens++;
65 if (token == Token.ERROR)
66 errors++;
68 Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
71 return;
74 void Parse (ModuleContainer module)
76 bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
77 var sources = module.Compiler.SourceFiles;
79 Location.Initialize (sources);
81 var session = new ParserSession {
82 UseJayGlobalArrays = true,
83 LocatedTokens = new LocatedToken[15000]
86 for (int i = 0; i < sources.Count; ++i) {
87 if (tokenize_only) {
88 tokenize_file (sources[i], module, session);
89 } else {
90 Parse (sources[i], module, session, Report);
95 #if false
96 void ParseParallel (ModuleContainer module)
98 var sources = module.Compiler.SourceFiles;
100 Location.Initialize (sources);
102 var pcount = Environment.ProcessorCount;
103 var threads = new Thread[System.Math.Max (2, pcount - 1)];
105 for (int i = 0; i < threads.Length; ++i) {
106 var t = new Thread (l => {
107 var session = new ParserSession () {
108 //UseJayGlobalArrays = true,
111 var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer
113 for (int ii = (int) l; ii < sources.Count; ii += threads.Length) {
114 Parse (sources[ii], module, session, report);
117 // TODO: Merge warning regions
120 t.Start (i);
121 threads[i] = t;
124 for (int t = 0; t < threads.Length; ++t) {
125 threads[t].Join ();
128 #endif
130 public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report)
132 Stream input;
134 try {
135 input = File.OpenRead (file.Name);
136 } catch {
137 report.Error (2001, "Source file `{0}' could not be found", file.Name);
138 return;
141 // Check 'MZ' header
142 if (input.ReadByte () == 77 && input.ReadByte () == 90) {
144 report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
145 input.Close ();
146 return;
149 input.Position = 0;
150 SeekableStreamReader reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer);
152 Parse (reader, file, module, session, report);
154 if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum) {
155 input.Position = 0;
156 var checksum = session.GetChecksumAlgorithm ();
157 file.SetChecksum (checksum.ComputeHash (input));
160 reader.Dispose ();
161 input.Close ();
164 public static void Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report)
166 var file = new CompilationSourceFile (module, sourceFile);
167 module.AddTypeContainer (file);
169 CSharpParser parser = new CSharpParser (reader, file, report, session);
170 parser.parse ();
173 public static int Main (string[] args)
175 Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
177 CommandLineParser cmd = new CommandLineParser (Console.Out);
178 var settings = cmd.ParseArguments (args);
179 if (settings == null)
180 return 1;
182 if (cmd.HasBeenStopped)
183 return 0;
185 Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ()));
187 if (d.Compile () && d.Report.Errors == 0) {
188 if (d.Report.Warnings > 0) {
189 Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
191 Environment.Exit (0);
192 return 0;
196 Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
197 d.Report.Errors, d.Report.Warnings);
198 Environment.Exit (1);
199 return 1;
202 public static string GetPackageFlags (string packages, Report report)
204 #if MONO_FEATURE_PROCESS_START
205 ProcessStartInfo pi = new ProcessStartInfo ();
206 pi.FileName = "pkg-config";
207 pi.RedirectStandardOutput = true;
208 pi.UseShellExecute = false;
209 pi.Arguments = "--libs " + packages;
210 Process p = null;
211 try {
212 p = Process.Start (pi);
213 } catch (Exception e) {
214 if (report == null)
215 throw;
217 report.Error (-27, "Couldn't run pkg-config: " + e.Message);
218 return null;
221 if (p.StandardOutput == null) {
222 if (report == null)
223 throw new ApplicationException ("Specified package did not return any information");
225 report.Warning (-27, 1, "Specified package did not return any information");
226 p.Close ();
227 return null;
230 string pkgout = p.StandardOutput.ReadToEnd ();
231 p.WaitForExit ();
232 if (p.ExitCode != 0) {
233 if (report == null)
234 throw new ApplicationException (pkgout);
236 report.Error (-27, "Error running pkg-config. Check the above output.");
237 p.Close ();
238 return null;
241 p.Close ();
242 return pkgout;
243 #else
244 throw new NotSupportedException ("Process.Start is not supported on this platform.");
245 #endif // MONO_FEATURE_PROCESS_START
249 // Main compilation method
251 public bool Compile ()
253 var settings = ctx.Settings;
256 // If we are an exe, require a source file for the entry point or
257 // if there is nothing to put in the assembly, and we are not a library
259 if (settings.FirstSourceFile == null &&
260 ((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) ||
261 settings.Resources == null)) {
262 Report.Error (2008, "No files to compile were specified");
263 return false;
266 if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) {
267 Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables");
268 return false;
271 TimeReporter tr = new TimeReporter (settings.Timestamps);
272 ctx.TimeReporter = tr;
273 tr.StartTotal ();
275 var module = new ModuleContainer (ctx);
276 RootContext.ToplevelTypes = module;
278 tr.Start (TimeReporter.TimerType.ParseTotal);
279 Parse (module);
280 tr.Stop (TimeReporter.TimerType.ParseTotal);
282 if (Report.Errors > 0)
283 return false;
285 if (settings.TokenizeOnly || settings.ParseOnly) {
286 tr.StopTotal ();
287 tr.ShowStats ();
288 return true;
291 var output_file = settings.OutputFile;
292 string output_file_name;
293 if (output_file == null) {
294 var source_file = settings.FirstSourceFile;
296 if (source_file == null) {
297 Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
298 return false;
301 output_file_name = source_file.Name;
302 int pos = output_file_name.LastIndexOf ('.');
304 if (pos > 0)
305 output_file_name = output_file_name.Substring (0, pos);
307 output_file_name += settings.TargetExt;
308 output_file = output_file_name;
309 } else {
310 output_file_name = Path.GetFileName (output_file);
312 if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) ||
313 output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) {
314 Report.Error (2021, "Output file name is not valid");
315 return false;
319 #if STATIC
320 var importer = new StaticImporter (module);
321 var references_loader = new StaticLoader (importer, ctx);
323 tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup);
324 var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file);
325 assembly.Create (references_loader.Domain);
326 tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup);
328 // Create compiler types first even before any referenced
329 // assembly is loaded to allow forward referenced types from
330 // loaded assembly into compiled builder to be resolved
331 // correctly
332 tr.Start (TimeReporter.TimerType.CreateTypeTotal);
333 module.CreateContainer ();
334 importer.AddCompiledAssembly (assembly);
335 references_loader.CompiledAssembly = assembly;
336 tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
338 references_loader.LoadReferences (module);
340 tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
341 if (!ctx.BuiltinTypes.CheckDefinitions (module))
342 return false;
344 tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
346 references_loader.LoadModules (assembly, module.GlobalRootNamespace);
347 #else
348 var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
349 module.SetDeclaringAssembly (assembly);
351 var importer = new ReflectionImporter (module, ctx.BuiltinTypes);
352 assembly.Importer = importer;
354 var loader = new DynamicLoader (importer, ctx);
355 loader.LoadReferences (module);
357 if (!ctx.BuiltinTypes.CheckDefinitions (module))
358 return false;
360 if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
361 return false;
363 module.CreateContainer ();
365 loader.LoadModules (assembly, module.GlobalRootNamespace);
366 #endif
367 module.InitializePredefinedTypes ();
369 if (settings.GetResourceStrings != null)
370 module.LoadGetResourceStrings (settings.GetResourceStrings);
372 tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
373 module.Define ();
374 tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
376 if (Report.Errors > 0)
377 return false;
379 if (settings.DocumentationFile != null) {
380 var doc = new DocumentationBuilder (module);
381 doc.OutputDocComment (output_file, settings.DocumentationFile);
384 assembly.Resolve ();
386 if (Report.Errors > 0)
387 return false;
390 tr.Start (TimeReporter.TimerType.EmitTotal);
391 assembly.Emit ();
392 tr.Stop (TimeReporter.TimerType.EmitTotal);
394 if (Report.Errors > 0){
395 return false;
398 tr.Start (TimeReporter.TimerType.CloseTypes);
399 module.CloseContainer ();
400 tr.Stop (TimeReporter.TimerType.CloseTypes);
402 tr.Start (TimeReporter.TimerType.Resouces);
403 if (!settings.WriteMetadataOnly)
404 assembly.EmbedResources ();
405 tr.Stop (TimeReporter.TimerType.Resouces);
407 if (Report.Errors > 0)
408 return false;
410 assembly.Save ();
412 #if STATIC
413 references_loader.Dispose ();
414 #endif
415 tr.StopTotal ();
416 tr.ShowStats ();
418 return Report.Errors == 0;
423 // This is the only public entry point
425 public class CompilerCallableEntryPoint : MarshalByRefObject {
426 public static bool InvokeCompiler (string [] args, TextWriter error)
428 try {
429 CommandLineParser cmd = new CommandLineParser (error);
430 var setting = cmd.ParseArguments (args);
431 if (setting == null)
432 return false;
434 var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error)));
435 return d.Compile ();
436 } finally {
437 Reset ();
441 public static int[] AllWarningNumbers {
442 get {
443 return Report.AllWarnings;
447 public static void Reset ()
449 Reset (true);
452 public static void PartialReset ()
454 Reset (false);
457 public static void Reset (bool full_flag)
459 Location.Reset ();
461 if (!full_flag)
462 return;
464 Linq.QueryBlock.TransparentParameter.Reset ();
465 TypeInfo.Reset ();