330 lines
11 KiB
Python
330 lines
11 KiB
Python
|
""" This script generates a pandadoc.hpp file representing the Python
|
||
|
wrappers that can be parsed by doxygen to generate the Python documentation.
|
||
|
You need to run this before invoking Doxyfile.python.
|
||
|
|
||
|
It requires a valid makepanda installation with interrogatedb .in
|
||
|
files in the lib/pandac/input directory. """
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
__all__ = []
|
||
|
|
||
|
import os, sys
|
||
|
from distutils import sysconfig
|
||
|
import panda3d, pandac
|
||
|
from panda3d.interrogatedb import *
|
||
|
|
||
|
|
||
|
if 'interrogate_element_is_sequence' not in globals():
|
||
|
def interrogate_element_is_sequence(element):
|
||
|
return False
|
||
|
|
||
|
if 'interrogate_element_is_mapping' not in globals():
|
||
|
def interrogate_element_is_mapping(element):
|
||
|
return False
|
||
|
|
||
|
|
||
|
LICENSE = """PANDA 3D SOFTWARE
|
||
|
Copyright (c) Carnegie Mellon University. All rights reserved.
|
||
|
All use of this software is subject to the terms of the revised BSD
|
||
|
license. You should have received a copy of this license along
|
||
|
with this source code in a file named \"LICENSE.\"""".split("\n")
|
||
|
|
||
|
MAINPAGE = """@mainpage Panda3D Python API Reference
|
||
|
Welcome to the Panda3D API reference.
|
||
|
|
||
|
Use the links at the top of this page to browse through the list of modules or
|
||
|
the list of classes.
|
||
|
|
||
|
This reference is automatically generated from comments in the source code.
|
||
|
"""
|
||
|
|
||
|
|
||
|
def comment(code):
|
||
|
if not code:
|
||
|
return ""
|
||
|
|
||
|
comment = ''
|
||
|
|
||
|
empty_line = False
|
||
|
for line in code.splitlines(False):
|
||
|
line = line.strip('\t\n /')
|
||
|
if line:
|
||
|
if empty_line:
|
||
|
# New paragraph.
|
||
|
comment += '\n\n'
|
||
|
empty_line = False
|
||
|
elif comment:
|
||
|
comment += '\n'
|
||
|
comment += '/// ' + line
|
||
|
else:
|
||
|
empty_line = True
|
||
|
|
||
|
if comment:
|
||
|
return comment
|
||
|
else:
|
||
|
return ''
|
||
|
|
||
|
def block_comment(code):
|
||
|
code = code.strip()
|
||
|
|
||
|
if not code.startswith('///<') and '@verbatim' not in code:
|
||
|
code = code.replace('<', '\\<').replace('>', '\\>')
|
||
|
|
||
|
if not code or code[0] != '/':
|
||
|
# Not really a comment; get rid of it.
|
||
|
return ""
|
||
|
|
||
|
return code
|
||
|
|
||
|
def translateFunctionName(name):
|
||
|
if name.startswith("__"):
|
||
|
return name
|
||
|
|
||
|
new = ""
|
||
|
for i in name.split("_"):
|
||
|
if new == "":
|
||
|
new += i
|
||
|
elif i == "":
|
||
|
pass
|
||
|
elif len(i) == 1:
|
||
|
new += i[0].upper()
|
||
|
else:
|
||
|
new += i[0].upper() + i[1:]
|
||
|
return new
|
||
|
|
||
|
def translateTypeName(name, mangle=True):
|
||
|
# Equivalent to C++ classNameFromCppName
|
||
|
class_name = ""
|
||
|
bad_chars = "!@#$%^&*()<>,.-=+~{}? "
|
||
|
next_cap = False
|
||
|
first_char = mangle
|
||
|
|
||
|
for chr in name:
|
||
|
if (chr == '_' or chr == ' ') and mangle:
|
||
|
next_cap = True
|
||
|
elif chr in bad_chars:
|
||
|
if not mangle:
|
||
|
class_name += '_'
|
||
|
elif next_cap or first_char:
|
||
|
class_name += chr.upper()
|
||
|
next_cap = False
|
||
|
first_char = False
|
||
|
else:
|
||
|
class_name += chr
|
||
|
|
||
|
return class_name
|
||
|
|
||
|
def translated_type_name(type, scoped=True):
|
||
|
while interrogate_type_is_wrapped(type):
|
||
|
if interrogate_type_is_const(type):
|
||
|
return 'const ' + translated_type_name(interrogate_type_wrapped_type(type))
|
||
|
else:
|
||
|
type = interrogate_type_wrapped_type(type)
|
||
|
|
||
|
typename = interrogate_type_name(type)
|
||
|
if typename in ("PyObject", "_object"):
|
||
|
return "object"
|
||
|
elif typename == "PN_stdfloat":
|
||
|
return "float"
|
||
|
elif typename == "size_t":
|
||
|
return "int"
|
||
|
|
||
|
if interrogate_type_is_atomic(type):
|
||
|
token = interrogate_type_atomic_token(type)
|
||
|
if token == 7:
|
||
|
return 'str'
|
||
|
elif token == 8:
|
||
|
return 'long'
|
||
|
elif token == 9:
|
||
|
return 'NoneType'
|
||
|
else:
|
||
|
return typename
|
||
|
|
||
|
if not typename.endswith('_t'):
|
||
|
# Hack: don't mangle size_t etc.
|
||
|
typename = translateTypeName(typename)
|
||
|
|
||
|
if scoped and interrogate_type_is_nested(type):
|
||
|
return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename
|
||
|
else:
|
||
|
return typename
|
||
|
|
||
|
|
||
|
def processElement(handle, element):
|
||
|
if interrogate_element_has_comment(element):
|
||
|
print(comment(interrogate_element_comment(element)), file=handle)
|
||
|
elif interrogate_element_has_getter(element):
|
||
|
# If the property has no comment, use the comment of the getter.
|
||
|
getter = interrogate_element_getter(element)
|
||
|
if interrogate_function_has_comment(getter):
|
||
|
print(block_comment(interrogate_function_comment(getter)), file=handle)
|
||
|
|
||
|
if interrogate_element_is_mapping(element) or \
|
||
|
interrogate_element_is_sequence(element):
|
||
|
suffix = "[]"
|
||
|
else:
|
||
|
suffix = ""
|
||
|
|
||
|
print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle)
|
||
|
print(interrogate_element_name(element) + suffix + ';', file=handle)
|
||
|
|
||
|
|
||
|
def processFunction(handle, function, isConstructor = False):
|
||
|
for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)):
|
||
|
wrapper = interrogate_function_python_wrapper(function, i_wrapper)
|
||
|
if interrogate_wrapper_has_comment(wrapper):
|
||
|
print(block_comment(interrogate_wrapper_comment(wrapper)), file=handle)
|
||
|
|
||
|
if not isConstructor:
|
||
|
if interrogate_function_is_method(function):
|
||
|
if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
|
||
|
print("static", end=' ', file=handle)
|
||
|
|
||
|
if interrogate_wrapper_has_return_value(wrapper):
|
||
|
print(translated_type_name(interrogate_wrapper_return_type(wrapper)), end=' ', file=handle)
|
||
|
else:
|
||
|
pass#print >>handle, "void",
|
||
|
|
||
|
print(translateFunctionName(interrogate_function_name(function)) + "(", end=' ', file=handle)
|
||
|
else:
|
||
|
print("__init__(", end=' ', file=handle)
|
||
|
|
||
|
first = True
|
||
|
for i_param in range(interrogate_wrapper_number_of_parameters(wrapper)):
|
||
|
if not interrogate_wrapper_parameter_is_this(wrapper, i_param):
|
||
|
if not first:
|
||
|
print(",", end=' ', file=handle)
|
||
|
print(translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)), end=' ', file=handle)
|
||
|
if interrogate_wrapper_parameter_has_name(wrapper, i_param):
|
||
|
print(interrogate_wrapper_parameter_name(wrapper, i_param), end=' ', file=handle)
|
||
|
first = False
|
||
|
|
||
|
print(");", file=handle)
|
||
|
|
||
|
|
||
|
def processType(handle, type):
|
||
|
typename = translated_type_name(type, scoped=False)
|
||
|
derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
|
||
|
|
||
|
if interrogate_type_has_comment(type):
|
||
|
print(block_comment(interrogate_type_comment(type)), file=handle)
|
||
|
|
||
|
if interrogate_type_is_enum(type):
|
||
|
print("enum %s {" % typename, file=handle)
|
||
|
for i_value in range(interrogate_type_number_of_enum_values(type)):
|
||
|
docstring = comment(interrogate_type_enum_value_comment(type, i_value))
|
||
|
if docstring:
|
||
|
print(docstring, file=handle)
|
||
|
print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle)
|
||
|
|
||
|
elif interrogate_type_is_typedef(type):
|
||
|
wrapped_type = interrogate_type_wrapped_type(type)
|
||
|
if interrogate_type_is_global(wrapped_type):
|
||
|
wrapped_type_name = translated_type_name(wrapped_type)
|
||
|
print("typedef %s %s;" % (wrapped_type_name, typename), file=handle)
|
||
|
return
|
||
|
else:
|
||
|
if interrogate_type_is_struct(type):
|
||
|
classtype = "struct"
|
||
|
elif interrogate_type_is_class(type):
|
||
|
classtype = "class"
|
||
|
elif interrogate_type_is_union(type):
|
||
|
classtype = "union"
|
||
|
else:
|
||
|
print("I don't know what type %s is" % interrogate_type_true_name(type))
|
||
|
return
|
||
|
|
||
|
if len(derivations) > 0:
|
||
|
print("%s %s : public %s {" % (classtype, typename, ", public ".join(derivations)), file=handle)
|
||
|
else:
|
||
|
print("%s %s {" % (classtype, typename), file=handle)
|
||
|
print("public:", file=handle)
|
||
|
|
||
|
for i_ntype in range(interrogate_type_number_of_nested_types(type)):
|
||
|
processType(handle, interrogate_type_get_nested_type(type, i_ntype))
|
||
|
|
||
|
for i_method in range(interrogate_type_number_of_constructors(type)):
|
||
|
processFunction(handle, interrogate_type_get_constructor(type, i_method), True)
|
||
|
|
||
|
for i_method in range(interrogate_type_number_of_methods(type)):
|
||
|
processFunction(handle, interrogate_type_get_method(type, i_method))
|
||
|
|
||
|
for i_method in range(interrogate_type_number_of_make_seqs(type)):
|
||
|
print("list", translateFunctionName(interrogate_make_seq_seq_name(interrogate_type_get_make_seq(type, i_method))), "();", file=handle)
|
||
|
|
||
|
for i_element in range(interrogate_type_number_of_elements(type)):
|
||
|
processElement(handle, interrogate_type_get_element(type, i_element))
|
||
|
|
||
|
print("};", file=handle)
|
||
|
|
||
|
def processModule(handle, package):
|
||
|
print("Processing module %s" % (package))
|
||
|
print("namespace %s {" % package, file=handle)
|
||
|
|
||
|
if package != "core":
|
||
|
print("using namespace core;", file=handle)
|
||
|
|
||
|
for i_type in range(interrogate_number_of_global_types()):
|
||
|
type = interrogate_get_global_type(i_type)
|
||
|
|
||
|
if interrogate_type_has_module_name(type):
|
||
|
module_name = interrogate_type_module_name(type)
|
||
|
if "panda3d." + package == module_name:
|
||
|
processType(handle, type)
|
||
|
else:
|
||
|
print("Type %s has no module name" % typename)
|
||
|
|
||
|
for i_func in range(interrogate_number_of_global_functions()):
|
||
|
func = interrogate_get_global_function(i_func)
|
||
|
|
||
|
if interrogate_function_has_module_name(func):
|
||
|
module_name = interrogate_function_module_name(func)
|
||
|
if "panda3d." + package == module_name:
|
||
|
processFunction(handle, func)
|
||
|
else:
|
||
|
print("Type %s has no module name" % typename)
|
||
|
|
||
|
print("}", file=handle)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
handle = open("pandadoc.hpp", "w")
|
||
|
|
||
|
mainpage = MAINPAGE.strip()
|
||
|
if mainpage:
|
||
|
print("/**\n * " + mainpage.replace('\n', '\n * ') + '\n */', file=handle)
|
||
|
|
||
|
print(comment("Panda3D modules that are implemented in C++."), file=handle)
|
||
|
print("namespace panda3d {", file=handle)
|
||
|
|
||
|
# Determine the path to the interrogatedb files
|
||
|
pandac = os.path.dirname(pandac.__file__)
|
||
|
interrogate_add_search_directory(os.path.join(pandac, "..", "..", "etc"))
|
||
|
interrogate_add_search_directory(os.path.join(pandac, "input"))
|
||
|
|
||
|
import panda3d.core
|
||
|
processModule(handle, "core")
|
||
|
|
||
|
# Determine the suffix for the extension modules.
|
||
|
if sys.version_info >= (3, 0):
|
||
|
import _imp
|
||
|
ext_suffix = _imp.extension_suffixes()[0]
|
||
|
elif sys.platform == "win32":
|
||
|
ext_suffix = ".pyd"
|
||
|
else:
|
||
|
ext_suffix = ".so"
|
||
|
|
||
|
for lib in os.listdir(os.path.dirname(panda3d.__file__)):
|
||
|
if lib.endswith(ext_suffix) and not lib.startswith('core.'):
|
||
|
module_name = lib[:-len(ext_suffix)]
|
||
|
__import__("panda3d." + module_name)
|
||
|
processModule(handle, module_name)
|
||
|
|
||
|
print("}", file=handle)
|
||
|
handle.close()
|
||
|
|
||
|
print("Wrote output to pandadoc.hpp. You can now run:")
|
||
|
print()
|
||
|
print(" doxygen built/direct/directscripts/Doxyfile.python")
|