Move the scripts into a separate directory and commit initial start on automatic bindings generator.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@60490 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
233
docs/doxygen/scripts/doxymlparser.py
Executable file
233
docs/doxygen/scripts/doxymlparser.py
Executable file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
Name: doxymlparser.py
|
||||
Author: Kevin Ollivier
|
||||
License: wxWidgets License
|
||||
"""
|
||||
|
||||
__description__ = """
|
||||
Takes the output of Doxygen XML and parses it to retrieve metadata about the classes and methods.
|
||||
|
||||
To create the Doxygen XML files, from the wxWidgets/docs/doxygen directory, do:
|
||||
|
||||
./regen.sh xml
|
||||
|
||||
To see the results from parsing a particular class, do:
|
||||
|
||||
python doxymlparser.py --report out/xml/classwx_<whatever>.xml
|
||||
"""
|
||||
|
||||
#!/usr/bin/env python
|
||||
import optparse
|
||||
import os
|
||||
import string
|
||||
|
||||
import sys
|
||||
import types
|
||||
from xml.dom import minidom
|
||||
|
||||
option_dict = {
|
||||
"report" : (False, "Print out the classes and methods found by this script."),
|
||||
"verbose" : (False, "Provide status updates and other information."),
|
||||
}
|
||||
|
||||
parser = optparse.OptionParser(usage="usage: %prog [options] <doxyml files to parse>\n" + __description__, version="%prog 1.0")
|
||||
|
||||
for opt in option_dict:
|
||||
default = option_dict[opt][0]
|
||||
|
||||
action = "store"
|
||||
if type(default) == types.BooleanType:
|
||||
action = "store_true"
|
||||
parser.add_option("--" + opt, default=default, action=action, dest=opt, help=option_dict[opt][1])
|
||||
|
||||
options, arguments = parser.parse_args()
|
||||
|
||||
def get_first_value(alist):
|
||||
if len(alist) > 0:
|
||||
return alist[0]
|
||||
else:
|
||||
return ""
|
||||
|
||||
class ClassDefinition:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.constructors = []
|
||||
self.destructors = []
|
||||
self.methods = []
|
||||
self.brief_description = ""
|
||||
self.detailed_description = ""
|
||||
self.includes = []
|
||||
self.bases = []
|
||||
self.enums = {}
|
||||
|
||||
def __str__(self):
|
||||
str_repr = """
|
||||
Class: %s
|
||||
Bases: %s
|
||||
Includes: %s
|
||||
Brief Description:
|
||||
%s
|
||||
|
||||
Detailed Description:
|
||||
%s
|
||||
""" % (self.name, string.join(self.bases, ", "), self.includes, self.brief_description, self.detailed_description)
|
||||
str_repr += "Methods:\n"
|
||||
|
||||
for method in self.methods:
|
||||
str_repr += str(method)
|
||||
|
||||
return str_repr
|
||||
|
||||
class MethodDefinition:
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.return_type = ""
|
||||
self.argsstring = ""
|
||||
self.definition = ""
|
||||
self.params = []
|
||||
self.brief_description = ""
|
||||
self.detailed_description = ""
|
||||
|
||||
def __str__(self):
|
||||
str_repr = """
|
||||
Method: %s
|
||||
Return Type: %s
|
||||
Params: %r
|
||||
Prototype: %s
|
||||
Brief Description:
|
||||
%s
|
||||
|
||||
Detailed Description:
|
||||
%s
|
||||
""" % (self.name, self.return_type, self.params, self.definition + self.argsstring, self.brief_description, self.detailed_description)
|
||||
return str_repr
|
||||
|
||||
def getTextValue(node, recursive=False):
|
||||
text = ""
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == child.ELEMENT_NODE and child.nodeName == "ref":
|
||||
text += getTextValue(child)
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
# Add a space to ensure we have a space between qualifiers and parameter names
|
||||
text += child.nodeValue.strip() + " "
|
||||
|
||||
return text.strip()
|
||||
|
||||
def doxyMLToText(node):
|
||||
return text
|
||||
|
||||
class DoxyMLParser:
|
||||
def __init__(self):
|
||||
self.classes = []
|
||||
|
||||
def find_class(self, name):
|
||||
for aclass in self.classes:
|
||||
if aclass.name == name:
|
||||
return aclass
|
||||
|
||||
return None
|
||||
|
||||
def get_enums_and_functions(self, filename, aclass):
|
||||
file_path = os.path.dirname(filename)
|
||||
enum_filename = os.path.join(file_path, aclass.name[2:] + "_8h.xml")
|
||||
if os.path.exists(enum_filename):
|
||||
root = minidom.parse(enum_filename).documentElement
|
||||
for method in root.getElementsByTagName("memberdef"):
|
||||
if method.getAttribute("kind") == "enum":
|
||||
self.parse_enum(aclass, method, root)
|
||||
|
||||
def is_derived_from_base(self, aclass, abase):
|
||||
base = get_first_value(aclass.bases)
|
||||
while base and base != "":
|
||||
|
||||
if base == abase:
|
||||
return True
|
||||
|
||||
parentclass = self.find_class(base)
|
||||
|
||||
if parentclass:
|
||||
base = get_first_value(parentclass.bases)
|
||||
else:
|
||||
base = None
|
||||
|
||||
return False
|
||||
|
||||
def parse(self, filename):
|
||||
self.xmldoc = minidom.parse(filename).documentElement
|
||||
for node in self.xmldoc.getElementsByTagName("compounddef"):
|
||||
new_class = self.parse_class(node)
|
||||
self.classes.append(new_class)
|
||||
self.get_enums_and_functions(filename, new_class)
|
||||
|
||||
def parse_class(self, class_node):
|
||||
new_class = ClassDefinition()
|
||||
new_class.name = getTextValue(class_node.getElementsByTagName("compoundname")[0])
|
||||
for node in class_node.childNodes:
|
||||
if node.nodeName == "basecompoundref":
|
||||
new_class.bases.append(getTextValue(node))
|
||||
elif node.nodeName == "briefdescription":
|
||||
# let the post-processor determ
|
||||
new_class.brief_description = node.toxml()
|
||||
elif node.nodeName == "detaileddescription":
|
||||
new_class.detailed_description = node.toxml()
|
||||
elif node.nodeName == "includes":
|
||||
new_class.includes.append(getTextValue(node))
|
||||
|
||||
self.parse_methods(new_class, class_node)
|
||||
|
||||
return new_class
|
||||
|
||||
def parse_enum(self, new_class, enum, root):
|
||||
enum_name = ""
|
||||
enum_values = []
|
||||
|
||||
for node in enum.childNodes:
|
||||
if node.nodeName == "name":
|
||||
enum_name = getTextValue(node)
|
||||
elif node.nodeName == "enumvalue":
|
||||
enum_values.append(getTextValue(node.getElementsByTagName("name")[0]))
|
||||
|
||||
new_class.enums[enum_name] = enum_values
|
||||
|
||||
def parse_methods(self, new_class, root):
|
||||
for method in root.getElementsByTagName("memberdef"):
|
||||
new_method = MethodDefinition()
|
||||
for node in method.childNodes:
|
||||
if node.nodeName == "name":
|
||||
new_method.name = getTextValue(node)
|
||||
elif node.nodeName == "type":
|
||||
new_method.return_type = getTextValue(node)
|
||||
elif node.nodeName == "definition":
|
||||
new_method.definition = getTextValue(node)
|
||||
elif node.nodeName == "argsstring":
|
||||
new_method.argsstring = getTextValue(node)
|
||||
elif node.nodeName == "param":
|
||||
param = {}
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == child.ELEMENT_NODE:
|
||||
param[child.nodeName] = getTextValue(child)
|
||||
new_method.params.append(param)
|
||||
|
||||
if options.verbose:
|
||||
print "Adding %s" % (new_method.name + new_method.argsstring)
|
||||
|
||||
if new_method.name == new_class.name:
|
||||
new_class.constructors.append(new_method)
|
||||
elif new_method.name == "~" + new_class.name:
|
||||
new_class.destructors.append(new_method)
|
||||
else:
|
||||
new_class.methods.append(new_method)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(arguments) < 1:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
doxyparse = DoxyMLParser()
|
||||
for arg in arguments:
|
||||
doxyparse.parse(arg)
|
||||
|
||||
if options.report:
|
||||
for aclass in doxyparse.classes:
|
||||
print str(aclass)
|
||||
|
347
docs/doxygen/scripts/make_bindings.py
Normal file
347
docs/doxygen/scripts/make_bindings.py
Normal file
@@ -0,0 +1,347 @@
|
||||
import doxymlparser
|
||||
import optparse
|
||||
import sys
|
||||
import os
|
||||
import string
|
||||
import types
|
||||
|
||||
option_dict = {
|
||||
"output_dir" : ("output", "Directory to output bindings to"),
|
||||
"sip" : (True, "Produce SIP bindings"),
|
||||
"swig" : (True, "Produce SWIG bindings."),
|
||||
|
||||
}
|
||||
|
||||
# format: class : {method : (prototype1, prototype2)}
|
||||
# using a "*" means all prototypes
|
||||
ignored_methods = {
|
||||
"wxIcon": {'wxIcon': (['const char', 'int', 'int'], )},
|
||||
|
||||
}
|
||||
|
||||
# these classes are either replaced by different data types in bindings, or have equivalent / better
|
||||
# functionality provided by the target language.
|
||||
excluded_classes = [
|
||||
"wxArchiveClassFactory",
|
||||
"wxArchiveEntry",
|
||||
"wxArchiveInputStream",
|
||||
"wxArchiveIterator",
|
||||
"wxArchiveNotifier",
|
||||
"wxArchiveOutputStream",
|
||||
"wxArray< T >",
|
||||
"wxArrayString",
|
||||
"wxAutomationObject",
|
||||
"wxBufferedInputStream",
|
||||
"wxBufferedOutputStream",
|
||||
"wxCharBuffer",
|
||||
"wxCharTypeBuffer",
|
||||
"wxClassInfo",
|
||||
"wxCmdLineParser",
|
||||
"wxCondition",
|
||||
"wxConnection",
|
||||
"wxConnectionBase",
|
||||
"wxConvAuto",
|
||||
"wxCountingOutputStream",
|
||||
"wxCriticalSection",
|
||||
"wxCriticalSectionLocker",
|
||||
"wxCSConv",
|
||||
"wxDatagramSocket",
|
||||
"wxDataInputStream",
|
||||
"wxDataOutputStream",
|
||||
"wxDir",
|
||||
"wxDirTraverser",
|
||||
"wxFFile",
|
||||
"wxFFileInputStream",
|
||||
"wxFFileOutputStream",
|
||||
"wxFile",
|
||||
"wxFileInputStream",
|
||||
"wxFileName",
|
||||
"wxFileOutputStream",
|
||||
"wxFileStream",
|
||||
"wxFilterClassFactory",
|
||||
"wxFilterInputStream",
|
||||
"wxFilterOutputStream",
|
||||
"wxFSFile",
|
||||
"wxFSVolume",
|
||||
"wxFTP",
|
||||
"wxHashMap",
|
||||
"wxHashSet",
|
||||
"wxHashTable",
|
||||
"wxHTTP",
|
||||
"wxImage::HSVValue",
|
||||
"wxImage::RGBValue",
|
||||
"wxInputStream",
|
||||
"wxIPAddress",
|
||||
"wxIPV4Address",
|
||||
"wxList< T >",
|
||||
"wxLongLong",
|
||||
"wxMBConv",
|
||||
"wxMBConvFile",
|
||||
"wxMBConvUTF7",
|
||||
"wxMBConvUTF8",
|
||||
"wxMBConvUTF16",
|
||||
"wxMBConvUTF32",
|
||||
"wxMemoryBuffer",
|
||||
"wxMemoryFSHandler",
|
||||
"wxMemoryInputStream",
|
||||
"wxMemoryOutputStream",
|
||||
"wxMessageQueue< T >",
|
||||
"wxModule",
|
||||
"wxMutex",
|
||||
"wxMutexLocker",
|
||||
"wxNode< T >",
|
||||
"wxObjectDataPtr< T >",
|
||||
"wxObjectRefData",
|
||||
"wxOutputStream",
|
||||
"wxProcess",
|
||||
"wxProcessEvent",
|
||||
"wxProtocol",
|
||||
"wxProtocolLog",
|
||||
"wxRecursionGuard",
|
||||
"wxRecursionGuardFlag",
|
||||
"wxRegKey",
|
||||
"wxScopedArray",
|
||||
"wxScopedCharTypeBuffer",
|
||||
"wxScopedPtr",
|
||||
"wxScopedPtr< T >",
|
||||
"wxSharedPtr< T >",
|
||||
"wxServer",
|
||||
"wxSockAddress",
|
||||
"wxSocketBase",
|
||||
"wxSocketClient",
|
||||
"wxSocketEvent",
|
||||
"wxSocketInputStream",
|
||||
"wxSocketOutputStream",
|
||||
"wxSortedArrayString",
|
||||
"wxStopWatch",
|
||||
"wxStreamBase",
|
||||
"wxStreamBuffer",
|
||||
"wxStreamToTextRedirector",
|
||||
"wxString",
|
||||
"wxStringBuffer",
|
||||
"wxStringBufferLength",
|
||||
"wxStringClientData",
|
||||
"wxStringInputStream",
|
||||
"wxStringOutputStream",
|
||||
"wxTarClassFactory",
|
||||
"wxTarEntry",
|
||||
"wxTarInputStream",
|
||||
"wxTarOutputStream",
|
||||
"wxTCPClient",
|
||||
"wxTCPConnection",
|
||||
"wxTCPServer",
|
||||
"wxTempFile",
|
||||
"wxTempFileOutputStream",
|
||||
"wxTextInputStream",
|
||||
"wxTextOutputStream",
|
||||
"wxThread",
|
||||
"wxThreadEvent",
|
||||
"wxThreadHelper",
|
||||
"wxULongLong",
|
||||
"wxUniChar",
|
||||
"wxUniCharRef",
|
||||
"wxURI",
|
||||
"wxURL",
|
||||
"wxUString",
|
||||
"wxVariant",
|
||||
"wxVariantData",
|
||||
"wxVector< T >",
|
||||
"wxVector< T >::reverse_iterator",
|
||||
"wxWCharBuffer",
|
||||
"wxWeakRef< T >",
|
||||
"wxWeakRefDynamic< T >",
|
||||
"wxZipInputStream",
|
||||
"wxZipOutputStream",
|
||||
"wxZlibInputStream",
|
||||
"wxZlibOutputStream",
|
||||
]
|
||||
|
||||
|
||||
parser = optparse.OptionParser(usage="usage: %prog <doxyml files to parse>\n" , version="%prog 1.0")
|
||||
|
||||
for opt in option_dict:
|
||||
default = option_dict[opt][0]
|
||||
|
||||
action = "store"
|
||||
if type(default) == types.BooleanType:
|
||||
action = "store_true"
|
||||
parser.add_option("--" + opt, default=default, action=action, dest=opt, help=option_dict[opt][1])
|
||||
|
||||
options, arguments = parser.parse_args()
|
||||
|
||||
def get_first_value(alist):
|
||||
if len(alist) > 0:
|
||||
return alist[0]
|
||||
else:
|
||||
return ""
|
||||
|
||||
def make_enums(aclass):
|
||||
retval = ""
|
||||
for enum in aclass.enums:
|
||||
retval += "enum %s {\n" % enum
|
||||
num_values = len(aclass.enums[enum])
|
||||
for value in aclass.enums[enum]:
|
||||
retval += " %s" % value
|
||||
if not value == aclass.enums[enum][-1]:
|
||||
retval += ", "
|
||||
retval += "\n"
|
||||
retval += "};\n\n"
|
||||
|
||||
return retval
|
||||
|
||||
class SIPBuilder:
|
||||
def __init__(self, doxyparse, outputdir):
|
||||
self.doxyparser = doxyparse
|
||||
self.output_dir = outputdir
|
||||
|
||||
def make_bindings(self):
|
||||
output_dir = os.path.abspath(os.path.join(self.output_dir, "sip"))
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
for aclass in self.doxyparser.classes:
|
||||
if aclass.name in excluded_classes:
|
||||
print "Skipping %s" % aclass.name
|
||||
continue
|
||||
|
||||
header_name = aclass.name[2:].lower()
|
||||
filename = os.path.join(output_dir, header_name + ".sip")
|
||||
enums_text = make_enums(aclass)
|
||||
method_text = self.make_sip_methods(aclass)
|
||||
base_class = get_first_value(aclass.bases)
|
||||
if base_class != "":
|
||||
base_class = ": %s" % base_class
|
||||
|
||||
text = """
|
||||
%s
|
||||
class %s %s
|
||||
{
|
||||
%%TypeHeaderCode
|
||||
#include <%s>
|
||||
%%End
|
||||
|
||||
public:
|
||||
%s
|
||||
};
|
||||
""" % (enums_text, aclass.name, base_class, get_first_value(aclass.includes), method_text)
|
||||
|
||||
afile = open(filename, "wb")
|
||||
afile.write(text)
|
||||
afile.close()
|
||||
|
||||
|
||||
def make_sip_methods(self, aclass):
|
||||
retval = ""
|
||||
|
||||
for amethod in aclass.constructors + aclass.methods:
|
||||
transfer = ""
|
||||
|
||||
# we need to come up with a way of filtering the methods out by various criteria
|
||||
# including parameters and method name, and how to deal with overloads
|
||||
if aclass.name in ignored_methods:
|
||||
should_ignore = False
|
||||
for method in ignored_methods[aclass.name]:
|
||||
print "method = %s" % method
|
||||
if method == amethod.name:
|
||||
params = ignored_methods[aclass.name][method]
|
||||
should_ignore = True
|
||||
for i in xrange(len(params)):
|
||||
if i >= len(amethod.params):
|
||||
should_ignore = False
|
||||
break
|
||||
elif amethod.params[i]["type"] != params[i]:
|
||||
print "param type = %s, amethod.param type = %s" % (params[i], amethod.params[i]["type"])
|
||||
should_ignore = False
|
||||
break
|
||||
|
||||
if should_ignore:
|
||||
print "Ignoring method %s..." % amethod.name
|
||||
continue
|
||||
|
||||
if amethod in aclass.constructors and self.doxyparser.is_derived_from_base(aclass, "wxWindow"):
|
||||
transfer = "/Transfer/"
|
||||
|
||||
if amethod.name.startswith("operator"):
|
||||
continue
|
||||
|
||||
retval += " %s %s%s%s;\n\n" % (amethod.return_type.replace("virtual ", ""), amethod.name, amethod.argsstring, transfer)
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
|
||||
class SWIGBuilder:
|
||||
def __init__(self, doxyparse, outputdir):
|
||||
self.doxyparser = doxyparse
|
||||
self.output_dir = outputdir
|
||||
|
||||
def make_bindings(self):
|
||||
output_dir = os.path.abspath(os.path.join(self.output_dir, "swig"))
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
for aclass in self.doxyparser.classes:
|
||||
header_name = aclass.name[2:].lower()
|
||||
if aclass.name in excluded_classes:
|
||||
#print "Skipping %s" % aclass.name
|
||||
continue
|
||||
|
||||
filename = os.path.join(output_dir, header_name + ".i")
|
||||
enums_text = make_enums(aclass)
|
||||
method_text = self.make_swig_methods(aclass)
|
||||
text = """
|
||||
%%newgroup
|
||||
|
||||
%s
|
||||
class %s : publib %s
|
||||
{
|
||||
|
||||
public:
|
||||
%s
|
||||
};
|
||||
""" % (enums_text, aclass.name, get_first_value(aclass.bases), method_text)
|
||||
|
||||
afile = open(filename, "wb")
|
||||
afile.write(text)
|
||||
afile.close()
|
||||
|
||||
|
||||
def make_swig_methods(self, aclass):
|
||||
retval = ""
|
||||
|
||||
retval += """
|
||||
%%pythonAppend %s "self._setOORInfo(self)"
|
||||
%%pythonAppend %s() ""
|
||||
%%typemap(out) %s*; // turn off this typemap
|
||||
""" % (aclass.name, aclass.name, aclass.name)
|
||||
|
||||
for amethod in aclass.constructors:
|
||||
retval += " %s%s;\n\n" % (amethod.name, amethod.argsstring)
|
||||
|
||||
retval += """
|
||||
// Turn it back on again
|
||||
%%typemap(out) %s* { $result = wxPyMake_wxObject($1, $owner); }
|
||||
""" % aclass.name
|
||||
|
||||
for amethod in aclass.methods:
|
||||
retval += " %s %s%s;\n\n" % (amethod.return_type, amethod.name, amethod.argsstring)
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(arguments) < 1:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
doxyparse = doxymlparser.DoxyMLParser()
|
||||
for arg in arguments:
|
||||
doxyparse.parse(arg)
|
||||
|
||||
if options.sip:
|
||||
builder = SIPBuilder(doxyparse, options.output_dir)
|
||||
builder.make_bindings()
|
||||
|
||||
if options.swig:
|
||||
builder = SWIGBuilder(doxyparse, options.output_dir)
|
||||
builder.make_bindings()
|
Reference in New Issue
Block a user