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