ScriptCompiler.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Text.RegularExpressions;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using BansheeEngine;
  11. namespace BansheeEditor
  12. {
  13. public enum ScriptAssemblyType
  14. {
  15. Game, Editor
  16. }
  17. public struct CompileData
  18. {
  19. public string[] files;
  20. public string defines;
  21. }
  22. public class CompilerInstance
  23. {
  24. private Process process;
  25. private Thread readErrorsThread;
  26. private List<CompilerMessage> errors = new List<CompilerMessage>();
  27. private List<CompilerMessage> warnings = new List<CompilerMessage>();
  28. private Regex compileErrorRegex = new Regex(@"\s*(?<file>.*)\(\s*(?<line>\d+)\s*,\s*(?<column>\d+)\s*\)\s*:\s*(?<type>warning|error)\s+(.*):\s*(?<message>.*)");
  29. private Regex compilerErrorRegex = new Regex(@"\s*error[^:]*:\s*(?<message>.*)");
  30. internal CompilerInstance(string[] files, string defines, string[] assemblyFolders, string[] assemblies,
  31. bool debugBuild, string outputFile)
  32. {
  33. ProcessStartInfo procStartInfo = new ProcessStartInfo();
  34. StringBuilder argumentsBuilder = new StringBuilder();
  35. if (!string.IsNullOrEmpty(defines))
  36. argumentsBuilder.Append(" -d:" + defines);
  37. if (assemblyFolders != null && assemblyFolders.Length > 0)
  38. {
  39. argumentsBuilder.Append(" -lib:");
  40. for (int i = 0; i < assemblyFolders.Length - 1; i++)
  41. argumentsBuilder.Append(assemblyFolders[i] + ",");
  42. argumentsBuilder.Append(assemblyFolders[assemblyFolders.Length - 1]);
  43. }
  44. if (assemblies != null && assemblies.Length > 0)
  45. {
  46. argumentsBuilder.Append(" -r:");
  47. for (int i = 0; i < assemblies.Length - 1; i++)
  48. argumentsBuilder.Append(assemblies[i] + ",");
  49. argumentsBuilder.Append(assemblies[assemblies.Length - 1]);
  50. }
  51. if (debugBuild)
  52. argumentsBuilder.Append(" -debug+ -o-");
  53. else
  54. argumentsBuilder.Append(" -debug- -o+");
  55. argumentsBuilder.Append(" -target:library -out:" + "\"" + outputFile + "\"");
  56. for (int i = 0; i < files.Length; i++)
  57. argumentsBuilder.Append(" \"" + files[i] + "\"");
  58. if (File.Exists(outputFile))
  59. File.Delete(outputFile);
  60. string outputDir = Path.GetDirectoryName(outputFile);
  61. if (!Directory.Exists(outputDir))
  62. Directory.CreateDirectory(outputDir);
  63. procStartInfo.Arguments = argumentsBuilder.ToString();
  64. procStartInfo.CreateNoWindow = true;
  65. procStartInfo.FileName = EditorApplication.CompilerPath;
  66. procStartInfo.RedirectStandardError = true;
  67. procStartInfo.RedirectStandardOutput = false;
  68. procStartInfo.UseShellExecute = false;
  69. procStartInfo.WorkingDirectory = EditorApplication.ProjectPath;
  70. process = new Process();
  71. process.StartInfo = procStartInfo;
  72. process.Start();
  73. readErrorsThread = new Thread(ReadErrorStream);
  74. readErrorsThread.Start();
  75. }
  76. private void ReadErrorStream()
  77. {
  78. while (true)
  79. {
  80. if (process == null || process.HasExited)
  81. return;
  82. string line = process.StandardError.ReadLine();
  83. if (string.IsNullOrEmpty(line))
  84. continue;
  85. CompilerMessage message;
  86. if (TryParseCompilerMessage(line, out message))
  87. {
  88. if (message.type == CompilerMessageType.Warning)
  89. {
  90. lock (warnings)
  91. warnings.Add(message);
  92. }
  93. else if (message.type == CompilerMessageType.Error)
  94. {
  95. lock (errors)
  96. errors.Add(message);
  97. }
  98. }
  99. }
  100. }
  101. private bool TryParseCompilerMessage(string messageText, out CompilerMessage message)
  102. {
  103. message = new CompilerMessage();
  104. Match matchCompile = compileErrorRegex.Match(messageText);
  105. if (matchCompile.Success)
  106. {
  107. message.file = matchCompile.Groups["file"].Value;
  108. message.line = Int32.Parse(matchCompile.Groups["line"].Value);
  109. message.column = Int32.Parse(matchCompile.Groups["column"].Value);
  110. message.type = matchCompile.Groups["type"].Value == "error"
  111. ? CompilerMessageType.Error
  112. : CompilerMessageType.Warning;
  113. message.message = matchCompile.Groups["message"].Value;
  114. return true;
  115. }
  116. Match matchCompiler = compilerErrorRegex.Match(messageText);
  117. if (matchCompiler.Success)
  118. {
  119. message.file = "";
  120. message.line = 0;
  121. message.column = 0;
  122. message.type = CompilerMessageType.Error;
  123. message.message = matchCompiler.Groups["message"].Value;
  124. return true;
  125. }
  126. return false;
  127. }
  128. public bool IsDone
  129. {
  130. get { return process.HasExited && readErrorsThread.ThreadState == System.Threading.ThreadState.Stopped; }
  131. }
  132. public bool HasErrors
  133. {
  134. get { return IsDone && process.ExitCode != 0; }
  135. }
  136. public CompilerMessage[] WarningMessages
  137. {
  138. get
  139. {
  140. lock (warnings)
  141. {
  142. return warnings.ToArray();
  143. }
  144. }
  145. }
  146. public CompilerMessage[] ErrorMessages
  147. {
  148. get
  149. {
  150. lock (errors)
  151. {
  152. return errors.ToArray();
  153. }
  154. }
  155. }
  156. public void Dispose()
  157. {
  158. if (process == null)
  159. return;
  160. if (!process.HasExited)
  161. {
  162. process.Kill();
  163. process.WaitForExit();
  164. }
  165. process.Dispose();
  166. }
  167. }
  168. public static class ScriptCompiler
  169. {
  170. public static CompilerInstance CompileAsync(ScriptAssemblyType type, PlatformType platform, bool debug, string outputDir)
  171. {
  172. LibraryEntry[] scriptEntries = ProjectLibrary.Search("*", new ResourceType[] { ResourceType.ScriptCode });
  173. List<string> scriptFiles = new List<string>();
  174. for (int i = 0; i < scriptEntries.Length; i++)
  175. {
  176. if(scriptEntries[i].Type != LibraryEntryType.File)
  177. continue;
  178. FileEntry fileEntry = (FileEntry)scriptEntries[i];
  179. ScriptCodeImportOptions io = (ScriptCodeImportOptions) fileEntry.Options;
  180. if (io.EditorScript && type == ScriptAssemblyType.Editor ||
  181. !io.EditorScript && type == ScriptAssemblyType.Game)
  182. {
  183. scriptFiles.Add(scriptEntries[i].Path);
  184. }
  185. }
  186. string[] assemblyFolders;
  187. string[] assemblies;
  188. string outputFile;
  189. string[] frameworkAssemblies = BuildManager.GetFrameworkAssemblies(platform);
  190. if (type == ScriptAssemblyType.Game)
  191. {
  192. assemblyFolders = new string[]
  193. {
  194. EditorApplication.BuiltinAssemblyPath,
  195. EditorApplication.FrameworkAssemblyPath
  196. };
  197. assemblies = new string[frameworkAssemblies.Length + 1];
  198. assemblies[assemblies.Length - 1] = EditorApplication.EngineAssembly;
  199. outputFile = Path.Combine(outputDir, EditorApplication.ScriptGameAssembly);
  200. }
  201. else
  202. {
  203. assemblyFolders = new string[]
  204. {
  205. EditorApplication.BuiltinAssemblyPath,
  206. EditorApplication.FrameworkAssemblyPath,
  207. EditorApplication.ScriptAssemblyPath
  208. };
  209. assemblies = new string[frameworkAssemblies.Length + 3];
  210. assemblies[assemblies.Length - 1] = EditorApplication.EngineAssembly;
  211. assemblies[assemblies.Length - 2] = EditorApplication.EditorAssembly;
  212. assemblies[assemblies.Length - 3] = EditorApplication.ScriptGameAssembly;
  213. outputFile = Path.Combine(outputDir, EditorApplication.ScriptEditorAssembly);
  214. }
  215. Array.Copy(frameworkAssemblies, assemblies, frameworkAssemblies.Length);
  216. string defines = BuildManager.GetDefines(platform);
  217. return new CompilerInstance(scriptFiles.ToArray(), defines, assemblyFolders, assemblies, debug, outputFile);
  218. }
  219. }
  220. public enum CompilerMessageType
  221. {
  222. Warning, Error
  223. }
  224. public struct CompilerMessage
  225. {
  226. public CompilerMessageType type;
  227. public string message;
  228. public string file;
  229. public int line;
  230. public int column;
  231. }
  232. }