Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / class / System / Microsoft.CSharp / CSharpCodeCompiler.cs
index e6562b44be016d8794a3438225aca605cf381f07..26039bdd79b3de87fccd138b2345b4f809967f16 100644 (file)
@@ -34,6 +34,7 @@ namespace Mono.CSharp
        using System;
        using System.CodeDom;
        using System.CodeDom.Compiler;
+       using System.ComponentModel;
        using System.IO;
        using System.Text;
        using System.Reflection;
@@ -41,12 +42,17 @@ namespace Mono.CSharp
        using System.Collections.Specialized;
        using System.Diagnostics;
        using System.Text.RegularExpressions;
-
+       using System.Threading;
+       using System.Collections.Generic;
+       
        internal class CSharpCodeCompiler : CSharpCodeGenerator, ICodeCompiler
        {
                static string windowsMcsPath;
                static string windowsMonoPath;
 
+               Mutex mcsOutMutex;
+               StringCollection mcsOutput;
+               
                static CSharpCodeCompiler ()
                {
                        if (Path.DirectorySeparatorChar == '\\') {
@@ -71,25 +77,11 @@ namespace Mono.CSharp
                                                "mono\\mono\\mini\\mono.exe");
                                if (!File.Exists (windowsMonoPath))
                                        throw new FileNotFoundException ("Windows mono path not found: " + windowsMonoPath);
-#if NET_2_0
-                               windowsMcsPath =
-                                       Path.Combine (p, "2.0\\gmcs.exe");
-#else
-                               windowsMcsPath =
-                                       Path.Combine (p, "1.0\\mcs.exe");
-#endif
+
+                               windowsMcsPath = Path.Combine (p, "4.5\\mcs.exe");
                                if (!File.Exists (windowsMcsPath))
-#if NET_2_0
-                                       windowsMcsPath = 
-                                               Path.Combine(
-                                                       Path.GetDirectoryName (p),
-                                                       "lib\\net_2_0\\gmcs.exe");
-#else
-                                       windowsMcsPath = 
-                                               Path.Combine(
-                                                       Path.GetDirectoryName (p),
-                                                       "lib\\default\\mcs.exe");
-#endif
+                                       windowsMcsPath = Path.Combine(Path.GetDirectoryName (p), "lib\\build\\mcs.exe");
+                               
                                if (!File.Exists (windowsMcsPath))
                                        throw new FileNotFoundException ("Windows mcs path not found: " + windowsMcsPath);
                        }
@@ -102,6 +94,11 @@ namespace Mono.CSharp
                {
                }
 
+               public CSharpCodeCompiler (IDictionary <string, string> providerOptions) :
+                       base (providerOptions)
+               {
+               }
+               
                //
                // Methods
                //
@@ -169,72 +166,145 @@ namespace Mono.CSharp
                        CompilerResults results=new CompilerResults(options.TempFiles);
                        Process mcs=new Process();
 
-                       string mcs_output;
-                       string mcs_stdout;
-                       string[] mcs_output_lines;
                        // FIXME: these lines had better be platform independent.
                        if (Path.DirectorySeparatorChar == '\\') {
                                mcs.StartInfo.FileName = windowsMonoPath;
-                               mcs.StartInfo.Arguments = "\"" + windowsMcsPath + "\" " + BuildArgs (options, fileNames);
+                               mcs.StartInfo.Arguments = "\"" + windowsMcsPath + "\" " +
+                                       BuildArgs (options, fileNames, ProviderOptions);
                        } else {
-#if NET_2_0
-                               // FIXME: This is a temporary hack to make code genaration work in 2.0
-                               mcs.StartInfo.FileName="gmcs";
-#else
                                mcs.StartInfo.FileName="mcs";
+                               mcs.StartInfo.Arguments=BuildArgs(options, fileNames, ProviderOptions);
+                       }
+
+                       mcsOutput = new StringCollection ();
+                       mcsOutMutex = new Mutex ();
+#if !NET_4_0
+                       /*
+                        * !:. KLUDGE WARNING .:!
+                        *
+                        * When running the 2.0 test suite some assemblies will invoke mcs via
+                        * CodeDOM and the new mcs process will find the MONO_PATH variable in its
+                        * environment pointing to the net_2_0 library which will cause the runtime
+                        * to attempt to load the 2.0 corlib into 4.0 process and thus mcs will
+                        * fail. At the same time, we must not touch MONO_PATH when running outside
+                        * the test suite, thus the kludge.
+                        *
+                        * !:. KLUDGE WARNING .:!
+                        */
+                       if (Environment.GetEnvironmentVariable ("MONO_TESTS_IN_PROGRESS") != null) {
+                               string monoPath = Environment.GetEnvironmentVariable ("MONO_PATH");
+                               if (!String.IsNullOrEmpty (monoPath)) {
+                                       monoPath = monoPath.Replace ("/class/lib/net_2_0", "/class/lib/net_4_0");
+                                       mcs.StartInfo.EnvironmentVariables ["MONO_PATH"] = monoPath;
+                               }
+                       }
 #endif
-                               mcs.StartInfo.Arguments=BuildArgs(options,fileNames);
+/*                    
+                       string monoPath = Environment.GetEnvironmentVariable ("MONO_PATH");
+                       if (monoPath != null)
+                               monoPath = String.Empty;
+
+                       string privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
+                       if (privateBinPath != null && privateBinPath.Length > 0)
+                               monoPath = String.Format ("{0}:{1}", privateBinPath, monoPath);
+
+                       if (monoPath.Length > 0) {
+                               StringDictionary dict = mcs.StartInfo.EnvironmentVariables;
+                               if (dict.ContainsKey ("MONO_PATH"))
+                                       dict ["MONO_PATH"] = monoPath;
+                               else
+                                       dict.Add ("MONO_PATH", monoPath);
                        }
+*/
+                       /*
+                        * reset MONO_GC_PARAMS - we are invoking compiler possibly with another GC that
+                        * may not handle some of the options causing compilation failure
+                        */
+                       mcs.StartInfo.EnvironmentVariables ["MONO_GC_PARAMS"] = String.Empty;
+
                        mcs.StartInfo.CreateNoWindow=true;
                        mcs.StartInfo.UseShellExecute=false;
                        mcs.StartInfo.RedirectStandardOutput=true;
                        mcs.StartInfo.RedirectStandardError=true;
+                       mcs.ErrorDataReceived += new DataReceivedEventHandler (McsStderrDataReceived);
+                       
                        try {
                                mcs.Start();
-                               // If there are a few kB in stdout, we might lock
-                               mcs_output=mcs.StandardError.ReadToEnd();
-                               mcs_stdout=mcs.StandardOutput.ReadToEnd ();
+                       } catch (Exception e) {
+                               Win32Exception exc = e as Win32Exception;
+                               if (exc != null) {
+                                       throw new SystemException (String.Format ("Error running {0}: {1}", mcs.StartInfo.FileName,
+                                                                       Win32Exception.W32ErrorMessage (exc.NativeErrorCode)));
+                               }
+                               throw;
+                       }
+
+                       try {
+                               mcs.BeginOutputReadLine ();
+                               mcs.BeginErrorReadLine ();
                                mcs.WaitForExit();
+                               
                                results.NativeCompilerReturnValue = mcs.ExitCode;
                        } finally {
+                               mcs.CancelErrorRead ();
+                               mcs.CancelOutputRead ();
                                mcs.Close();
                        }
-                       mcs_output_lines=mcs_output.Split(
-                               System.Environment.NewLine.ToCharArray());
-                       bool loadIt=true;
-                       foreach (string error_line in mcs_output_lines)
-                       {
-                               CompilerError error=CreateErrorFromString(error_line);
-                               if (null!=error)
-                               {
-                                       results.Errors.Add(error);
-                                       if (!error.IsWarning) loadIt=false;
+
+                       StringCollection sc = mcsOutput;
+                      
+                       bool loadIt=true;
+                       foreach (string error_line in mcsOutput) {
+                               CompilerError error = CreateErrorFromString (error_line);
+                               if (error != null) {
+                                       results.Errors.Add (error);
+                                       if (!error.IsWarning)
+                                               loadIt = false;
                                }
                        }
+                       
+                       if (sc.Count > 0) {
+                               sc.Insert (0, mcs.StartInfo.FileName + " " + mcs.StartInfo.Arguments + Environment.NewLine);
+                               results.Output = sc;
+                       }
+
                        if (loadIt) {
                                if (!File.Exists (options.OutputAssembly)) {
-                                       throw new Exception ("Compiler failed to produce the assembly. Stderr='"
-                                               +mcs_output+"', Stdout='"+mcs_stdout+"'");
+                                       StringBuilder sb = new StringBuilder ();
+                                       foreach (string s in sc)
+                                               sb.Append (s + Environment.NewLine);
+                                       
+                                       throw new Exception ("Compiler failed to produce the assembly. Output: '" + sb.ToString () + "'");
                                }
+                               
                                if (options.GenerateInMemory) {
                                        using (FileStream fs = File.OpenRead(options.OutputAssembly)) {
                                                byte[] buffer = new byte[fs.Length];
                                                fs.Read(buffer, 0, buffer.Length);
-                                               results.CompiledAssembly = Assembly.Load(buffer, null, options.Evidence);
+                                               results.CompiledAssembly = Assembly.Load(buffer, null);
                                                fs.Close();
                                        }
                                } else {
-                                       results.CompiledAssembly = Assembly.LoadFrom(options.OutputAssembly);
+                                       // Avoid setting CompiledAssembly right now since the output might be a netmodule
                                        results.PathToAssembly = options.OutputAssembly;
                                }
                        } else {
                                results.CompiledAssembly = null;
                        }
-
+                       
                        return results;
                }
 
-               private static string BuildArgs(CompilerParameters options,string[] fileNames)
+               void McsStderrDataReceived (object sender, DataReceivedEventArgs args)
+               {
+                       if (args.Data != null) {
+                               mcsOutMutex.WaitOne ();
+                               mcsOutput.Add (args.Data);
+                               mcsOutMutex.ReleaseMutex ();
+                       }
+               }               
+
+               private static string BuildArgs(CompilerParameters options,string[] fileNames, IDictionary <string, string> providerOptions)
                {
                        StringBuilder args=new StringBuilder();
                        if (options.GenerateExecutable)
@@ -242,6 +312,10 @@ namespace Mono.CSharp
                        else
                                args.Append("/target:library ");
 
+                       string privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
+                       if (privateBinPath != null && privateBinPath.Length > 0)
+                               args.AppendFormat ("/lib:\"{0}\" ", privateBinPath);
+                       
                        if (options.Win32Resource != null)
                                args.AppendFormat("/win32res:\"{0}\" ",
                                        options.Win32Resource);
@@ -276,7 +350,6 @@ namespace Mono.CSharp
                                args.Append (" ");
                        }
 
-#if NET_2_0
                        foreach (string embeddedResource in options.EmbeddedResources) {
                                args.AppendFormat("/resource:\"{0}\" ", embeddedResource);
                        }
@@ -284,6 +357,37 @@ namespace Mono.CSharp
                        foreach (string linkedResource in options.LinkedResources) {
                                args.AppendFormat("/linkresource:\"{0}\" ", linkedResource);
                        }
+                       
+                       if (providerOptions != null && providerOptions.Count > 0) {
+                               string langver;
+
+                               if (!providerOptions.TryGetValue ("CompilerVersion", out langver))
+#if NET_4_0
+                                       langver = "3.5";
+#else
+                                       langver = "2.0";
+#endif
+
+                               if (langver.Length >= 1 && langver [0] == 'v')
+                                       langver = langver.Substring (1);
+
+                               switch (langver) {
+                                       case "2.0":
+                                               args.Append ("/langversion:ISO-2 ");
+                                               break;
+
+                                       case "3.5":
+                                               // current default, omit the switch
+                                               break;
+                               }
+                       }
+
+#if NET_4_5                    
+                       args.Append("/sdk:4.5");
+#elif NET_4_0
+                       args.Append("/sdk:4");
+#else
+                       args.Append("/sdk:2");
 #endif
 
                        args.Append (" -- ");
@@ -293,10 +397,9 @@ namespace Mono.CSharp
                }
                private static CompilerError CreateErrorFromString(string error_string)
                {
-#if NET_2_0
                        if (error_string.StartsWith ("BETA"))
                                return null;
-#endif
+
                        if (error_string == null || error_string == "")
                                return null;
 
@@ -304,7 +407,13 @@ namespace Mono.CSharp
                        Regex reg = new Regex (@"^(\s*(?<file>.*)\((?<line>\d*)(,(?<column>\d*))?\)(:)?\s+)*(?<level>\w+)\s*(?<number>.*):\s(?<message>.*)",
                                RegexOptions.Compiled | RegexOptions.ExplicitCapture);
                        Match match=reg.Match(error_string);
-                       if (!match.Success) return null;
+                       if (!match.Success) {
+                               // We had some sort of runtime crash
+                               error.ErrorText = error_string;
+                               error.IsWarning = false;
+                               error.ErrorNumber = "";
+                               return error;
+                       }
                        if (String.Empty != match.Result("${file}"))
                                error.FileName=match.Result("${file}");
                        if (String.Empty != match.Result("${line}"))