2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System / Microsoft.VisualBasic / VBCodeCompiler.cs
1 //\r
2 // Microsoft VisualBasic VBCodeCompiler Class implementation\r
3 //\r
4 // Authors:\r
5 //      Jochen Wezel (jwezel@compumaster.de)\r
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)\r
7 //\r
8 // (c) 2003 Jochen Wezel (http://www.compumaster.de)\r
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)\r
10 //\r
11 // Modifications:\r
12 // 2003-11-28 JW: create reference to Microsoft.VisualBasic if not explicitly done\r
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 \r
35 namespace Microsoft.VisualBasic\r
36 {\r
37         using System;\r
38         using System.CodeDom;\r
39         using System.CodeDom.Compiler;\r
40         using System.IO;\r
41         using System.Text;\r
42         using System.Reflection;\r
43         using System.Collections;\r
44         using System.Collections.Specialized;\r
45         using System.Diagnostics;\r
46         using System.Text.RegularExpressions;\r
47 \r
48         internal class VBCodeCompiler: VBCodeGenerator, ICodeCompiler\r
49         {\r
50                 static string windowsMonoPath;\r
51                 static string windowsMbasPath;\r
52                 static VBCodeCompiler ()\r
53                 {\r
54                         if (Path.DirectorySeparatorChar == '\\') {\r
55                                 // FIXME: right now we use "fixed" version 1.0\r
56                                 // mcs at any time.\r
57                                 PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static|BindingFlags.NonPublic);\r
58                                 MethodInfo get_gac = gac.GetGetMethod (true);\r
59                                 string p = Path.GetDirectoryName (\r
60                                         (string) get_gac.Invoke (null, null));\r
61                                 windowsMonoPath = Path.Combine (\r
62                                         Path.GetDirectoryName (\r
63                                                 Path.GetDirectoryName (p)),\r
64                                         "bin\\mono.bat");\r
65                                 if (!File.Exists (windowsMonoPath))\r
66                                         windowsMonoPath = Path.Combine (\r
67                                                 Path.GetDirectoryName (\r
68                                                         Path.GetDirectoryName (p)),\r
69                                                 "bin\\mono.exe");\r
70                                 windowsMbasPath =\r
71                                         Path.Combine (p, "1.0\\mbas.exe");\r
72                         }\r
73                 }\r
74 \r
75                 //\r
76                 // Constructors\r
77                 //\r
78                 public VBCodeCompiler()\r
79                 {\r
80                 }\r
81 \r
82                 //\r
83                 // Methods\r
84                 //\r
85                 [MonoTODO]\r
86                 public CompilerResults CompileAssemblyFromDom (CompilerParameters options,CodeCompileUnit e)\r
87                 {\r
88                         return CompileAssemblyFromDomBatch (options, new CodeCompileUnit []{e});\r
89                 }\r
90 \r
91                 public CompilerResults CompileAssemblyFromDomBatch (CompilerParameters options,\r
92                                                                     CodeCompileUnit [] ea)\r
93                 {\r
94                         string [] fileNames = new string [ea.Length];\r
95                         int i = 0;\r
96                         if (options == null)\r
97                         options = new CompilerParameters ();\r
98 \r
99                         StringCollection assemblies = options.ReferencedAssemblies;\r
100 \r
101                         foreach (CodeCompileUnit e in ea) {\r
102                                 fileNames [i] = GetTempFileNameWithExtension (options.TempFiles, "vb");\r
103                                 FileStream f = new FileStream (fileNames [i], FileMode.OpenOrCreate);\r
104                                 StreamWriter s = new StreamWriter (f);\r
105                                 if (e.ReferencedAssemblies != null) {\r
106                                         foreach (string str in e.ReferencedAssemblies) {\r
107                                                 if (!assemblies.Contains (str))\r
108                                                         assemblies.Add (str);\r
109                                         }\r
110                                 }\r
111 \r
112                                 ((ICodeGenerator)this).GenerateCodeFromCompileUnit (e, s, new CodeGeneratorOptions());\r
113                                 s.Close();\r
114                                 f.Close();\r
115                                 i++;\r
116                         }\r
117                         return CompileAssemblyFromFileBatch (options, fileNames);\r
118                 }\r
119 \r
120                 public CompilerResults CompileAssemblyFromFile (CompilerParameters options, string fileName)\r
121                 {\r
122                         return CompileAssemblyFromFileBatch (options, new string []{fileName});\r
123                 }\r
124 \r
125                 public CompilerResults CompileAssemblyFromFileBatch (CompilerParameters options,\r
126                                                                      string [] fileNames)\r
127                 {\r
128                         if (null == options)\r
129                                 throw new ArgumentNullException ("options");\r
130 \r
131                         if (null == fileNames)\r
132                                 throw new ArgumentNullException ("fileNames");\r
133 \r
134                         CompilerResults results = new CompilerResults (options.TempFiles);\r
135                         Process mbas = new Process ();\r
136 \r
137                         string mbas_output;\r
138                         string [] mbas_output_lines;\r
139                         // FIXME: these lines had better be platform independent.\r
140                         if (Path.DirectorySeparatorChar == '\\') {\r
141                                 mbas.StartInfo.FileName = windowsMonoPath;\r
142                                 mbas.StartInfo.Arguments = windowsMbasPath + ' ' + BuildArgs (options, fileNames);\r
143                         }\r
144                         else {\r
145                                 mbas.StartInfo.FileName = "mbas";\r
146                                 mbas.StartInfo.Arguments = BuildArgs (options,fileNames);\r
147                         }\r
148                         mbas.StartInfo.CreateNoWindow = true;\r
149                         mbas.StartInfo.UseShellExecute = false;\r
150                         mbas.StartInfo.RedirectStandardOutput = true;\r
151                         try {\r
152                                 mbas.Start();\r
153                                 mbas_output = mbas.StandardOutput.ReadToEnd ();\r
154                                 mbas.WaitForExit();\r
155                         } finally {\r
156                                 results.NativeCompilerReturnValue = mbas.ExitCode;\r
157                                 mbas.Close ();\r
158                         }\r
159 \r
160                         mbas_output_lines = mbas_output.Split(Environment.NewLine.ToCharArray());\r
161                         bool loadIt=true;\r
162                         foreach (string error_line in mbas_output_lines) {\r
163                                 CompilerError error = CreateErrorFromString (error_line);\r
164                                 if (null != error) {\r
165                                         results.Errors.Add (error);\r
166                                         if (!error.IsWarning)\r
167                                                 loadIt = false;\r
168                                 }\r
169                         }\r
170 \r
171                         if (loadIt)\r
172                                 results.CompiledAssembly=Assembly.LoadFrom(options.OutputAssembly);\r
173                         else\r
174                                 results.CompiledAssembly=null;\r
175 \r
176                         return results;\r
177                 }\r
178 \r
179                 public CompilerResults CompileAssemblyFromSource (CompilerParameters options,\r
180                                                                   string source)\r
181                 {\r
182                         return CompileAssemblyFromSourceBatch (options, new string [] {source});\r
183                 }\r
184 \r
185                 public CompilerResults CompileAssemblyFromSourceBatch (CompilerParameters options,\r
186                                                                         string [] sources)\r
187                 {\r
188                         string [] fileNames = new string [sources.Length];\r
189                         int i = 0;\r
190                         foreach (string source in sources) {\r
191                                 fileNames [i] = GetTempFileNameWithExtension (options.TempFiles, "vb");\r
192                                 FileStream f = new FileStream (fileNames [i], FileMode.OpenOrCreate);\r
193                                 StreamWriter s = new StreamWriter (f);\r
194                                 s.Write (source);\r
195                                 s.Close ();\r
196                                 f.Close ();\r
197                                 i++;\r
198                         }\r
199                         return CompileAssemblyFromFileBatch(options,fileNames);\r
200                 }\r
201 \r
202                 static string BuildArgs (CompilerParameters options, string [] fileNames)\r
203                 {\r
204                         StringBuilder args = new StringBuilder ();
205                         args.AppendFormat ("/quiet ");\r
206                         if (options.GenerateExecutable)\r
207                                 args.AppendFormat("/target:exe ");\r
208                         else\r
209                                 args.AppendFormat("/target:library ");\r
210 \r
211                         /* Disabled. It causes problems now. -- Gonzalo\r
212                         if (options.IncludeDebugInformation)\r
213                                 args.AppendFormat("/debug ");\r
214                         */\r
215 \r
216                         if (options.TreatWarningsAsErrors)\r
217                                 args.AppendFormat ("/warnaserror ");\r
218 \r
219                         if (options.WarningLevel != -1)\r
220                                 args.AppendFormat ("/wlevel:{0} ", options.WarningLevel);\r
221 \r
222                         if (options.OutputAssembly == null) {\r
223                                 string ext = (options.GenerateExecutable ? "exe" : "dll");\r
224                                 options.OutputAssembly = GetTempFileNameWithExtension (options.TempFiles, ext);\r
225                         }\r
226 \r
227                         args.AppendFormat ("/out:\"{0}\" ", options.OutputAssembly);\r
228 \r
229                         bool Reference2MSVBFound;\r
230                         Reference2MSVBFound = false;\r
231                         if (null != options.ReferencedAssemblies) \r
232                         {\r
233                                 foreach (string import in options.ReferencedAssemblies)\r
234                                 {\r
235                                         if (string.Compare (import, "Microsoft.VisualBasic", true, System.Globalization.CultureInfo.InvariantCulture) == 0)\r
236                                                 Reference2MSVBFound = true;\r
237                                         args.AppendFormat ("/r:\"{0}\" ", import);\r
238                                 }\r
239                         }\r
240                         // add standard import to Microsoft.VisualBasic if missing\r
241                         if (Reference2MSVBFound == false)\r
242                                 args.AppendFormat ("/r:\"{0}\" ", "Microsoft.VisualBasic");\r
243 \r
244                         args.AppendFormat(" -- "); // makes mbas not try to process filenames as options\r
245 \r
246                         foreach (string source in fileNames)\r
247                                 args.AppendFormat("\"{0}\" ",source);\r
248 \r
249                         return args.ToString();\r
250                 }\r
251 \r
252                 static CompilerError CreateErrorFromString (string error_string)\r
253                 {\r
254                         // When IncludeDebugInformation is true, prevents the debug symbols stats from braeking this.\r
255                         if (error_string.StartsWith ("WROTE SYMFILE") || error_string.StartsWith ("OffsetTable"))\r
256                                 return null;\r
257 \r
258                         CompilerError error = new CompilerError ();\r
259                         Regex reg = new Regex (@"^(\s*(?<file>.*)\((?<line>\d*)(,(?<column>\d*))?\)\s+)*" +\r
260                                                 @"(?<level>error|warning)\s*(?<number>.*):\s(?<message>.*)",\r
261                                                 RegexOptions.Compiled | RegexOptions.ExplicitCapture);\r
262 \r
263                         Match match = reg.Match (error_string);\r
264                         if (!match.Success)\r
265                                 return null;\r
266 \r
267                         if (String.Empty != match.Result("${file}"))\r
268                                 error.FileName = match.Result ("${file}");\r
269 \r
270                         if (String.Empty != match.Result ("${line}"))\r
271                                 error.Line = Int32.Parse (match.Result ("${line}"));\r
272 \r
273                         if (String.Empty != match.Result( "${column}"))\r
274                                 error.Column = Int32.Parse (match.Result ("${column}"));\r
275 \r
276                         if (match.Result ("${level}") =="warning")\r
277                                 error.IsWarning = true;\r
278 \r
279                         error.ErrorNumber = match.Result ("${number}");\r
280                         error.ErrorText = match.Result ("${message}");\r
281                         return error;\r
282                 }\r
283 \r
284                 static string GetTempFileNameWithExtension (TempFileCollection temp_files, string extension)\r
285                 {\r
286                         return temp_files.AddExtension (extension);\r
287                 }\r
288         }\r
289 }\r
290 \r