4 from __future__
import print_function
7 from xml_docstring
import XmlDocString
10 template_file_header =
"""#ifndef DOXYGEN_AUTODOC_{header_guard}
11 #define DOXYGEN_AUTODOC_{header_guard}
13 #include "{path}/doxygen.hh"
15 template_file_footer =
"""
16 #endif // DOXYGEN_AUTODOC_{header_guard}
19 template_class_doc =
"""
21 struct class_doc_impl< {classname} >
23 static inline const char* run ()
27 static inline const char* attribute (const char* attrib)
29 (void)attrib; // turn off unused parameter warning.
33 template_class_attribute_body =
"""
34 if (strcmp(attrib, "{attribute}") == 0)
35 return "{docstring}";"""
36 template_constructor_doc =
"""
38 struct constructor_{nargs}_impl< {classname_prefix}{comma}{argsstring} >
40 static inline const char* doc ()
44 static inline boost::python::detail::keywords<{nargs}+1> args ()
46 return ({argnamesstring});
49 template_destructor_doc =
"""
51 struct destructor_doc_impl < {classname_prefix} >
53 static inline const char* run ()
58 template_member_func_doc =
"""
59 {template}inline const char* member_func_doc ({rettype} ({classname_prefix}*function_ptr) {argsstring})
63 template_member_func_doc_body =
"""
64 if (function_ptr == static_cast<{rettype} ({classname_prefix}*) {argsstring}>(&{classname_prefix}{membername}))
65 return "{docstring}";"""
66 template_member_func_args =
"""
67 {template}inline boost::python::detail::keywords<{n}> member_func_args ({rettype} ({classname_prefix}*function_ptr) {argsstring})
69 return ({default_args});
71 template_member_func_args_body =
"""
72 if (function_ptr == static_cast<{rettype} ({classname_prefix}*) {argsstring}>(&{classname_prefix}{membername}))
74 template_static_func_doc =
"""
75 {template}inline const char* member_func_doc ({rettype} (*function_ptr) {argsstring})
79 template_static_func_doc_body =
"""
80 if (function_ptr == static_cast<{rettype} (*) {argsstring}>(&{namespace}::{membername}))
81 return "{docstring}";"""
82 template_open_namespace =
"""namespace {namespace} {{"""
83 template_close_namespace =
"""}} // namespace {namespace}"""
84 template_include_intern =
"""#include <doxygen_autodoc/{filename}>
86 template_include_extern =
"""#include <{filename}>
91 type_ = param.find(
"type")
92 declname = param.find(
"declname")
93 defname = param.find(
"defname")
97 if defname
is None and declname
is None:
101 for c
in type_.iter():
104 if c.text
is not None:
106 if c.tail
is not None:
108 if typetext.startswith(
"typename")
or typetext.startswith(
"class"):
109 if sys.version_info.major == 2:
111 return {
"type": s[0].strip(),
"name": typetext[len(s[0]) :].strip()}
113 s = typetext.split(maxsplit=1)
115 return {
"type": s[0].strip(),
"name": s[1].strip()}
117 return {
"type": type_.text,
"name":
""}
119 assert defname.text == declname.text
120 return {
"type": type_.text,
"name": defname.text}
124 return filename.upper().replace(
".",
"_").replace(
"/",
"_").replace(
"-",
"_")
128 b = [el.text.strip()
for el
in brief.iter()
if el.text]
if brief
is not None else []
130 [el.text.strip()
for el
in detailed.iter()
if el.text]
131 if detailed
is not None
136 text +=
"\n" +
"".join(d)
146 def xmlToType(self, node, array=None, parentClass=None, tplargs=None):
149 - parentClass: a class
150 - tplargs: if one of the args is parentClass and no template arguments are provided,
151 set the template arguments to this value
152 - array: content of the sibling tag 'array'
154 if node.text
is not None:
155 t = node.text.strip()
158 for c
in node.iterchildren():
160 refid = c.attrib[
"refid"]
161 if parentClass
is not None and refid == parentClass.id:
162 t +=
" " + parentClass.name
163 if c.tail
is not None and c.tail.lstrip()[0] !=
"<":
164 if tplargs
is not None:
167 parentClass
is not None
168 and isinstance(parentClass, ClassCompound)
169 and parentClass.hasTypeDef(c.text.strip())
171 parent_has_templates = len(parentClass.template_params) > 0
172 if parent_has_templates:
173 t +=
" typename " + parentClass._className() +
"::"
175 t +=
" " + parentClass._className() +
"::"
176 self_has_templates = (
177 c.tail
is not None and c.tail.strip().find(
"<") != -1
179 if self_has_templates:
182 elif self.
index.hasref(refid):
183 t +=
" " + self.
index.getref(refid).name
185 self.
index.output.warn(
"Unknown reference: ", c.text, refid)
186 t +=
" " + c.text.strip()
188 if c.text
is not None:
189 t +=
" " + c.text.strip()
190 if c.tail
is not None:
191 t +=
" " + c.tail.strip()
192 if array
is not None:
202 id=memberdefxml.attrib[
"id"],
203 name=memberdefxml.find(
"definition").text,
208 self.
const = memberdefxml.attrib[
"const"] ==
"yes"
209 self.
static = memberdefxml.attrib[
"static"] ==
"yes"
213 (param.find(
"type"), param.find(
"declname"), param.find(
"array"))
214 for param
in self.
xml.findall(
"param")
218 self.
rettype.text
is None and len(self.
rettype.getchildren()) == 0
239 for param
in self.
xml.findall(
"param")
247 return "({0}){1}".format(self.
s_args(),
" const" if self.
const else "")
253 if len(self.
parent.template_params) > 0:
256 +
", ".join([d[
"name"]
for d
in self.
parent.template_params])
262 type, array, parentClass=self.
parent, tplargs=tplargs
264 for type, declname, array
in self.
params
271 for type, declname, array
in self.
params
279 return ", ".join([d[
"type"] +
" " + d[
"name"]
for d
in self.
template_params])
284 ),
"Member {} ({}) is a special function and no return type".format(
287 if len(self.
parent.template_params) > 0:
290 +
", ".join([d[
"name"]
for d
in self.
parent.template_params])
295 if isinstance(self.
parent, ClassCompound):
303 return self.
xml.find(
"name").text.strip()
306 return self.
index.xml_docstring.getDocString(
307 self.
xml.find(
"briefdescription"),
308 self.
xml.find(
"detaileddescription"),
316 def getdeclname(i, declname):
317 if declname
is None or declname.text
is None or declname.text.strip() ==
"":
318 return "arg{}".format(i)
319 return declname.text.strip()
321 arg =
"""boost::python::arg("{}")"""
324 ] + [getdeclname(i, declname)
for i, (_, declname, _)
in enumerate(self.
params)]
325 return ", ".join([arg.format(n)
for n
in argnames])
328 loc = self.
xml.find(
"location")
330 return loc.attrib[
"file"].split(
"/", 1)[1]
336 self.
filename = path.join(index.directory, compound.attrib[
"refid"] +
".xml")
342 name=self.
definition.find(
"compoundname").text,
348 super(NamespaceCompound, self).
__init__(*args)
355 for section
in self.
definition.iterchildren(
"sectiondef"):
356 assert "kind" in section.attrib
357 kind = section.attrib[
"kind"]
360 elif kind ==
"typedef":
366 for member
in section.iterchildren(
"memberdef"):
369 id=member.attrib[
"id"],
370 name=self.
name +
"::" + member.find(
"name").text,
372 self.
index.registerReference(ref)
373 self.
enums.append(member)
374 for value
in member.iterchildren(
"enumvalue"):
377 id=value.attrib[
"id"],
378 name=self.
name +
"::" + member.find(
"name").text,
382 for member
in section.iterchildren(
"memberdef"):
385 id=member.attrib[
"id"],
386 name=self.
name +
"::" + member.find(
"name").text,
388 self.
index.registerReference(ref)
392 for member
in section.iterchildren(
"memberdef"):
402 class ClassCompound(CompoundBase):
404 super(ClassCompound, self).
__init__(*args)
417 for memberdef
in self.
definition.iter(tag=
"memberdef"):
418 if memberdef.attrib[
"prot"] !=
"public":
420 if memberdef.attrib[
"kind"] ==
"variable":
422 elif memberdef.attrib[
"kind"] ==
"typedef":
425 id=memberdef.attrib[
"id"],
426 name=self.
_className() +
"::" + memberdef.find(
"name").text,
428 self.
index.registerReference(ref)
429 self.
typedef[memberdef.find(
"name").text.strip()] =
True
431 elif memberdef.attrib[
"kind"] ==
"enum":
432 if memberdef.find(
"name").text
is None:
433 ref_name = self.
_className() +
"::" +
"anonymous_enum"
435 ref_name = self.
_className() +
"::" + memberdef.find(
"name").text
438 id=memberdef.attrib[
"id"],
441 self.
index.registerReference(ref)
442 for value
in memberdef.iterchildren(
"enumvalue"):
445 id=value.attrib[
"id"],
448 self.
index.registerReference(value_ref)
449 elif memberdef.attrib[
"kind"] ==
"function":
461 if not hasattr(self,
"template_params")
or len(self.
template_params) == 0:
463 return ", ".join([d[
"type"] +
" " + d[
"name"]
for d
in self.
template_params])
466 if not hasattr(self,
"template_params")
or len(self.
template_params) == 0:
476 return typename
in self.
typedef
491 docstring = self.
index.xml_docstring.getDocString(
496 attribute_docstrings =
""
498 _dc = self.
index.xml_docstring.getDocString(
499 member.find(
"briefdescription"),
500 member.find(
"detaileddescription"),
505 attribute_docstrings += template_class_attribute_body.format(
506 attribute=member.find(
"name").text,
509 if len(docstring) == 0
and len(attribute_docstrings) == 0:
512 template_class_doc.format(
516 attributes=attribute_docstrings,
525 "Disable class {} because template argument are not resolved for templated class specialization.".format(
533 output.err(
"Does not know where to write doc of", self.
name)
535 output.open(include.text)
536 output.out(template_include_extern.format(filename=include.text))
537 output.out(template_open_namespace.format(namespace=
"doxygen"))
543 member_funcs = dict()
545 prototype = m.prototypekey()
546 if prototype
in member_funcs:
547 member_funcs[prototype].append(m)
549 member_funcs[prototype] = [
556 docstring = member.s_docstring()
557 argnamesstring = member.s_argnamesstring()
558 if len(docstring) == 0
and len(argnamesstring) == 0:
560 if member.s_name()[0] ==
"~":
562 template_destructor_doc.format(
570 template_constructor_doc.format(
573 d[
"type"] +
" " + d[
"name"]
577 nargs=len(member.params),
578 comma=
", " if len(member.params) > 0
else "",
580 argsstring=member.s_args(),
582 argnamesstring=argnamesstring,
586 for prototype, members
in member_funcs.items():
588 documented_members = []
591 for member
in members:
592 docstring = member.s_docstring()
593 argnamesstring = member.s_argnamesstring()
594 if len(docstring) == 0
and len(argnamesstring) == 0:
596 documented_members.append(member)
597 docstrings.append(docstring)
598 argnamesstrings.append(argnamesstring)
599 if len(documented_members) == 0:
605 template_member_func_doc_body.format(
606 classname_prefix=classname_prefix,
607 membername=member.s_name(),
609 rettype=member.s_rettype(),
610 argsstring=member.s_prototypeArgs(),
612 for member, docstring
in zip(documented_members, docstrings)
619 d[
"type"] +
" " + d[
"name"]
624 template_member_func_doc.format(
625 template=
"template <{}>\n".format(tplargs)
628 rettype=member.s_rettype(),
629 classname_prefix=classname_prefix,
630 argsstring=member.s_prototypeArgs(),
638 template_member_func_args_body.format(
639 classname_prefix=classname_prefix,
640 membername=member.s_name(),
642 rettype=member.s_rettype(),
643 argsstring=member.s_prototypeArgs(),
645 for member, argnamesstring
in zip(
646 documented_members, argnamesstrings
651 n_args = member.n_args()
653 default_args =
", ".join(
655 """boost::python::arg("self")""",
657 + [
"""boost::python::arg("arg{}")""".format(i)
for i
in range(n_args)]
660 template_member_func_args.format(
661 template=
"template <{}>\n".format(tplargs)
664 rettype=member.s_rettype(),
666 default_args=default_args,
667 classname_prefix=classname_prefix,
668 argsstring=member.s_prototypeArgs(),
673 output.out(template_close_namespace.format(namespace=
"doxygen"))
682 This class is responsible for generating the list of all C++-usable documented elements.
694 for compound
in self.
tree.getroot().iterchildren(
"compound"):
695 if compound.attrib[
"kind"]
in [
"class",
"struct"]:
697 elif compound.attrib[
"kind"] ==
"namespace":
706 self.
output.open(
"doxygen_xml_parser_for_cmake.hh")
713 compound.write(self.
output)
715 self.
output.open(
"functions.h")
718 static_funcs = dict()
723 for m
in compound.static_funcs:
724 include = m.include()
725 if include
not in includes:
726 includes.append(include)
727 docstring = m.s_docstring()
728 if len(docstring) == 0:
730 prototype = m.prototypekey()
731 if prototype
in static_funcs:
732 static_funcs[prototype].append((m, docstring))
734 static_funcs[prototype] = [
737 prototypes.append(prototype)
742 template_include_extern.format(filename=filename)
743 for filename
in includes
748 self.
output.out(template_open_namespace.format(namespace=
"doxygen"))
750 for prototype
in prototypes:
751 member_and_docstring_s = static_funcs[prototype]
754 template_static_func_doc_body.format(
755 namespace=member.parent.innerNamespace(),
756 membername=member.s_name(),
758 rettype=member.s_rettype(),
759 argsstring=member.s_prototypeArgs(),
761 for member, docstring
in member_and_docstring_s
765 member = member_and_docstring_s[0][0]
769 d[
"type"] +
" " + d[
"name"]
770 for d
in member.parent.template_params + member.template_params
774 template_static_func_doc.format(
775 template=
"template <{}>\n".format(tplargs)
778 rettype=member.s_rettype(),
779 argsstring=member.s_prototypeArgs(),
784 self.
output.out(template_close_namespace.format(namespace=
"doxygen"))
791 "!!!! Compounds " + obj.id +
" already exists.",
796 self.
output.warn(
"Reference " + obj.id +
" already exists.", obj.name)
809 def __init__(self, output_dir, warn, error, errorPrefix=""):
819 assert self.
_out is None,
"You did not close the previous file"
822 fullname = os.path.join(self.
output_dir, name)
823 dirname = os.path.dirname(fullname)
824 if not os.path.isdir(dirname):
832 if sys.version_info >= (3,):
836 self.
_out = codecs.open(fullname, mode=
"w", encoding=encoding)
841 template_file_header.format(
842 path=os.path.dirname(os.path.abspath(__file__)),
855 template_file_footer.format(
864 if sys.version_info >= (3,):
865 print(*args, file=self.
_out)
867 print(
" ".join(
str(arg)
for arg
in args).decode(
"latin1"), file=self.
_out)
876 if __name__ ==
"__main__":
879 parser = argparse.ArgumentParser(
880 description=
"Process Doxygen XML documentation and generate C++ code."
882 parser.add_argument(
"doxygen_index_xml", type=str, help=
"the Doxygen XML index.")
883 parser.add_argument(
"output_directory", type=str, help=
"the output directory.")
884 args = parser.parse_args()
888 output=
OutputStreams(args.output_directory, sys.stdout, sys.stderr),
890 index.parseCompound()
892 index.output.writeFooterAndCloseFiles()
893 assert index.output._out
is None