source: trunk/tools/vslol/MenuGenerateCompilers.cs @ 1932

Last change on this file since 1932 was 1932, checked in by sam, 8 years ago

vslol: create a Visual Studio plugin that will let us rebuild parsers & lexers.

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