using System;
using System.CodeDom;
using System.CodeDom.Compiler;
+ using System.ComponentModel;
using System.IO;
using System.Text;
using System.Reflection;
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 == '\\') {
"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);
}
{
}
+ public CSharpCodeCompiler (IDictionary <string, string> providerOptions) :
+ base (providerOptions)
+ {
+ }
+
//
// Methods
//
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)
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);
if (options.WarningLevel >= 0)
args.AppendFormat ("/warn:{0} ", options.WarningLevel);
- if (options.OutputAssembly==null)
- options.OutputAssembly = GetTempFileNameWithExtension (options.TempFiles, "dll", !options.GenerateInMemory);
+ if (options.OutputAssembly == null || options.OutputAssembly.Length == 0) {
+ string extension = (options.GenerateExecutable ? "exe" : "dll");
+ options.OutputAssembly = GetTempFileNameWithExtension (options.TempFiles, extension,
+ !options.GenerateInMemory);
+ }
args.AppendFormat("/out:\"{0}\" ",options.OutputAssembly);
foreach (string import in options.ReferencedAssemblies) {
args.Append (" ");
}
-#if NET_2_0
foreach (string embeddedResource in options.EmbeddedResources) {
args.AppendFormat("/resource:\"{0}\" ", embeddedResource);
}
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 (" -- ");
}
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;
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}"))