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

Last change on this file since 2115 was 2115, checked in by sam, 7 years ago

vslol: create an etc/ directory beside bison's and flex's to work
around an MSYS bug in the main DLL, and copy the m4.exe binary we
ship with bison into the flex directory, too.

File size: 10.2 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, error_count = 0;
98
99            foreach (Project project in cmd.projects)
100            {
101                cmd.WriteToOutputPane("Project " + project.Name + "\n");
102
103                string project_path = Path.GetDirectoryName(project.FullName);
104
105                /* FIXME: find this using the solution globals! */
106                string external_path = project_path;
107                for (int i = 0; i < 10; ++i)
108                {
109                    external_path += "\\..";
110                    if (Directory.Exists(external_path + "\\external"))
111                        break;
112                }
113
114                /* FIXME: do not hardcode shit! */
115                string flex_path = external_path + "\\external\\flex-2.5.35";
116                string bison_path = external_path + "\\external\\bison-2.4.2";
117
118                /* Workaround for an MSYS bug. If these directories don't
119                 * exist, fork() will fail. Yeah, wtf. */
120                try
121                {
122                    Directory.CreateDirectory(flex_path + "\\etc");
123                    Directory.CreateDirectory(bison_path + "\\etc");
124                }
125                catch (Exception e) { }
126
127                // Run flex on all the .l files
128                foreach (ProjectItem item in ParseProjectItems(project))
129                {
130                    string filename = item.get_FileNames(0);
131
132                    if (filename.StartsWith(project_path + "\\"))
133                    {
134                        filename = filename.Substring(project_path.Length + 1);
135                        filename = filename.Replace("\\", "/");
136                    }
137
138                    if (item.Name.EndsWith("-scanner.l"))
139                    {
140                        cmd.WriteToOutputPane("flex.exe " + filename + "\n");
141
142                        string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-scanner.l")));
143                        if (!cmd.Run(project_path,
144                                     flex_path + "\\bin\\flex.exe",
145                                     "-v -o "
146                                      + "generated/" + basename + "-scanner.cpp "
147                                      + filename,
148                                     ""))
149                            ++error_count;
150
151                        ++scanner_count;
152                    }
153
154                    if (item.Name.EndsWith("-parser.y"))
155                    {
156                        cmd.WriteToOutputPane("bison.exe " + filename + "\n");
157
158                        string basename = Path.GetFileName(filename.Substring(0, filename.LastIndexOf("-parser.y")));
159                        if (!cmd.Run(project_path,
160                                     bison_path + "\\bin\\bison.exe",
161                                     "-v -o "
162                                      + "generated/" + basename + "-parser.cpp "
163                                      + "--defines=generated/" + basename + "-parser.h "
164                                      + "-d "
165                                      + "-b "
166                                      + "generated/" + basename + " "
167                                      + filename,
168                                     "BISON_PKGDATADIR=" + bison_path + "\\share\\bison"))
169                            ++error_count;
170
171                        ++parser_count;
172                    }
173                }
174            }
175
176            cmd.WriteToOutputPane(string.Format("========== Done: {0} scanner(s), {1} parser(s), {2} error(s) ==========\n",
177                                  scanner_count, parser_count, error_count));
178        }
179
180        bool Run(string directory, string executable, string arguments, string env)
181        {
182            System.Diagnostics.Process p = new System.Diagnostics.Process();
183            p.StartInfo.FileName = executable;
184            p.StartInfo.Arguments = arguments;
185            foreach (string s in env.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries))
186            {
187                int i = s.IndexOf("=");
188                if (i > 0 && i < s.Length - 1)
189                    p.StartInfo.EnvironmentVariables[s.Substring(0, i - 1)] = s.Substring(i + 1);
190            }
191            p.StartInfo.WorkingDirectory = directory;
192            p.StartInfo.CreateNoWindow = true;
193            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
194            p.StartInfo.RedirectStandardError = true;
195            p.StartInfo.RedirectStandardOutput = true;
196            p.StartInfo.RedirectStandardInput = true;
197            p.StartInfo.UseShellExecute = false;
198
199            try
200            {
201                p.Start();
202                string output = p.StandardError.ReadToEnd()
203                              + p.StandardOutput.ReadToEnd();
204                p.WaitForExit();
205                WriteToOutputPane(output);
206                if (p.ExitCode != 0)
207                {
208                    WriteToOutputPane("Error: " + executable + " exited with code " + p.ExitCode + "\n");
209                    if (arguments != "")
210                        WriteToOutputPane("Error: args: " + arguments + "\n");
211                    if (env != "")
212                        WriteToOutputPane("Error: env: " + env + "\n");
213                    return false;
214                }
215            }
216            catch (Exception e)
217            {
218                WriteToOutputPane("Error: failed to launch " + executable + "\n");
219                return false;
220            }
221
222            return true;
223        }
224
225        private void ClearOutputPane()
226        {
227            IVsOutputWindow win = sp.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
228            if (null == win)
229            {
230                Trace.WriteLine("Failed to get a reference to IVsOutputWindow");
231                pane = null;
232            }
233
234            Guid guid = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid;
235            if (Microsoft.VisualStudio.ErrorHandler.Failed(win.GetPane(ref guid, out pane)))
236            {
237                Trace.WriteLine("Failed to get a reference to the Output window Build pane");
238                pane = null;
239            }
240
241            pane.Activate();
242            pane.Clear();
243        }
244
245        private void WriteToOutputPane(string s)
246        {
247            if (pane != null)
248                pane.OutputString(s);
249        }
250
251        private static IEnumerable<ProjectItem> ParseProjectItems(object o)
252        {
253            ProjectItems subitems;
254            if (o is Project)
255            {
256                subitems = (o as Project).ProjectItems;
257            }
258            else
259            {
260                yield return (o as ProjectItem);
261                subitems = (o as ProjectItem).ProjectItems;
262            }
263
264            foreach (ProjectItem item in subitems)
265                foreach (ProjectItem i in ParseProjectItems(item))
266                    yield return i;
267        }
268
269        private ServiceProvider sp;
270        private IVsOutputWindowPane pane;
271
272        private List<Project> projects;
273    }
274}
275
Note: See TracBrowser for help on using the repository browser.