260 rindas
8.9 KiB

  1. //
  2. // Lol Engine - VsLol add-in for Visual Studio
  3. //
  4. // Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. using System;
  11. using System.Collections;
  12. using System.Collections.Generic;
  13. using System.ComponentModel.Design;
  14. using System.Diagnostics;
  15. using System.Globalization;
  16. using System.IO;
  17. using System.Runtime.InteropServices;
  18. using System.Text;
  19. using EnvDTE;
  20. using Microsoft.VisualStudio.Shell;
  21. using Microsoft.VisualStudio.Shell.Interop;
  22. using VSConstants = Microsoft.VisualStudio.VSConstants;
  23. namespace lol
  24. {
  25. internal class MenuGenerateCompilers : OleMenuCommand
  26. {
  27. public MenuGenerateCompilers(ServiceProvider sp, CommandID id) :
  28. base(new EventHandler(ClickCallback), id, VsLol.ResourceManager.GetString("GenerateCompilersText"))
  29. {
  30. this.sp = sp;
  31. this.projects = new List<Project>();
  32. this.BeforeQueryStatus += new EventHandler(OnBeforeQueryStatus);
  33. }
  34. private void OnBeforeQueryStatus(object sender, EventArgs e)
  35. {
  36. projects.Clear();
  37. var cmd = sender as OleMenuCommand;
  38. if (cmd == null)
  39. return;
  40. IVsMonitorSelection monitorSelection = sp.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection;
  41. if (monitorSelection == null)
  42. return;
  43. IntPtr hier = IntPtr.Zero;
  44. UInt32 itemid;
  45. IVsMultiItemSelect multiitem = null;
  46. IntPtr container = IntPtr.Zero;
  47. try
  48. {
  49. monitorSelection.GetCurrentSelection(out hier, out itemid, out multiitem, out container);
  50. /* Bail out if nothing is selected */
  51. if (itemid != VSConstants.VSITEMID_SELECTION && itemid != VSConstants.VSITEMID_NIL)
  52. {
  53. if (hier == IntPtr.Zero)
  54. {
  55. /* FIXME: parse the whole solution */
  56. }
  57. else
  58. {
  59. object project = null;
  60. IVsHierarchy hierarchy = (IVsHierarchy)Marshal.GetObjectForIUnknown(hier);
  61. hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out project);
  62. projects.Add(project as Project);
  63. }
  64. }
  65. }
  66. finally
  67. {
  68. if (hier != IntPtr.Zero)
  69. Marshal.Release(hier);
  70. if (container != IntPtr.Zero)
  71. Marshal.Release(container);
  72. }
  73. // If there are .l or .y files in this project, display the context menu
  74. Visible = false;
  75. foreach (Project project in projects)
  76. foreach (ProjectItem item in ParseProjectItems(project))
  77. {
  78. if (item.Name.EndsWith("-scanner.l", StringComparison.CurrentCultureIgnoreCase)
  79. || item.Name.EndsWith("-parser.y", StringComparison.CurrentCultureIgnoreCase))
  80. Visible = true;
  81. }
  82. }
  83. private static void ClickCallback(object sender, EventArgs args)
  84. {
  85. MenuGenerateCompilers cmd = sender as MenuGenerateCompilers;
  86. if (cmd == null)
  87. return;
  88. Logger.Clear();
  89. Logger.Info("------ Build started: Generating Compilers ------\n");
  90. int scanner_count = 0, parser_count = 0, error_count = 0;
  91. foreach (Project project in cmd.projects)
  92. {
  93. Logger.Info("Project " + project.Name + "\n");
  94. string project_path = Path.GetDirectoryName(project.FullName);
  95. /* FIXME: find this using the solution globals! */
  96. string external_path = project_path;
  97. for (int i = 0; i < 10; ++i)
  98. {
  99. external_path += "\\..";
  100. if (Directory.Exists(external_path + "\\external"))
  101. break;
  102. }
  103. /* FIXME: do not hardcode shit! */
  104. string flex_path = external_path + "\\external\\flex-2.5.35";
  105. string bison_path = external_path + "\\external\\bison-2.4.2";
  106. /* Workaround for an MSYS bug. If these directories don't
  107. * exist, fork() will fail. Yeah, wtf. */
  108. try
  109. {
  110. Directory.CreateDirectory(flex_path + "\\etc");
  111. Directory.CreateDirectory(bison_path + "\\etc");
  112. }
  113. catch (Exception e) { }
  114. // Run flex on all the .l files
  115. foreach (ProjectItem item in ParseProjectItems(project))
  116. {
  117. string filename = item.get_FileNames(0);
  118. if (filename.StartsWith(project_path + "\\"))
  119. {
  120. filename = filename.Substring(project_path.Length + 1);
  121. filename = filename.Replace("\\", "/");
  122. }
  123. if (item.Name.EndsWith("-scanner.l"))
  124. {
  125. Logger.Info("flex.exe " + filename + "\n");
  126. string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-scanner.l")));
  127. if (!cmd.Run(project_path,
  128. flex_path + "\\bin\\flex.exe",
  129. "-v -o "
  130. + "generated/" + basename + "-scanner.cpp "
  131. + filename,
  132. ""))
  133. ++error_count;
  134. ++scanner_count;
  135. }
  136. if (item.Name.EndsWith("-parser.y"))
  137. {
  138. Logger.Info("bison.exe " + filename + "\n");
  139. string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-parser.y")));
  140. if (!cmd.Run(project_path,
  141. bison_path + "\\bin\\bison.exe",
  142. "-v -o "
  143. + "generated/" + basename + "-parser.cpp "
  144. + "--defines=generated/" + basename + "-parser.h "
  145. + "-d "
  146. + "-b "
  147. + "generated/" + basename + " "
  148. + filename,
  149. "BISON_PKGDATADIR=" + bison_path + "\\share\\bison"))
  150. ++error_count;
  151. ++parser_count;
  152. }
  153. }
  154. }
  155. Logger.Info(string.Format("========== Done: {0} scanner(s), {1} parser(s), {2} error(s) ==========\n",
  156. scanner_count, parser_count, error_count));
  157. }
  158. bool Run(string directory, string executable, string arguments, string env)
  159. {
  160. System.Diagnostics.Process p = new System.Diagnostics.Process();
  161. p.StartInfo.FileName = executable;
  162. p.StartInfo.Arguments = arguments;
  163. foreach (string s in env.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries))
  164. {
  165. int i = s.IndexOf("=");
  166. if (i > 0 && i < s.Length - 1)
  167. p.StartInfo.EnvironmentVariables[s.Substring(0, i - 1)] = s.Substring(i + 1);
  168. }
  169. p.StartInfo.WorkingDirectory = directory;
  170. p.StartInfo.CreateNoWindow = true;
  171. p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
  172. p.StartInfo.RedirectStandardError = true;
  173. p.StartInfo.RedirectStandardOutput = true;
  174. p.StartInfo.RedirectStandardInput = true;
  175. p.StartInfo.UseShellExecute = false;
  176. try
  177. {
  178. p.Start();
  179. string output = p.StandardError.ReadToEnd()
  180. + p.StandardOutput.ReadToEnd();
  181. p.WaitForExit();
  182. Logger.Info(output);
  183. if (p.ExitCode != 0)
  184. {
  185. Logger.Info("Error: " + executable + " exited with code " + p.ExitCode + "\n");
  186. if (!string.IsNullOrEmpty(arguments))
  187. Logger.Info("Error: args: " + arguments + "\n");
  188. if (!string.IsNullOrEmpty(env))
  189. Logger.Info("Error: env: " + env + "\n");
  190. return false;
  191. }
  192. }
  193. catch (Exception e)
  194. {
  195. Logger.Info("Error: failed to launch " + executable + "\n");
  196. return false;
  197. }
  198. return true;
  199. }
  200. private static IEnumerable<ProjectItem> ParseProjectItems(object o)
  201. {
  202. ProjectItems subitems;
  203. if (o is Project)
  204. {
  205. subitems = (o as Project).ProjectItems;
  206. }
  207. else
  208. {
  209. yield return (o as ProjectItem);
  210. subitems = (o as ProjectItem).ProjectItems;
  211. }
  212. foreach (ProjectItem item in subitems)
  213. foreach (ProjectItem i in ParseProjectItems(item))
  214. yield return i;
  215. }
  216. private ServiceProvider sp;
  217. private List<Project> projects;
  218. }
  219. } /* namespace lol */