I also fixed a couple of bugs in generate_docstrings.py:
1) We were checking for an empty string where a result could also be Nonn.
2) If you forgot to end the argument path with a slash, the output would be incorrect
3) We were coupled to a specific version of clang for no reason
4) Python 3 clang binding support was only added in April 2017 (https://reviews.llvm.org/D31568). Using python3 by default will not work on the clang bindings which ship with pip and most Linux distros. Let's change the hashbang to use python2 for now.
5) The documentation for the script was a bit confusing.
Former-commit-id: 230ab71e20
135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
import sys
|
|
import os
|
|
from threading import Thread
|
|
|
|
import clang.cindex
|
|
import ccsyspath
|
|
import itertools
|
|
from mako.template import Template
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_annotations(node):
|
|
return [c.displayname for c in node.get_children()
|
|
if c.kind == clang.cindex.CursorKind.ANNOTATE_ATTR]
|
|
|
|
|
|
class Function(object):
|
|
def __init__(self, cursor):
|
|
self.name = cursor.spelling
|
|
self.annotations = get_annotations(cursor)
|
|
self.access = cursor.access_specifier
|
|
# template_pars = [c.extent for c in cursor.get_children() if c.kind == clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER]
|
|
parameter_dec = [c for c in cursor.get_children() if c.kind == clang.cindex.CursorKind.PARM_DECL]
|
|
|
|
parameters = []
|
|
for p in parameter_dec:
|
|
children = []
|
|
for c in p.get_children():
|
|
# print(c.spelling)
|
|
children.append(c.spelling)
|
|
parameters.append((p.spelling, p.type.spelling, children))
|
|
|
|
self.parameters = parameters
|
|
self.documentation = cursor.raw_comment
|
|
|
|
|
|
class Enum(object):
|
|
def __init__(self, cursor):
|
|
self.name = cursor.spelling
|
|
self.constants = [c.spelling for c in cursor.get_children() if c.kind ==
|
|
clang.cindex.CursorKind.ENUM_CONSTANT_DECL]
|
|
self.documentation = cursor.raw_comment
|
|
|
|
class Class(object):
|
|
def __init__(self, cursor):
|
|
self.name = cursor.spelling
|
|
# self.functions = []
|
|
self.annotations = get_annotations(cursor)
|
|
|
|
# for c in cursor.get_children():
|
|
# if (c.kind == clang.cindex.CursorKind.CXX_METHOD and
|
|
# c.access_specifier == clang.cindex.AccessSpecifier.PUBLIC):
|
|
# f = Function(c)
|
|
# self.functions.append(f)
|
|
|
|
|
|
|
|
def traverse(c, path, objects):
|
|
if c.location.file and not c.location.file.name.endswith(path):
|
|
return
|
|
|
|
if c.spelling == "PARULA_COLOR_MAP": # Fix to prevent python stack overflow from infinite recursion
|
|
return
|
|
|
|
# print(c.kind, c.spelling)
|
|
|
|
|
|
if c.kind == clang.cindex.CursorKind.TRANSLATION_UNIT or c.kind == clang.cindex.CursorKind.UNEXPOSED_DECL:
|
|
# Ignore other cursor kinds
|
|
pass
|
|
|
|
elif c.kind == clang.cindex.CursorKind.NAMESPACE:
|
|
objects["namespaces"].append(c.spelling)
|
|
# print("Namespace", c.spelling, c.get_children())
|
|
pass
|
|
|
|
elif c.kind == clang.cindex.CursorKind.FUNCTION_TEMPLATE:
|
|
# print("Function Template", c.spelling, c.raw_comment)
|
|
objects["functions"].append(Function(c))
|
|
return
|
|
|
|
elif c.kind == clang.cindex.CursorKind.FUNCTION_DECL:
|
|
# print("FUNCTION_DECL", c.spelling, c.raw_comment)
|
|
objects["functions"].append(Function(c))
|
|
return
|
|
|
|
elif c.kind == clang.cindex.CursorKind.ENUM_DECL:
|
|
# print("ENUM_DECL", c.spelling, c.raw_comment)
|
|
objects["enums"].append(Enum(c))
|
|
return
|
|
|
|
elif c.kind == clang.cindex.CursorKind.CLASS_DECL:
|
|
objects["classes"].append(Class(c))
|
|
return
|
|
|
|
elif c.kind == clang.cindex.CursorKind.CLASS_TEMPLATE:
|
|
objects["classes"].append(Class(c))
|
|
return
|
|
|
|
elif c.kind == clang.cindex.CursorKind.STRUCT_DECL:
|
|
objects["structs"].append(Class(c))
|
|
return
|
|
|
|
else:
|
|
# print("Unknown", c.kind, c.spelling)
|
|
pass
|
|
|
|
for child_node in c.get_children():
|
|
traverse(child_node, path, objects)
|
|
|
|
|
|
def parse(path):
|
|
index = clang.cindex.Index.create()
|
|
# Clang can't parse files with missing definitions, add static library definition or not?
|
|
args = ['-x', 'c++', '-std=c++11', '-fparse-all-comments', '-DIGL_STATIC_LIBRARY']
|
|
args.append('-I/usr/include/eigen3/') # TODO Properly add all needed includes
|
|
syspath = ccsyspath.system_include_paths('clang++') # Add the system libraries
|
|
incargs = [(b'-I' + inc).decode("utf-8") for inc in syspath]
|
|
args.extend(incargs)
|
|
|
|
tu = index.parse(path, args)
|
|
objects = {"functions": [], "enums": [], "namespaces": [], "classes": [], "structs": []}
|
|
traverse(tu.cursor, path, objects)
|
|
return objects
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) != 2:
|
|
print("Usage: python3 parser.py <headerfile_path>")
|
|
exit(-1)
|
|
parse(sys.argv[1])
|