f1a06bb0f86b98cdb7e996eebd6410da50acaf20
[mono.git] / mcs / class / System / Microsoft.CSharp / CSharpCodeCompiler.cs
1 //
2 // Mono.CSharp CSharpCodeCompiler Class implementation
3 //
4 // Author:
5 //   Sean Kasun (seank@users.sf.net)
6 //
7
8 namespace Mono.CSharp
9 {
10         using System;
11         using System.CodeDom;
12         using System.CodeDom.Compiler;
13         using System.IO;
14         using System.Text;
15         using System.Reflection;
16         using System.Collections;
17         using System.Collections.Specialized;
18         using System.Diagnostics;
19         using System.Text.RegularExpressions;
20
21         internal class CSharpCodeCompiler : CSharpCodeGenerator, ICodeCompiler
22         {
23                 //
24                 // Constructors
25                 //
26                 public CSharpCodeCompiler()
27                 {
28                 }
29
30                 //
31                 // Methods
32                 //
33                 [MonoTODO]
34                 public CompilerResults CompileAssemblyFromDom (
35                         CompilerParameters options,CodeCompileUnit e)
36                 {
37                         return CompileAssemblyFromDomBatch(options,new CodeCompileUnit[]{e});
38                 }
39                 public CompilerResults CompileAssemblyFromDomBatch (
40                         CompilerParameters options,CodeCompileUnit[] ea)
41                 {
42                         string[] fileNames=new string[ea.Length];
43                         int i=0;
44                         if (options == null)
45                                 options = new CompilerParameters ();
46                         
47                         StringCollection assemblies = options.ReferencedAssemblies;
48
49                         foreach (CodeCompileUnit e in ea) {
50                                 fileNames [i] = GetTempFileNameWithExtension ("cs");
51                                 FileStream f=new FileStream(fileNames[i],FileMode.OpenOrCreate);
52                                 StreamWriter s=new StreamWriter(f);
53                                 if (e.ReferencedAssemblies != null) {
54                                         foreach (string str in e.ReferencedAssemblies) {
55                                                 if (!assemblies.Contains (str))
56                                                         assemblies.Add (str);
57                                         }
58                                 }
59
60                                 ((ICodeGenerator)this).GenerateCodeFromCompileUnit (e, s, new CodeGeneratorOptions());
61                                 s.Close();
62                                 f.Close();
63                                 i++;
64                         }
65                         return CompileAssemblyFromFileBatch (options, fileNames, true);
66                 }
67                 
68                 public CompilerResults CompileAssemblyFromFile (
69                         CompilerParameters options,string fileName)
70                 {
71                         return CompileAssemblyFromFileBatch (options, new string []{fileName}, false);
72                 }
73
74                 public CompilerResults CompileAssemblyFromFileBatch (CompilerParameters options, string[] fileNames)
75                 {
76                         return CompileAssemblyFromFileBatch (options, fileNames, false);
77                 }
78                 
79                 CompilerResults CompileAssemblyFromFileBatch (CompilerParameters options, string [] fileNames,
80                                                               bool removeFiles)
81                 {
82                         if (null == options)
83                                 throw new ArgumentNullException("options");
84                         if (null == fileNames)
85                                 throw new ArgumentNullException("fileNames");
86
87                         CompilerResults results=new CompilerResults(options.TempFiles);
88                         Process mcs=new Process();
89
90                         string mcs_output;
91                         string[] mcs_output_lines;
92                         mcs.StartInfo.FileName="mcs";
93                         mcs.StartInfo.Arguments=BuildArgs(options,fileNames);
94                         mcs.StartInfo.CreateNoWindow=true;
95                         mcs.StartInfo.UseShellExecute=false;
96                         mcs.StartInfo.RedirectStandardOutput=true;
97                         try {
98                                 mcs.Start();
99                                 mcs_output=mcs.StandardOutput.ReadToEnd();
100                                 mcs.WaitForExit();
101                         } finally {
102                                 results.NativeCompilerReturnValue = mcs.ExitCode;
103                                 mcs.Close();
104                         }
105                         mcs_output_lines=mcs_output.Split(
106                                 System.Environment.NewLine.ToCharArray());
107                         bool loadIt=true;
108                         foreach (string error_line in mcs_output_lines)
109                         {
110                                 CompilerError error=CreateErrorFromString(error_line);
111                                 if (null!=error)
112                                 {
113                                         results.Errors.Add(error);
114                                         if (!error.IsWarning) loadIt=false;
115                                 }
116                         }
117                         if (loadIt)
118                                 results.CompiledAssembly=Assembly.LoadFrom(options.OutputAssembly);
119                         else
120                                 results.CompiledAssembly=null;
121
122                         if (removeFiles) {
123                                 foreach (string fi in fileNames) {
124                                         FileInfo info = new FileInfo (fi);
125                                         info.Delete ();
126                                 }
127                         }
128                         
129                         return results;
130                 }
131                 public CompilerResults CompileAssemblyFromSource (
132                         CompilerParameters options,string source)
133                 {
134                         return CompileAssemblyFromSourceBatch(options,new string[]{source});
135                 }
136                 public CompilerResults CompileAssemblyFromSourceBatch (
137                         CompilerParameters options,string[] sources)
138                 {
139                         string[] fileNames=new string[sources.Length];
140                         int i=0;
141                         foreach (string source in sources) {
142                                 fileNames [i] = GetTempFileNameWithExtension ("cs");
143                                 FileStream f=new FileStream(fileNames[i],FileMode.OpenOrCreate);
144                                 StreamWriter s=new StreamWriter(f);
145                                 s.Write(source);
146                                 s.Close();
147                                 f.Close();
148                                 i++;
149                         }
150                         return CompileAssemblyFromFileBatch (options, fileNames, true);
151                 }
152                 private static string BuildArgs(
153                         CompilerParameters options,string[] fileNames)
154                 {
155                         StringBuilder args=new StringBuilder();
156                         if (options.GenerateExecutable)
157                                 args.AppendFormat("/target:exe ");
158                         else
159                                 args.AppendFormat("/target:library ");
160                         if (options.IncludeDebugInformation)
161                                 args.AppendFormat("/debug ");
162                         if (options.TreatWarningsAsErrors)
163                                 args.AppendFormat("/warnaserror ");
164
165                         if (options.WarningLevel != -1)
166                                 args.AppendFormat ("/warn:{0} ", options.WarningLevel);
167
168                         if (options.OutputAssembly==null)
169                                 options.OutputAssembly = GetTempFileNameWithExtension ("dll");
170                         args.AppendFormat("/out:\"{0}\" ",options.OutputAssembly);
171                         if (null != options.ReferencedAssemblies)
172                         {
173                                 foreach (string import in options.ReferencedAssemblies)
174                                         args.AppendFormat("/r:\"{0}\" ",import);
175                         }
176                         foreach (string source in fileNames)
177                                 args.AppendFormat("\"{0}\" ",source);
178                         return args.ToString();
179                 }
180                 private static CompilerError CreateErrorFromString(string error_string)
181                 {
182                         // When IncludeDebugInformation is true, prevents the debug symbols stats from braeking this.
183                         if (error_string.StartsWith ("WROTE SYMFILE") || error_string.StartsWith ("OffsetTable"))
184                                 return null;
185
186                         CompilerError error=new CompilerError();
187                         Regex reg = new Regex (@"^(\s*(?<file>.*)\((?<line>\d*)(,(?<column>\d*))?\)\s+)*(?<level>\w+)\s*(?<number>.*):\s(?<message>.*)",
188                                 RegexOptions.Compiled | RegexOptions.ExplicitCapture);
189                         Match match=reg.Match(error_string);
190                         if (!match.Success) return null;
191                         if (String.Empty != match.Result("${file}"))
192                                 error.FileName=match.Result("${file}");
193                         if (String.Empty != match.Result("${line}"))
194                                 error.Line=Int32.Parse(match.Result("${line}"));
195                         if (String.Empty != match.Result("${column}"))
196                                 error.Column=Int32.Parse(match.Result("${column}"));
197                         if (match.Result("${level}")=="warning")
198                                 error.IsWarning=true;
199                         error.ErrorNumber=match.Result("${number}");
200                         error.ErrorText=match.Result("${message}");
201                         return error;
202                 }
203
204                 static string GetTempFileNameWithExtension (string extension)
205                 {
206                         Exception exc;
207                         string extFile;
208
209                         do {
210                                 string tmpFile = Path.GetTempFileName ();
211                                 FileInfo fileInfo = new FileInfo (tmpFile);
212                                 extFile = Path.ChangeExtension (tmpFile, extension);
213                                 try {
214                                         fileInfo.MoveTo (extFile);
215                                         exc = null;
216                                 } catch (Exception e) {
217                                         exc = e;
218                                 }
219                         } while (exc != null);
220
221                         return extFile;
222                 }
223         }
224 }