497 lines
13 KiB
C#
497 lines
13 KiB
C#
//
|
|
// driver.cs: The compiler command line driver.
|
|
//
|
|
// Authors:
|
|
// Miguel de Icaza (miguel@gnu.org)
|
|
// Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
|
//
|
|
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
|
|
// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
|
|
// Copyright 2011 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Globalization;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
|
|
namespace Mono.CSharp
|
|
{
|
|
/// <summary>
|
|
/// The compiler driver.
|
|
/// </summary>
|
|
class Driver
|
|
{
|
|
readonly CompilerContext ctx;
|
|
|
|
public Driver (CompilerContext ctx)
|
|
{
|
|
this.ctx = ctx;
|
|
}
|
|
|
|
Report Report {
|
|
get {
|
|
return ctx.Report;
|
|
}
|
|
}
|
|
|
|
void tokenize_file (SourceFile sourceFile, ModuleContainer module, ParserSession session)
|
|
{
|
|
Stream input = null;
|
|
SeekableStreamReader reader = null;
|
|
|
|
try {
|
|
if (sourceFile.GetInputStream != null) {
|
|
reader = sourceFile.GetInputStream (sourceFile);
|
|
if (reader == null) {
|
|
throw new FileNotFoundException ("Delegate returned null", sourceFile.Name);
|
|
}
|
|
} else {
|
|
input = File.OpenRead (sourceFile.Name);
|
|
}
|
|
} catch {
|
|
Report.Error (2001, "Source file `" + sourceFile.Name + "' could not be found");
|
|
return;
|
|
}
|
|
|
|
if (reader == null) {
|
|
using (input) {
|
|
reader = new SeekableStreamReader (input, ctx.Settings.Encoding);
|
|
DoTokenize (sourceFile, module, session, reader);
|
|
}
|
|
} else {
|
|
DoTokenize (sourceFile, module, session, reader);
|
|
}
|
|
}
|
|
|
|
void DoTokenize (SourceFile sourceFile, ModuleContainer module, ParserSession session, SeekableStreamReader reader) {
|
|
var file = new CompilationSourceFile (module, sourceFile);
|
|
|
|
Tokenizer lexer = new Tokenizer (reader, file, session, ctx.Report);
|
|
int token, tokens = 0, errors = 0;
|
|
|
|
while ((token = lexer.token ()) != Token.EOF) {
|
|
tokens++;
|
|
if (token == Token.ERROR)
|
|
errors++;
|
|
}
|
|
Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
|
|
}
|
|
|
|
void Parse (ModuleContainer module)
|
|
{
|
|
bool tokenize_only = module.Compiler.Settings.TokenizeOnly;
|
|
var sources = module.Compiler.SourceFiles;
|
|
|
|
Location.Initialize (sources);
|
|
|
|
var session = new ParserSession {
|
|
UseJayGlobalArrays = true,
|
|
LocatedTokens = new LocatedToken[15000]
|
|
};
|
|
|
|
for (int i = 0; i < sources.Count; ++i) {
|
|
if (tokenize_only) {
|
|
tokenize_file (sources[i], module, session);
|
|
} else {
|
|
Parse (sources[i], module, session, Report);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if false
|
|
void ParseParallel (ModuleContainer module)
|
|
{
|
|
var sources = module.Compiler.SourceFiles;
|
|
|
|
Location.Initialize (sources);
|
|
|
|
var pcount = Environment.ProcessorCount;
|
|
var threads = new Thread[System.Math.Max (2, pcount - 1)];
|
|
|
|
for (int i = 0; i < threads.Length; ++i) {
|
|
var t = new Thread (l => {
|
|
var session = new ParserSession () {
|
|
//UseJayGlobalArrays = true,
|
|
};
|
|
|
|
var report = new Report (ctx, Report.Printer); // TODO: Implement flush at once printer
|
|
|
|
for (int ii = (int) l; ii < sources.Count; ii += threads.Length) {
|
|
Parse (sources[ii], module, session, report);
|
|
}
|
|
|
|
// TODO: Merge warning regions
|
|
});
|
|
|
|
t.Start (i);
|
|
threads[i] = t;
|
|
}
|
|
|
|
for (int t = 0; t < threads.Length; ++t) {
|
|
threads[t].Join ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public void Parse (SourceFile file, ModuleContainer module, ParserSession session, Report report)
|
|
{
|
|
Stream input = null;
|
|
SeekableStreamReader reader = null;
|
|
|
|
try {
|
|
if (file.GetInputStream != null) {
|
|
reader = file.GetInputStream (file);
|
|
if (reader == null) {
|
|
throw new FileNotFoundException ("Delegate returned null", file.Name);
|
|
}
|
|
} else {
|
|
input = File.OpenRead (file.Name);
|
|
}
|
|
} catch {
|
|
report.Error (2001, "Source file `{0}' could not be found", file.Name);
|
|
return;
|
|
}
|
|
|
|
if (reader == null) {
|
|
using (input) {
|
|
// Check 'MZ' header
|
|
if (input.ReadByte () == 77 && input.ReadByte () == 90) {
|
|
|
|
report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
|
|
return;
|
|
}
|
|
|
|
input.Position = 0;
|
|
reader = new SeekableStreamReader (input, ctx.Settings.Encoding, session.StreamReaderBuffer);
|
|
|
|
DoParse (file, module, session, report, reader);
|
|
}
|
|
} else {
|
|
DoParse (file, module, session, report, reader);
|
|
}
|
|
}
|
|
|
|
void DoParse (SourceFile file, ModuleContainer module, ParserSession session, Report report, SeekableStreamReader reader) {
|
|
Parse (reader, file, module, session, report);
|
|
|
|
if (ctx.Settings.GenerateDebugInfo && report.Errors == 0 && !file.HasChecksum) {
|
|
reader.Stream.Position = 0;
|
|
var checksum = session.GetChecksumAlgorithm ();
|
|
file.SetChecksum (checksum.ComputeHash (reader.Stream));
|
|
}
|
|
}
|
|
|
|
public static void Parse (SeekableStreamReader reader, SourceFile sourceFile, ModuleContainer module, ParserSession session, Report report)
|
|
{
|
|
var file = new CompilationSourceFile (module, sourceFile);
|
|
module.AddTypeContainer (file);
|
|
|
|
CSharpParser parser = new CSharpParser (reader, file, report, session);
|
|
parser.parse ();
|
|
}
|
|
|
|
public static int Main (string[] args)
|
|
{
|
|
Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
|
|
|
|
CommandLineParser cmd = new CommandLineParser (Console.Out);
|
|
var settings = cmd.ParseArguments (args);
|
|
if (settings == null)
|
|
return 1;
|
|
|
|
if (cmd.HasBeenStopped)
|
|
return 0;
|
|
|
|
Driver d = new Driver (new CompilerContext (settings, new ConsoleReportPrinter ()));
|
|
|
|
if (d.Compile () && d.Report.Errors == 0) {
|
|
if (d.Report.Warnings > 0) {
|
|
Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
|
|
}
|
|
Environment.Exit (0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
|
|
d.Report.Errors, d.Report.Warnings);
|
|
Environment.Exit (1);
|
|
return 1;
|
|
}
|
|
|
|
public static string GetPackageFlags (string packages, Report report)
|
|
{
|
|
#if MONO_FEATURE_PROCESS_START
|
|
ProcessStartInfo pi = new ProcessStartInfo ();
|
|
pi.FileName = "pkg-config";
|
|
pi.RedirectStandardOutput = true;
|
|
pi.UseShellExecute = false;
|
|
pi.Arguments = "--libs " + packages;
|
|
Process p = null;
|
|
try {
|
|
p = Process.Start (pi);
|
|
} catch (Exception e) {
|
|
if (report == null)
|
|
throw;
|
|
|
|
report.Error (-27, "Couldn't run pkg-config: " + e.Message);
|
|
return null;
|
|
}
|
|
|
|
if (p.StandardOutput == null) {
|
|
if (report == null)
|
|
throw new ApplicationException ("Specified package did not return any information");
|
|
|
|
report.Warning (-27, 1, "Specified package did not return any information");
|
|
p.Close ();
|
|
return null;
|
|
}
|
|
|
|
string pkgout = p.StandardOutput.ReadToEnd ();
|
|
p.WaitForExit ();
|
|
if (p.ExitCode != 0) {
|
|
if (report == null)
|
|
throw new ApplicationException (pkgout);
|
|
|
|
report.Error (-27, "Error running pkg-config. Check the above output.");
|
|
p.Close ();
|
|
return null;
|
|
}
|
|
|
|
p.Close ();
|
|
return pkgout;
|
|
#else
|
|
throw new NotSupportedException ("Process.Start is not supported on this platform.");
|
|
#endif // MONO_FEATURE_PROCESS_START
|
|
}
|
|
|
|
//
|
|
// Main compilation method
|
|
//
|
|
public bool Compile ()
|
|
{
|
|
var settings = ctx.Settings;
|
|
|
|
//
|
|
// If we are an exe, require a source file for the entry point or
|
|
// if there is nothing to put in the assembly, and we are not a library
|
|
//
|
|
if (settings.FirstSourceFile == null &&
|
|
((settings.Target == Target.Exe || settings.Target == Target.WinExe || settings.Target == Target.Module) ||
|
|
settings.Resources == null)) {
|
|
Report.Error (2008, "No files to compile were specified");
|
|
return false;
|
|
}
|
|
|
|
if (settings.Platform == Platform.AnyCPU32Preferred && (settings.Target == Target.Library || settings.Target == Target.Module)) {
|
|
Report.Error (4023, "Platform option `anycpu32bitpreferred' is valid only for executables");
|
|
return false;
|
|
}
|
|
|
|
TimeReporter tr = new TimeReporter (settings.Timestamps);
|
|
ctx.TimeReporter = tr;
|
|
tr.StartTotal ();
|
|
|
|
var module = new ModuleContainer (ctx);
|
|
RootContext.ToplevelTypes = module;
|
|
|
|
tr.Start (TimeReporter.TimerType.ParseTotal);
|
|
Parse (module);
|
|
tr.Stop (TimeReporter.TimerType.ParseTotal);
|
|
|
|
if (Report.Errors > 0)
|
|
return false;
|
|
|
|
if (settings.TokenizeOnly || settings.ParseOnly) {
|
|
tr.StopTotal ();
|
|
tr.ShowStats ();
|
|
return true;
|
|
}
|
|
|
|
var output_file = settings.OutputFile;
|
|
string output_file_name;
|
|
if (output_file == null) {
|
|
var source_file = settings.FirstSourceFile;
|
|
|
|
if (source_file == null) {
|
|
Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
|
|
return false;
|
|
}
|
|
|
|
output_file_name = source_file.Name;
|
|
int pos = output_file_name.LastIndexOf ('.');
|
|
|
|
if (pos > 0)
|
|
output_file_name = output_file_name.Substring (0, pos);
|
|
|
|
output_file_name += settings.TargetExt;
|
|
output_file = output_file_name;
|
|
} else {
|
|
output_file_name = Path.GetFileName (output_file);
|
|
|
|
if (string.IsNullOrEmpty (Path.GetFileNameWithoutExtension (output_file_name)) ||
|
|
output_file_name.IndexOfAny (Path.GetInvalidFileNameChars ()) >= 0) {
|
|
Report.Error (2021, "Output file name is not valid");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if STATIC
|
|
var importer = new StaticImporter (module);
|
|
var references_loader = new StaticLoader (importer, ctx);
|
|
|
|
tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup);
|
|
var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file);
|
|
assembly.Create (references_loader.Domain);
|
|
tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup);
|
|
|
|
// Create compiler types first even before any referenced
|
|
// assembly is loaded to allow forward referenced types from
|
|
// loaded assembly into compiled builder to be resolved
|
|
// correctly
|
|
tr.Start (TimeReporter.TimerType.CreateTypeTotal);
|
|
module.CreateContainer ();
|
|
importer.AddCompiledAssembly (assembly);
|
|
references_loader.CompiledAssembly = assembly;
|
|
tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
|
|
|
|
references_loader.LoadReferences (module);
|
|
|
|
tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
|
|
if (!ctx.BuiltinTypes.CheckDefinitions (module))
|
|
return false;
|
|
|
|
tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
|
|
|
|
references_loader.LoadModules (assembly, module.GlobalRootNamespace);
|
|
#else
|
|
var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
|
|
module.SetDeclaringAssembly (assembly);
|
|
|
|
var importer = new ReflectionImporter (module, ctx.BuiltinTypes);
|
|
assembly.Importer = importer;
|
|
|
|
var loader = new DynamicLoader (importer, ctx);
|
|
loader.LoadReferences (module);
|
|
|
|
if (!ctx.BuiltinTypes.CheckDefinitions (module))
|
|
return false;
|
|
|
|
if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
|
|
return false;
|
|
|
|
module.CreateContainer ();
|
|
|
|
loader.LoadModules (assembly, module.GlobalRootNamespace);
|
|
#endif
|
|
module.InitializePredefinedTypes ();
|
|
|
|
if (settings.GetResourceStrings != null)
|
|
module.LoadGetResourceStrings (settings.GetResourceStrings);
|
|
|
|
tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
|
|
module.Define ();
|
|
tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
|
|
|
|
if (Report.Errors > 0)
|
|
return false;
|
|
|
|
if (settings.DocumentationFile != null) {
|
|
var doc = new DocumentationBuilder (module);
|
|
doc.OutputDocComment (output_file, settings.DocumentationFile);
|
|
}
|
|
|
|
assembly.Resolve ();
|
|
|
|
if (Report.Errors > 0)
|
|
return false;
|
|
|
|
|
|
tr.Start (TimeReporter.TimerType.EmitTotal);
|
|
assembly.Emit ();
|
|
tr.Stop (TimeReporter.TimerType.EmitTotal);
|
|
|
|
if (Report.Errors > 0){
|
|
return false;
|
|
}
|
|
|
|
tr.Start (TimeReporter.TimerType.CloseTypes);
|
|
module.CloseContainer ();
|
|
tr.Stop (TimeReporter.TimerType.CloseTypes);
|
|
|
|
tr.Start (TimeReporter.TimerType.Resouces);
|
|
if (!settings.WriteMetadataOnly)
|
|
assembly.EmbedResources ();
|
|
tr.Stop (TimeReporter.TimerType.Resouces);
|
|
|
|
if (Report.Errors > 0)
|
|
return false;
|
|
|
|
assembly.Save ();
|
|
|
|
#if STATIC
|
|
references_loader.Dispose ();
|
|
#endif
|
|
tr.StopTotal ();
|
|
tr.ShowStats ();
|
|
|
|
return Report.Errors == 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is the only public entry point
|
|
//
|
|
public class CompilerCallableEntryPoint : MarshalByRefObject {
|
|
public static bool InvokeCompiler (string [] args, TextWriter error)
|
|
{
|
|
try {
|
|
CommandLineParser cmd = new CommandLineParser (error);
|
|
var setting = cmd.ParseArguments (args);
|
|
if (setting == null)
|
|
return false;
|
|
|
|
var d = new Driver (new CompilerContext (setting, new StreamReportPrinter (error)));
|
|
return d.Compile ();
|
|
} finally {
|
|
Reset ();
|
|
}
|
|
}
|
|
|
|
public static int[] AllWarningNumbers {
|
|
get {
|
|
return Report.AllWarnings;
|
|
}
|
|
}
|
|
|
|
public static void Reset ()
|
|
{
|
|
Reset (true);
|
|
}
|
|
|
|
public static void PartialReset ()
|
|
{
|
|
Reset (false);
|
|
}
|
|
|
|
public static void Reset (bool full_flag)
|
|
{
|
|
Location.Reset ();
|
|
|
|
if (!full_flag)
|
|
return;
|
|
|
|
Linq.QueryBlock.TransparentParameter.Reset ();
|
|
TypeInfo.Reset ();
|
|
}
|
|
}
|
|
}
|