]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/ifacecheck.cpp
add pure virtual function parsing; give more informative warnings; fix the initial...
[wxWidgets.git] / utils / ifacecheck / src / ifacecheck.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ifacecheck.cpp
3 // Purpose: Interface headers <=> real headers coherence checker
4 // Author: Francesco Montorsi
5 // Created: 2008/03/17
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Francesco Montorsi
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 // for all others, include the necessary headers
19 #ifndef WX_PRECOMP
20 #include "wx/app.h"
21 #endif
22
23 #include "wx/cmdline.h"
24 #include "wx/textfile.h"
25 #include "wx/stopwatch.h" // for wxGetLocalTime
26 #include "xmlparser.h"
27
28 // global verbosity flag
29 bool g_verbose = false;
30
31
32
33 // ----------------------------------------------------------------------------
34 // IfaceCheckApp
35 // ----------------------------------------------------------------------------
36
37 #define API_DUMP_FILE "dump.api.txt"
38 #define INTERFACE_DUMP_FILE "dump.interface.txt"
39
40 #define MODIFY_SWITCH "m"
41 #define DUMP_SWITCH "dump"
42 #define HELP_SWITCH "h"
43 #define VERBOSE_SWITCH "v"
44
45 static const wxCmdLineEntryDesc g_cmdLineDesc[] =
46 {
47 { wxCMD_LINE_SWITCH, MODIFY_SWITCH, "modify",
48 "modify the interface headers to match the real ones" },
49 { wxCMD_LINE_SWITCH, "", DUMP_SWITCH,
50 "dump both interface and API to plain text" },
51 { wxCMD_LINE_SWITCH, HELP_SWITCH, "help",
52 "show help message", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
53 { wxCMD_LINE_SWITCH, VERBOSE_SWITCH, "verbose",
54 "be verbose" },
55 { wxCMD_LINE_PARAM, NULL, NULL,
56 "gccXML", wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
57 { wxCMD_LINE_PARAM, NULL, NULL,
58 "doxygenXML", wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
59 wxCMD_LINE_DESC_END
60 };
61
62 class IfaceCheckApp : public wxAppConsole
63 {
64 public:
65 // don't use builtin cmd line parsing:
66 virtual bool OnInit() { m_modify=false; return true; }
67 virtual int OnRun();
68
69 bool Compare();
70 int CompareClasses(const wxClass* iface, const wxClassPtrArray& api);
71 void FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api);
72
73 void ShowProgress();
74 void PrintStatistics(long secs);
75
76 protected:
77 wxXmlGccInterface m_api; // "real" headers API
78 wxXmlDoxygenInterface m_interface; // doxygen-commented headers API
79
80 // was the MODIFY_SWITCH passed?
81 bool m_modify;
82 };
83
84 IMPLEMENT_APP_CONSOLE(IfaceCheckApp)
85
86 int IfaceCheckApp::OnRun()
87 {
88 long startTime = wxGetLocalTime(); // for timing purpose
89
90 // parse the command line...
91 wxCmdLineParser parser(g_cmdLineDesc, argc, argv);
92 bool ok = true;
93 switch (parser.Parse())
94 {
95 case -1:
96 // HELP_SWITCH was passed
97 return 0;
98
99 case 0:
100 if (parser.Found(VERBOSE_SWITCH))
101 g_verbose = true;
102
103 if (!m_api.Parse(parser.GetParam(0)) ||
104 !m_interface.Parse(parser.GetParam(1)))
105 return 1;
106
107 if (parser.Found(DUMP_SWITCH))
108 {
109 LogMessage("Dumping real API to '%s'...", API_DUMP_FILE);
110 m_api.Dump(API_DUMP_FILE);
111
112 LogMessage("Dumping interface API to '%s'...", INTERFACE_DUMP_FILE);
113 m_interface.Dump(INTERFACE_DUMP_FILE);
114 }
115 else
116 {
117 if (parser.Found(MODIFY_SWITCH))
118 m_modify = true;
119
120 ok = Compare();
121 }
122
123 PrintStatistics(wxGetLocalTime() - startTime);
124 return ok ? 0 : 1;
125 }
126
127 return 1;
128 }
129
130 void IfaceCheckApp::ShowProgress()
131 {
132 wxPrint(".");
133 //fflush(stdout);
134 }
135
136 bool IfaceCheckApp::Compare()
137 {
138 const wxClassArray& interface = m_interface.GetClasses();
139 const wxClass* c;
140 wxClassPtrArray api;
141 int mcount = 0, ccount = 0;
142
143 LogMessage("Comparing the interface API to the real API (%d classes to compare)...",
144 interface.GetCount());
145
146 for (unsigned int i=0; i<interface.GetCount(); i++)
147 {
148 wxString cname = interface[i].GetName();
149
150 api.Empty();
151
152 // search in the real headers for i-th interface class
153 // for both class cname and cnameBase as in wxWidgets world, most often
154 // class cname is platform-specific while the real public interface of
155 // that class is part of the cnameBase class.
156 c = m_api.FindClass(cname);
157 if (c) api.Add(c);
158 c = m_api.FindClass(cname + "Base");
159 if (c) api.Add(c);
160
161 if (api.GetCount()>0) {
162
163 // there is a class with exactly the same name!
164 mcount += CompareClasses(&interface[i], api);
165
166 } else {
167
168 // shorten the name of the header so the log file is more readable
169 wxString header = interface[i].GetHeader().AfterLast('/');
170
171 LogMessage("%s: couldn't find the real interface for the '%s' class",
172 header, cname);
173 ccount++;
174 }
175 }
176
177 LogMessage("%d methods (%.1f%%) of the interface headers do not exist in the real headers",
178 mcount, (float)(100.0 * mcount/m_interface.GetMethodCount()));
179 LogMessage("%d classes (%.1f%%) of the interface headers do not exist in the real headers",
180 ccount, (float)(100.0 * ccount/m_interface.GetClassesCount()));
181
182 return true;
183 }
184
185 int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& api)
186 {
187 wxString searchedclasses;
188 const wxMethod *real;
189 int count = 0;
190
191 wxASSERT(iface && api.GetCount()>0);
192
193 // build a string with the names of the API classes compared to iface
194 for (unsigned int j=0; j<api.GetCount(); j++)
195 searchedclasses += "/" + api[j]->GetName();
196 searchedclasses.Remove(0, 1);
197
198 // shorten the name of the header so the log file is more readable
199 wxString header = iface->GetHeader().AfterLast('/');
200
201 for (unsigned int i=0; i<iface->GetMethodCount(); i++)
202 {
203 const wxMethod& m = iface->GetMethod(i);
204 int matches = 0;
205
206 // search in the methods of the api classes provided
207 for (unsigned int j=0; j<api.GetCount(); j++)
208 {
209 real = api[j]->FindMethod(m);
210 if (real)
211 matches++; // there is a real matching prototype! It's ok!
212 }
213
214 if (matches == 0)
215 {
216 wxMethodPtrArray overloads;
217
218 // try searching for a method with the same name but with
219 // different return type / arguments / qualifiers
220 for (unsigned int j=0; j<api.GetCount(); j++)
221 {
222 wxMethodPtrArray results = api[j]->FindMethodNamed(m.GetName());
223
224 // append "results" array to "overloads"
225 WX_APPEND_ARRAY(overloads, results);
226 }
227
228 if (overloads.GetCount()==0)
229 {
230 LogMessage("%s: real '%s' class has no method '%s'",
231 header, searchedclasses, m.GetAsString());
232 // we've found no overloads
233 }
234 else
235 {
236 // first, output a warning
237 wxString warning = header;
238 if (overloads.GetCount()>1)
239 warning += wxString::Format(": in the real headers there are %d overloads of '%s' for "
240 "'%s' all with different signatures:\n",
241 overloads.GetCount(), m.GetName(), searchedclasses);
242 else
243 warning += wxString::Format(": in the real headers there is a method '%s' for '%s'"
244 " but has different signature:\n",
245 m.GetName(), searchedclasses);
246
247 warning += "\tdoxy header: " + m.GetAsString();
248 for (unsigned int j=0; j<overloads.GetCount(); j++)
249 warning += "\n\treal header: " + overloads[j]->GetAsString();
250
251 wxPrint(warning + "\n");
252 count++;
253
254 if (overloads.GetCount()>1)
255 {
256 // TODO: decide which of these overloads is the most "similar" to m
257 // and eventually modify it
258 if (m_modify)
259 wxPrint("\tmanual fix is required\n");
260 }
261 else
262 {
263 wxASSERT(overloads.GetCount() == 1);
264
265 if (m_modify)
266 {
267 wxPrint("\tfixing it...\n");
268
269 // try to modify it!
270 FixMethod(iface->GetHeader(), &m, overloads[0]);
271 }
272 }
273 }
274
275 count++;
276 }
277 }
278
279 return count;
280 }
281
282 void IfaceCheckApp::FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api)
283 {
284 wxASSERT(iface && api);
285
286 wxTextFile file;
287 if (!file.Open(header)) {
288 LogError("can't open the '%s' header file.", header);
289 return;
290 }
291
292 // GetLocation() returns the line where the last part of the prototype is placed:
293 int end = iface->GetLocation()-1;
294 if (end <= 0 || end >= (int)file.GetLineCount()) {
295 LogWarning("invalid location info for method '%s': %d.",
296 iface->GetAsString(), iface->GetLocation());
297 return;
298 }
299
300 if (!file.GetLine(end).Contains(iface->GetName())) {
301 LogWarning("invalid location info for method '%s': %d.",
302 iface->GetAsString(), iface->GetLocation());
303 return;
304 }
305
306 // find the start point of this prototype declaration:
307 int start = end-1;
308 while (start > 0 &&
309 !file.GetLine(start).Contains(";") &&
310 !file.GetLine(start).Contains("*/"))
311 start--;
312
313 if (start <= 0)
314 {
315 LogError("can't find the beginning of the declaration of '%s' method in '%s' header",
316 iface->GetAsString(), header);
317 return;
318 }
319
320 // start-th line contains either the declaration of another prototype
321 // or the closing tag */ of a doxygen comment; start one line below
322 start++;
323
324 // remove the old prototype
325 for (int i=start; i<=end; i++)
326 file.RemoveLine(start); // remove (end-start)-nth times the start-th line
327
328 #define INDENTATION_STR wxString(" ")
329
330 // if possible, add also the @deprecated tag in the doxygen comment
331 if (file.GetLine(start-1).Contains("*/") && api->IsDeprecated())
332 {
333 file.RemoveLine(start-1);
334 file.InsertLine(INDENTATION_STR + INDENTATION_STR +
335 "@deprecated @todo provide deprecation description", start-1);
336 file.InsertLine(INDENTATION_STR + "*/", start++);
337 }
338
339 // insert the new one
340 file.InsertLine(INDENTATION_STR + api->GetAsString() + ";", start);
341
342 // now save the modification
343 if (!file.Write()) {
344 LogError("can't save the '%s' header file.", header);
345 return;
346 }
347 }
348
349 void IfaceCheckApp::PrintStatistics(long secs)
350 {
351 LogMessage("wx real headers contains declaration of %d classes (%d methods)",
352 m_api.GetClassesCount(), m_api.GetMethodCount());
353 LogMessage("wx interface headers contains declaration of %d classes (%d methods)",
354 m_interface.GetClassesCount(), m_interface.GetMethodCount());
355 LogMessage("total processing took %d seconds.", secs);
356 }
357