VBCodeCompiler.cs 11 KB

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