]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/ifacecheck.cpp
b96f5ed60cc955b37277a06404433795ad108c16
[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
199 // shorten the name of the header so the log file is more readable
200 wxString header = iface->GetHeader().AfterLast('/');
201
202 for (unsigned int i=0; i<iface->GetMethodCount(); i++)
203 {
204 const wxMethod& m = iface->GetMethod(i);
205 const wxString& tofind = m.GetAsString();
206 int matches = 0;
207
208 // search in the methods of the api classes provided
209 for (unsigned int j=0; j<api.GetCount(); j++)
210 {
211 real = api[j]->FindMethod(m);
212 if (real) {
213
214 // there is a matching prototype! It's ok!
215 //LogMessage("the doxygen method '%s' has a match in the real interface of '%s'!",
216 // tofind, api[j]->GetName());
217 matches++;
218 }
219 }
220
221 if (matches == 0)
222 {
223 wxMethodPtrArray overloads;
224
225 // try searching for a method with the same name but with
226 // different return type / arguments / qualifiers
227 for (unsigned int j=0; j<api.GetCount(); j++)
228 {
229 wxMethodPtrArray results = api[j]->FindMethodNamed(m.GetName());
230
231 // append "results" array to "overloads"
232 WX_APPEND_ARRAY(overloads, results);
233 }
234
235 if (overloads.GetCount()>1)
236 {
237 // TODO: decide which of these overloads is the most "similar" to m
238 // and eventually modify it
239 LogWarning("%s: there are %d overloads of method '%s' in the classes '%s' "
240 "all with different signatures; manual fix is required",
241 header, overloads.GetCount(), tofind, searchedclasses);
242 }
243 else if (overloads.GetCount() == 1)
244 {
245 wxString tmp;
246 if (m_modify) tmp = "; fixing it...";
247
248 LogWarning("%s: the method '%s' of classes '%s' has a different signature%s",
249 header, tofind, searchedclasses, tmp);
250 count++;
251
252 // try to modify it!
253 if (m_modify)
254 FixMethod(iface->GetHeader(), &m, overloads[0]);
255 }
256 else
257 {
258 LogMessage("%s: real '%s' class has no method '%s'",
259 header, searchedclasses, tofind);
260 count++; // count this type of warnings
261 }
262 }
263 }
264
265 return count;
266 }
267
268 void IfaceCheckApp::FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api)
269 {
270 wxASSERT(iface && api);
271
272 wxTextFile file;
273 if (!file.Open(header)) {
274 LogError("can't open the '%s' header file.", header);
275 return;
276 }
277
278 // GetLocation() returns the line where the last part of the prototype is placed:
279 int end = iface->GetLocation()-1;
280 if (end <= 0 || end >= (int)file.GetLineCount()) {
281 LogWarning("invalid location info for method '%s': %d.",
282 iface->GetAsString(), iface->GetLocation());
283 return;
284 }
285
286 // find the start point of this prototype declaration:
287 int start = end;
288 while (start > 0 &&
289 !file.GetLine(start).Contains(";") &&
290 !file.GetLine(start).Contains("*/"))
291 start--;
292
293 if (start <= 0)
294 {
295 LogError("can't find the beginning of the declaration of '%s' method in '%s' header",
296 iface->GetAsString(), header);
297 return;
298 }
299
300 // remove the old prototype
301 for (int i=start; i<=end; i++)
302 file.RemoveLine(start); // remove (end-start)-nth times the start-th line
303
304 #define INDENTATION_STR wxString(" ")
305
306 // if possible, add also the @deprecated tag in the doxygen comment
307 if (file.GetLine(start-1).Contains("*/") && api->IsDeprecated())
308 {
309 file.RemoveLine(start-1);
310 file.InsertLine(INDENTATION_STR + INDENTATION_STR +
311 "@deprecated @todo provide deprecation description", start-1);
312 file.InsertLine(INDENTATION_STR + "*/", start++);
313 }
314
315 // insert the new one
316 file.InsertLine(INDENTATION_STR + api->GetAsString() + ";", start);
317
318 // now save the modification
319 if (!file.Write()) {
320 LogError("can't save the '%s' header file.", header);
321 return;
322 }
323 }
324
325 void IfaceCheckApp::PrintStatistics(long secs)
326 {
327 LogMessage("wx real headers contains declaration of %d classes (%d methods)",
328 m_api.GetClassesCount(), m_api.GetMethodCount());
329 LogMessage("wx interface headers contains declaration of %d classes (%d methods)",
330 m_interface.GetClassesCount(), m_interface.GetMethodCount());
331 LogMessage("total processing took %d seconds.", secs);
332 }
333