############
-gccxmloutput="wxapi.xml" # where do we put the gccXML output:
-allheaders="/tmp/wx-all.h" # headers which includes all wx public headers
+gccxmloutput="wxapi.xml" # the file where we store the gccXML output
+preprocoutput="wxapi-preproc.txt" # the file where we store the preprocessor's #define values
+allheaders="/tmp/wx-all.h" # the header which includes all wx public headers (autogenerated)
# the list of all wxWidgets public headers
listcmd="ls wx/*.h wx/aui/*.h wx/html/*.h wx/protocol/*.h wx/richtext/*.h wx/stc/*.h wx/xml/*.h wx/xrc/*.h"
flags="@CXXFLAGS@"
# NOTE: it's important to define __WXDEBUG__ because some functions of wx
-# are declared (and thus parsed by gcc) only if that symbol is defined;
-# so we remove __WXDEBUG__ symbol from $flags, in case it's defined:
+# are declared (and thus parsed by gcc) only if that symbol is defined.
+# So we remove it from $flags (in case it's defined) and then readd it.
flags=`echo "$flags" | sed -e 's/-pthread//g' | sed -e 's/__WXDEBUG__//g'`
+# append some other flags:
+flags="-I . -I @top_srcdir@/include $flags -D__WXDEBUG__ -D__WX@TOOLKIT@__ -DWXBUILDING $allheaders"
+
# run gccxml with the same flag used for the real compilation of wx sources:
echo "Running gccxml on the $allheaders file..."
if [[ -f "$gccxmloutput" ]]; then rm $gccxmloutput; fi
-gccxml -I . -I @top_srcdir@/include $flags -D__WX@TOOLKIT@__ -DWXBUILDING $allheaders -fxml=$gccxmloutput
+gccxml $flags -fxml=$gccxmloutput
+
+# now get the list of the #defined values for wx headers, so that the result
+# can be passed to ifacecheck to aid the comparison
+echo "Running gccxml's preprocessor on the $allheaders file..."
+gccxml -E -dM $flags >$preprocoutput
# cleanup
rm $allheaders
// 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",
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);
// 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;
};
{
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;
+ // 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_interface.AddPreprocessorValue("NULL", "0");
+
+ // parse the two XML files which contain the real and the doxygen interfaces
+ // for wxWidgets API:
if (!m_api.Parse(parser.GetParam(0)) ||
!m_interface.Parse(parser.GetParam(1)))
return 1;
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("\"") &&
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;
}
}
+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_interface.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)",
// wxArgumentType
// ----------------------------------------------------------------------------
-void wxArgumentType::SetDefaultValue(const wxString& defval)
+void wxArgumentType::SetDefaultValue(const wxString& defval, const wxString& defvalForCmp)
{
m_strDefaultValue=defval.Strip(wxString::both);
-
+ m_strDefaultValueForCmp=defvalForCmp.Strip(wxString::both);
+/*
// in order to make valid&simple comparison on argument defaults,
// we reduce some of the multiple forms in which the same things may appear
// to a single form:
m_strDefaultValue.Replace("0", "NULL");
else
m_strDefaultValue.Replace("NULL", "0");
-
+*/
if (m_strDefaultValue.Contains("wxGetTranslation"))
m_strDefaultValue = "_(TOFIX)"; // TODO: wxGetTranslation gives problems to gccxml
if ((const wxType&)(*this) != (const wxType&)m)
return false;
- if (m_strDefaultValue != m.m_strDefaultValue)
+ const wxString& def1 = m_strDefaultValueForCmp.IsEmpty() ? m_strDefaultValue : m_strDefaultValueForCmp;
+ const wxString& def2 = m.m_strDefaultValueForCmp.IsEmpty() ? m.m_strDefaultValue : m.m_strDefaultValueForCmp;
+
+ if (def1 != def2)
return false;
// we deliberately avoid checks on the argument name
return false;
}
- wxASSERT((m_bVirtual && m_bPureVirtual) || !m_bVirtual);
+ wxASSERT(!m_bPureVirtual || (m_bPureVirtual && m_bVirtual));
for (unsigned int i=0; i<m_args.GetCount(); i++)
if (!m_args[i].IsOk()) {
bool wxXmlInterface::CheckParseResults() const
{
// this check can be quite slow, so do it only for debug releases:
-#ifdef __WXDEBUG__
+//#ifdef __WXDEBUG__
for (unsigned int i=0; i<m_classes.GetCount(); i++)
if (!m_classes[i].CheckConsistency())
return false;
-#endif
+//#endif
return true;
}
else if (n->GetName() == "declname")
namestr = GetTextFromChildren(n);
else if (n->GetName() == "defval")
- defstr = GetTextFromChildren(n);
+ defstr = GetTextFromChildren(n).Strip(wxString::both);
else if (n->GetName() == "array")
arrstr = GetTextFromChildren(n);
return false;
}
- args.Add(wxArgumentType(typestr + arrstr, defstr, namestr));
+ wxArgumentType newarg(typestr + arrstr, defstr, namestr);
+
+ // can we use preprocessor output to transform the default value
+ // into the same form which gets processed by wxXmlGccInterface?
+ wxStringHashMap::const_iterator it = m_preproc.find(defstr);
+ if (it != m_preproc.end())
+ newarg.SetDefaultValue(defstr, it->second);
+
+ args.Add(newarg);
}
else if (child->GetName() == "location")
{
wxString GetArgumentName() const
{ return m_strArgName; }
- void SetDefaultValue(const wxString& defval);
+ void SetDefaultValue(const wxString& defval, const wxString& defvalForCmp = wxEmptyString);
wxString GetDefaultValue() const
{ return m_strDefaultValue; }
protected:
wxString m_strDefaultValue;
+
+ // this string may differ from m_strDefaultValue if there were
+ // preprocessor substitutions; can be wxEmptyString.
+ wxString m_strDefaultValueForCmp;
+
wxString m_strArgName; // this one only makes sense when this wxType is
// used as argument type (and not as return type)
// and can be empty.
WX_DECLARE_HASH_MAP( unsigned long, wxString,
wxIntegerHash, wxIntegerEqual,
wxTypeIdHashMap );
+
+WX_DECLARE_STRING_HASH_MAP( wxString, wxStringHashMap );
#else
#include <map>
typedef std::basic_string<char> stlString;
public:
wxXmlGccInterface() {}
- // !!SPEEDUP-TODO!!
- // Using wxXmlDocument::Load as is, all the types contained in the
- // the entire gccXML file are stored in memory while parsing;
- // however we are only interested to wx's own structs/classes/funcs/etc
- // so that we could use the file IDs to avoid loading stuff which does
- // not belong to wx. See the very end of the gccXML file: it contains
- // a set of <File> nodes referenced by all nodes above.
-
bool Parse(const wxString& filename);
bool ParseMethod(const wxXmlNode *p,
const wxTypeIdHashMap& types,
bool Parse(const wxString& filename);
bool ParseCompoundDefinition(const wxString& filename);
bool ParseMethod(const wxXmlNode*, wxMethod&, wxString& header);
+
+ // this class can take advantage of the preprocessor output to give
+ // a minor number of false positive warnings in the final comparison
+ void AddPreprocessorValue(const wxString& name, const wxString& val)
+ { m_preproc[name]=val; }
+
+protected:
+ wxStringHashMap m_preproc;
};