using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel.Design; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; using EnvDTE; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using VSConstants = Microsoft.VisualStudio.VSConstants; namespace Lol.VisualStudio.Plugin { internal class MenuGenerateCompilers : OleMenuCommand { public MenuGenerateCompilers(ServiceProvider sp, CommandID id) : base(new EventHandler(ClickCallback), id, VsLol.ResourceManager.GetString("GenerateCompilersText")) { this.sp = sp; this.projects = new List(); this.BeforeQueryStatus += new EventHandler(OnBeforeQueryStatus); } private void OnBeforeQueryStatus(object sender, EventArgs e) { projects.Clear(); var cmd = sender as OleMenuCommand; if (cmd == null) return; IVsMonitorSelection monitorSelection = sp.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection; if (monitorSelection == null) return; IntPtr hier = IntPtr.Zero; UInt32 itemid; IVsMultiItemSelect multiitem = null; IntPtr container = IntPtr.Zero; try { monitorSelection.GetCurrentSelection(out hier, out itemid, out multiitem, out container); /* Bail out if nothing is selected */ if (itemid != VSConstants.VSITEMID_SELECTION && itemid != VSConstants.VSITEMID_NIL) { if (hier == IntPtr.Zero) { /* FIXME: parse the whole solution */ } else { object project = null; IVsHierarchy hierarchy = (IVsHierarchy)Marshal.GetObjectForIUnknown(hier); hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out project); projects.Add(project as Project); } } } finally { if (hier != IntPtr.Zero) Marshal.Release(hier); if (container != IntPtr.Zero) Marshal.Release(container); } // If there are .l or .y files in this project, display the context menu Visible = false; foreach (Project project in projects) foreach (ProjectItem item in ParseProjectItems(project)) { if (item.Name.EndsWith("-scanner.l") || item.Name.EndsWith("-parser.y")) Visible = true; } } private static void ClickCallback(object sender, EventArgs args) { MenuGenerateCompilers cmd = sender as MenuGenerateCompilers; if (cmd == null) return; cmd.ClearOutputPane(); cmd.WriteToOutputPane("------ Build started: Generating Compilers ------\n"); int scanner_count = 0, parser_count = 0, error_count = 0; foreach (Project project in cmd.projects) { cmd.WriteToOutputPane("Project " + project.Name + "\n"); string project_path = Path.GetDirectoryName(project.FullName); /* FIXME: find this using the solution globals! */ string contrib_path = project_path; for (int i = 0; i < 10; ++i) { contrib_path += "\\.."; if (Directory.Exists(contrib_path + "\\contrib")) break; } /* FIXME: do not hardcode shit! */ string flex_path = contrib_path + "\\contrib\\flex-2.5.35"; string bison_path = contrib_path + "\\contrib\\bison-2.4.2"; // Run flex on all the .l files foreach (ProjectItem item in ParseProjectItems(project)) { string filename = item.get_FileNames(0); if (filename.StartsWith(project_path + "\\")) { filename = filename.Substring(project_path.Length + 1); filename = filename.Replace("\\", "/"); } if (item.Name.EndsWith("-scanner.l")) { cmd.WriteToOutputPane("flex.exe " + filename + "\n"); string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-scanner.l"))); if (!cmd.Run(project_path, flex_path + "\\bin\\flex.exe", "-v -o " + "generated/" + basename + "-scanner.cpp " + filename, "")) ++error_count; ++scanner_count; } if (item.Name.EndsWith("-parser.y")) { cmd.WriteToOutputPane("bison.exe " + filename + "\n"); string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-parser.y"))); if (!cmd.Run(project_path, bison_path + "\\bin\\bison.exe", "-v -o " + "generated/" + basename + "-parser.cpp " + "--defines=generated/" + basename + "-parser.h " + "-d " + "-b " + "generated/" + basename + " " + filename, "BISON_PKGDATADIR=" + bison_path + "\\share\\bison")) ++error_count; ++parser_count; } } } cmd.WriteToOutputPane(string.Format("========== Done: {0} scanner(s), {1} parser(s), {2} error(s) ==========\n", scanner_count, parser_count, error_count)); } bool Run(string directory, string executable, string arguments, string env) { System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = executable; p.StartInfo.Arguments = arguments; foreach (string s in env.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries)) { int i = s.IndexOf("="); if (i > 0 && i < s.Length - 1) p.StartInfo.EnvironmentVariables[s.Substring(0, i - 1)] = s.Substring(i + 1); } p.StartInfo.WorkingDirectory = directory; p.StartInfo.CreateNoWindow = true; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardInput = true; p.StartInfo.UseShellExecute = false; try { p.Start(); string output = p.StandardError.ReadToEnd() + p.StandardOutput.ReadToEnd(); p.WaitForExit(); WriteToOutputPane(output); if (p.ExitCode != 0) { WriteToOutputPane("Error: " + executable + " exited with code " + p.ExitCode + "\n"); return false; } } catch (Exception e) { WriteToOutputPane("Error: failed to launch " + executable + "\n"); return false; } return true; } private void ClearOutputPane() { IVsOutputWindow win = sp.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow; if (null == win) { Trace.WriteLine("Failed to get a reference to IVsOutputWindow"); pane = null; } Guid guid = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid; if (Microsoft.VisualStudio.ErrorHandler.Failed(win.GetPane(ref guid, out pane))) { Trace.WriteLine("Failed to get a reference to the Output window Build pane"); pane = null; } pane.Activate(); pane.Clear(); } private void WriteToOutputPane(string s) { if (pane != null) pane.OutputString(s); } private static IEnumerable ParseProjectItems(object o) { ProjectItems subitems; if (o is Project) { subitems = (o as Project).ProjectItems; } else { yield return (o as ProjectItem); subitems = (o as ProjectItem).ProjectItems; } foreach (ProjectItem item in subitems) foreach (ProjectItem i in ParseProjectItems(item)) yield return i; } private ServiceProvider sp; private IVsOutputWindowPane pane; private List projects; } }