You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
9.2 KiB

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