]> git.saurik.com Git - wxWidgets.git/blob - utils/ifacecheck/src/ifacecheck.cpp
speedup a little the parser by converting gccXML ID attributes to numbers, instead...
[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 LogMessage("%s: couldn't find the real interface for the '%s' class",
169 interface[i].GetHeader(), cname);
170 ccount++;
171 }
172 }
173
174 LogMessage("%d methods (%.1f%%) of the interface headers do not exist in the real headers",
175 mcount, (float)(100.0 * mcount/m_interface.GetMethodCount()));
176 LogMessage("%d classes (%.1f%%) of the interface headers do not exist in the real headers",
177 ccount, (float)(100.0 * ccount/m_interface.GetClassesCount()));
178
179 return true;
180 }
181
182 int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& api)
183 {
184 wxString searchedclasses;
185 const wxMethod *real;
186 int count = 0;
187
188 wxASSERT(iface && api.GetCount()>0);
189
190 // build a string with the names of the API classes compared to iface
191 for (unsigned int j=0; j<api.GetCount(); j++)
192 searchedclasses += "/" + api[j]->GetName();
193 searchedclasses.Remove(0, 1);
194
195
196 // shorten the name of the header so the log file is more readable
197 wxString header = iface->GetHeader().AfterLast('/');
198
199 for (unsigned int i=0; i<iface->GetMethodCount(); i++)
200 {
201 const wxMethod& m = iface->GetMethod(i);
202 wxString tofind = m.GetAsString();
203 int matches = 0;
204
205 // search in the methods of the api classes provided
206 for (unsigned int j=0; j<api.GetCount(); j++)
207 {
208 real = api[j]->FindMethod(m);
209 if (real) {
210
211 // there is a matching prototype! It's ok!
212 //LogMessage("the doxygen method '%s' has a match in the real interface of '%s'!",
213 // tofind, api[j]->GetName());
214 matches++;
215 }
216 }
217
218 if (matches == 0)
219 {
220 wxMethodPtrArray overloads;
221
222 // try searching for a method with the same name but with
223 // different return type / arguments / qualifiers
224 for (unsigned int j=0; j<api.GetCount(); j++)
225 {
226 wxMethodPtrArray results = api[j]->FindMethodNamed(m.GetName());
227
228 // append "results" array to "overloads"
229 WX_APPEND_ARRAY(overloads, results);
230 }
231
232 if (overloads.GetCount()>1)
233 {
234 // TODO: decide which of these overloads is the most "similar" to m
235 // and eventually modify it
236 LogWarning("%s: there are %d overloads of method '%s' in the classes '%s' "
237 "all with different signatures; manual fix is required",
238 header, overloads.GetCount(), tofind, searchedclasses);
239 }
240 else if (overloads.GetCount() == 1)
241 {
242 wxString tmp;
243 if (m_modify) tmp = "; fixing it...";
244
245 LogWarning("%s: the method '%s' of classes '%s' has a different signature%s",
246 header, tofind, searchedclasses, tmp);
247
248 // try to modify it!
249 if (m_modify)
250 FixMethod(iface->GetHeader(), &m, overloads[0]);
251 }
252 else
253 {
254 LogMessage("%s: real '%s' class has no method '%s'",
255 header, searchedclasses, tofind);
256 count++; // count this type of warnings
257 }
258 }
259 }
260
261 return count;
262 }
263
264 void IfaceCheckApp::FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api)
265 {
266 wxASSERT(iface && api);
267
268 wxTextFile file;
269 if (!file.Open(header)) {
270 LogError("can't open the '%s' header file.", header);
271 return;
272 }
273
274 #if 0
275 // fix iface signature to match that of "api" method:
276 file.RemoveLine(iface->GetLocation());
277 file.InsertLine(api->GetAsString(), iface->GetLocation());
278 #else
279 // GetLocation() returns the line where the last part of the prototype is placed:
280 int end = iface->GetLocation()-1;
281
282 // find the start point of this prototype declaration:
283 int start = end;
284 while (!file.GetLine(start).Contains(";") && !file.GetLine(start).Contains("*/"))
285 {
286 start--;
287 if (start <= 0)
288 {
289 LogError("can't find the beginning of the declaration of '%s' method in '%s' header",
290 iface->GetAsString(), header);
291 return;
292 }
293 }
294
295 // remove the old prototype
296 for (int i=start; i<=end; i++)
297 file.RemoveLine(i);
298
299 // insert the new one
300 file.InsertLine(" " + api->GetAsString(), start);
301 #endif
302
303 if (!file.Write()) {
304 LogError("can't save the '%s' header file.", header);
305 return;
306 }
307 }
308
309 void IfaceCheckApp::PrintStatistics(long secs)
310 {
311 LogMessage("wx real headers contains declaration of %d classes (%d methods)",
312 m_api.GetClassesCount(), m_api.GetMethodCount());
313 LogMessage("wx interface headers contains declaration of %d classes (%d methods)",
314 m_interface.GetClassesCount(), m_interface.GetMethodCount());
315 LogMessage("total processing took %d seconds.", secs);
316 }
317