-Changes to header files to support parsing
-Added header parser and docstring generator
This commit is contained in:
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Syntax: generate_docstrings.py <path_to_c++_header_files> <path_to_python_files>
|
||||
#
|
||||
# Extract documentation from C++ header files to use it in libiglPython bindings
|
||||
#
|
||||
|
||||
import os, sys, glob
|
||||
from joblib import Parallel, delayed
|
||||
from multiprocessing import cpu_count
|
||||
from mako.template import Template
|
||||
from parser import parse
|
||||
|
||||
|
||||
# http://stackoverflow.com/questions/3207219/how-to-list-all-files-of-a-directory-in-python
|
||||
def get_filepaths(directory):
|
||||
"""
|
||||
This function will generate the file names in a directory
|
||||
tree by walking the tree either top-down or bottom-up. For each
|
||||
directory in the tree rooted at directory top (including top itself),
|
||||
it yields a 3-tuple (dirpath, dirnames, filenames).
|
||||
"""
|
||||
file_paths = [] # List which will store all of the full filepaths.
|
||||
|
||||
# Walk the tree.
|
||||
for root, directories, files in os.walk(directory):
|
||||
for filename in files:
|
||||
# Join the two strings in order to form the full filepath.
|
||||
filepath = os.path.join(root, filename)
|
||||
file_paths.append(filepath) # Add it to the list.
|
||||
|
||||
return file_paths # Self-explanatory.
|
||||
|
||||
|
||||
def get_name_from_path(path, basepath, prefix, postfix):
|
||||
f_clean = path[len(basepath):]
|
||||
f_clean = f_clean.replace(basepath, "")
|
||||
f_clean = f_clean.replace(postfix, "")
|
||||
f_clean = f_clean.replace(prefix, "")
|
||||
f_clean = f_clean.replace("/", "_")
|
||||
f_clean = f_clean.replace("\\", "_")
|
||||
f_clean = f_clean.replace(" ", "_")
|
||||
f_clean = f_clean.replace(".", "_")
|
||||
return f_clean
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print('Syntax: %s <path_to_c++_header_files> <path_to_python_files>' % sys.argv[0])
|
||||
exit(-1)
|
||||
|
||||
# List all files in the given folder and subfolders
|
||||
cpp_base_path = sys.argv[1]
|
||||
py_base_path = sys.argv[2]
|
||||
cpp_file_paths = get_filepaths(cpp_base_path)
|
||||
py_file_paths = get_filepaths(py_base_path)
|
||||
|
||||
# Add all the .h filepaths to a dict
|
||||
mapping = {}
|
||||
for f in cpp_file_paths:
|
||||
if f.endswith(".h"):
|
||||
name = get_name_from_path(f, cpp_base_path, "", ".h")
|
||||
mapping[name] = f
|
||||
|
||||
# Add all python binding files to a list
|
||||
implemented_names = []
|
||||
for f in py_file_paths:
|
||||
if f.endswith(".cpp"):
|
||||
name = get_name_from_path(f, py_base_path, "py_", ".cpp")
|
||||
implemented_names.append(name)
|
||||
|
||||
# Create a list of cpp header files for which a python binding file exists
|
||||
files_to_parse = []
|
||||
for n in implemented_names:
|
||||
if n not in mapping:
|
||||
print("No cpp header file for python function %s found." % n)
|
||||
continue
|
||||
files_to_parse.append(mapping[n])
|
||||
# print(mapping[n])
|
||||
|
||||
# Parse c++ header files
|
||||
job_count = cpu_count()
|
||||
dicts = Parallel(n_jobs=job_count)(delayed(parse)(path) for path in files_to_parse)
|
||||
|
||||
hpplines = []
|
||||
cpplines = []
|
||||
|
||||
for idx, n in enumerate(implemented_names):
|
||||
dict = dicts[idx]
|
||||
contained_elements = sum(map(lambda x: len(x), dict.values()))
|
||||
# Check for files that don't contain functions/enums/classes
|
||||
if contained_elements == 0:
|
||||
print("Function %s contains no parseable content in cpp header. Something might be wrong." % n)
|
||||
continue
|
||||
else:
|
||||
names = []
|
||||
namespaces = "_".join(dict["namespaces"]) # Assumption that all entities lie in deepest namespace
|
||||
for f in dict["functions"]:
|
||||
h_string = "extern const char *__doc_" + namespaces + "_" + f.name + ";\n"
|
||||
docu_string = "See " + f.name + " for the documentation."
|
||||
if f.documentation != "":
|
||||
docu_string = f.documentation
|
||||
cpp_string = "const char *__doc_" + namespaces + "_" + f.name + " = R\"igl_Qu8mg5v7(" + docu_string + ")igl_Qu8mg5v7\";\n"
|
||||
|
||||
if f.name not in names: # Prevent multiple additions of declarations, TODO: Possible fix is to merge comments and add them to all functions
|
||||
hpplines.append(h_string)
|
||||
cpplines.append(cpp_string)
|
||||
names.append(f.name)
|
||||
|
||||
# Change directory to become independent of execution directory
|
||||
path = os.path.dirname(__file__)
|
||||
if path != "":
|
||||
os.chdir(path)
|
||||
|
||||
# Update the two files py_doc.h and py_doc.cpp
|
||||
with open('../py_doc.h', 'w') as fh:
|
||||
fh.writelines(hpplines)
|
||||
with open('../py_doc.cpp', 'w') as fc:
|
||||
fc.writelines(cpplines)
|
||||
|
||||
# Write python_shared_cpp file
|
||||
tpl = Template(filename='python_shared.mako')
|
||||
rendered = tpl.render(functions=implemented_names)
|
||||
with open("../python_shared.cpp", 'w') as fs:
|
||||
fs.write(rendered)
|
||||
Reference in New Issue
Block a user