2 // driver.cs: The compiler command line driver.
5 // Miguel de Icaza (miguel@gnu.org)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
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
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Collections.Generic;
21 using System.Globalization;
22 using System.Diagnostics;
23 using System.Threading;
28 /// The compiler driver.
32 readonly CompilerContext ctx;
34 public Driver (CompilerContext ctx)
45 void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session)
48 SeekableStreamReader reader = null;
51 if (sourceFile.GetInputStream != null) {
52 reader = sourceFile.GetInputStream (sourceFile);
54 throw new FileNotFoundException ("Delegate returned null", sourceFile.Name);
57 input = File.OpenRead (sourceFile.Name);
60 Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found");
66 reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
67 DoTokenize (sourceFile, module, session, reader);
70 DoTokenize (sourceFile, module, session, reader);
74 void DoTokenize (SourceFile sourceFile, ModuleContainer module, ParserSession session, SeekableStreamReader reader) {
75 var file = new CompilationSourceFile (module, sourceFile);
77 Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report);
78 int token, tokens = 0, errors = 0;
80 while ((token = lexer.token ()) != Token.EOF) {
82 if (token == Token.ERROR)
85 Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
88 void Parse (ModuleContainer module)
90 bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
91 var sources = module.Compiler.SourceFiles;
93 Location.Initialize (sources);
95 var session = new ParserSession {
96 UseJayGlobalArrays = true,
97 LocatedTokens = new LocatedToken[15000]
100 for (int i = 0; i < sources.Count; ++i) {
102 tokenize_file (sources[i], module, session);
104 Parse (sources[i], module, session, Report);
110 void ParseParallel (ModuleContainer module)
112 var sources = module.Compiler.SourceFiles;
114 Location.Initialize (sources);
116 var pcount = Environment.ProcessorCount;
117 var threads = new Thread[System.Math.Max (2, pcount - 1)];
119 for (int i = 0; i < threads.Length; ++i) {
120 var t = new Thread (l => {
121 var session = new ParserSession () {
122 //UseJayGlobalArrays = true,
125 var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer
127 for (int ii = (int) l; ii < sources.Count; ii += threads.Length) {
128 Parse (sources[ii], module, session, report);
131 // TODO: Merge warning regions
138 for (int t = 0; t < threads.Length; ++t) {
144 public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report)
147 SeekableStreamReader reader = null;
150 if (file.GetInputStream != null) {
151 reader = file.GetInputStream (file);
152 if (reader == null) {
153 throw new FileNotFoundException ("Delegate returned null", file.Name);
156 input = File.OpenRead (file.Name);
159 report.Error (2001, "Source file `{0}' could not be found", file.Name);
163 if (reader == null) {
166 if (input.ReadByte () == 77 && input.ReadByte () == 90) {
168 report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
173 reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer);
175 DoParse (file, module, session, report, reader);
178 DoParse (file, module, session, report, reader);
182 void DoParse (SourceFile file, ModuleContainer module, ParserSession session, Report report, SeekableStreamReader reader) {
183 Parse (reader, file, module, session, report);
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));
192 public static void Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report)
194 var file = new CompilationSourceFile (module, sourceFile);
195 module.AddTypeContainer (file);
197 CSharpParser parser = new CSharpParser (reader, file, report, session);
201 public static int Main (string[] args)
203 Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
205 CommandLineParser cmd = new CommandLineParser (Console.Out);
206 var settings = cmd.ParseArguments (args);
207 if (settings == null)
210 if (cmd.HasBeenStopped)
213 Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ()));
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);
219 Environment.Exit (0);
224 Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
225 d.Report.Errors, d.Report.Warnings);
226 Environment.Exit (1);
230 public static string GetPackageFlags (string packages, Report report)
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;
240 p = Process.Start (pi);
241 } catch (Exception e) {
245 report.Error (-27, "Couldn't run pkg-config: " + e.Message);
249 if (p.StandardOutput == null) {
251 throw new ApplicationException ("Specified package did not return any information");
253 report.Warning (-27, 1, "Specified package did not return any information");
258 string pkgout = p.StandardOutput.ReadToEnd ();
260 if (p.ExitCode != 0) {
262 throw new ApplicationException (pkgout);
264 report.Error (-27, "Error running pkg-config. Check the above output.");
272 throw new NotSupportedException ("Process.Start is not supported on this platform.");
273 #endif // MONO_FEATURE_PROCESS_START
277 // Main compilation method
279 public bool Compile ()
281 var settings = ctx.Settings;
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
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");
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");
299 TimeReporter tr = new TimeReporter (settings.Timestamps);
300 ctx.TimeReporter = tr;
303 var module = new ModuleContainer (ctx);
304 RootContext.ToplevelTypes = module;
306 tr.Start (TimeReporter.TimerType.ParseTotal);
308 tr.Stop (TimeReporter.TimerType.ParseTotal);
310 if (Report.Errors > 0)
313 if (settings.TokenizeOnly || settings.ParseOnly) {
319 var output_file = settings.OutputFile;
320 string output_file_name;
321 if (output_file == null) {
322 var source_file = settings.FirstSourceFile;
324 if (source_file == null) {
325 Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
329 output_file_name = source_file.Name;
330 int pos = output_file_name.LastIndexOf ('.');
333 output_file_name = output_file_name.Substring (0, pos);
335 output_file_name += settings.TargetExt;
336 output_file = output_file_name;
338 output_file_name = Path.GetFileName (output_file);
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");
348 var importer = new StaticImporter (module);
349 var references_loader = new StaticLoader (importer, ctx);
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);
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
360 tr.Start (TimeReporter.TimerType.CreateTypeTotal);
361 module.CreateContainer ();
362 importer.AddCompiledAssembly (assembly);
363 references_loader.CompiledAssembly = assembly;
364 tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
366 references_loader.LoadReferences (module);
368 tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
369 if (!ctx.BuiltinTypes.CheckDefinitions (module))
372 tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
374 references_loader.LoadModules (assembly, module.GlobalRootNamespace);
376 var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
377 module.SetDeclaringAssembly (assembly);
379 var importer = new ReflectionImporter (module, ctx.BuiltinTypes);
380 assembly.Importer = importer;
382 var loader = new DynamicLoader (importer, ctx);
383 loader.LoadReferences (module);
385 if (!ctx.BuiltinTypes.CheckDefinitions (module))
388 if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
391 module.CreateContainer ();
393 loader.LoadModules (assembly, module.GlobalRootNamespace);
395 module.InitializePredefinedTypes ();
397 if (settings.GetResourceStrings != null)
398 module.LoadGetResourceStrings (settings.GetResourceStrings);
400 tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
402 tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
404 if (Report.Errors > 0)
407 if (settings.DocumentationFile != null) {
408 var doc = new DocumentationBuilder (module);
409 doc.OutputDocComment (output_file, settings.DocumentationFile);
414 if (Report.Errors > 0)
418 tr.Start (TimeReporter.TimerType.EmitTotal);
420 tr.Stop (TimeReporter.TimerType.EmitTotal);
422 if (Report.Errors > 0){
426 tr.Start (TimeReporter.TimerType.CloseTypes);
427 module.CloseContainer ();
428 tr.Stop (TimeReporter.TimerType.CloseTypes);
430 tr.Start (TimeReporter.TimerType.Resouces);
431 if (!settings.WriteMetadataOnly)
432 assembly.EmbedResources ();
433 tr.Stop (TimeReporter.TimerType.Resouces);
435 if (Report.Errors > 0)
441 references_loader.Dispose ();
446 return Report.Errors == 0;
451 // This is the only public entry point
453 public class CompilerCallableEntryPoint : MarshalByRefObject {
454 public static bool InvokeCompiler (string [] args, TextWriter error)
457 CommandLineParser cmd = new CommandLineParser (error);
458 var setting = cmd.ParseArguments (args);
462 var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error)));
469 public static int[] AllWarningNumbers {
471 return Report.AllWarnings;
475 public static void Reset ()
480 public static void PartialReset ()
485 public static void Reset (bool full_flag)
492 Linq.QueryBlock.TransparentParameter.Reset ();