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

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

vslol: add licensing information to important files.

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