X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/203ba76a12c701e24e1cd8dadb31748976af98a2..fe1a36a70c2f5ed8a6f9ea0b82005ebb987e5860:/utils/ifacecheck/src/ifacecheck.cpp diff --git a/utils/ifacecheck/src/ifacecheck.cpp b/utils/ifacecheck/src/ifacecheck.cpp index c119238532..a8ce9b2bc6 100644 --- a/utils/ifacecheck/src/ifacecheck.cpp +++ b/utils/ifacecheck/src/ifacecheck.cpp @@ -35,18 +35,23 @@ bool g_verbose = false; // IfaceCheckApp // ---------------------------------------------------------------------------- -#define API_DUMP_FILE "dump.api.txt" -#define INTERFACE_DUMP_FILE "dump.interface.txt" +#define API_DUMP_FILE "dump.api.txt" +#define INTERFACE_DUMP_FILE "dump.interface.txt" -#define PROCESS_ONLY_SWITCH "p" -#define MODIFY_SWITCH "m" -#define DUMP_SWITCH "d" -#define HELP_SWITCH "h" -#define VERBOSE_SWITCH "v" +#define PROCESS_ONLY_OPTION "p" +#define USE_PREPROCESSOR_OPTION "u" + +#define MODIFY_SWITCH "m" +#define DUMP_SWITCH "d" +#define HELP_SWITCH "h" +#define VERBOSE_SWITCH "v" static const wxCmdLineEntryDesc g_cmdLineDesc[] = { - { wxCMD_LINE_OPTION, PROCESS_ONLY_SWITCH, "process-only", + { wxCMD_LINE_OPTION, USE_PREPROCESSOR_OPTION, "use-preproc", + "uses the preprocessor output to increase the checker accuracy", + wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR }, + { wxCMD_LINE_OPTION, PROCESS_ONLY_OPTION, "process-only", "processes only header files matching the given wildcard", wxCMD_LINE_VAL_STRING, wxCMD_LINE_NEEDS_SEPARATOR }, { wxCMD_LINE_SWITCH, MODIFY_SWITCH, "modify", @@ -71,6 +76,8 @@ public: virtual bool OnInit() { m_modify=false; return true; } virtual int OnRun(); + bool ParsePreprocessorOutput(const wxString& filename); + bool Compare(); int CompareClasses(const wxClass* iface, const wxClassPtrArray& api); void FixMethod(const wxString& header, const wxMethod* iface, const wxMethod* api); @@ -86,13 +93,13 @@ public: } protected: - wxXmlGccInterface m_api; // "real" headers API - wxXmlDoxygenInterface m_interface; // doxygen-commented headers API + wxXmlGccInterface m_gccInterface; // "real" headers API + wxXmlDoxygenInterface m_doxyInterface; // doxygen-commented headers API // was the MODIFY_SWITCH passed? bool m_modify; - // if non-empty, then PROCESS_ONLY_SWITCH was passed and this is the + // if non-empty, then PROCESS_ONLY_OPTION was passed and this is the // wildcard expression to match wxString m_strToMatch; }; @@ -103,37 +110,51 @@ int IfaceCheckApp::OnRun() { long startTime = wxGetLocalTime(); // for timing purpose - // parse the command line... wxCmdLineParser parser(g_cmdLineDesc, argc, argv); + parser.SetLogo( + wxString::Format("wxWidgets Interface checker utility (built %s against %s)", + __DATE__, wxVERSION_STRING)); + + // parse the command line... bool ok = true; + wxString preprocFile; switch (parser.Parse()) { - case -1: - // HELP_SWITCH was passed - return 0; - case 0: if (parser.Found(VERBOSE_SWITCH)) g_verbose = true; - if (!m_api.Parse(parser.GetParam(0)) || - !m_interface.Parse(parser.GetParam(1))) + // IMPORTANT: parsing #define values must be done _before_ actually + // parsing the GCC/doxygen XML files + if (parser.Found(USE_PREPROCESSOR_OPTION, &preprocFile)) + { + if (!ParsePreprocessorOutput(preprocFile)) + return 1; + } + + // in any case set basic std preprocessor #defines: + m_doxyInterface.AddPreprocessorValue("NULL", "0"); + + // parse the two XML files which contain the real and the doxygen interfaces + // for wxWidgets API: + if (!m_gccInterface.Parse(parser.GetParam(0)) || + !m_doxyInterface.Parse(parser.GetParam(1))) return 1; if (parser.Found(DUMP_SWITCH)) { LogMessage("Dumping real API to '%s'...", API_DUMP_FILE); - m_api.Dump(API_DUMP_FILE); + m_gccInterface.Dump(API_DUMP_FILE); LogMessage("Dumping interface API to '%s'...", INTERFACE_DUMP_FILE); - m_interface.Dump(INTERFACE_DUMP_FILE); + m_doxyInterface.Dump(INTERFACE_DUMP_FILE); } else { if (parser.Found(MODIFY_SWITCH)) m_modify = true; - if (parser.Found(PROCESS_ONLY_SWITCH, &m_strToMatch)) + if (parser.Found(PROCESS_ONLY_OPTION, &m_strToMatch)) { size_t len = m_strToMatch.Len(); if (m_strToMatch.StartsWith("\"") && @@ -147,6 +168,20 @@ int IfaceCheckApp::OnRun() PrintStatistics(wxGetLocalTime() - startTime); return ok ? 0 : 1; + + default: + wxPrintf("\nThis utility checks that the interface XML files created by Doxygen are in\n"); + wxPrintf("synch with the real headers (whose contents are extracted by the gcc XML file).\n\n"); + wxPrintf("The 'gccXML' parameter should be the wxapi.xml file created by the 'rungccxml.sh'\n"); + wxPrintf("script which resides in 'utils/ifacecheck'.\n"); + wxPrintf("The 'doxygenXML' parameter should be the index.xml file created by Doxygen\n"); + wxPrintf("for the wxWidgets 'interface' folder.\n\n"); + wxPrintf("Since the gcc XML file does not contain info about #defines, if you use\n"); + wxPrintf("the -%s option, you'll get a smaller number of false warnings.\n", + USE_PREPROCESSOR_OPTION); + + // HELP_SWITCH was passed or a syntax error occurred + return 0; } return 1; @@ -160,7 +195,7 @@ void IfaceCheckApp::ShowProgress() bool IfaceCheckApp::Compare() { - const wxClassArray& interface = m_interface.GetClasses(); + const wxClassArray& interface = m_doxyInterface.GetClasses(); const wxClass* c; wxClassPtrArray api; int mcount = 0, ccount = 0; @@ -173,6 +208,18 @@ bool IfaceCheckApp::Compare() for (unsigned int i=0; i0) { @@ -207,9 +254,9 @@ bool IfaceCheckApp::Compare() } LogMessage("%d methods (%.1f%%) of the interface headers do not exist in the real headers", - mcount, (float)(100.0 * mcount/m_interface.GetMethodCount())); + mcount, (float)(100.0 * mcount/m_doxyInterface.GetMethodCount())); LogMessage("%d classes (%.1f%%) of the interface headers do not exist in the real headers", - ccount, (float)(100.0 * ccount/m_interface.GetClassesCount())); + ccount, (float)(100.0 * ccount/m_doxyInterface.GetClassesCount())); return true; } @@ -235,6 +282,18 @@ int IfaceCheckApp::CompareClasses(const wxClass* iface, const wxClassPtrArray& a const wxMethod& m = iface->GetMethod(i); int matches = 0; + // only compare the methods which are available for the port + // for which the gcc XML was produced + if (m.GetAvailability() != wxPORT_UNKNOWN && + (m.GetAvailability() & m_gccInterface.GetInterfacePort()) == 0) { + + if (g_verbose) + LogMessage("skipping method '%s' since it's not available for the %s port.", + m.GetAsString(), m_gccInterface.GetInterfacePortName()); + + continue; // skip this method + } + // search in the methods of the api classes provided for (unsigned int j=0; jGetLocation()-1; if (end <= 0 || end >= (int)file.GetLineCount()) { - LogWarning("invalid location info for method '%s': %d.", + LogWarning("\tinvalid location info for method '%s': %d.", iface->GetAsString(), iface->GetLocation()); return; } - if (!file.GetLine(end).Contains(iface->GetName())) { - LogWarning("invalid location info for method '%s': %d.", + if (!file.GetLine(end).Contains(";")) { + LogWarning("\tinvalid location info for method '%s': %d.", iface->GetAsString(), iface->GetLocation()); return; } // find the start point of this prototype declaration: int start = end-1; + bool founddecl = false; while (start > 0 && !file.GetLine(start).Contains(";") && !file.GetLine(start).Contains("*/")) + { start--; - if (start <= 0) + founddecl |= file.GetLine(start).Contains(iface->GetName()); + } + + if (start <= 0 || !founddecl) { - LogError("can't find the beginning of the declaration of '%s' method in '%s' header", + LogError("\tcan't find the beginning of the declaration of '%s' method in '%s' header", iface->GetAsString(), header); return; } @@ -366,13 +430,18 @@ void IfaceCheckApp::FixMethod(const wxString& header, const wxMethod* iface, con #define INDENTATION_STR wxString(" ") - // if possible, add also the @deprecated tag in the doxygen comment - if (file.GetLine(start-1).Contains("*/") && api->IsDeprecated()) + // if possible, add also the @deprecated tag in the doxygen comment if it's missing + int deprecationOffset = 0; + if (file.GetLine(start-1).Contains("*/") && + (api->IsDeprecated() && !iface->IsDeprecated())) { file.RemoveLine(start-1); file.InsertLine(INDENTATION_STR + INDENTATION_STR + "@deprecated @todo provide deprecation description", start-1); file.InsertLine(INDENTATION_STR + "*/", start++); + + // we have added a new line in the final balance + deprecationOffset=1; } wxMethod tmp(*api); @@ -389,22 +458,117 @@ void IfaceCheckApp::FixMethod(const wxString& header, const wxMethod* iface, con tmp.SetArgumentTypes(realargs); } - // insert the new one - file.InsertLine(INDENTATION_STR + tmp.GetAsString() + ";", start); +#define WRAP_COLUMN 80 + + wxArrayString toinsert; + toinsert.Add(INDENTATION_STR + tmp.GetAsString() + ";"); + + int nStartColumn = toinsert[0].Find('('); + wxASSERT(nStartColumn != wxNOT_FOUND); + + // wrap lines too long at comma boundaries + for (unsigned int i=0; i WRAP_COLUMN) + { + wxASSERT(i == toinsert.GetCount()-1); + + // break this line + wxString tmpleft = toinsert[i].Left(WRAP_COLUMN); + int comma = tmpleft.Find(',', true /* from end */); + if (comma == wxNOT_FOUND) + break; // break out of the for cycle... + + toinsert.Add(wxString(' ', nStartColumn+1) + + toinsert[i].Right(len-comma-2)); // exclude the comma and the space after it + toinsert[i] = tmpleft.Left(comma+1); // include the comma + } + } + + // insert the new lines + for (unsigned int i=0; iGetMethodCount(); j++) + { + wxMethod& m = cToUpdate[i]->GetMethod(j); + if (m.GetLocation() > iface->GetLocation()) + { + // update the location of this method + m.SetLocation(m.GetLocation()+nOffset); + } + } + } +} + +bool IfaceCheckApp::ParsePreprocessorOutput(const wxString& filename) +{ + wxTextFile tf; + if (!tf.Open(filename)) { + LogError("can't open the '%s' preprocessor output file.", filename); + return false; + } + + size_t useful = 0; + for (unsigned int i=0; i < tf.GetLineCount(); i++) + { + const wxString& line = tf.GetLine(i); + wxString defnameval = line.Mid(8); // what follows the "#define " string + + // the format of this line should be: + // #define DEFNAME DEFVALUE + if (!line.StartsWith("#define ") || !defnameval.Contains(" ")) { + LogError("unexpected content in '%s' at line %d.", filename, i); + return false; + } + + // get DEFNAME + wxString defname = defnameval.BeforeFirst(' '); + if (defname.Contains("(")) + continue; // this is a macro, skip it! + + // get DEFVAL + wxString defval = defnameval.AfterFirst(' ').Strip(wxString::both); + if (defval.StartsWith("(") && defval.EndsWith(")")) + defval = defval.Mid(1, defval.Len()-2); + + // store this pair in the doxygen interface, where it can be useful + m_doxyInterface.AddPreprocessorValue(defname, defval); + useful++; + } + + LogMessage("Parsed %d preprocessor #defines from '%s' which will be used later...", + useful, filename); + + return true; } void IfaceCheckApp::PrintStatistics(long secs) { LogMessage("wx real headers contains declaration of %d classes (%d methods)", - m_api.GetClassesCount(), m_api.GetMethodCount()); + m_gccInterface.GetClassesCount(), m_gccInterface.GetMethodCount()); LogMessage("wx interface headers contains declaration of %d classes (%d methods)", - m_interface.GetClassesCount(), m_interface.GetMethodCount()); + m_doxyInterface.GetClassesCount(), m_doxyInterface.GetMethodCount()); LogMessage("total processing took %d seconds.", secs); }